gofront 1.6.0 → 1.7.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 (80) hide show
  1. package/README.md +35 -39
  2. package/package.json +37 -4
  3. package/src/backend/js/expressions.js +559 -144
  4. package/src/backend/js/index.js +174 -49
  5. package/src/backend/js/runtime.js +1518 -54
  6. package/src/backend/js/statements.js +91 -73
  7. package/src/backend/js/stdlib/base64.js +12 -0
  8. package/src/backend/js/stdlib/binary.js +102 -0
  9. package/src/backend/js/stdlib/bits.js +15 -1
  10. package/src/backend/js/stdlib/builder.js +28 -9
  11. package/src/backend/js/stdlib/bytes.js +75 -18
  12. package/src/backend/js/stdlib/errors.js +25 -1
  13. package/src/backend/js/stdlib/fmt.js +70 -13
  14. package/src/backend/js/stdlib/hex.js +17 -0
  15. package/src/backend/js/stdlib/html.js +1 -1
  16. package/src/backend/js/stdlib/index.js +28 -0
  17. package/src/backend/js/stdlib/io.js +105 -3
  18. package/src/backend/js/stdlib/json.js +972 -0
  19. package/src/backend/js/stdlib/math.js +13 -1
  20. package/src/backend/js/stdlib/path.js +18 -4
  21. package/src/backend/js/stdlib/rand.js +10 -5
  22. package/src/backend/js/stdlib/sha256.js +12 -0
  23. package/src/backend/js/stdlib/simd.js +24 -0
  24. package/src/backend/js/stdlib/slices.js +52 -26
  25. package/src/backend/js/stdlib/strconv.js +21 -5
  26. package/src/backend/js/stdlib/strings.js +148 -15
  27. package/src/backend/js/stdlib/sync.js +13 -0
  28. package/src/backend/js/stdlib/time.js +90 -19
  29. package/src/backend/js/stdlib/unicode.js +2 -2
  30. package/src/backend/wasm/boundary.js +1225 -41
  31. package/src/backend/wasm/const-fold.js +205 -0
  32. package/src/backend/wasm/emit-builtins.js +220 -76
  33. package/src/backend/wasm/emit-exprs.js +475 -276
  34. package/src/backend/wasm/emit-maps.js +144 -8
  35. package/src/backend/wasm/emit-stdlib.js +12 -3005
  36. package/src/backend/wasm/emit-stmts.js +264 -270
  37. package/src/backend/wasm/emit.js +124 -87
  38. package/src/backend/wasm/encode.js +140 -29
  39. package/src/backend/wasm/index.js +1001 -32
  40. package/src/backend/wasm/optimize.js +17 -6
  41. package/src/backend/wasm/stdlib/bytes.js +5039 -0
  42. package/src/backend/wasm/stdlib/core.js +8903 -0
  43. package/src/backend/wasm/stdlib/index.js +43 -0
  44. package/src/backend/wasm/stdlib/io.js +2161 -0
  45. package/src/backend/wasm/stdlib/packages.js +2400 -0
  46. package/src/backend/wasm/stdlib/pure.js +720 -0
  47. package/src/backend/wasm/stdlib/simd.js +234 -0
  48. package/src/backend/wasm/stdlib/time.js +1200 -0
  49. package/src/backend/wasm/types.js +174 -1
  50. package/src/backend/wasm/wat.js +37 -0
  51. package/src/cli-core.js +1 -1
  52. package/src/compiler.js +18 -4
  53. package/src/dts-parser.js +150 -140
  54. package/src/index.js +2 -6
  55. package/src/lexer.js +80 -29
  56. package/src/lower/boxing.js +1 -7
  57. package/src/lower/captures.js +2 -6
  58. package/src/lower/embedding.js +2 -1
  59. package/src/lower/ownership.js +30 -9
  60. package/src/minifier.js +20 -68
  61. package/src/parser/declarations.js +3 -6
  62. package/src/parser/expressions.js +3 -8
  63. package/src/parser/index.js +18 -3
  64. package/src/parser/statements.js +25 -38
  65. package/src/parser/struct-tag.js +144 -0
  66. package/src/parser/types.js +64 -8
  67. package/src/resolver.js +2 -27
  68. package/src/templ-lexer.js +29 -63
  69. package/src/templ-parser.js +22 -45
  70. package/src/typechecker/assignability.js +126 -13
  71. package/src/typechecker/expressions.js +168 -16
  72. package/src/typechecker/index.js +35 -8
  73. package/src/typechecker/resolve.js +187 -8
  74. package/src/typechecker/statements.js +68 -9
  75. package/src/typechecker/stdlib/core.js +280 -89
  76. package/src/typechecker/stdlib/extended.js +490 -1
  77. package/src/typechecker/stdlib/simd.js +73 -0
  78. package/src/typechecker/stdlib.js +2 -0
  79. package/src/typechecker/types.js +362 -9
  80. package/src/vendor.js +86 -11
@@ -1,6 +1,7 @@
1
1
  // src/backend/wasm/emit-exprs.js
2
2
  // FunctionEmitter mixin: expression emission (literals, operators, indexing, conversions).
3
3
 
4
+ import { foldConstExpr } from "./const-fold.js";
4
5
  import {
5
6
  getMapKeyValTypes,
6
7
  isAnyType,
@@ -65,8 +66,7 @@ export class ExprsEmitter {
65
66
  this.emitCloneStruct(sInfo, structWType);
66
67
  }
67
68
  }
68
- const box = this.mod.getBoxType(goType);
69
- this.pushInstruction({ op: "struct.new", typeIndex: box.typeIndex });
69
+ this._emitBoxValue(this.mod.getIfaceBoxType(goType));
70
70
  return;
71
71
  }
72
72
 
@@ -83,15 +83,23 @@ export class ExprsEmitter {
83
83
 
84
84
  // Scalar type -> box into struct
85
85
  const scalarType = goType ?? { kind: "basic", name: "int" };
86
- const box = this.mod.getBoxType(scalarType);
86
+ const box = this.mod.getIfaceBoxType(scalarType);
87
87
  this._emitRawExpr(expr, box.wType);
88
- this.pushInstruction({ op: "struct.new", typeIndex: box.typeIndex });
88
+ this._emitBoxValue(box);
89
89
  return;
90
90
  }
91
91
 
92
92
  this._emitRawExpr(expr, targetWasmType);
93
93
  }
94
94
 
