gofront 1.6.0 → 1.6.1

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 (68) hide show
  1. package/package.json +1 -1
  2. package/src/backend/js/expressions.js +238 -109
  3. package/src/backend/js/index.js +38 -1
  4. package/src/backend/js/runtime.js +953 -53
  5. package/src/backend/js/statements.js +55 -67
  6. package/src/backend/js/stdlib/base64.js +12 -0
  7. package/src/backend/js/stdlib/binary.js +102 -0
  8. package/src/backend/js/stdlib/bits.js +15 -1
  9. package/src/backend/js/stdlib/builder.js +28 -9
  10. package/src/backend/js/stdlib/bytes.js +75 -18
  11. package/src/backend/js/stdlib/errors.js +20 -1
  12. package/src/backend/js/stdlib/fmt.js +70 -13
  13. package/src/backend/js/stdlib/hex.js +17 -0
  14. package/src/backend/js/stdlib/html.js +1 -1
  15. package/src/backend/js/stdlib/index.js +16 -0
  16. package/src/backend/js/stdlib/io.js +105 -3
  17. package/src/backend/js/stdlib/math.js +13 -1
  18. package/src/backend/js/stdlib/path.js +18 -4
  19. package/src/backend/js/stdlib/rand.js +10 -5
  20. package/src/backend/js/stdlib/sha256.js +12 -0
  21. package/src/backend/js/stdlib/slices.js +52 -26
  22. package/src/backend/js/stdlib/strconv.js +21 -5
  23. package/src/backend/js/stdlib/strings.js +148 -15
  24. package/src/backend/js/stdlib/sync.js +13 -0
  25. package/src/backend/js/stdlib/time.js +90 -19
  26. package/src/backend/js/stdlib/unicode.js +2 -2
  27. package/src/backend/wasm/boundary.js +1207 -41
  28. package/src/backend/wasm/const-fold.js +205 -0
  29. package/src/backend/wasm/emit-builtins.js +203 -76
  30. package/src/backend/wasm/emit-exprs.js +464 -276
  31. package/src/backend/wasm/emit-maps.js +144 -8
  32. package/src/backend/wasm/emit-stdlib.js +12 -3005
  33. package/src/backend/wasm/emit-stmts.js +264 -270
  34. package/src/backend/wasm/emit.js +115 -87
  35. package/src/backend/wasm/encode.js +55 -29
  36. package/src/backend/wasm/index.js +1001 -32
  37. package/src/backend/wasm/optimize.js +13 -4
  38. package/src/backend/wasm/stdlib/bytes.js +5039 -0
  39. package/src/backend/wasm/stdlib/core.js +8903 -0
  40. package/src/backend/wasm/stdlib/index.js +40 -0
  41. package/src/backend/wasm/stdlib/io.js +2161 -0
  42. package/src/backend/wasm/stdlib/packages.js +2400 -0
  43. package/src/backend/wasm/stdlib/pure.js +720 -0
  44. package/src/backend/wasm/stdlib/time.js +1200 -0
  45. package/src/backend/wasm/types.js +153 -1
  46. package/src/cli-core.js +1 -1
  47. package/src/compiler.js +18 -4
  48. package/src/dts-parser.js +79 -126
  49. package/src/index.js +2 -6
  50. package/src/lexer.js +80 -29
  51. package/src/lower/boxing.js +1 -2
  52. package/src/lower/captures.js +2 -6
  53. package/src/lower/ownership.js +30 -9
  54. package/src/minifier.js +20 -68
  55. package/src/parser/declarations.js +3 -6
  56. package/src/parser/expressions.js +3 -8
  57. package/src/parser/index.js +18 -3
  58. package/src/parser/statements.js +25 -38
  59. package/src/parser/types.js +1 -2
  60. package/src/resolver.js +2 -27
  61. package/src/templ-lexer.js +29 -63
  62. package/src/templ-parser.js +22 -45
  63. package/src/typechecker/assignability.js +81 -13
  64. package/src/typechecker/expressions.js +102 -2
  65. package/src/typechecker/resolve.js +36 -6
  66. package/src/typechecker/stdlib/core.js +280 -89
  67. package/src/typechecker/stdlib/extended.js +453 -1
  68. package/src/typechecker/types.js +344 -6
