@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.
Files changed (97) hide show
  1. package/dist/IR/IRNodes/IRConst.js +14 -3
  2. package/dist/IR/IRNodes/IRNative/index.js +5 -1
  3. package/dist/IR/toUPLC/CompilerOptions.d.ts +22 -7
  4. package/dist/IR/toUPLC/CompilerOptions.js +5 -1
  5. package/dist/IR/tree_utils/bytesToHex.d.ts +8 -0
  6. package/dist/IR/tree_utils/bytesToHex.js +69 -0
  7. package/dist/ast/nodes/expr/functions/FuncExpr.d.ts +16 -2
  8. package/dist/ast/nodes/expr/functions/FuncExpr.js +17 -0
  9. package/dist/ast/nodes/expr/litteral/LitteralExpr.d.ts +2 -1
  10. package/dist/ast/nodes/expr/litteral/LitteralExpr.js +2 -0
  11. package/dist/ast/nodes/expr/litteral/TemplateStrExpr.d.ts +30 -0
  12. package/dist/ast/nodes/expr/litteral/TemplateStrExpr.js +35 -0
  13. package/dist/ast/nodes/statements/ExportStmt.d.ts +3 -3
  14. package/dist/ast/nodes/statements/PebbleStmt.d.ts +4 -3
  15. package/dist/ast/nodes/statements/PebbleStmt.js +6 -2
  16. package/dist/ast/nodes/statements/TestParam.d.ts +18 -0
  17. package/dist/ast/nodes/statements/TestParam.js +18 -0
  18. package/dist/ast/nodes/statements/TestStmt.d.ts +5 -3
  19. package/dist/ast/nodes/statements/TestStmt.js +3 -1
  20. package/dist/ast/nodes/statements/UsingStmt.d.ts +32 -2
  21. package/dist/ast/nodes/statements/UsingStmt.js +39 -3
  22. package/dist/ast/nodes/statements/declarations/NamespaceDecl.d.ts +21 -0
  23. package/dist/ast/nodes/statements/declarations/NamespaceDecl.js +31 -0
  24. package/dist/compiler/AstCompiler/AstCompiler.d.ts +26 -0
  25. package/dist/compiler/AstCompiler/AstCompiler.js +203 -3
  26. package/dist/compiler/AstCompiler/internal/_deriveContractBody/_deriveContractBody.js +14 -4
  27. package/dist/compiler/AstCompiler/internal/exprs/_compileCallExpr.js +97 -6
  28. package/dist/compiler/AstCompiler/internal/exprs/_compileFuncExpr.js +12 -5
  29. package/dist/compiler/AstCompiler/internal/exprs/_compileLitteralExpr.js +59 -0
  30. package/dist/compiler/AstCompiler/internal/exprs/_compilePropAccessExpr.d.ts +2 -3
  31. package/dist/compiler/AstCompiler/internal/exprs/_compilePropAccessExpr.js +28 -0
  32. package/dist/compiler/AstCompiler/internal/exprs/_compileVarAccessExpr.js +2 -0
  33. package/dist/compiler/AstCompiler/internal/statements/_compileStatement.js +4 -1
  34. package/dist/compiler/AstCompiler/internal/statements/_compileTestStmt.d.ts +15 -1
  35. package/dist/compiler/AstCompiler/internal/statements/_compileTestStmt.js +70 -30
  36. package/dist/compiler/AstCompiler/internal/statements/_compileUsingAliasStmt.d.ts +11 -0
  37. package/dist/compiler/AstCompiler/internal/statements/_compileUsingAliasStmt.js +26 -0
  38. package/dist/compiler/AstCompiler/internal/statements/_compileUsingStmt.d.ts +9 -4
  39. package/dist/compiler/AstCompiler/internal/statements/_compileUsingStmt.js +51 -10
  40. package/dist/compiler/AstCompiler/internal/types/_compileDataEncodedConcreteType.js +21 -2
  41. package/dist/compiler/AstCompiler/internal/types/_compileSopEncodedConcreteType.js +17 -2
  42. package/dist/compiler/AstCompiler/scope/AstScope.d.ts +70 -1
  43. package/dist/compiler/AstCompiler/scope/AstScope.js +91 -0
  44. package/dist/compiler/AstCompiler/utils/getPropAccessReturnType.js +25 -1
  45. package/dist/compiler/AstCompiler/utils/monomorphizeGeneric.d.ts +36 -0
  46. package/dist/compiler/AstCompiler/utils/monomorphizeGeneric.js +123 -0
  47. package/dist/compiler/AstCompiler/utils/resolveNamespaceChain.d.ts +28 -0
  48. package/dist/compiler/AstCompiler/utils/resolveNamespaceChain.js +95 -0
  49. package/dist/compiler/AstCompiler/utils/resolveNamespacePath.d.ts +37 -0
  50. package/dist/compiler/AstCompiler/utils/resolveNamespacePath.js +93 -0
  51. package/dist/compiler/Compiler.d.ts +9 -1
  52. package/dist/compiler/Compiler.js +204 -9
  53. package/dist/compiler/TirCompiler/expressify/expressifyVars.js +26 -7
  54. package/dist/compiler/test/TestResult.d.ts +38 -0
  55. package/dist/compiler/test/TestResult.js +6 -0
  56. package/dist/compiler/test/fuzz/PRNG.d.ts +26 -0
  57. package/dist/compiler/test/fuzz/PRNG.js +59 -0
  58. package/dist/compiler/tir/expressions/TirExpr.d.ts +2 -1
  59. package/dist/compiler/tir/expressions/TirExpr.js +2 -0
  60. package/dist/compiler/tir/expressions/TirNativeFunc.d.ts +17 -0
  61. package/dist/compiler/tir/expressions/TirNativeFunc.js +53 -106
  62. package/dist/compiler/tir/expressions/TirShowExpr.d.ts +52 -0
  63. package/dist/compiler/tir/expressions/TirShowExpr.js +199 -0
  64. package/dist/compiler/tir/expressions/TirTraceExpr.js +11 -7
  65. package/dist/compiler/tir/program/TypedProgram.d.ts +101 -0
  66. package/dist/compiler/tir/program/TypedProgram.js +43 -0
  67. package/dist/compiler/tir/program/stdScope/populateBuiltinInterfaces.d.ts +17 -0
  68. package/dist/compiler/tir/program/stdScope/populateBuiltinInterfaces.js +70 -0
  69. package/dist/compiler/tir/program/stdScope/populateStdNamespace.d.ts +22 -0
  70. package/dist/compiler/tir/program/stdScope/populateStdNamespace.js +574 -0
  71. package/dist/compiler/tir/program/stdScope/stdScope.d.ts +1 -0
  72. package/dist/compiler/tir/program/stdScope/stdScope.js +1 -1
  73. package/dist/compiler/tir/statements/TirStmt.js +0 -1
  74. package/dist/compiler/tir/statements/TirTestStmt.d.ts +46 -0
  75. package/dist/compiler/tir/statements/TirTestStmt.js +35 -0
  76. package/dist/compiler/tir/types/TirNativeType/TirNativeType.d.ts +50 -1
  77. package/dist/compiler/tir/types/TirNativeType/TirNativeType.js +53 -1
  78. package/dist/compiler/tir/types/TirType.js +3 -1
  79. package/dist/compiler/tir/types/utils/canAssignTo.js +8 -1
  80. package/dist/compiler/tir/types/utils/inferTypeArgs.d.ts +19 -0
  81. package/dist/compiler/tir/types/utils/inferTypeArgs.js +79 -0
  82. package/dist/compiler/tir/types/utils/substituteTypeParams.d.ts +9 -0
  83. package/dist/compiler/tir/types/utils/substituteTypeParams.js +62 -0
  84. package/dist/diagnostics/diagnosticMessages.generated.d.ts +5 -0
  85. package/dist/diagnostics/diagnosticMessages.generated.js +10 -0
  86. package/dist/index.d.ts +2 -0
  87. package/dist/index.js +2 -0
  88. package/dist/parser/Parser.d.ts +73 -3
  89. package/dist/parser/Parser.js +333 -33
  90. package/dist/tokenizer/Token.d.ts +105 -102
  91. package/dist/tokenizer/Token.js +110 -109
  92. package/dist/tokenizer/utils/tokenFromKeyword.js +9 -6
  93. package/dist/utils/semverSatisfies.d.ts +1 -0
  94. package/dist/utils/semverSatisfies.js +161 -0
  95. package/dist/version.generated.d.ts +1 -0
  96. package/dist/version.generated.js +2 -0
  97. 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 { defaultOptions } from "../IR/toUPLC/CompilerOptions.js";
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 = defaultOptions, diagnostics) {
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
- const outDir = cfg.outDir;
135
- const outPath = outDir + (outDir.endsWith("/") ? "" : "/") + "out.flat";
136
- this.io.writeFile(outPath, serialized, cfg.root);
137
- this.io.stdout.write(`compiled program written to ${outPath}\n`);
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 (!tirMethodName)
449
- throw new Error(`Method '${methodName}' does not exist on type '${objectType.toString()}'`);
450
- const funcExpr = ctx.program.functions.get(tirMethodName);
451
- if (!funcExpr)
452
- throw new Error(`Definition of method '${methodName}' on type '${objectType.toString()}' is missing.`);
453
- return new TirCallExpr(funcExpr, [objectExpr, ...methodCall.args], methodCall.type, SourceRange.join(methodIdentifierProp.range, methodCall.range.atEnd()));
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,6 @@
1
+ export function zeroBudget() {
2
+ return { cpu: 0n, mem: 0n };
3
+ }
4
+ export function addBudget(a, b) {
5
+ return { cpu: a.cpu + b.cpu, mem: a.mem + b.mem };
6
+ }
@@ -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
- 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 | TirInlineClosedIR);
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;