@harmoniclabs/pebble 0.1.9 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/IR/IRNodes/IRConst.js +14 -3
- package/dist/IR/IRNodes/IRNative/index.js +5 -1
- package/dist/IR/toUPLC/CompilerOptions.d.ts +22 -7
- package/dist/IR/toUPLC/CompilerOptions.js +5 -1
- package/dist/IR/tree_utils/bytesToHex.d.ts +8 -0
- package/dist/IR/tree_utils/bytesToHex.js +69 -0
- package/dist/ast/nodes/expr/functions/FuncExpr.d.ts +16 -2
- package/dist/ast/nodes/expr/functions/FuncExpr.js +17 -0
- package/dist/ast/nodes/expr/litteral/LitteralExpr.d.ts +2 -1
- package/dist/ast/nodes/expr/litteral/LitteralExpr.js +2 -0
- package/dist/ast/nodes/expr/litteral/TemplateStrExpr.d.ts +30 -0
- package/dist/ast/nodes/expr/litteral/TemplateStrExpr.js +35 -0
- package/dist/ast/nodes/statements/ExportStmt.d.ts +3 -3
- package/dist/ast/nodes/statements/PebbleStmt.d.ts +4 -3
- package/dist/ast/nodes/statements/PebbleStmt.js +6 -2
- package/dist/ast/nodes/statements/TestParam.d.ts +18 -0
- package/dist/ast/nodes/statements/TestParam.js +18 -0
- package/dist/ast/nodes/statements/TestStmt.d.ts +5 -3
- package/dist/ast/nodes/statements/TestStmt.js +3 -1
- package/dist/ast/nodes/statements/UsingStmt.d.ts +32 -2
- package/dist/ast/nodes/statements/UsingStmt.js +39 -3
- package/dist/ast/nodes/statements/declarations/NamespaceDecl.d.ts +21 -0
- package/dist/ast/nodes/statements/declarations/NamespaceDecl.js +31 -0
- package/dist/compiler/AstCompiler/AstCompiler.d.ts +26 -0
- package/dist/compiler/AstCompiler/AstCompiler.js +203 -3
- package/dist/compiler/AstCompiler/internal/_deriveContractBody/_deriveContractBody.js +14 -4
- package/dist/compiler/AstCompiler/internal/exprs/_compileCallExpr.js +97 -6
- package/dist/compiler/AstCompiler/internal/exprs/_compileFuncExpr.js +12 -5
- package/dist/compiler/AstCompiler/internal/exprs/_compileLitteralExpr.js +59 -0
- package/dist/compiler/AstCompiler/internal/exprs/_compilePropAccessExpr.d.ts +2 -3
- package/dist/compiler/AstCompiler/internal/exprs/_compilePropAccessExpr.js +28 -0
- package/dist/compiler/AstCompiler/internal/exprs/_compileVarAccessExpr.js +2 -0
- package/dist/compiler/AstCompiler/internal/statements/_compileStatement.js +4 -1
- package/dist/compiler/AstCompiler/internal/statements/_compileTestStmt.d.ts +15 -1
- package/dist/compiler/AstCompiler/internal/statements/_compileTestStmt.js +70 -30
- package/dist/compiler/AstCompiler/internal/statements/_compileUsingAliasStmt.d.ts +11 -0
- package/dist/compiler/AstCompiler/internal/statements/_compileUsingAliasStmt.js +26 -0
- package/dist/compiler/AstCompiler/internal/statements/_compileUsingStmt.d.ts +9 -4
- package/dist/compiler/AstCompiler/internal/statements/_compileUsingStmt.js +51 -10
- package/dist/compiler/AstCompiler/internal/types/_compileDataEncodedConcreteType.js +21 -2
- package/dist/compiler/AstCompiler/internal/types/_compileSopEncodedConcreteType.js +17 -2
- package/dist/compiler/AstCompiler/scope/AstScope.d.ts +70 -1
- package/dist/compiler/AstCompiler/scope/AstScope.js +91 -0
- package/dist/compiler/AstCompiler/utils/getPropAccessReturnType.js +25 -1
- package/dist/compiler/AstCompiler/utils/monomorphizeGeneric.d.ts +36 -0
- package/dist/compiler/AstCompiler/utils/monomorphizeGeneric.js +123 -0
- package/dist/compiler/AstCompiler/utils/resolveNamespaceChain.d.ts +28 -0
- package/dist/compiler/AstCompiler/utils/resolveNamespaceChain.js +95 -0
- package/dist/compiler/AstCompiler/utils/resolveNamespacePath.d.ts +37 -0
- package/dist/compiler/AstCompiler/utils/resolveNamespacePath.js +93 -0
- package/dist/compiler/Compiler.d.ts +9 -1
- package/dist/compiler/Compiler.js +204 -9
- package/dist/compiler/TirCompiler/expressify/expressifyVars.js +26 -7
- package/dist/compiler/test/TestResult.d.ts +38 -0
- package/dist/compiler/test/TestResult.js +6 -0
- package/dist/compiler/test/fuzz/PRNG.d.ts +26 -0
- package/dist/compiler/test/fuzz/PRNG.js +59 -0
- package/dist/compiler/tir/expressions/TirExpr.d.ts +2 -1
- package/dist/compiler/tir/expressions/TirExpr.js +2 -0
- package/dist/compiler/tir/expressions/TirNativeFunc.d.ts +17 -0
- package/dist/compiler/tir/expressions/TirNativeFunc.js +53 -106
- package/dist/compiler/tir/expressions/TirShowExpr.d.ts +52 -0
- package/dist/compiler/tir/expressions/TirShowExpr.js +199 -0
- package/dist/compiler/tir/expressions/TirTraceExpr.js +11 -7
- package/dist/compiler/tir/program/TypedProgram.d.ts +101 -0
- package/dist/compiler/tir/program/TypedProgram.js +43 -0
- package/dist/compiler/tir/program/stdScope/populateBuiltinInterfaces.d.ts +17 -0
- package/dist/compiler/tir/program/stdScope/populateBuiltinInterfaces.js +70 -0
- package/dist/compiler/tir/program/stdScope/populateStdNamespace.d.ts +22 -0
- package/dist/compiler/tir/program/stdScope/populateStdNamespace.js +574 -0
- package/dist/compiler/tir/program/stdScope/stdScope.d.ts +1 -0
- package/dist/compiler/tir/program/stdScope/stdScope.js +1 -1
- package/dist/compiler/tir/statements/TirStmt.js +0 -1
- package/dist/compiler/tir/statements/TirTestStmt.d.ts +46 -0
- package/dist/compiler/tir/statements/TirTestStmt.js +35 -0
- package/dist/compiler/tir/types/TirNativeType/TirNativeType.d.ts +50 -1
- package/dist/compiler/tir/types/TirNativeType/TirNativeType.js +53 -1
- package/dist/compiler/tir/types/TirType.js +3 -1
- package/dist/compiler/tir/types/utils/canAssignTo.js +8 -1
- package/dist/compiler/tir/types/utils/inferTypeArgs.d.ts +19 -0
- package/dist/compiler/tir/types/utils/inferTypeArgs.js +79 -0
- package/dist/compiler/tir/types/utils/substituteTypeParams.d.ts +9 -0
- package/dist/compiler/tir/types/utils/substituteTypeParams.js +62 -0
- package/dist/diagnostics/diagnosticMessages.generated.d.ts +5 -0
- package/dist/diagnostics/diagnosticMessages.generated.js +10 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/parser/Parser.d.ts +73 -3
- package/dist/parser/Parser.js +333 -33
- package/dist/tokenizer/Token.d.ts +105 -102
- package/dist/tokenizer/Token.js +110 -109
- package/dist/tokenizer/utils/tokenFromKeyword.js +9 -6
- package/dist/utils/semverSatisfies.d.ts +1 -0
- package/dist/utils/semverSatisfies.js +161 -0
- package/dist/version.generated.d.ts +1 -0
- package/dist/version.generated.js +2 -0
- package/package.json +4 -2
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { compileUPLC, Force, parseUPLC, UPLCProgram } from "@harmoniclabs/uplc";
|
|
2
|
-
import { Machine } from "@harmoniclabs/plutus-machine";
|
|
1
|
+
import { Application, compileUPLC, Force, parseUPLC, UPLCConst, UPLCProgram } from "@harmoniclabs/uplc";
|
|
2
|
+
import { CEKError, Machine } from "@harmoniclabs/plutus-machine";
|
|
3
|
+
import { DiagnosticCategory } from "../diagnostics/DiagnosticCategory.js";
|
|
3
4
|
import { DiagnosticEmitter } from "../diagnostics/DiagnosticEmitter.js";
|
|
4
|
-
import {
|
|
5
|
+
import { COMPILER_VERSION } from "../version.generated.js";
|
|
6
|
+
import { semverSatisfies } from "../utils/semverSatisfies.js";
|
|
5
7
|
import { AstCompiler } from "./AstCompiler/AstCompiler.js";
|
|
6
8
|
import { createMemoryCompilerIoApi } from "./io/CompilerIoApi.js";
|
|
7
9
|
import { compileTypedProgram } from "./TirCompiler/compileTirProgram.js";
|
|
@@ -12,14 +14,27 @@ import { __unsafe_clear_letted_hash_to_symbol } from "../IR/IRNodes/IRLetted.js"
|
|
|
12
14
|
import { __unsafe_clear_hoisted_cache } from "../IR/toUPLC/subRoutines/replaceHoistedWithLetted.js";
|
|
13
15
|
import { __unsafe_clear_mapToType_cache } from "./TirCompiler/expressify/expressifyVars.js";
|
|
14
16
|
import { compileIRToUPLC } from "../IR/toUPLC/compileIRToUPLC.js";
|
|
17
|
+
import { TirFuncExpr } from "./tir/expressions/TirFuncExpr.js";
|
|
18
|
+
import { addBudget, zeroBudget, } from "./test/TestResult.js";
|
|
19
|
+
import { PRNG } from "./test/fuzz/PRNG.js";
|
|
15
20
|
export { SourceTypeMap } from "./SourceTypeMap.js";
|
|
16
21
|
export class Compiler extends DiagnosticEmitter {
|
|
17
22
|
io;
|
|
18
23
|
cfg;
|
|
19
|
-
constructor(io = createMemoryCompilerIoApi({ useConsoleAsOutput: true }), cfg
|
|
24
|
+
constructor(io = createMemoryCompilerIoApi({ useConsoleAsOutput: true }), cfg, diagnostics) {
|
|
20
25
|
super(diagnostics);
|
|
21
26
|
this.io = io;
|
|
22
27
|
this.cfg = cfg;
|
|
28
|
+
const range = cfg?.compilerVersion;
|
|
29
|
+
if (typeof range !== "string" || range.length === 0) {
|
|
30
|
+
throw new Error(`Pebble compiler config is missing "compilerVersion". ` +
|
|
31
|
+
`Starting from @harmoniclabs/pebble@0.2.0 this field is required ` +
|
|
32
|
+
`and must be an npm-style semver range (e.g. "^0.2.0").`);
|
|
33
|
+
}
|
|
34
|
+
if (!semverSatisfies(COMPILER_VERSION, range)) {
|
|
35
|
+
throw new Error(`Pebble compiler version ${COMPILER_VERSION} does not satisfy ` +
|
|
36
|
+
`the configured "compilerVersion" range "${range}".`);
|
|
37
|
+
}
|
|
23
38
|
if (cfg.silent === true) {
|
|
24
39
|
this.io.stdout = { write() { } };
|
|
25
40
|
}
|
|
@@ -102,6 +117,167 @@ export class Compiler extends DiagnosticEmitter {
|
|
|
102
117
|
// compiles to `Delay(body)`; force it so the body executes.
|
|
103
118
|
return Machine.eval(new Force(uplcProgram.body));
|
|
104
119
|
}
|
|
120
|
+
async test(config) {
|
|
121
|
+
const cfg = {
|
|
122
|
+
...this.cfg,
|
|
123
|
+
...config,
|
|
124
|
+
silent: true,
|
|
125
|
+
addMarker: false,
|
|
126
|
+
};
|
|
127
|
+
const nameFilter = config?.nameFilter;
|
|
128
|
+
const matches = (!nameFilter ? () => true :
|
|
129
|
+
typeof nameFilter === "string" ? (n) => n.includes(nameFilter) :
|
|
130
|
+
(n) => nameFilter.test(n));
|
|
131
|
+
const propertyIterations = Math.max(1, config?.propertyIterations ?? 100);
|
|
132
|
+
const seed = config?.seed ?? 0;
|
|
133
|
+
// 1) discovery pass: parse + check, populate program.tests.
|
|
134
|
+
// diagnostics from this pass are surfaced once; subsequent per-test
|
|
135
|
+
// passes use their own diagnostic arrays so we don't double-report.
|
|
136
|
+
const discovery = new AstCompiler(cfg, this.io, this.diagnostics);
|
|
137
|
+
const discoveryResult = await discovery.check();
|
|
138
|
+
const descriptors = discoveryResult.program.tests
|
|
139
|
+
.filter(t => matches(t.name))
|
|
140
|
+
.map(t => {
|
|
141
|
+
const fn = discoveryResult.program.functions.get(t.tirFuncName);
|
|
142
|
+
const paramNames = (fn instanceof TirFuncExpr) ? fn.params.map(p => p.sourceName ?? p.name) : [];
|
|
143
|
+
return {
|
|
144
|
+
name: t.name,
|
|
145
|
+
tirFuncName: t.tirFuncName,
|
|
146
|
+
sourceFile: t.sourceFile,
|
|
147
|
+
range: t.range,
|
|
148
|
+
paramNames,
|
|
149
|
+
fuzzerInfos: t.fuzzerInfos,
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
const results = new Array(descriptors.length);
|
|
153
|
+
for (let i = 0; i < descriptors.length; i++) {
|
|
154
|
+
results[i] = await this._runOneTest(cfg, descriptors[i], propertyIterations, seed);
|
|
155
|
+
}
|
|
156
|
+
return results;
|
|
157
|
+
}
|
|
158
|
+
async _runOneTest(cfg, desc, propertyIterations, seed) {
|
|
159
|
+
const isProperty = desc.fuzzerInfos.length > 0;
|
|
160
|
+
// fresh AstCompiler so the expressify pass starts from a clean program
|
|
161
|
+
const localDiagnostics = [];
|
|
162
|
+
const astCompiler = new AstCompiler(cfg, this.io, localDiagnostics);
|
|
163
|
+
await astCompiler.compileFile(cfg.entry, true);
|
|
164
|
+
if (localDiagnostics.some(d => d.category === DiagnosticCategory.Error)) {
|
|
165
|
+
return _failedTestResult(desc, "compile error: " + localDiagnostics.find(d => d.category === DiagnosticCategory.Error).toString(), isProperty ? "property" : "unit");
|
|
166
|
+
}
|
|
167
|
+
const fn = astCompiler.program.functions.get(desc.tirFuncName);
|
|
168
|
+
if (!(fn instanceof TirFuncExpr)) {
|
|
169
|
+
return _failedTestResult(desc, `test function "${desc.name}" not found after re-parse`, isProperty ? "property" : "unit");
|
|
170
|
+
}
|
|
171
|
+
astCompiler.program.contractTirFuncName = desc.tirFuncName;
|
|
172
|
+
let serialized;
|
|
173
|
+
try {
|
|
174
|
+
serialized = this._compileBackend(cfg, astCompiler.program, true);
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
return _failedTestResult(desc, "backend error: " + (err instanceof Error ? err.message : String(err)), isProperty ? "property" : "unit");
|
|
178
|
+
}
|
|
179
|
+
const uplcProgram = parseUPLC(serialized);
|
|
180
|
+
if (!isProperty) {
|
|
181
|
+
const evalResult = Machine.eval(new Force(uplcProgram.body));
|
|
182
|
+
const isErr = evalResult.result instanceof CEKError;
|
|
183
|
+
const budget = {
|
|
184
|
+
cpu: BigInt(evalResult.budgetSpent.cpu),
|
|
185
|
+
mem: BigInt(evalResult.budgetSpent.mem),
|
|
186
|
+
};
|
|
187
|
+
const iter = {
|
|
188
|
+
passed: !isErr,
|
|
189
|
+
budgetSpent: budget,
|
|
190
|
+
logs: evalResult.logs.slice(),
|
|
191
|
+
error: isErr ? { msg: evalResult.result.msg } : undefined,
|
|
192
|
+
};
|
|
193
|
+
return {
|
|
194
|
+
name: desc.name,
|
|
195
|
+
sourceFile: desc.sourceFile,
|
|
196
|
+
range: desc.range,
|
|
197
|
+
kind: "unit",
|
|
198
|
+
passed: !isErr,
|
|
199
|
+
iterations: [iter],
|
|
200
|
+
totalBudget: addBudget(zeroBudget(), budget),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
// ── Property test ──────────────────────────────────────────────
|
|
204
|
+
// Check that every parameter has an executable fuzzer in v1 (Phase 1).
|
|
205
|
+
const unsupported = desc.fuzzerInfos.find(fi => fi.kind === "unsupported" || fi.kind === "via_not_implemented");
|
|
206
|
+
if (unsupported) {
|
|
207
|
+
const reason = unsupported.kind === "via_not_implemented"
|
|
208
|
+
? "user-defined fuzzers via the 'via' keyword are not yet executable (Phase 2)"
|
|
209
|
+
: unsupported.reason;
|
|
210
|
+
return {
|
|
211
|
+
name: desc.name,
|
|
212
|
+
sourceFile: desc.sourceFile,
|
|
213
|
+
range: desc.range,
|
|
214
|
+
kind: "property",
|
|
215
|
+
passed: false,
|
|
216
|
+
iterations: [],
|
|
217
|
+
totalBudget: zeroBudget(),
|
|
218
|
+
skippedReason: reason,
|
|
219
|
+
seed,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
// Run N iterations with TS-side sampling.
|
|
223
|
+
const prng = new PRNG(seed);
|
|
224
|
+
const iterations = [];
|
|
225
|
+
let totalBudget = zeroBudget();
|
|
226
|
+
let passedAll = true;
|
|
227
|
+
for (let i = 0; i < propertyIterations; i++) {
|
|
228
|
+
const inputs = [];
|
|
229
|
+
const args = [];
|
|
230
|
+
for (let p = 0; p < desc.fuzzerInfos.length; p++) {
|
|
231
|
+
const fi = desc.fuzzerInfos[p];
|
|
232
|
+
if (fi.kind !== "primitive")
|
|
233
|
+
throw new Error("unreachable: non-primitive after unsupported check");
|
|
234
|
+
const paramName = desc.paramNames[p] ?? `param${p}`;
|
|
235
|
+
if (fi.primitive === "int") {
|
|
236
|
+
const v = prng.nextIntBiased();
|
|
237
|
+
inputs.push({ name: paramName, value: v });
|
|
238
|
+
args.push(UPLCConst.int(v));
|
|
239
|
+
}
|
|
240
|
+
else // bool
|
|
241
|
+
{
|
|
242
|
+
const v = prng.nextBool();
|
|
243
|
+
inputs.push({ name: paramName, value: v });
|
|
244
|
+
args.push(UPLCConst.bool(v));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
let app = uplcProgram.body;
|
|
248
|
+
for (const arg of args)
|
|
249
|
+
app = new Application(app, arg);
|
|
250
|
+
const evalResult = Machine.eval(app);
|
|
251
|
+
const isErr = evalResult.result instanceof CEKError;
|
|
252
|
+
const budget = {
|
|
253
|
+
cpu: BigInt(evalResult.budgetSpent.cpu),
|
|
254
|
+
mem: BigInt(evalResult.budgetSpent.mem),
|
|
255
|
+
};
|
|
256
|
+
const iter = {
|
|
257
|
+
passed: !isErr,
|
|
258
|
+
budgetSpent: budget,
|
|
259
|
+
logs: evalResult.logs.slice(),
|
|
260
|
+
error: isErr ? { msg: evalResult.result.msg } : undefined,
|
|
261
|
+
inputs,
|
|
262
|
+
};
|
|
263
|
+
iterations.push(iter);
|
|
264
|
+
totalBudget = addBudget(totalBudget, budget);
|
|
265
|
+
if (isErr) {
|
|
266
|
+
passedAll = false;
|
|
267
|
+
break; // early exit on first failure (Phase 1; shrinking is Phase 2)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
name: desc.name,
|
|
272
|
+
sourceFile: desc.sourceFile,
|
|
273
|
+
range: desc.range,
|
|
274
|
+
kind: "property",
|
|
275
|
+
passed: passedAll,
|
|
276
|
+
iterations,
|
|
277
|
+
totalBudget,
|
|
278
|
+
seed,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
105
281
|
async runRepl(config) {
|
|
106
282
|
const cfg = {
|
|
107
283
|
...this.cfg,
|
|
@@ -126,15 +302,17 @@ export class Compiler extends DiagnosticEmitter {
|
|
|
126
302
|
// compiles to `Delay(body)`; force it so the body executes.
|
|
127
303
|
return Machine.eval(new Force(uplcProgram.body));
|
|
128
304
|
}
|
|
129
|
-
_compileBackend(cfg, program) {
|
|
305
|
+
_compileBackend(cfg, program, skipFileOutput = false) {
|
|
130
306
|
// backend starts here
|
|
131
307
|
const ir = compileTypedProgram(cfg, program);
|
|
132
308
|
const uplc = compileIRToUPLC(ir, cfg);
|
|
133
309
|
const serialized = compileUPLC(new UPLCProgram(cfg.targetUplcVersion, uplc));
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
310
|
+
if (!skipFileOutput) {
|
|
311
|
+
const outDir = cfg.outDir;
|
|
312
|
+
const outPath = outDir + (outDir.endsWith("/") ? "" : "/") + "out.flat";
|
|
313
|
+
this.io.writeFile(outPath, serialized, cfg.root);
|
|
314
|
+
this.io.stdout.write(`compiled program written to ${outPath}\n`);
|
|
315
|
+
}
|
|
138
316
|
__VERY_UNSAFE_FORGET_IRHASH_ONLY_USE_AT_END_OF_UPLC_COMPILATION();
|
|
139
317
|
__VERY_UNSAFE_FORGET_VAR_SYM_HASHES_ONLY_USE_AT_END_OF_UPLC_COMPILATION();
|
|
140
318
|
__unsafe_clear_hoisted_hash_to_symbol();
|
|
@@ -144,3 +322,20 @@ export class Compiler extends DiagnosticEmitter {
|
|
|
144
322
|
return serialized;
|
|
145
323
|
}
|
|
146
324
|
}
|
|
325
|
+
function _failedTestResult(desc, msg, kind = "unit") {
|
|
326
|
+
const budget = zeroBudget();
|
|
327
|
+
return {
|
|
328
|
+
name: desc.name,
|
|
329
|
+
sourceFile: desc.sourceFile,
|
|
330
|
+
range: desc.range,
|
|
331
|
+
kind,
|
|
332
|
+
passed: false,
|
|
333
|
+
iterations: [{
|
|
334
|
+
passed: false,
|
|
335
|
+
budgetSpent: budget,
|
|
336
|
+
logs: [],
|
|
337
|
+
error: { msg },
|
|
338
|
+
}],
|
|
339
|
+
totalBudget: budget,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
@@ -30,6 +30,7 @@ import { TirTernaryExpr } from "../../tir/expressions/TirTernaryExpr.js";
|
|
|
30
30
|
import { TirToDataExpr } from "../../tir/expressions/TirToDataExpr.js";
|
|
31
31
|
import { TirTraceIfFalseExpr } from "../../tir/expressions/TirTraceIfFalseExpr.js";
|
|
32
32
|
import { TirTraceExpr } from "../../tir/expressions/TirTraceExpr.js";
|
|
33
|
+
import { TirShowExpr } from "../../tir/expressions/TirShowExpr.js";
|
|
33
34
|
import { TirTypeConversionExpr } from "../../tir/expressions/TirTypeConversionExpr.js";
|
|
34
35
|
import { TirVariableAccessExpr } from "../../tir/expressions/TirVariableAccessExpr.js";
|
|
35
36
|
import { TirUnaryExclamation } from "../../tir/expressions/unary/TirUnaryExclamation.js";
|
|
@@ -242,6 +243,11 @@ export function expressifyVars(ctx, expr) {
|
|
|
242
243
|
}
|
|
243
244
|
if (expr instanceof TirInlineClosedIR)
|
|
244
245
|
return expr;
|
|
246
|
+
if (expr instanceof TirShowExpr) {
|
|
247
|
+
// recurse into inner so any nested variables / casts get expressified;
|
|
248
|
+
// the show-bytes lowering happens at IR-emit time inside TirShowExpr.toIR.
|
|
249
|
+
return new TirShowExpr(expressifyVars(ctx, expr.inner), expr.range);
|
|
250
|
+
}
|
|
245
251
|
const tsEnsureExhautstiveCheck = expr;
|
|
246
252
|
console.error(expr);
|
|
247
253
|
throw new Error("unreachable::expressifyVars");
|
|
@@ -276,7 +282,9 @@ function expressifyPropAccess(ctx, propAccessExpr) {
|
|
|
276
282
|
|| expr instanceof TirAssertAndContinueExpr
|
|
277
283
|
|| expr instanceof TirTraceIfFalseExpr
|
|
278
284
|
|| expr instanceof TirTraceExpr
|
|
279
|
-
|| expr instanceof TirInlineClosedIR
|
|
285
|
+
|| expr instanceof TirInlineClosedIR
|
|
286
|
+
|| expr instanceof TirShowExpr // result is bytes, properties already handled via .show() returning bytes_t
|
|
287
|
+
)
|
|
280
288
|
throw new Error("Invalid property access expression");
|
|
281
289
|
if (expr instanceof TirLitThisExpr) {
|
|
282
290
|
let varName = ctx.properties.get("this")?.get(prop);
|
|
@@ -445,12 +453,15 @@ function expressifyMethodCall(ctx, methodCall) {
|
|
|
445
453
|
}
|
|
446
454
|
const structMethods = objectType.methodNamesPtr;
|
|
447
455
|
const tirMethodName = structMethods.get(methodName);
|
|
448
|
-
if (
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
456
|
+
if (tirMethodName) {
|
|
457
|
+
const funcExpr = ctx.program.functions.get(tirMethodName);
|
|
458
|
+
if (!funcExpr)
|
|
459
|
+
throw new Error(`Definition of method '${methodName}' on type '${objectType.toString()}' is missing.`);
|
|
460
|
+
return new TirCallExpr(funcExpr, [objectExpr, ...methodCall.args], methodCall.type, SourceRange.join(methodIdentifierProp.range, methodCall.range.atEnd()));
|
|
461
|
+
}
|
|
462
|
+
// No user impl — fall through to the generic `.show()` fallback at the
|
|
463
|
+
// end of expressifyMethodCall (which auto-derives via _showIR for
|
|
464
|
+
// data-encoded structs).
|
|
454
465
|
}
|
|
455
466
|
if (objectType instanceof TirListT) {
|
|
456
467
|
const result = expressifyListMethodCall(ctx, objectExpr, methodCall, methodName, objectType, SourceRange.join(methodIdentifierProp.range, methodCall.range.atEnd()));
|
|
@@ -491,6 +502,14 @@ function expressifyMethodCall(ctx, methodCall) {
|
|
|
491
502
|
return new TirCallExpr(TirNativeFunc.consByteString, [methodCall.args[0], objectExpr], bytes_t, exprRange);
|
|
492
503
|
}
|
|
493
504
|
}
|
|
505
|
+
// Generic `.show()` fallback: any value whose type has a built-in
|
|
506
|
+
// `_showIR` impl can be shown by emitting a TirShowExpr. User types
|
|
507
|
+
// override this by declaring `type X implements Show { show(self): bytes
|
|
508
|
+
// { ... } }` — that path is taken earlier (alias/struct method-table
|
|
509
|
+
// dispatch) and never reaches here.
|
|
510
|
+
if (methodName === "show" && methodCall.args.length === 0) {
|
|
511
|
+
return new TirShowExpr(objectExpr, SourceRange.join(methodIdentifierProp.range, methodCall.range.atEnd())); // TirShowExpr is in TirExpr union, cast for return-type compat
|
|
512
|
+
}
|
|
494
513
|
throw new Error(`not implemented::expressifyMethodCall for type '${objectType.toString()}' (method name: '${methodName}')`);
|
|
495
514
|
// const tsEnsureExhautstiveCheck: never = objectType;
|
|
496
515
|
throw new Error(`Cannot call method '${methodName}' on non-struct type '${objectType.toString()}'`);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { SourceRange } from "../../ast/Source/SourceRange.js";
|
|
2
|
+
export type TestKind = "unit" | "property";
|
|
3
|
+
export interface TestBudget {
|
|
4
|
+
cpu: bigint;
|
|
5
|
+
mem: bigint;
|
|
6
|
+
}
|
|
7
|
+
export interface TestInput {
|
|
8
|
+
name: string;
|
|
9
|
+
value: unknown;
|
|
10
|
+
}
|
|
11
|
+
export interface TestIterationResult {
|
|
12
|
+
passed: boolean;
|
|
13
|
+
budgetSpent: TestBudget;
|
|
14
|
+
logs: string[];
|
|
15
|
+
error?: {
|
|
16
|
+
msg?: string;
|
|
17
|
+
};
|
|
18
|
+
/** the fuzzed input tuple for this iteration; undefined for unit tests */
|
|
19
|
+
inputs?: TestInput[];
|
|
20
|
+
}
|
|
21
|
+
export interface TestResult {
|
|
22
|
+
name: string;
|
|
23
|
+
sourceFile: string;
|
|
24
|
+
range: SourceRange;
|
|
25
|
+
kind: TestKind;
|
|
26
|
+
/** aggregate pass/fail across all iterations */
|
|
27
|
+
passed: boolean;
|
|
28
|
+
/** length 1 for unit tests, N for property tests */
|
|
29
|
+
iterations: TestIterationResult[];
|
|
30
|
+
/** sum of `budgetSpent` across iterations */
|
|
31
|
+
totalBudget: TestBudget;
|
|
32
|
+
/** non-empty when the test could not be executed at all (e.g. unsupported feature) */
|
|
33
|
+
skippedReason?: string;
|
|
34
|
+
/** seed used by the property runner (only set for property tests) */
|
|
35
|
+
seed?: number;
|
|
36
|
+
}
|
|
37
|
+
export declare function zeroBudget(): TestBudget;
|
|
38
|
+
export declare function addBudget(a: TestBudget, b: TestBudget): TestBudget;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tiny seedable PRNG (mulberry32).
|
|
3
|
+
*
|
|
4
|
+
* Single-purpose, deterministic, no dependencies. Used by the Phase 1
|
|
5
|
+
* property-test runner to sample primitive values for parameters whose
|
|
6
|
+
* type has a built-in TS-side fuzzer (`int`, `bool`).
|
|
7
|
+
*
|
|
8
|
+
* Phase 2 will replace this with a Pebble-side fuzzer pipeline that runs
|
|
9
|
+
* on the CEK machine; that will let user-defined fuzzers (`via <expr>`)
|
|
10
|
+
* compose deterministically with the built-in ones.
|
|
11
|
+
*/
|
|
12
|
+
export declare class PRNG {
|
|
13
|
+
private state;
|
|
14
|
+
constructor(seed: number);
|
|
15
|
+
/** Returns an unsigned 32-bit integer. */
|
|
16
|
+
next32(): number;
|
|
17
|
+
/** Returns a boolean (uniform). */
|
|
18
|
+
nextBool(): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Returns a random bigint, biased toward edge values.
|
|
21
|
+
*
|
|
22
|
+
* 1 in 16 returns one of: 0, 1, -1, INT64_MAX, INT64_MIN, INT32_MAX, INT32_MIN.
|
|
23
|
+
* Otherwise samples uniformly across signed 64-bit range.
|
|
24
|
+
*/
|
|
25
|
+
nextIntBiased(): bigint;
|
|
26
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tiny seedable PRNG (mulberry32).
|
|
3
|
+
*
|
|
4
|
+
* Single-purpose, deterministic, no dependencies. Used by the Phase 1
|
|
5
|
+
* property-test runner to sample primitive values for parameters whose
|
|
6
|
+
* type has a built-in TS-side fuzzer (`int`, `bool`).
|
|
7
|
+
*
|
|
8
|
+
* Phase 2 will replace this with a Pebble-side fuzzer pipeline that runs
|
|
9
|
+
* on the CEK machine; that will let user-defined fuzzers (`via <expr>`)
|
|
10
|
+
* compose deterministically with the built-in ones.
|
|
11
|
+
*/
|
|
12
|
+
export class PRNG {
|
|
13
|
+
state;
|
|
14
|
+
constructor(seed) {
|
|
15
|
+
// ensure non-zero, fits in u32
|
|
16
|
+
this.state = (seed | 0) || 1;
|
|
17
|
+
}
|
|
18
|
+
/** Returns an unsigned 32-bit integer. */
|
|
19
|
+
next32() {
|
|
20
|
+
// mulberry32
|
|
21
|
+
let t = (this.state = (this.state + 0x6D2B79F5) | 0);
|
|
22
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
23
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
24
|
+
return (t ^ (t >>> 14)) >>> 0;
|
|
25
|
+
}
|
|
26
|
+
/** Returns a boolean (uniform). */
|
|
27
|
+
nextBool() {
|
|
28
|
+
return (this.next32() & 1) === 1;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Returns a random bigint, biased toward edge values.
|
|
32
|
+
*
|
|
33
|
+
* 1 in 16 returns one of: 0, 1, -1, INT64_MAX, INT64_MIN, INT32_MAX, INT32_MIN.
|
|
34
|
+
* Otherwise samples uniformly across signed 64-bit range.
|
|
35
|
+
*/
|
|
36
|
+
nextIntBiased() {
|
|
37
|
+
const edgeRoll = this.next32() & 0xF; // 0..15
|
|
38
|
+
if (edgeRoll === 0) {
|
|
39
|
+
const edges = [
|
|
40
|
+
0n,
|
|
41
|
+
1n,
|
|
42
|
+
-1n,
|
|
43
|
+
(1n << 63n) - 1n,
|
|
44
|
+
-(1n << 63n),
|
|
45
|
+
(1n << 31n) - 1n,
|
|
46
|
+
-(1n << 31n),
|
|
47
|
+
];
|
|
48
|
+
return edges[this.next32() % edges.length];
|
|
49
|
+
}
|
|
50
|
+
// assemble a 64-bit signed value from two u32 samples
|
|
51
|
+
const hi = BigInt(this.next32());
|
|
52
|
+
const lo = BigInt(this.next32());
|
|
53
|
+
let v = (hi << 32n) | lo;
|
|
54
|
+
// interpret as signed (sign bit is bit 63)
|
|
55
|
+
if (v & (1n << 63n))
|
|
56
|
+
v -= (1n << 64n);
|
|
57
|
+
return v;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -21,5 +21,6 @@ import { TirTraceExpr } from "./TirTraceExpr.js";
|
|
|
21
21
|
import { TirNativeFunc } from "./TirNativeFunc.js";
|
|
22
22
|
import { TirInlineClosedIR } from "./TirInlineClosedIR.js";
|
|
23
23
|
import { TirIsExpr } from "./TirIsExpr.js";
|
|
24
|
-
|
|
24
|
+
import { TirShowExpr } from "./TirShowExpr.js";
|
|
25
|
+
export type TirExpr = (TirUnaryPrefixExpr | TirLitteralExpr | TirParentesizedExpr | TirFuncExpr | TirCallExpr | TirCaseExpr | TirTypeConversionExpr | TirIsExpr | TirElemAccessExpr | TirTernaryExpr | TirPropAccessExpr | TirBinaryExpr | TirVariableAccessExpr | TirLettedExpr | TirNativeFunc | TirFailExpr | TirHoistedExpr | TirFromDataExpr | TirToDataExpr | TirAssertAndContinueExpr | TirTraceIfFalseExpr | TirTraceExpr | TirShowExpr | TirInlineClosedIR);
|
|
25
26
|
export declare function isTirExpr(thing: any): thing is TirExpr;
|
|
@@ -22,6 +22,7 @@ import { TirTraceExpr } from "./TirTraceExpr.js";
|
|
|
22
22
|
import { TirNativeFunc } from "./TirNativeFunc.js";
|
|
23
23
|
import { TirInlineClosedIR } from "./TirInlineClosedIR.js";
|
|
24
24
|
import { TirIsExpr } from "./TirIsExpr.js";
|
|
25
|
+
import { TirShowExpr } from "./TirShowExpr.js";
|
|
25
26
|
export function isTirExpr(thing) {
|
|
26
27
|
return isObject(thing) && (isTirUnaryPrefixExpr(thing)
|
|
27
28
|
|| isTirLitteralExpr(thing)
|
|
@@ -45,5 +46,6 @@ export function isTirExpr(thing) {
|
|
|
45
46
|
|| thing instanceof TirAssertAndContinueExpr
|
|
46
47
|
|| thing instanceof TirTraceIfFalseExpr
|
|
47
48
|
|| thing instanceof TirTraceExpr
|
|
49
|
+
|| thing instanceof TirShowExpr
|
|
48
50
|
|| thing instanceof TirInlineClosedIR);
|
|
49
51
|
}
|
|
@@ -65,6 +65,23 @@ export declare class TirNativeFunc implements ITirExpr {
|
|
|
65
65
|
static get serialiseData(): TirNativeFunc;
|
|
66
66
|
static get verifyEcdsaSecp256k1Signature(): TirNativeFunc;
|
|
67
67
|
static get verifySchnorrSecp256k1Signature(): TirNativeFunc;
|
|
68
|
+
static get bls12_381_G1_add(): TirNativeFunc;
|
|
69
|
+
static get bls12_381_G1_neg(): TirNativeFunc;
|
|
70
|
+
static get bls12_381_G1_scalarMul(): TirNativeFunc;
|
|
71
|
+
static get bls12_381_G1_equal(): TirNativeFunc;
|
|
72
|
+
static get bls12_381_G1_hashToGroup(): TirNativeFunc;
|
|
73
|
+
static get bls12_381_G1_compress(): TirNativeFunc;
|
|
74
|
+
static get bls12_381_G1_uncompress(): TirNativeFunc;
|
|
75
|
+
static get bls12_381_G2_add(): TirNativeFunc;
|
|
76
|
+
static get bls12_381_G2_neg(): TirNativeFunc;
|
|
77
|
+
static get bls12_381_G2_scalarMul(): TirNativeFunc;
|
|
78
|
+
static get bls12_381_G2_equal(): TirNativeFunc;
|
|
79
|
+
static get bls12_381_G2_hashToGroup(): TirNativeFunc;
|
|
80
|
+
static get bls12_381_G2_compress(): TirNativeFunc;
|
|
81
|
+
static get bls12_381_G2_uncompress(): TirNativeFunc;
|
|
82
|
+
static get bls12_381_millerLoop(): TirNativeFunc;
|
|
83
|
+
static get bls12_381_mulMlResult(): TirNativeFunc;
|
|
84
|
+
static get bls12_381_finalVerify(): TirNativeFunc;
|
|
68
85
|
static get keccak_256(): TirNativeFunc;
|
|
69
86
|
static get blake2b_224(): TirNativeFunc;
|
|
70
87
|
static get integerToByteString(): TirNativeFunc;
|