95
+ _emitBoxValue(box) {
96
+ this.pushInstruction({
97
+ op: "ref.null",
98
+ heapType: box.tagTypeIndex,
99
+ });
100
+ this.pushInstruction({ op: "struct.new", typeIndex: box.typeIndex });
101
+ }
102
+
95
103
  _resolveExprGoType(expr) {
96
104
  if (expr._type) return expr._type;
97
105
  if (expr.kind === "BasicLit") {
@@ -121,6 +129,17 @@ export class ExprsEmitter {
121
129
  }
122
130
 
123
131
  _emitRawExpr(expr, targetWasmType = null) {
132
+ if (expr.kind === "BinaryExpr" || expr.kind === "UnaryExpr") {
133
+ const folded = foldConstExpr(expr, (name) =>
134
+ this.mod.pkgConsts?.get(name),
135
+ );
136
+ if (folded) {
137
+ folded._type = expr._type;
138
+ this.emitBasicLit(folded, targetWasmType);
139
+ return;
140
+ }
141
+ }
142
+
124
143
  switch (expr.kind) {
125
144
  case "BasicLit":
126
145
  this.emitBasicLit(expr, targetWasmType);
@@ -157,6 +176,16 @@ export class ExprsEmitter {
157
176
  this.emitMathConst(expr.field, targetWasmType);
158
177
  else if (this._isUtf8Const(expr))
159
178
  this.emitUtf8Const(expr.field, targetWasmType);
179
+ else if (this._isStrconvConst(expr))
180
+ this.emitStrconvConst(expr.field, targetWasmType);
181
+ else if (this._isPathConst(expr))
182
+ this.emitPathConst(expr.field, targetWasmType);
183
+ else if (this._isIoConst(expr))
184
+ this.emitIoConst(expr.field, targetWasmType);
185
+ else if (this._isTimeConst(expr))
186
+ this.emitTimeConst(expr.field, targetWasmType);
187
+ else if (this._isExtendedPkgConst?.(expr))
188
+ this.emitExtendedPkgConst(expr, targetWasmType);
160
189
  else this.emitSelectorExpr(expr);
161
190
  break;
162
191
  }
@@ -218,35 +247,7 @@ export class ExprsEmitter {
218
247
  if (capturedNames.length === 0) {
219
248
  this.pushInstruction({ op: "ref.null", heapType: "any" });
220
249
  } else {
221
- const envFields = [];
222
- for (const name of capturedNames) {
223
- const isBoxed = this.mutatedCaptures.has(name);
224
- const goType = this._findCapturedVarGoType(name);
225
- if (isBoxed) {
226
- const boxInfo = this.mod.getBoxType(goType);
227
- envFields.push({
228
- name,
229
- goType,
230
- isBoxed: true,
231
- boxInfo,
232
- wType: {
233
- kind: "ref",
234
- nullable: true,
235
- typeIndex: boxInfo.typeIndex,
236
- },
237
- });
238
- } else {
239
- const wType = this.toWasmType(goType);
240
- envFields.push({
241
- name,
242
- goType,
243
- isBoxed: false,
244
- boxInfo: null,
245
- wType,
246
- });
247
- }
248
- }
249
-
250
+ const envFields = this._buildEnvFields(capturedNames);
250
251
  const envInfo = this.mod.getEnvType(envFields);
251
252
 
252
253
  for (const field of envFields) {
@@ -274,6 +275,17 @@ export class ExprsEmitter {
274
275
 
275
276
  emitCompositeLit(lit) {
276
277
  const litType = lit._type ?? lit.typeExpr?._type ?? lit.typeExpr;
278
+ if (
279
+ litType?.kind === "simd" ||
280
+ litType?.name === "simd.F32x4" ||
281
+ litType?.underlying?.kind === "simd" ||
282
+ (lit.typeExpr?.kind === "SelectorExpr" &&
283
+ lit.typeExpr.field === "F32x4" &&
284
+ lit.typeExpr.expr?.name === "simd")
285
+ ) {
286
+ this.emitSimdCompositeLit(lit);
287
+ return;
288
+ }
277
289
  if (isMapType(litType, this.mod.checker)) {
278
290
  const { keyType, valType } = getMapKeyValTypes(litType, this.mod.checker);
279
291
  const mapInfo = this.mod.getMapType(keyType, valType);
@@ -363,7 +375,10 @@ export class ExprsEmitter {
363
375
  const typeName =
364
376
  lit.typeExpr?.name ??
365
377
  (litType?.kind === "named" ? litType.name : litType?.name);
366
- const structInfo = this.mod.getStructType(typeName);
378
+ let structInfo = typeName ? this.mod.getStructType(typeName) : null;
379
+ if (!structInfo) {
380
+ structInfo = this._resolveStructInfo(lit);
381
+ }
367
382
  if (!structInfo) {
368
383
  throw new Error(`Unknown struct in CompositeLit: ${typeName}`);
369
384
  }
@@ -780,6 +795,59 @@ export class ExprsEmitter {
780
795
  this.pushInstruction({ op: "local.set", index: dstArrTmp });
781
796
 
782
797
  const iTmp = this.acquireTemp("i32");
798
+
799
+ // If copying within the same array and shifting right (dstOff > srcOff),
800
+ // copy backwards to prevent clobbering overlapping elements.
801
+ this.pushInstruction({ op: "local.get", index: dstArrTmp });
802
+ this.pushInstruction({ op: "local.get", index: srcArrTmp });
803
+ this.pushInstruction("ref.eq");
804
+ this.pushInstruction({ op: "local.get", index: dstOffTmp });
805
+ this.pushInstruction({ op: "local.get", index: srcOffTmp });
806
+ this.pushInstruction("i32.gt_u");
807
+ this.pushInstruction("i32.and");
808
+ this.pushInstruction({ op: "if", blockType: "void" });
809
+
810
+ // Backwards copy: i from lenTmp - 1 down to 0
811
+ this.pushInstruction({ op: "local.get", index: lenTmp });
812
+ this.pushInstruction({ op: "i32.const", value: 1 });
813
+ this.pushInstruction("i32.sub");
814
+ this.pushInstruction({ op: "local.set", index: iTmp });
815
+
816
+ this.pushInstruction({ op: "block", blockType: "void" });
817
+ this.pushInstruction({ op: "loop", blockType: "void" });
818
+ this.pushInstruction({ op: "local.get", index: iTmp });
819
+ this.pushInstruction({ op: "i32.const", value: 0 });
820
+ this.pushInstruction("i32.lt_s");
821
+ this.pushInstruction({ op: "br_if", depth: 1 });
822
+
823
+ this.pushInstruction({ op: "local.get", index: dstArrTmp });
824
+ this.pushInstruction({ op: "local.get", index: dstOffTmp });
825
+ this.pushInstruction({ op: "local.get", index: iTmp });
826
+ this.pushInstruction("i32.add");
827
+
828
+ this.pushInstruction({ op: "local.get", index: srcArrTmp });
829
+ this.pushInstruction({ op: "local.get", index: srcOffTmp });
830
+ this.pushInstruction({ op: "local.get", index: iTmp });
831
+ this.pushInstruction("i32.add");
832
+ this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
833
+
834
+ if (sInfo) {
835
+ this.emitCloneStruct(sInfo, arrInfo.elemWType);
836
+ }
837
+
838
+ this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
839
+
840
+ this.pushInstruction({ op: "local.get", index: iTmp });
841
+ this.pushInstruction({ op: "i32.const", value: 1 });
842
+ this.pushInstruction("i32.sub");
843
+ this.pushInstruction({ op: "local.set", index: iTmp });
844
+ this.pushInstruction({ op: "br", depth: 0 });
845
+ this.pushInstruction("end");
846
+ this.pushInstruction("end");
847
+
848
+ this.pushInstruction("else");
849
+
850
+ // Forwards copy: i from 0 up to lenTmp - 1
783
851
  this.pushInstruction({ op: "i32.const", value: 0 });
784
852
  this.pushInstruction({ op: "local.set", index: iTmp });
785
853
 
@@ -815,6 +883,8 @@ export class ExprsEmitter {
815
883
  this.pushInstruction("end");
816
884
  this.pushInstruction("end");
817
885
 
886
+ this.pushInstruction("end");
887
+
818
888
  this.releaseTemp(iTmp, "i32");
819
889
  this.releaseTemp(dstArrTmp, {
820
890
  kind: "ref",
@@ -964,6 +1034,31 @@ export class ExprsEmitter {
964
1034
  nullable: true,
965
1035
  typeIndex: arrInfo.typeIndex,
966
1036
  };
1037
+
1038
+ const rawBaseType = baseNode._type ?? this._resolveExprGoType(baseNode);
1039
+ const baseType =
1040
+ rawBaseType?.kind === "pointer" ? rawBaseType.base : rawBaseType;
1041
+ const arrayLen =
1042
+ baseType?.kind === "array" ? (baseType.len ?? baseType.size) : null;
1043
+ let constIdx = null;
1044
+ if (indexNode.kind === "BasicLit" && indexNode.litKind === "INT") {
1045
+ try {
1046
+ const v = Number(indexNode.value);
1047
+ if (Number.isInteger(v)) constIdx = v;
1048
+ } catch {}
1049
+ }
1050
+ if (
1051
+ arrayLen !== null &&
1052
+ arrayLen !== undefined &&
1053
+ constIdx !== null &&
1054
+ constIdx >= 0 &&
1055
+ constIdx < arrayLen
1056
+ ) {
1057
+ this.emitExpr(baseNode, arrWType);
1058
+ this.pushInstruction({ op: "i32.const", value: constIdx });
1059
+ return;
1060
+ }
1061
+
967
1062
  const arrTmp = this.acquireTemp(arrWType);
968
1063
  this.emitExpr(baseNode, arrWType);
969
1064
  this.pushInstruction({ op: "local.tee", index: arrTmp });
@@ -998,14 +1093,16 @@ export class ExprsEmitter {
998
1093
  (lit.litKind === "INT" ? "i64" : lit.litKind === "FLOAT" ? "f64" : "i32");
999
1094
 
1000
1095
  if (lit.litKind === "INT") {
1001
- const n = Number(lit.value);
1096
+ const s = String(lit.value);
1097
+ const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
1098
+ const n = Number(clean);
1002
1099
  if (wType === "i32") {
1003
1100
  this.pushInstruction({ op: "i32.const", value: n | 0 });
1004
1101
  } else if (wType === "f64" || wType === "f32") {
1005
1102
  // Untyped integer constant in a float context (`x * 2`).
1006
1103
  this.pushInstruction({ op: `${wType}.const`, value: n });
1007
1104
  } else {
1008
- this.pushInstruction({ op: "i64.const", value: BigInt(lit.value) });
1105
+ this.pushInstruction({ op: "i64.const", value: BigInt(clean) });
1009
1106
  }
1010
1107
  return;
1011
1108
  }
@@ -1029,31 +1126,7 @@ export class ExprsEmitter {
1029
1126
  }
1030
1127
 
1031
1128
  if (lit.litKind === "NIL") {
1032
- const effectiveType = targetWasmType ?? this.toWasmType(lit._type);
1033
- if (
1034
- (lit._type && isSliceType(lit._type, this.mod.checker)) ||
1035
- (effectiveType &&
1036
- typeof effectiveType === "object" &&
1037
- typeof effectiveType.typeIndex === "number" &&
1038
- this.mod.getSliceTypeByIndex(effectiveType.typeIndex))
1039
- ) {
1040
- const elemType = lit._type ? this._getSliceElemType(lit._type) : null;
1041
- const sliceInfo = elemType
1042
- ? this.mod.getSliceType(elemType)
1043
- : this.mod.getSliceTypeByIndex(effectiveType.typeIndex);
1044
- if (sliceInfo) {
1045
- this.pushInstruction({
1046
- op: "global.get",
1047
- index: sliceInfo.emptyGlobalIndex,
1048
- });
1049
- return;
1050
- }
1051
- }
1052
- const heapType =
1053
- typeof effectiveType === "object" && effectiveType !== null
1054
- ? (effectiveType.typeIndex ?? effectiveType.heapType ?? "any")
1055
- : "any";
1056
- this.pushInstruction({ op: "ref.null", heapType });
1129
+ this._emitNilValue(lit._type, targetWasmType);
1057
1130
  return;
1058
1131
  }
1059
1132
 
@@ -1068,6 +1141,42 @@ export class ExprsEmitter {
1068
1141
  }
1069
1142
  }
1070
1143
 
1144
+ _emitNilValue(goType, targetWasmType) {
1145
+ const effectiveType = targetWasmType ?? this.toWasmType(goType);
1146
+ if (
1147
+ (goType && isSliceType(goType, this.mod.checker)) ||
1148
+ (effectiveType &&
1149
+ typeof effectiveType === "object" &&
1150
+ typeof effectiveType.typeIndex === "number" &&
1151
+ this.mod.getSliceTypeByIndex(effectiveType.typeIndex))
1152
+ ) {
1153
+ const elemType = goType ? this._getSliceElemType(goType) : null;
1154
+ const sliceInfo = elemType
1155
+ ? this.mod.getSliceType(elemType)
1156
+ : this.mod.getSliceTypeByIndex(effectiveType.typeIndex);
1157
+ if (sliceInfo) {
1158
+ this.pushInstruction({
1159
+ op: "global.get",
1160
+ index: sliceInfo.emptyGlobalIndex,
1161
+ });
1162
+ return;
1163
+ }
1164
+ }
1165
+ const heapType =
1166
+ typeof effectiveType === "object" && effectiveType !== null
1167
+ ? (effectiveType.typeIndex ?? effectiveType.heapType ?? "any")
1168
+ : "any";
1169
+ this.pushInstruction({ op: "ref.null", heapType });
1170
+ }
1171
+
1172
+ _coerceIntWidth(srcType, targetWasmType) {
1173
+ if (srcType === "i32" && targetWasmType === "i64") {
1174
+ this.pushInstruction("i64.extend_i32_s");
1175
+ } else if (srcType === "i64" && targetWasmType === "i32") {
1176
+ this.pushInstruction("i32.wrap_i64");
1177
+ }
1178
+ }
1179
+
1071
1180
  emitIdent(ident, targetWasmType = null) {
1072
1181
  if (ident.name === "true") {
1073
1182
  this.pushInstruction({ op: "i32.const", value: 1 });
@@ -1078,33 +1187,7 @@ export class ExprsEmitter {
1078
1187
  return;
1079
1188
  }
1080
1189
  if (ident.name === "nil") {
1081
- const effectiveType = targetWasmType ?? this.toWasmType(ident._type);
1082
- if (
1083
- (ident._type && isSliceType(ident._type, this.mod.checker)) ||
1084
- (effectiveType &&
1085
- typeof effectiveType === "object" &&
1086
- typeof effectiveType.typeIndex === "number" &&
1087
- this.mod.getSliceTypeByIndex(effectiveType.typeIndex))
1088
- ) {
1089
- const elemType = ident._type
1090
- ? this._getSliceElemType(ident._type)
1091
- : null;
1092
- const sliceInfo = elemType
1093
- ? this.mod.getSliceType(elemType)
1094
- : this.mod.getSliceTypeByIndex(effectiveType.typeIndex);
1095
- if (sliceInfo) {
1096
- this.pushInstruction({
1097
- op: "global.get",
1098
- index: sliceInfo.emptyGlobalIndex,
1099
- });
1100
- return;
1101
- }
1102
- }
1103
- const heapType =
1104
- typeof effectiveType === "object" && effectiveType !== null
1105
- ? (effectiveType.typeIndex ?? effectiveType.heapType ?? "any")
1106
- : "any";
1107
- this.pushInstruction({ op: "ref.null", heapType });
1190
+ this._emitNilValue(ident._type, targetWasmType);
1108
1191
  return;
1109
1192
  }
1110
1193
 
@@ -1120,33 +1203,21 @@ export class ExprsEmitter {
1120
1203
  } else {
1121
1204
  this.pushInstruction({ op: "local.get", index: localInfo.index });
1122
1205
  }
1123
- if (localInfo.type === "i32" && targetWasmType === "i64") {
1124
- this.pushInstruction("i64.extend_i32_s");
1125
- } else if (localInfo.type === "i64" && targetWasmType === "i32") {
1126
- this.pushInstruction("i32.wrap_i64");
1127
- }
1206
+ this._coerceIntWidth(localInfo.type, targetWasmType);
1128
1207
  return;
1129
1208
  }
1130
1209
 
1131
1210
  if (this.cachedGlobals.has(ident.name)) {
1132
1211
  const cached = this.cachedGlobals.get(ident.name);
1133
1212
  this.pushInstruction({ op: "local.get", index: cached.index });
1134
- if (cached.globalInfo.type === "i32" && targetWasmType === "i64") {
1135
- this.pushInstruction("i64.extend_i32_s");
1136
- } else if (cached.globalInfo.type === "i64" && targetWasmType === "i32") {
1137
- this.pushInstruction("i32.wrap_i64");
1138
- }
1213
+ this._coerceIntWidth(cached.globalInfo.type, targetWasmType);
1139
1214
  return;
1140
1215
  }
1141
1216
 
1142
1217
  const globalInfo = this.mod.resolveGlobal(ident.name);
1143
1218
  if (globalInfo) {
1144
1219
  this.pushInstruction({ op: "global.get", index: globalInfo.index });
1145
- if (globalInfo.type === "i32" && targetWasmType === "i64") {
1146
- this.pushInstruction("i64.extend_i32_s");
1147
- } else if (globalInfo.type === "i64" && targetWasmType === "i32") {
1148
- this.pushInstruction("i32.wrap_i64");
1149
- }
1220
+ this._coerceIntWidth(globalInfo.type, targetWasmType);
1150
1221
  return;
1151
1222
  }
1152
1223
 
@@ -1158,11 +1229,6 @@ export class ExprsEmitter {
1158
1229
  );
1159
1230
  return;
1160
1231
  }
1161
- if (this.mod.isNonLiteralConst(ident.name)) {
1162
- throw new Error(
1163
- `constant '${ident.name}' has a non-literal value, which the wasm backend does not support yet (planned)`,
1164
- );
1165
- }
1166
1232
 
1167
1233
  const trampName = `_tramp$${ident.name}`;
1168
1234
  const trampFuncIdx = this.mod.resolveFuncIndex(trampName);
@@ -1256,6 +1322,13 @@ export class ExprsEmitter {
1256
1322
  break;
1257
1323
 
1258
1324
  case "&": {
1325
+ if (operand.kind === "Ident") {
1326
+ const loc = this.resolveLocal(operand.name);
1327
+ if (loc?.isBoxed) {
1328
+ this.pushInstruction({ op: "local.get", index: loc.index });
1329
+ return;
1330
+ }
1331
+ }
1259
1332
  if (operand.kind === "CompositeLit") {
1260
1333
  this.emitExpr(operand);
1261
1334
  return;
@@ -1526,6 +1599,26 @@ export class ExprsEmitter {
1526
1599
  this.pushInstruction(`${wType}.mul`);
1527
1600
  this.emitNarrowIntWrap(goType);
1528
1601
  break;
1602
+ case "/":
1603
+ if (wType === "f32" || wType === "f64") {
1604
+ this.pushInstruction(`${wType}.div`);
1605
+ } else {
1606
+ this.pushInstruction(signed ? `${wType}.div_s` : `${wType}.div_u`);
1607
+ this.emitNarrowIntWrap(goType);
1608
+ }
1609
+ break;
1610
+ case "%":
1611
+ this.pushInstruction(signed ? `${wType}.rem_s` : `${wType}.rem_u`);
1612
+ this.emitNarrowIntWrap(goType);
1613
+ break;
1614
+ case "<<":
1615
+ this.pushInstruction(`${wType}.shl`);
1616
+ this.emitNarrowIntWrap(goType);
1617
+ break;
1618
+ case ">>":
1619
+ this.pushInstruction(signed ? `${wType}.shr_s` : `${wType}.shr_u`);
1620
+ this.emitNarrowIntWrap(goType);
1621
+ break;
1529
1622
 
1530
1623
  case "&":
1531
1624
  this.pushInstruction(`${wType}.and`);
@@ -1576,45 +1669,21 @@ export class ExprsEmitter {
1576
1669
  }
1577
1670
  break;
1578
1671
  case "<":
1579
- if (isStringType(goType, this.mod.checker) || wType === "externref") {
1580
- const cmpIdx = this.mod.getStringCmpImportIndex("<");
1581
- this.pushInstruction({ op: "call", funcIndex: cmpIdx });
1582
- } else if (wType === "f32" || wType === "f64") {
1583
- this.pushInstruction(`${wType}.lt`);
1584
- } else {
1585
- this.pushInstruction(`${wType}.lt_${signed ? "s" : "u"}`);
1586
- }
1587
- break;
1588
1672
  case "<=":
1589
- if (isStringType(goType, this.mod.checker) || wType === "externref") {
1590
- const cmpIdx = this.mod.getStringCmpImportIndex("<=");
1591
- this.pushInstruction({ op: "call", funcIndex: cmpIdx });
1592
- } else if (wType === "f32" || wType === "f64") {
1593
- this.pushInstruction(`${wType}.le`);
1594
- } else {
1595
- this.pushInstruction(`${wType}.le_${signed ? "s" : "u"}`);
1596
- }
1597
- break;
1598
1673
  case ">":
1674
+ case ">=": {
1599
1675
  if (isStringType(goType, this.mod.checker) || wType === "externref") {
1600
- const cmpIdx = this.mod.getStringCmpImportIndex(">");
1601
- this.pushInstruction({ op: "call", funcIndex: cmpIdx });
1602
- } else if (wType === "f32" || wType === "f64") {
1603
- this.pushInstruction(`${wType}.gt`);
1604
- } else {
1605
- this.pushInstruction(`${wType}.gt_${signed ? "s" : "u"}`);
1606
- }
1607
- break;
1608
- case ">=":
1609
- if (isStringType(goType, this.mod.checker) || wType === "externref") {
1610
- const cmpIdx = this.mod.getStringCmpImportIndex(">=");
1676
+ const cmpIdx = this.mod.getStringCmpImportIndex(op);
1611
1677
  this.pushInstruction({ op: "call", funcIndex: cmpIdx });
1612
- } else if (wType === "f32" || wType === "f64") {
1613
- this.pushInstruction(`${wType}.ge`);
1614
1678
  } else {
1615
- this.pushInstruction(`${wType}.ge_${signed ? "s" : "u"}`);
1679
+ const name =
1680
+ op === "<" ? "lt" : op === "<=" ? "le" : op === ">" ? "gt" : "ge";
1681
+ const suffix =
1682
+ wType === "f32" || wType === "f64" ? "" : `_${signed ? "s" : "u"}`;
1683
+ this.pushInstruction(`${wType}.${name}${suffix}`);
1616
1684
  }
1617
1685
  break;
1686
+ }
1618
1687
 
1619
1688
  default:
1620
1689
  throw new Error(`Unsupported binary operator: ${op}`);
@@ -1662,6 +1731,56 @@ export class ExprsEmitter {
1662
1731
  }
1663
1732
 
1664
1733
  const signed = isSigned(goType);
1734
+ const is64 = wType === "i64";
1735
+ const divOp = `${wType}.${op === "/" ? "div" : "rem"}_${signed ? "s" : "u"}`;
1736
+
1737
+ let constDivisor = null;
1738
+ if (right.kind === "BasicLit" && right.litKind === "INT") {
1739
+ try {
1740
+ constDivisor = BigInt(right.value);
1741
+ } catch {}
1742
+ }
1743
+
1744
+ if (constDivisor !== null && constDivisor !== 0n && constDivisor !== -1n) {
1745
+ // Specialize unsigned powers-of-two to shifts or bitwise masks
1746
+ if (
1747
+ !signed &&
1748
+ constDivisor > 0n &&
1749
+ (constDivisor & (constDivisor - 1n)) === 0n
1750
+ ) {
1751
+ let k = 0n;
1752
+ let temp = constDivisor;
1753
+ while (temp > 1n) {
1754
+ temp >>= 1n;
1755
+ k++;
1756
+ }
1757
+ if (op === "/") {
1758
+ this.emitExpr(left, wType);
1759
+ this.pushInstruction({
1760
+ op: `${wType}.const`,
1761
+ value: is64 ? k : Number(k),
1762
+ });
1763
+ this.pushInstruction(`${wType}.shr_u`);
1764
+ } else {
1765
+ this.emitExpr(left, wType);
1766
+ this.pushInstruction({
1767
+ op: `${wType}.const`,
1768
+ value: is64 ? constDivisor - 1n : Number(constDivisor - 1n),
1769
+ });
1770
+ this.pushInstruction(`${wType}.and`);
1771
+ }
1772
+ if (!is64) this.emitNarrowIntWrap(goType);
1773
+ return;
1774
+ }
1775
+
1776
+ // Constant divisor != 0 and != -1: no dynamic zero or overflow check needed
1777
+ this.emitExpr(left, wType);
1778
+ this.emitExpr(right, wType);
1779
+ this.pushInstruction(divOp);
1780
+ if (!is64) this.emitNarrowIntWrap(goType);
1781
+ return;
1782
+ }
1783
+
1665
1784
  const tmpA = this.acquireTemp(wType);
1666
1785
  const tmpB = this.acquireTemp(wType);
1667
1786
 
@@ -1678,64 +1797,34 @@ export class ExprsEmitter {
1678
1797
  this.emitPanic("runtime error: integer divide by zero");
1679
1798
  this.pushInstruction("end");
1680
1799
 
1681
- if (wType === "i32") {
1800
+ if (wType === "i32" || wType === "i64") {
1682
1801
  if (signed) {
1683
- // MinInt32 / -1 wrap guard
1802
+ const minVal = is64 ? -9223372036854775808n : -2147483648;
1803
+ const negOne = is64 ? -1n : -1;
1804
+ const zeroVal = is64 ? 0n : 0;
1684
1805
  this.pushInstruction({ op: "local.get", index: tmpA });
1685
- this.pushInstruction({ op: "i32.const", value: -2147483648 });
1686
- this.pushInstruction("i32.eq");
1806
+ this.pushInstruction({ op: `${wType}.const`, value: minVal });
1807
+ this.pushInstruction(`${wType}.eq`);
1687
1808
  this.pushInstruction({ op: "local.get", index: tmpB });
1688
- this.pushInstruction({ op: "i32.const", value: -1 });
1689
- this.pushInstruction("i32.eq");
1809
+ this.pushInstruction({ op: `${wType}.const`, value: negOne });
1810
+ this.pushInstruction(`${wType}.eq`);
1690
1811
  this.pushInstruction("i32.and");
1691
- this.pushInstruction({ op: "if", blockType: "i32" });
1692
- if (op === "/") {
1693
- this.pushInstruction({ op: "i32.const", value: -2147483648 });
1694
- } else {
1695
- this.pushInstruction({ op: "i32.const", value: 0 });
1696
- }
1697
- this.pushInstruction("else");
1698
- this.pushInstruction({ op: "local.get", index: tmpA });
1699
- this.pushInstruction({ op: "local.get", index: tmpB });
1700
- this.pushInstruction(op === "/" ? "i32.div_s" : "i32.rem_s");
1701
- this.pushInstruction("end");
1702
- } else {
1703
- this.pushInstruction({ op: "local.get", index: tmpA });
1704
- this.pushInstruction({ op: "local.get", index: tmpB });
1705
- this.pushInstruction(op === "/" ? "i32.div_u" : "i32.rem_u");
1706
- }
1707
- this.emitNarrowIntWrap(goType);
1708
- } else if (wType === "i64") {
1709
- if (signed) {
1710
- this.pushInstruction({ op: "local.get", index: tmpA });
1812
+ this.pushInstruction({ op: "if", blockType: wType });
1711
1813
  this.pushInstruction({
1712
- op: "i64.const",
1713
- value: -9223372036854775808n,
1814
+ op: `${wType}.const`,
1815
+ value: op === "/" ? minVal : zeroVal,
1714
1816
  });
1715
- this.pushInstruction("i64.eq");
1716
- this.pushInstruction({ op: "local.get", index: tmpB });
1717
- this.pushInstruction({ op: "i64.const", value: -1n });
1718
- this.pushInstruction("i64.eq");
1719
- this.pushInstruction("i32.and");
1720
- this.pushInstruction({ op: "if", blockType: "i64" });
1721
- if (op === "/") {
1722
- this.pushInstruction({
1723
- op: "i64.const",
1724
- value: -9223372036854775808n,
1725
- });
1726
- } else {
1727
- this.pushInstruction({ op: "i64.const", value: 0n });
1728
- }
1729
1817
  this.pushInstruction("else");
1730
1818
  this.pushInstruction({ op: "local.get", index: tmpA });
1731
1819
  this.pushInstruction({ op: "local.get", index: tmpB });
1732
- this.pushInstruction(op === "/" ? "i64.div_s" : "i64.rem_s");
1820
+ this.pushInstruction(divOp);
1733
1821
  this.pushInstruction("end");
1734
1822
  } else {
1735
1823
  this.pushInstruction({ op: "local.get", index: tmpA });
1736
1824
  this.pushInstruction({ op: "local.get", index: tmpB });
1737
- this.pushInstruction(op === "/" ? "i64.div_u" : "i64.rem_u");
1825
+ this.pushInstruction(divOp);
1738
1826
  }
1827
+ if (!is64) this.emitNarrowIntWrap(goType);
1739
1828
  }
1740
1829
 
1741
1830
  this.releaseTemp(tmpB, wType);
@@ -1747,6 +1836,10 @@ export class ExprsEmitter {
1747
1836
  const wType = targetWasmType ?? toWasmType(goType, this.mod.checker);
1748
1837
  const signed = isSigned(goType);
1749
1838
  const rightWType = toWasmType(right._type, this.mod.checker);
1839
+ const is64 = wType === "i64";
1840
+ const zeroVal = is64 ? 0n : 0;
1841
+ const negOne = is64 ? -1n : -1;
1842
+ const widthVal = is64 ? 64n : 32;
1750
1843
 
1751
1844
  const tmpVal = this.acquireTemp(wType);
1752
1845
  const tmpShift = this.acquireTemp(wType);
@@ -1762,68 +1855,34 @@ export class ExprsEmitter {
1762
1855
  }
1763
1856
  this.pushInstruction({ op: "local.set", index: tmpShift });
1764
1857
 
1765
- if (wType === "i32") {
1766
- this.pushInstruction({ op: "local.get", index: tmpShift });
1767
- this.pushInstruction({ op: "i32.const", value: 32 });
1768
- this.pushInstruction("i32.ge_u");
1769
- this.pushInstruction({ op: "if", blockType: "i32" });
1770
-
1771
- if (op === ">>" && signed) {
1772
- this.pushInstruction({ op: "local.get", index: tmpVal });
1773
- this.pushInstruction({ op: "i32.const", value: 0 });
1774
- this.pushInstruction("i32.lt_s");
1775
- this.pushInstruction({ op: "if", blockType: "i32" });
1776
- this.pushInstruction({ op: "i32.const", value: -1 });
1777
- this.pushInstruction("else");
1778
- this.pushInstruction({ op: "i32.const", value: 0 });
1779
- this.pushInstruction("end");
1780
- } else {
1781
- this.pushInstruction({ op: "i32.const", value: 0 });
1782
- }
1783
-
1784
- this.pushInstruction("else");
1858
+ this.pushInstruction({ op: "local.get", index: tmpShift });
1859
+ this.pushInstruction({ op: `${wType}.const`, value: widthVal });
1860
+ this.pushInstruction(`${wType}.ge_u`);
1861
+ this.pushInstruction({ op: "if", blockType: wType });
1785
1862
 
1863
+ if (op === ">>" && signed) {
1786
1864
  this.pushInstruction({ op: "local.get", index: tmpVal });
1787
- this.pushInstruction({ op: "local.get", index: tmpShift });
1788
- if (op === "<<") {
1789
- this.pushInstruction("i32.shl");
1790
- } else {
1791
- this.pushInstruction(signed ? "i32.shr_s" : "i32.shr_u");
1792
- }
1793
-
1865
+ this.pushInstruction({ op: `${wType}.const`, value: zeroVal });
1866
+ this.pushInstruction(`${wType}.lt_s`);
1867
+ this.pushInstruction({ op: "if", blockType: wType });
1868
+ this.pushInstruction({ op: `${wType}.const`, value: negOne });
1869
+ this.pushInstruction("else");
1870
+ this.pushInstruction({ op: `${wType}.const`, value: zeroVal });
1794
1871
  this.pushInstruction("end");
1795
- this.emitNarrowIntWrap(goType);
1796
1872
  } else {
1797
- this.pushInstruction({ op: "local.get", index: tmpShift });
1798
- this.pushInstruction({ op: "i64.const", value: 64n });
1799
- this.pushInstruction("i64.ge_u");
1800
- this.pushInstruction({ op: "if", blockType: "i64" });
1801
-
1802
- if (op === ">>" && signed) {
1803
- this.pushInstruction({ op: "local.get", index: tmpVal });
1804
- this.pushInstruction({ op: "i64.const", value: 0n });
1805
- this.pushInstruction("i64.lt_s");
1806
- this.pushInstruction({ op: "if", blockType: "i64" });
1807
- this.pushInstruction({ op: "i64.const", value: -1n });
1808
- this.pushInstruction("else");
1809
- this.pushInstruction({ op: "i64.const", value: 0n });
1810
- this.pushInstruction("end");
1811
- } else {
1812
- this.pushInstruction({ op: "i64.const", value: 0n });
1813
- }
1873
+ this.pushInstruction({ op: `${wType}.const`, value: zeroVal });
1874
+ }
1814
1875
 
1815
- this.pushInstruction("else");
1876
+ this.pushInstruction("else");
1816
1877
 
1817
- this.pushInstruction({ op: "local.get", index: tmpVal });
1818
- this.pushInstruction({ op: "local.get", index: tmpShift });
1819
- if (op === "<<") {
1820
- this.pushInstruction("i64.shl");
1821
- } else {
1822
- this.pushInstruction(signed ? "i64.shr_s" : "i64.shr_u");
1823
- }
1878
+ this.pushInstruction({ op: "local.get", index: tmpVal });
1879
+ this.pushInstruction({ op: "local.get", index: tmpShift });
1880
+ this.pushInstruction(
1881
+ op === "<<" ? `${wType}.shl` : `${wType}.shr_${signed ? "s" : "u"}`,
1882
+ );
1824
1883
 
1825
- this.pushInstruction("end");
1826
- }
1884
+ this.pushInstruction("end");
1885
+ if (!is64) this.emitNarrowIntWrap(goType);
1827
1886
 
1828
1887
  this.releaseTemp(tmpShift, wType);
1829
1888
  this.releaseTemp(tmpVal, wType);
@@ -1842,6 +1901,24 @@ export class ExprsEmitter {
1842
1901
  return;
1843
1902
  }
1844
1903
 
1904
+ if (
1905
+ fromWType === "externref" &&
1906
+ isSliceType(targetType, this.mod.checker) &&
1907
+ (targetType.elem?.name === "byte" || targetType.elem?.name === "uint8")
1908
+ ) {
1909
+ this._emitStringToByteSlice(targetType);
1910
+ return;
1911
+ }
1912
+
1913
+ if (
1914
+ toWType === "externref" &&
1915
+ isSliceType(fromType, this.mod.checker) &&
1916
+ (fromType.elem?.name === "byte" || fromType.elem?.name === "uint8")
1917
+ ) {
1918
+ this._emitByteSliceToString(fromType);
1919
+ return;
1920
+ }
1921
+
1845
1922
  if (toWType === "externref") {
1846
1923
  if (fromWType === "i64") {
1847
1924
  this.pushInstruction("i32.wrap_i64");
@@ -1858,51 +1935,173 @@ export class ExprsEmitter {
1858
1935
  return;
1859
1936
  }
1860
1937
 
1938
+ const srcSign = isSigned(fromType) ? "s" : "u";
1939
+ const dstSign = isSigned(targetType) ? "s" : "u";
1861
1940
  if (fromWType === "i32" && toWType === "i64") {
1862
- this.pushInstruction(
1863
- isSigned(fromType) ? "i64.extend_i32_s" : "i64.extend_i32_u",
1864
- );
1941
+ this.pushInstruction(`i64.extend_i32_${srcSign}`);
1865
1942
  } else if (fromWType === "i64" && toWType === "i32") {
1866
1943
  this.pushInstruction("i32.wrap_i64");
1867
1944
  this.emitNarrowIntWrap(targetType);
1868
- } else if (fromWType === "i32" && toWType === "f32") {
1869
- this.pushInstruction(
1870
- isSigned(fromType) ? "f32.convert_i32_s" : "f32.convert_i32_u",
1871
- );
1872
- } else if (fromWType === "i32" && toWType === "f64") {
1873
- this.pushInstruction(
1874
- isSigned(fromType) ? "f64.convert_i32_s" : "f64.convert_i32_u",
1875
- );
1876
- } else if (fromWType === "i64" && toWType === "f32") {
1877
- this.pushInstruction(
1878
- isSigned(fromType) ? "f32.convert_i64_s" : "f32.convert_i64_u",
1879
- );
1880
- } else if (fromWType === "i64" && toWType === "f64") {
1881
- this.pushInstruction(
1882
- isSigned(fromType) ? "f64.convert_i64_s" : "f64.convert_i64_u",
1883
- );
1945
+ } else if (
1946
+ (fromWType === "i32" || fromWType === "i64") &&
1947
+ (toWType === "f32" || toWType === "f64")
1948
+ ) {
1949
+ this.pushInstruction(`${toWType}.convert_${fromWType}_${srcSign}`);
1884
1950
  } else if (fromWType === "f32" && toWType === "f64") {
1885
1951
  this.pushInstruction("f64.promote_f32");
1886
1952
  } else if (fromWType === "f64" && toWType === "f32") {
1887
1953
  this.pushInstruction("f32.demote_f64");
1888
- } else if (fromWType === "f64" && toWType === "i32") {
1889
- this.pushInstruction(
1890
- isSigned(targetType) ? "i32.trunc_sat_f64_s" : "i32.trunc_sat_f64_u",
1891
- );
1892
- this.emitNarrowIntWrap(targetType);
1893
- } else if (fromWType === "f32" && toWType === "i32") {
1894
- this.pushInstruction(
1895
- isSigned(targetType) ? "i32.trunc_sat_f32_s" : "i32.trunc_sat_f32_u",
1896
- );
1897
- this.emitNarrowIntWrap(targetType);
1898
- } else if (fromWType === "f64" && toWType === "i64") {
1899
- this.pushInstruction(
1900
- isSigned(targetType) ? "i64.trunc_sat_f64_s" : "i64.trunc_sat_f64_u",
1901
- );
1902
- } else if (fromWType === "f32" && toWType === "i64") {
1903
- this.pushInstruction(
1904
- isSigned(targetType) ? "i64.trunc_sat_f32_s" : "i64.trunc_sat_f32_u",
1905
- );
1954
+ } else if (
1955
+ (fromWType === "f32" || fromWType === "f64") &&
1956
+ (toWType === "i32" || toWType === "i64")
1957
+ ) {
1958
+ this.pushInstruction(`${toWType}.trunc_sat_${fromWType}_${dstSign}`);
1959
+ if (toWType === "i32") this.emitNarrowIntWrap(targetType);
1906
1960
  }
1907
1961
  }
1962
+
1963
+ _emitStringToByteSlice(targetType) {
1964
+ const elemGo = targetType.elem;
1965
+ const sliceInfo = this.mod.getSliceType(elemGo);
1966
+ const arrInfo = sliceInfo.arrInfo;
1967
+ const strTmp = this.acquireTemp("externref");
1968
+ this.pushInstruction({ op: "local.set", index: strTmp });
1969
+ const lenTmp = this.acquireTemp("i32");
1970
+ this.pushInstruction({ op: "local.get", index: strTmp });
1971
+ this.pushInstruction({
1972
+ op: "call",
1973
+ funcIndex: this.mod.getStrToBytesImportIndex(),
1974
+ });
1975
+ this.pushInstruction({ op: "local.tee", index: lenTmp });
1976
+ this.pushInstruction({
1977
+ op: "array.new_default",
1978
+ typeIndex: arrInfo.typeIndex,
1979
+ });
1980
+ const arrTmp = this.acquireTemp({
1981
+ kind: "ref",
1982
+ nullable: true,
1983
+ typeIndex: arrInfo.typeIndex,
1984
+ });
1985
+ this.pushInstruction({ op: "local.set", index: arrTmp });
1986
+ const iTmp = this.acquireTemp("i32");
1987
+ this.pushInstruction({ op: "i32.const", value: 0 });
1988
+ this.pushInstruction({ op: "local.set", index: iTmp });
1989
+
1990
+ this.pushInstruction({ op: "block", blockType: "void" });
1991
+ this.pushInstruction({ op: "loop", blockType: "void" });
1992
+ this.pushInstruction({ op: "local.get", index: iTmp });
1993
+ this.pushInstruction({ op: "local.get", index: lenTmp });
1994
+ this.pushInstruction("i32.ge_s");
1995
+ this.pushInstruction({ op: "br_if", depth: 1 });
1996
+
1997
+ this.pushInstruction({ op: "local.get", index: arrTmp });
1998
+ this.pushInstruction({ op: "local.get", index: iTmp });
1999
+ this.pushInstruction({ op: "local.get", index: iTmp });
2000
+ this.pushInstruction({
2001
+ op: "call",
2002
+ funcIndex: this.mod.getStrByteAtImportIndex(),
2003
+ });
2004
+ this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
2005
+
2006
+ this.pushInstruction({ op: "local.get", index: iTmp });
2007
+ this.pushInstruction({ op: "i32.const", value: 1 });
2008
+ this.pushInstruction("i32.add");
2009
+ this.pushInstruction({ op: "local.set", index: iTmp });
2010
+ this.pushInstruction({ op: "br", depth: 0 });
2011
+ this.pushInstruction("end");
2012
+ this.pushInstruction("end");
2013
+
2014
+ this.pushInstruction({ op: "local.get", index: arrTmp });
2015
+ this.pushInstruction({ op: "i32.const", value: 0 });
2016
+ this.pushInstruction({ op: "local.get", index: lenTmp });
2017
+ this.pushInstruction({ op: "local.get", index: lenTmp });
2018
+ this.pushInstruction({
2019
+ op: "struct.new",
2020
+ typeIndex: sliceInfo.typeIndex,
2021
+ });
2022
+
2023
+ this.releaseTemp(arrTmp, {
2024
+ kind: "ref",
2025
+ nullable: true,
2026
+ typeIndex: arrInfo.typeIndex,
2027
+ });
2028
+ this.releaseTemp(iTmp, "i32");
2029
+ this.releaseTemp(lenTmp, "i32");
2030
+ this.releaseTemp(strTmp, "externref");
2031
+ }
2032
+
2033
+ _emitByteSliceToString(fromType) {
2034
+ const elemGo = fromType.elem;
2035
+ const sliceInfo = this.mod.getSliceType(elemGo);
2036
+ const arrInfo = sliceInfo.arrInfo;
2037
+ const sliceW = {
2038
+ kind: "ref",
2039
+ nullable: true,
2040
+ typeIndex: sliceInfo.typeIndex,
2041
+ };
2042
+ const arrW = {
2043
+ kind: "ref",
2044
+ nullable: true,
2045
+ typeIndex: arrInfo.typeIndex,
2046
+ };
2047
+ const sTmp = this.acquireTemp(sliceW);
2048
+ this.pushInstruction({ op: "local.set", index: sTmp });
2049
+ const arrTmp = this.acquireTemp(arrW);
2050
+ const offTmp = this.acquireTemp("i32");
2051
+ const lenTmp = this.acquireTemp("i32");
2052
+ const capTmp = this.acquireTemp("i32");
2053
+ this._emitSliceUnpack(sTmp, sliceInfo, {
2054
+ arr: arrTmp,
2055
+ off: offTmp,
2056
+ len: lenTmp,
2057
+ cap: capTmp,
2058
+ });
2059
+ this.releaseTemp(sTmp, sliceW);
2060
+ this.releaseTemp(capTmp, "i32");
2061
+
2062
+ this.pushInstruction({ op: "local.get", index: lenTmp });
2063
+ this.pushInstruction({
2064
+ op: "call",
2065
+ funcIndex: this.mod.getBytesToStrInitImportIndex(),
2066
+ });
2067
+
2068
+ const iTmp = this.acquireTemp("i32");
2069
+ this.pushInstruction({ op: "i32.const", value: 0 });
2070
+ this.pushInstruction({ op: "local.set", index: iTmp });
2071
+
2072
+ this.pushInstruction({ op: "block", blockType: "void" });
2073
+ this.pushInstruction({ op: "loop", blockType: "void" });
2074
+ this.pushInstruction({ op: "local.get", index: iTmp });
2075
+ this.pushInstruction({ op: "local.get", index: lenTmp });
2076
+ this.pushInstruction("i32.ge_s");
2077
+ this.pushInstruction({ op: "br_if", depth: 1 });
2078
+
2079
+ this.pushInstruction({ op: "local.get", index: arrTmp });
2080
+ this.pushInstruction({ op: "local.get", index: offTmp });
2081
+ this.pushInstruction({ op: "local.get", index: iTmp });
2082
+ this.pushInstruction("i32.add");
2083
+ this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
2084
+ this.pushInstruction({
2085
+ op: "call",
2086
+ funcIndex: this.mod.getBytesToStrPushImportIndex(),
2087
+ });
2088
+
2089
+ this.pushInstruction({ op: "local.get", index: iTmp });
2090
+ this.pushInstruction({ op: "i32.const", value: 1 });
2091
+ this.pushInstruction("i32.add");
2092
+ this.pushInstruction({ op: "local.set", index: iTmp });
2093
+ this.pushInstruction({ op: "br", depth: 0 });
2094
+ this.pushInstruction("end");
2095
+ this.pushInstruction("end");
2096
+
2097
+ this.pushInstruction({
2098
+ op: "call",
2099
+ funcIndex: this.mod.getBytesToStrFinishImportIndex(),
2100
+ });
2101
+
2102
+ this.releaseTemp(iTmp, "i32");
2103
+ this.releaseTemp(lenTmp, "i32");
2104
+ this.releaseTemp(offTmp, "i32");
2105
+ this.releaseTemp(arrTmp, arrW);
2106
+ }
1908
2107
  }