@@ -8,8 +8,15 @@ import { BuiltinsEmitter } from "./emit-builtins.js";
8
8
  import { ExprsEmitter } from "./emit-exprs.js";
9
9
  import { MapsEmitter } from "./emit-maps.js";
10
10
  import { SharedEmitter } from "./emit-shared.js";
11
- import { StdlibEmitter } from "./emit-stdlib.js";
12
11
  import { StmtsEmitter } from "./emit-stmts.js";
12
+ import {
13
+ BytesEmitter,
14
+ ExtendedPackagesEmitter,
15
+ IoEmitter,
16
+ PurePackagesEmitter,
17
+ StdlibCoreEmitter,
18
+ TimeEmitter,
19
+ } from "./stdlib/index.js";
13
20
  import {
14
21
  isArrayType,
15
22
  isPointerToStruct,
@@ -34,7 +41,14 @@ export class FunctionEmitter {
34
41
  this.mod.lowerResult?.captures?.get(
35
42
  this.rootFuncDecl._sourceDecl ?? this.rootFuncDecl,
36
43
  ) ?? null;
37
- this.mutatedCaptures = this.captureAnalysis?.mutatedCaptures ?? new Set();
44
+ this.mutatedCaptures = new Set(this.captureAnalysis?.mutatedCaptures ?? []);
45
+ const lowerBoxed =
46
+ this.mod.lowerResult?.boxedVars?.get(
47
+ this.rootFuncDecl._sourceDecl ?? this.rootFuncDecl,
48
+ ) ?? [];
49
+ for (const name of lowerBoxed) {
50
+ this.mutatedCaptures.add(name);
51
+ }
38
52
 
39
53
  this.capturedNames = this.funcDecl._funcLit
40
54
  ? Array.from(
@@ -126,33 +140,7 @@ export class FunctionEmitter {
126
140
  return;
127
141
  }
128
142
 
129
- const namedReturns = this.funcDecl.returnType?._namedReturns;
130
- if (namedReturns && namedReturns.length > 0) {
131
- this.namedReturnVars = [];
132
- for (let i = 0; i < namedReturns.length; i++) {
133
- const r = namedReturns[i];
134
- const isBlank = !r.name || r.name === "_";
135
- const varName = isBlank ? `__ret_blank$${i}` : r.name;
136
- const wType = this.toWasmType(r.type);
137
- const localIdx = this.allocLocal(varName, wType, r.type, isBlank);
138
- this.namedReturnVars.push({
139
- name: varName,
140
- localIdx,
141
- type: wType,
142
- goType: r.type,
143
- isBlank,
144
- });
145
- this.emitZeroValue(r.type, wType);
146
- const localInfo = this.locals.get(varName);
147
- if (localInfo?.isBoxed) {
148
- this.pushInstruction({
149
- op: "struct.new",
150
- typeIndex: localInfo.boxInfo.typeIndex,
151
- });
152
- }
153
- this.pushInstruction({ op: "local.set", index: localIdx });
154
- }
155
- }
143
+ this._initNamedReturns();
156
144
 
157
145
  const needsOobBlock = this._needsOobBlock(body);
158
146
  if (needsOobBlock) {
@@ -181,34 +169,38 @@ export class FunctionEmitter {
181
169
  }
182
170
  }
183
171
 
184
- _emitFunctionBodyWithDefer(body) {
172
+ _initNamedReturns() {
185
173
  const namedReturns = this.funcDecl.returnType?._namedReturns;
186
- if (namedReturns && namedReturns.length > 0) {
187
- this.namedReturnVars = [];
188
- for (let i = 0; i < namedReturns.length; i++) {
189
- const r = namedReturns[i];
190
- const isBlank = !r.name || r.name === "_";
191
- const varName = isBlank ? `__ret_blank$${i}` : r.name;
192
- const wType = this.toWasmType(r.type);
193
- const localIdx = this.allocLocal(varName, wType, r.type, isBlank);
194
- this.namedReturnVars.push({
195
- name: varName,
196
- localIdx,
197
- type: wType,
198
- goType: r.type,
199
- isBlank,
174
+ if (!namedReturns?.length) return false;
175
+ this.namedReturnVars = [];
176
+ for (let i = 0; i < namedReturns.length; i++) {
177
+ const r = namedReturns[i];
178
+ const isBlank = !r.name || r.name === "_";
179
+ const varName = isBlank ? `__ret_blank$${i}` : r.name;
180
+ const wType = this.toWasmType(r.type);
181
+ const localIdx = this.allocLocal(varName, wType, r.type, isBlank);
182
+ this.namedReturnVars.push({
183
+ name: varName,
184
+ localIdx,
185
+ type: wType,
186
+ goType: r.type,
187
+ isBlank,
188
+ });
189
+ this.emitZeroValue(r.type, wType);
190
+ const localInfo = this.locals.get(varName);
191
+ if (localInfo?.isBoxed) {
192
+ this.pushInstruction({
193
+ op: "struct.new",
194
+ typeIndex: localInfo.boxInfo.typeIndex,
200
195
  });
201
- this.emitZeroValue(r.type, wType);
202
- const localInfo = this.locals.get(varName);
203
- if (localInfo?.isBoxed) {
204
- this.pushInstruction({
205
- op: "struct.new",
206
- typeIndex: localInfo.boxInfo.typeIndex,
207
- });
208
- }
209
- this.pushInstruction({ op: "local.set", index: localIdx });
210
196
  }
211
- } else if (this.returnTypes.length > 0) {
197
+ this.pushInstruction({ op: "local.set", index: localIdx });
198
+ }
199
+ return true;
200
+ }
201
+
202
+ _emitFunctionBodyWithDefer(body) {
203
+ if (!this._initNamedReturns() && this.returnTypes.length > 0) {
212
204
  this.returnTempLocals = [];
213
205
  for (let i = 0; i < this.returnTypes.length; i++) {
214
206
  const wType = this.returnTypes[i];
@@ -662,36 +654,27 @@ export class FunctionEmitter {
662
654
  return null;
663
655
  }
664
656
 
665
- _unpackClosureEnv() {
657
+ _buildEnvFields(capturedNames) {
666
658
  const envFields = [];
667
- for (const name of this.capturedNames) {
659
+ for (const name of capturedNames) {
668
660
  const isBoxed = this.mutatedCaptures.has(name);
669
661
  const goType = this._findCapturedVarGoType(name);
670
- if (isBoxed) {
671
- const boxInfo = this.mod.getBoxType(goType);
672
- envFields.push({
673
- name,
674
- goType,
675
- isBoxed: true,
676
- boxInfo,
677
- wType: {
678
- kind: "ref",
679
- nullable: true,
680
- typeIndex: boxInfo.typeIndex,
681
- },
682
- });
683
- } else {
684
- const wType = this.toWasmType(goType);
685
- envFields.push({
686
- name,
687
- goType,
688
- isBoxed: false,
689
- boxInfo: null,
690
- wType,
691
- });
692
- }
662
+ const boxInfo = isBoxed ? this.mod.getBoxType(goType) : null;
663
+ envFields.push({
664
+ name,
665
+ goType,
666
+ isBoxed,
667
+ boxInfo,
668
+ wType: isBoxed
669
+ ? { kind: "ref", nullable: true, typeIndex: boxInfo.typeIndex }
670
+ : this.toWasmType(goType),
671
+ });
693
672
  }
673
+ return envFields;
674
+ }
694
675
 
676
+ _unpackClosureEnv() {
677
+ const envFields = this._buildEnvFields(this.capturedNames);
695
678
  const envInfo = this.mod.getEnvType(envFields);
696
679
  this.envInfo = envInfo;
697
680
  this.envFields = envFields;
@@ -873,6 +856,21 @@ export class FunctionEmitter {
873
856
  if (this.mod.structTypes.has(t.name))
874
857
  return this.mod.structTypes.get(t.name);
875
858
  }
859
+ if (t.kind === "struct" || t.kind === "StructType") {
860
+ const rawFields = [];
861
+ if (t.fields instanceof Map) {
862
+ for (const [k, v] of t.fields.entries())
863
+ rawFields.push({ name: k, goType: v });
864
+ } else if (Array.isArray(t.fields)) {
865
+ for (const f of t.fields) {
866
+ const names = f.names ?? (f.name ? [f.name] : []);
867
+ for (const n of names) rawFields.push({ name: n, goType: f.type });
868
+ }
869
+ }
870
+ if (rawFields.length > 0 && this.mod.getAnonStructType) {
871
+ return this.mod.getAnonStructType(rawFields);
872
+ }
873
+ }
876
874
  return null;
877
875
  }
878
876
 
@@ -930,6 +928,7 @@ export class FunctionEmitter {
930
928
  return;
931
929
  if (
932
930
  node.kind === "CompositeLit" ||
931
+ node.kind === "CallExpr" ||
933
932
  (node.kind === "UnaryExpr" && node.op === "*")
934
933
  )
935
934
  return;
@@ -969,6 +968,10 @@ export class FunctionEmitter {
969
968
  return this._getReceiverTypeName(loc.goType);
970
969
  }
971
970
  }
971
+ if (node && node.kind === "SelectorExpr") {
972
+ const pkg = node.expr?.name;
973
+ if (pkg) return `${pkg}.${node.field}`;
974
+ }
972
975
  return null;
973
976
  }
974
977
 
@@ -1082,6 +1085,18 @@ export class FunctionEmitter {
1082
1085
  return defaultSize;
1083
1086
  }
1084
1087
 
1088
+ _getArrayElemType(t) {
1089
+ if (!t) return null;
1090
+ if (t.elem) return t.elem;
1091
+ if (t.underlying?.elem) return t.underlying.elem;
1092
+ if (t.kind === "ArrayType") return t.elem;
1093
+ if (t.name && this.mod.checker?.types?.has(t.name)) {
1094
+ const resolved = this.mod.checker.types.get(t.name);
1095
+ return this._getArrayElemType(resolved);
1096
+ }
1097
+ return null;
1098
+ }
1099
+
1085
1100
  emitZeroValue(goType, wType) {
1086
1101
  if (wType === "i32") {
1087
1102
  this.pushInstruction({ op: "i32.const", value: 0 });
@@ -1264,13 +1279,7 @@ export class FunctionEmitter {
1264
1279
  }
1265
1280
 
1266
1281
  resolveBranchDepthToOob() {
1267
- for (let i = this.controlStack.length - 1; i >= 0; i--) {
1268
- const ctrl = this.controlStack[i];
1269
- if (ctrl.role === "oob") {
1270
- return this.controlStack.length - 1 - i;
1271
- }
1272
- }
1273
- return null;
1282
+ return this.resolveBranchDepthToRole("oob");
1274
1283
  }
1275
1284
 
1276
1285
  resolveBranchDepthToRole(targetRole) {
@@ -1360,11 +1369,25 @@ export class FunctionEmitter {
1360
1369
  break;
1361
1370
  }
1362
1371
  }
1372
+
1373
+ _isPkgExpr(expr, pkgName) {
1374
+ if (expr.expr?.kind !== "Ident") return false;
1375
+ const name = expr.expr.name;
1376
+ if (pkgName !== undefined && name !== pkgName) return false;
1377
+ return !this.resolveLocal(name) && !this.mod.resolveGlobal(name);
1378
+ }
1363
1379
  }
1364
1380
 
1365
1381
  function copyMethods(target, source) {
1366
1382
  const descriptors = Object.getOwnPropertyDescriptors(source.prototype);
1367
1383
  delete descriptors.constructor;
1384
+ for (const key of Object.keys(descriptors)) {
1385
+ if (key in target.prototype) {
1386
+ throw new Error(
1387
+ `Duplicate method "${key}" found while mixing ${source.name} into ${target.name}`,
1388
+ );
1389
+ }
1390
+ }
1368
1391
  Object.defineProperties(target.prototype, descriptors);
1369
1392
  }
1370
1393
 
@@ -1372,5 +1395,10 @@ copyMethods(FunctionEmitter, MapsEmitter);
1372
1395
  copyMethods(FunctionEmitter, StmtsEmitter);
1373
1396
  copyMethods(FunctionEmitter, ExprsEmitter);
1374
1397
  copyMethods(FunctionEmitter, BuiltinsEmitter);
1375
- copyMethods(FunctionEmitter, StdlibEmitter);
1398
+ copyMethods(FunctionEmitter, StdlibCoreEmitter);
1399
+ copyMethods(FunctionEmitter, BytesEmitter);
1400
+ copyMethods(FunctionEmitter, PurePackagesEmitter);
1401
+ copyMethods(FunctionEmitter, IoEmitter);
1402
+ copyMethods(FunctionEmitter, TimeEmitter);
1403
+ copyMethods(FunctionEmitter, ExtendedPackagesEmitter);
1376
1404
  copyMethods(FunctionEmitter, SharedEmitter);
@@ -52,20 +52,32 @@ export function encodeI64LEB(val) {
52
52
  return bytes;
53
53
  }
54
54
 
55
+ const _f32Arr = new Float32Array(1);
56
+ const _f32Bytes = new Uint8Array(_f32Arr.buffer);
55
57
  export function encodeF32(val) {
56
- const buf = new ArrayBuffer(4);
57
- new DataView(buf).setFloat32(0, Number(val), true); // little-endian
58
- return Array.from(new Uint8Array(buf));
58
+ _f32Arr[0] = Number(val);
59
+ return [_f32Bytes[0], _f32Bytes[1], _f32Bytes[2], _f32Bytes[3]];
59
60
  }
60
61
 
62
+ const _f64Arr = new Float64Array(1);
63
+ const _f64Bytes = new Uint8Array(_f64Arr.buffer);
61
64
  export function encodeF64(val) {
62
- const buf = new ArrayBuffer(8);
63
- new DataView(buf).setFloat64(0, Number(val), true); // little-endian
64
- return Array.from(new Uint8Array(buf));
65
+ _f64Arr[0] = Number(val);
66
+ return [
67
+ _f64Bytes[0],
68
+ _f64Bytes[1],
69
+ _f64Bytes[2],
70
+ _f64Bytes[3],
71
+ _f64Bytes[4],
72
+ _f64Bytes[5],
73
+ _f64Bytes[6],
74
+ _f64Bytes[7],
75
+ ];
65
76
  }
66
77
 
78
+ const _textEncoder = new TextEncoder();
67
79
  function encodeString(str) {
68
- const utf8 = new TextEncoder().encode(str);
80
+ const utf8 = _textEncoder.encode(str);
69
81
  return [...encodeU32LEB(utf8.length), ...utf8];
70
82
  }
71
83
 
@@ -907,31 +919,45 @@ export function encodeModule(mod) {
907
919
  const sectionOffsets = [];
908
920
  const codeSec = encodeCodeSection(mod.funcs, sectionOffsets);
909
921
 
910
- const allBytes = [
911
- ...magic,
912
- ...version,
913
- ...GOFRONT_SECTION_BYTES,
914
- ...typeSec,
915
- ...importSec,
916
- ...funcSec,
917
- ...memorySec,
918
- ...tagSec,
919
- ...globalSec,
920
- ...exportSec,
921
- ...startSec,
922
- ...elemSec,
922
+ const sections = [
923
+ magic,
924
+ version,
925
+ GOFRONT_SECTION_BYTES,
926
+ typeSec,
927
+ importSec,
928
+ funcSec,
929
+ memorySec,
930
+ tagSec,
931
+ globalSec,
932
+ exportSec,
933
+ startSec,
934
+ elemSec,
923
935
  ];
924
- const codeStart = allBytes.length;
936
+ let totalLen = 0;
937
+ for (const s of sections) totalLen += s.length;
938
+ const codeStart = totalLen;
925
939
  mod.codeOffsets = sectionOffsets.map((o) => codeStart + o);
926
- allBytes.push(...codeSec);
940
+ totalLen += codeSec.length;
941
+ let smSec = null;
927
942
  if (mod.sourceMappingURL) {
928
- allBytes.push(
929
- ...encodeSection(0, [
930
- ...encodeString("sourceMappingURL"),
931
- ...encodeString(mod.sourceMappingURL),
932
- ]),
933
- );
943
+ smSec = encodeSection(0, [
944
+ ...encodeString("sourceMappingURL"),
945
+ ...encodeString(mod.sourceMappingURL),
946
+ ]);
947
+ totalLen += smSec.length;
948
+ }
949
+
950
+ const out = new Uint8Array(totalLen);
951
+ let offset = 0;
952
+ for (const s of sections) {
953
+ out.set(s, offset);
954
+ offset += s.length;
955
+ }
956
+ out.set(codeSec, offset);
957
+ offset += codeSec.length;
958
+ if (smSec) {
959
+ out.set(smSec, offset);
934
960
  }
935
961
 
936
- return new Uint8Array(allBytes);
962
+ return out;
937
963
  }