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
@@ -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) {
@@ -363,7 +364,10 @@ export class ExprsEmitter {
363
364
  const typeName =
364
365
  lit.typeExpr?.name ??
365
366
  (litType?.kind === "named" ? litType.name : litType?.name);
366
- const structInfo = this.mod.getStructType(typeName);
367
+ let structInfo = typeName ? this.mod.getStructType(typeName) : null;
368
+ if (!structInfo) {
369
+ structInfo = this._resolveStructInfo(lit);
370
+ }
367
371
  if (!structInfo) {
368
372
  throw new Error(`Unknown struct in CompositeLit: ${typeName}`);
369
373
  }
@@ -780,6 +784,59 @@ export class ExprsEmitter {
780
784
  this.pushInstruction({ op: "local.set", index: dstArrTmp });
781
785
 
782
786
  const iTmp = this.acquireTemp("i32");
787
+
788
+ // If copying within the same array and shifting right (dstOff > srcOff),
789
+ // copy backwards to prevent clobbering overlapping elements.
790
+ this.pushInstruction({ op: "local.get", index: dstArrTmp });
791
+ this.pushInstruction({ op: "local.get", index: srcArrTmp });
792
+ this.pushInstruction("ref.eq");
793
+ this.pushInstruction({ op: "local.get", index: dstOffTmp });
794
+ this.pushInstruction({ op: "local.get", index: srcOffTmp });
795
+ this.pushInstruction("i32.gt_u");
796
+ this.pushInstruction("i32.and");
797
+ this.pushInstruction({ op: "if", blockType: "void" });
798
+
799
+ // Backwards copy: i from lenTmp - 1 down to 0
800
+ this.pushInstruction({ op: "local.get", index: lenTmp });
801
+ this.pushInstruction({ op: "i32.const", value: 1 });
802
+ this.pushInstruction("i32.sub");
803
+ this.pushInstruction({ op: "local.set", index: iTmp });
804
+
805
+ this.pushInstruction({ op: "block", blockType: "void" });
806
+ this.pushInstruction({ op: "loop", blockType: "void" });
807
+ this.pushInstruction({ op: "local.get", index: iTmp });
808
+ this.pushInstruction({ op: "i32.const", value: 0 });
809
+ this.pushInstruction("i32.lt_s");
810
+ this.pushInstruction({ op: "br_if", depth: 1 });
811
+
812
+ this.pushInstruction({ op: "local.get", index: dstArrTmp });
813
+ this.pushInstruction({ op: "local.get", index: dstOffTmp });
814
+ this.pushInstruction({ op: "local.get", index: iTmp });
815
+ this.pushInstruction("i32.add");
816
+
817
+ this.pushInstruction({ op: "local.get", index: srcArrTmp });
818
+ this.pushInstruction({ op: "local.get", index: srcOffTmp });
819
+ this.pushInstruction({ op: "local.get", index: iTmp });
820
+ this.pushInstruction("i32.add");
821
+ this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
822
+
823
+ if (sInfo) {
824
+ this.emitCloneStruct(sInfo, arrInfo.elemWType);
825
+ }
826
+
827
+ this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
828
+
829
+ this.pushInstruction({ op: "local.get", index: iTmp });
830
+ this.pushInstruction({ op: "i32.const", value: 1 });
831
+ this.pushInstruction("i32.sub");
832
+ this.pushInstruction({ op: "local.set", index: iTmp });
833
+ this.pushInstruction({ op: "br", depth: 0 });
834
+ this.pushInstruction("end");
835
+ this.pushInstruction("end");
836
+
837
+ this.pushInstruction("else");
838
+
839
+ // Forwards copy: i from 0 up to lenTmp - 1
783
840
  this.pushInstruction({ op: "i32.const", value: 0 });
784
841
  this.pushInstruction({ op: "local.set", index: iTmp });
785
842
 
@@ -815,6 +872,8 @@ export class ExprsEmitter {
815
872
  this.pushInstruction("end");
816
873
  this.pushInstruction("end");
817
874
 
875
+ this.pushInstruction("end");
876
+
818
877
  this.releaseTemp(iTmp, "i32");
819
878
  this.releaseTemp(dstArrTmp, {
820
879
  kind: "ref",
@@ -964,6 +1023,31 @@ export class ExprsEmitter {
964
1023
  nullable: true,
965
1024
  typeIndex: arrInfo.typeIndex,
966
1025
  };
1026
+
1027
+ const rawBaseType = baseNode._type ?? this._resolveExprGoType(baseNode);
1028
+ const baseType =
1029
+ rawBaseType?.kind === "pointer" ? rawBaseType.base : rawBaseType;
1030
+ const arrayLen =
1031
+ baseType?.kind === "array" ? (baseType.len ?? baseType.size) : null;
1032
+ let constIdx = null;
1033
+ if (indexNode.kind === "BasicLit" && indexNode.litKind === "INT") {
1034
+ try {
1035
+ const v = Number(indexNode.value);
1036
+ if (Number.isInteger(v)) constIdx = v;
1037
+ } catch {}
1038
+ }
1039
+ if (
1040
+ arrayLen !== null &&
1041
+ arrayLen !== undefined &&
1042
+ constIdx !== null &&
1043
+ constIdx >= 0 &&
1044
+ constIdx < arrayLen
1045
+ ) {
1046
+ this.emitExpr(baseNode, arrWType);
1047
+ this.pushInstruction({ op: "i32.const", value: constIdx });
1048
+ return;
1049
+ }
1050
+
967
1051
  const arrTmp = this.acquireTemp(arrWType);
968
1052
  this.emitExpr(baseNode, arrWType);
969
1053
  this.pushInstruction({ op: "local.tee", index: arrTmp });
@@ -998,14 +1082,16 @@ export class ExprsEmitter {
998
1082
  (lit.litKind === "INT" ? "i64" : lit.litKind === "FLOAT" ? "f64" : "i32");
999
1083
 
1000
1084
  if (lit.litKind === "INT") {
1001
- const n = Number(lit.value);
1085
+ const s = String(lit.value);
1086
+ const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
1087
+ const n = Number(clean);
1002
1088
  if (wType === "i32") {
1003
1089
  this.pushInstruction({ op: "i32.const", value: n | 0 });
1004
1090
  } else if (wType === "f64" || wType === "f32") {
1005
1091
  // Untyped integer constant in a float context (`x * 2`).
1006
1092
  this.pushInstruction({ op: `${wType}.const`, value: n });
1007
1093
  } else {
1008
- this.pushInstruction({ op: "i64.const", value: BigInt(lit.value) });
1094
+ this.pushInstruction({ op: "i64.const", value: BigInt(clean) });
1009
1095
  }
1010
1096
  return;
1011
1097
  }
@@ -1029,31 +1115,7 @@ export class ExprsEmitter {
1029
1115
  }
1030
1116
 
1031
1117
  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 });
1118
+ this._emitNilValue(lit._type, targetWasmType);
1057
1119
  return;
1058
1120
  }
1059
1121
 
@@ -1068,6 +1130,42 @@ export class ExprsEmitter {
1068
1130
  }
1069
1131
  }
1070
1132
 
1133
+ _emitNilValue(goType, targetWasmType) {
1134
+ const effectiveType = targetWasmType ?? this.toWasmType(goType);
1135
+ if (
1136
+ (goType && isSliceType(goType, this.mod.checker)) ||
1137
+ (effectiveType &&
1138
+ typeof effectiveType === "object" &&
1139
+ typeof effectiveType.typeIndex === "number" &&
1140
+ this.mod.getSliceTypeByIndex(effectiveType.typeIndex))
1141
+ ) {
1142
+ const elemType = goType ? this._getSliceElemType(goType) : null;
1143
+ const sliceInfo = elemType
1144
+ ? this.mod.getSliceType(elemType)
1145
+ : this.mod.getSliceTypeByIndex(effectiveType.typeIndex);
1146
+ if (sliceInfo) {
1147
+ this.pushInstruction({
1148
+ op: "global.get",
1149
+ index: sliceInfo.emptyGlobalIndex,
1150
+ });
1151
+ return;
1152
+ }
1153
+ }
1154
+ const heapType =
1155
+ typeof effectiveType === "object" && effectiveType !== null
1156
+ ? (effectiveType.typeIndex ?? effectiveType.heapType ?? "any")
1157
+ : "any";
1158
+ this.pushInstruction({ op: "ref.null", heapType });
1159
+ }
1160
+
1161
+ _coerceIntWidth(srcType, targetWasmType) {
1162
+ if (srcType === "i32" && targetWasmType === "i64") {
1163
+ this.pushInstruction("i64.extend_i32_s");
1164
+ } else if (srcType === "i64" && targetWasmType === "i32") {
1165
+ this.pushInstruction("i32.wrap_i64");
1166
+ }
1167
+ }
1168
+
1071
1169
  emitIdent(ident, targetWasmType = null) {
1072
1170
  if (ident.name === "true") {
1073
1171
  this.pushInstruction({ op: "i32.const", value: 1 });
@@ -1078,33 +1176,7 @@ export class ExprsEmitter {
1078
1176
  return;
1079
1177
  }
1080
1178
  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 });
1179
+ this._emitNilValue(ident._type, targetWasmType);
1108
1180
  return;
1109
1181
  }
1110
1182
 
@@ -1120,33 +1192,21 @@ export class ExprsEmitter {
1120
1192
  } else {
1121
1193
  this.pushInstruction({ op: "local.get", index: localInfo.index });
1122
1194
  }
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
- }
1195
+ this._coerceIntWidth(localInfo.type, targetWasmType);
1128
1196
  return;
1129
1197
  }
1130
1198
 
1131
1199
  if (this.cachedGlobals.has(ident.name)) {
1132
1200
  const cached = this.cachedGlobals.get(ident.name);
1133
1201
  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
- }
1202
+ this._coerceIntWidth(cached.globalInfo.type, targetWasmType);
1139
1203
  return;
1140
1204
  }
1141
1205
 
1142
1206
  const globalInfo = this.mod.resolveGlobal(ident.name);
1143
1207
  if (globalInfo) {
1144
1208
  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
- }
1209
+ this._coerceIntWidth(globalInfo.type, targetWasmType);
1150
1210
  return;
1151
1211
  }
1152
1212
 
@@ -1158,11 +1218,6 @@ export class ExprsEmitter {
1158
1218
  );
1159
1219
  return;
1160
1220
  }
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
1221
 
1167
1222
  const trampName = `_tramp$${ident.name}`;
1168
1223
  const trampFuncIdx = this.mod.resolveFuncIndex(trampName);
@@ -1256,6 +1311,13 @@ export class ExprsEmitter {
1256
1311
  break;
1257
1312
 
1258
1313
  case "&": {
1314
+ if (operand.kind === "Ident") {
1315
+ const loc = this.resolveLocal(operand.name);
1316
+ if (loc?.isBoxed) {
1317
+ this.pushInstruction({ op: "local.get", index: loc.index });
1318
+ return;
1319
+ }
1320
+ }
1259
1321
  if (operand.kind === "CompositeLit") {
1260
1322
  this.emitExpr(operand);
1261
1323
  return;
@@ -1526,6 +1588,26 @@ export class ExprsEmitter {
1526
1588
  this.pushInstruction(`${wType}.mul`);
1527
1589
  this.emitNarrowIntWrap(goType);
1528
1590
  break;
1591
+ case "/":
1592
+ if (wType === "f32" || wType === "f64") {
1593
+ this.pushInstruction(`${wType}.div`);
1594
+ } else {
1595
+ this.pushInstruction(signed ? `${wType}.div_s` : `${wType}.div_u`);
1596
+ this.emitNarrowIntWrap(goType);
1597
+ }
1598
+ break;
1599
+ case "%":
1600
+ this.pushInstruction(signed ? `${wType}.rem_s` : `${wType}.rem_u`);
1601
+ this.emitNarrowIntWrap(goType);
1602
+ break;
1603
+ case "<<":
1604
+ this.pushInstruction(`${wType}.shl`);
1605
+ this.emitNarrowIntWrap(goType);
1606
+ break;
1607
+ case ">>":
1608
+ this.pushInstruction(signed ? `${wType}.shr_s` : `${wType}.shr_u`);
1609
+ this.emitNarrowIntWrap(goType);
1610
+ break;
1529
1611
 
1530
1612
  case "&":
1531
1613
  this.pushInstruction(`${wType}.and`);
@@ -1576,45 +1658,21 @@ export class ExprsEmitter {
1576
1658
  }
1577
1659
  break;
1578
1660
  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
1661
  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
1662
  case ">":
1663
+ case ">=": {
1599
1664
  if (isStringType(goType, this.mod.checker) || wType === "externref") {
1600
- const cmpIdx = this.mod.getStringCmpImportIndex(">");
1665
+ const cmpIdx = this.mod.getStringCmpImportIndex(op);
1601
1666
  this.pushInstruction({ op: "call", funcIndex: cmpIdx });
1602
- } else if (wType === "f32" || wType === "f64") {
1603
- this.pushInstruction(`${wType}.gt`);
1604
1667
  } 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(">=");
1611
- this.pushInstruction({ op: "call", funcIndex: cmpIdx });
1612
- } else if (wType === "f32" || wType === "f64") {
1613
- this.pushInstruction(`${wType}.ge`);
1614
- } else {
1615
- this.pushInstruction(`${wType}.ge_${signed ? "s" : "u"}`);
1668
+ const name =
1669
+ op === "<" ? "lt" : op === "<=" ? "le" : op === ">" ? "gt" : "ge";
1670
+ const suffix =
1671
+ wType === "f32" || wType === "f64" ? "" : `_${signed ? "s" : "u"}`;
1672
+ this.pushInstruction(`${wType}.${name}${suffix}`);
1616
1673
  }
1617
1674
  break;
1675
+ }
1618
1676
 
1619
1677
  default:
1620
1678
  throw new Error(`Unsupported binary operator: ${op}`);
@@ -1662,6 +1720,56 @@ export class ExprsEmitter {
1662
1720
  }
1663
1721
 
1664
1722
  const signed = isSigned(goType);
1723
+ const is64 = wType === "i64";
1724
+ const divOp = `${wType}.${op === "/" ? "div" : "rem"}_${signed ? "s" : "u"}`;
1725
+
1726
+ let constDivisor = null;
1727
+ if (right.kind === "BasicLit" && right.litKind === "INT") {
1728
+ try {
1729
+ constDivisor = BigInt(right.value);
1730
+ } catch {}
1731
+ }
1732
+
1733
+ if (constDivisor !== null && constDivisor !== 0n && constDivisor !== -1n) {
1734
+ // Specialize unsigned powers-of-two to shifts or bitwise masks
1735
+ if (
1736
+ !signed &&
1737
+ constDivisor > 0n &&
1738
+ (constDivisor & (constDivisor - 1n)) === 0n
1739
+ ) {
1740
+ let k = 0n;
1741
+ let temp = constDivisor;
1742
+ while (temp > 1n) {
1743
+ temp >>= 1n;
1744
+ k++;
1745
+ }
1746
+ if (op === "/") {
1747
+ this.emitExpr(left, wType);
1748
+ this.pushInstruction({
1749
+ op: `${wType}.const`,
1750
+ value: is64 ? k : Number(k),
1751
+ });
1752
+ this.pushInstruction(`${wType}.shr_u`);
1753
+ } else {
1754
+ this.emitExpr(left, wType);
1755
+ this.pushInstruction({
1756
+ op: `${wType}.const`,
1757
+ value: is64 ? constDivisor - 1n : Number(constDivisor - 1n),
1758
+ });
1759
+ this.pushInstruction(`${wType}.and`);
1760
+ }
1761
+ if (!is64) this.emitNarrowIntWrap(goType);
1762
+ return;
1763
+ }
1764
+
1765
+ // Constant divisor != 0 and != -1: no dynamic zero or overflow check needed
1766
+ this.emitExpr(left, wType);
1767
+ this.emitExpr(right, wType);
1768
+ this.pushInstruction(divOp);
1769
+ if (!is64) this.emitNarrowIntWrap(goType);
1770
+ return;
1771
+ }
1772
+
1665
1773
  const tmpA = this.acquireTemp(wType);
1666
1774
  const tmpB = this.acquireTemp(wType);
1667
1775
 
@@ -1678,64 +1786,34 @@ export class ExprsEmitter {
1678
1786
  this.emitPanic("runtime error: integer divide by zero");
1679
1787
  this.pushInstruction("end");
1680
1788
 
1681
- if (wType === "i32") {
1789
+ if (wType === "i32" || wType === "i64") {
1682
1790
  if (signed) {
1683
- // MinInt32 / -1 wrap guard
1791
+ const minVal = is64 ? -9223372036854775808n : -2147483648;
1792
+ const negOne = is64 ? -1n : -1;
1793
+ const zeroVal = is64 ? 0n : 0;
1684
1794
  this.pushInstruction({ op: "local.get", index: tmpA });
1685
- this.pushInstruction({ op: "i32.const", value: -2147483648 });
1686
- this.pushInstruction("i32.eq");
1795
+ this.pushInstruction({ op: `${wType}.const`, value: minVal });
1796
+ this.pushInstruction(`${wType}.eq`);
1687
1797
  this.pushInstruction({ op: "local.get", index: tmpB });
1688
- this.pushInstruction({ op: "i32.const", value: -1 });
1689
- this.pushInstruction("i32.eq");
1798
+ this.pushInstruction({ op: `${wType}.const`, value: negOne });
1799
+ this.pushInstruction(`${wType}.eq`);
1690
1800
  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 });
1801
+ this.pushInstruction({ op: "if", blockType: wType });
1711
1802
  this.pushInstruction({
1712
- op: "i64.const",
1713
- value: -9223372036854775808n,
1803
+ op: `${wType}.const`,
1804
+ value: op === "/" ? minVal : zeroVal,
1714
1805
  });
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
1806
  this.pushInstruction("else");
1730
1807
  this.pushInstruction({ op: "local.get", index: tmpA });
1731
1808
  this.pushInstruction({ op: "local.get", index: tmpB });
1732
- this.pushInstruction(op === "/" ? "i64.div_s" : "i64.rem_s");
1809
+ this.pushInstruction(divOp);
1733
1810
  this.pushInstruction("end");
1734
1811
  } else {
1735
1812
  this.pushInstruction({ op: "local.get", index: tmpA });
1736
1813
  this.pushInstruction({ op: "local.get", index: tmpB });
1737
- this.pushInstruction(op === "/" ? "i64.div_u" : "i64.rem_u");
1814
+ this.pushInstruction(divOp);
1738
1815
  }
1816
+ if (!is64) this.emitNarrowIntWrap(goType);
1739
1817
  }
1740
1818
 
1741
1819
  this.releaseTemp(tmpB, wType);
@@ -1747,6 +1825,10 @@ export class ExprsEmitter {
1747
1825
  const wType = targetWasmType ?? toWasmType(goType, this.mod.checker);
1748
1826
  const signed = isSigned(goType);
1749
1827
  const rightWType = toWasmType(right._type, this.mod.checker);
1828
+ const is64 = wType === "i64";
1829
+ const zeroVal = is64 ? 0n : 0;
1830
+ const negOne = is64 ? -1n : -1;
1831
+ const widthVal = is64 ? 64n : 32;
1750
1832
 
1751
1833
  const tmpVal = this.acquireTemp(wType);
1752
1834
  const tmpShift = this.acquireTemp(wType);
@@ -1762,68 +1844,34 @@ export class ExprsEmitter {
1762
1844
  }
1763
1845
  this.pushInstruction({ op: "local.set", index: tmpShift });
1764
1846
 
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");
1847
+ this.pushInstruction({ op: "local.get", index: tmpShift });
1848
+ this.pushInstruction({ op: `${wType}.const`, value: widthVal });
1849
+ this.pushInstruction(`${wType}.ge_u`);
1850
+ this.pushInstruction({ op: "if", blockType: wType });
1785
1851
 
1852
+ if (op === ">>" && signed) {
1786
1853
  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
-
1854
+ this.pushInstruction({ op: `${wType}.const`, value: zeroVal });
1855
+ this.pushInstruction(`${wType}.lt_s`);
1856
+ this.pushInstruction({ op: "if", blockType: wType });
1857
+ this.pushInstruction({ op: `${wType}.const`, value: negOne });
1858
+ this.pushInstruction("else");
1859
+ this.pushInstruction({ op: `${wType}.const`, value: zeroVal });
1794
1860
  this.pushInstruction("end");
1795
- this.emitNarrowIntWrap(goType);
1796
1861
  } 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
- }
1862
+ this.pushInstruction({ op: `${wType}.const`, value: zeroVal });
1863
+ }
1814
1864
 
1815
- this.pushInstruction("else");
1865
+ this.pushInstruction("else");
1816
1866
 
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
- }
1867
+ this.pushInstruction({ op: "local.get", index: tmpVal });
1868
+ this.pushInstruction({ op: "local.get", index: tmpShift });
1869
+ this.pushInstruction(
1870
+ op === "<<" ? `${wType}.shl` : `${wType}.shr_${signed ? "s" : "u"}`,
1871
+ );
1824
1872
 
1825
- this.pushInstruction("end");
1826
- }
1873
+ this.pushInstruction("end");
1874
+ if (!is64) this.emitNarrowIntWrap(goType);
1827
1875
 
1828
1876
  this.releaseTemp(tmpShift, wType);
1829
1877
  this.releaseTemp(tmpVal, wType);
@@ -1842,6 +1890,24 @@ export class ExprsEmitter {
1842
1890
  return;
1843
1891
  }
1844
1892
 
1893
+ if (
1894
+ fromWType === "externref" &&
1895
+ isSliceType(targetType, this.mod.checker) &&
1896
+ (targetType.elem?.name === "byte" || targetType.elem?.name === "uint8")
1897
+ ) {
1898
+ this._emitStringToByteSlice(targetType);
1899
+ return;
1900
+ }
1901
+
1902
+ if (
1903
+ toWType === "externref" &&
1904
+ isSliceType(fromType, this.mod.checker) &&
1905
+ (fromType.elem?.name === "byte" || fromType.elem?.name === "uint8")
1906
+ ) {
1907
+ this._emitByteSliceToString(fromType);
1908
+ return;
1909
+ }
1910
+
1845
1911
  if (toWType === "externref") {
1846
1912
  if (fromWType === "i64") {
1847
1913
  this.pushInstruction("i32.wrap_i64");
@@ -1858,51 +1924,173 @@ export class ExprsEmitter {
1858
1924
  return;
1859
1925
  }
1860
1926
 
1927
+ const srcSign = isSigned(fromType) ? "s" : "u";
1928
+ const dstSign = isSigned(targetType) ? "s" : "u";
1861
1929
  if (fromWType === "i32" && toWType === "i64") {
1862
- this.pushInstruction(
1863
- isSigned(fromType) ? "i64.extend_i32_s" : "i64.extend_i32_u",
1864
- );
1930
+ this.pushInstruction(`i64.extend_i32_${srcSign}`);
1865
1931
  } else if (fromWType === "i64" && toWType === "i32") {
1866
1932
  this.pushInstruction("i32.wrap_i64");
1867
1933
  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
- );
1934
+ } else if (
1935
+ (fromWType === "i32" || fromWType === "i64") &&
1936
+ (toWType === "f32" || toWType === "f64")
1937
+ ) {
1938
+ this.pushInstruction(`${toWType}.convert_${fromWType}_${srcSign}`);
1884
1939
  } else if (fromWType === "f32" && toWType === "f64") {
1885
1940
  this.pushInstruction("f64.promote_f32");
1886
1941
  } else if (fromWType === "f64" && toWType === "f32") {
1887
1942
  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
- );
1943
+ } else if (
1944
+ (fromWType === "f32" || fromWType === "f64") &&
1945
+ (toWType === "i32" || toWType === "i64")
1946
+ ) {
1947
+ this.pushInstruction(`${toWType}.trunc_sat_${fromWType}_${dstSign}`);
1948
+ if (toWType === "i32") this.emitNarrowIntWrap(targetType);
1906
1949
  }
1907
1950
  }
1951
+
1952
+ _emitStringToByteSlice(targetType) {
1953
+ const elemGo = targetType.elem;
1954
+ const sliceInfo = this.mod.getSliceType(elemGo);
1955
+ const arrInfo = sliceInfo.arrInfo;
1956
+ const strTmp = this.acquireTemp("externref");
1957
+ this.pushInstruction({ op: "local.set", index: strTmp });
1958
+ const lenTmp = this.acquireTemp("i32");
1959
+ this.pushInstruction({ op: "local.get", index: strTmp });
1960
+ this.pushInstruction({
1961
+ op: "call",
1962
+ funcIndex: this.mod.getStrToBytesImportIndex(),
1963
+ });
1964
+ this.pushInstruction({ op: "local.tee", index: lenTmp });
1965
+ this.pushInstruction({
1966
+ op: "array.new_default",
1967
+ typeIndex: arrInfo.typeIndex,
1968
+ });
1969
+ const arrTmp = this.acquireTemp({
1970
+ kind: "ref",
1971
+ nullable: true,
1972
+ typeIndex: arrInfo.typeIndex,
1973
+ });
1974
+ this.pushInstruction({ op: "local.set", index: arrTmp });
1975
+ const iTmp = this.acquireTemp("i32");
1976
+ this.pushInstruction({ op: "i32.const", value: 0 });
1977
+ this.pushInstruction({ op: "local.set", index: iTmp });
1978
+
1979
+ this.pushInstruction({ op: "block", blockType: "void" });
1980
+ this.pushInstruction({ op: "loop", blockType: "void" });
1981
+ this.pushInstruction({ op: "local.get", index: iTmp });
1982
+ this.pushInstruction({ op: "local.get", index: lenTmp });
1983
+ this.pushInstruction("i32.ge_s");
1984
+ this.pushInstruction({ op: "br_if", depth: 1 });
1985
+
1986
+ this.pushInstruction({ op: "local.get", index: arrTmp });
1987
+ this.pushInstruction({ op: "local.get", index: iTmp });
1988
+ this.pushInstruction({ op: "local.get", index: iTmp });
1989
+ this.pushInstruction({
1990
+ op: "call",
1991
+ funcIndex: this.mod.getStrByteAtImportIndex(),
1992
+ });
1993
+ this.pushInstruction({ op: "array.set", typeIndex: arrInfo.typeIndex });
1994
+
1995
+ this.pushInstruction({ op: "local.get", index: iTmp });
1996
+ this.pushInstruction({ op: "i32.const", value: 1 });
1997
+ this.pushInstruction("i32.add");
1998
+ this.pushInstruction({ op: "local.set", index: iTmp });
1999
+ this.pushInstruction({ op: "br", depth: 0 });
2000
+ this.pushInstruction("end");
2001
+ this.pushInstruction("end");
2002
+
2003
+ this.pushInstruction({ op: "local.get", index: arrTmp });
2004
+ this.pushInstruction({ op: "i32.const", value: 0 });
2005
+ this.pushInstruction({ op: "local.get", index: lenTmp });
2006
+ this.pushInstruction({ op: "local.get", index: lenTmp });
2007
+ this.pushInstruction({
2008
+ op: "struct.new",
2009
+ typeIndex: sliceInfo.typeIndex,
2010
+ });
2011
+
2012
+ this.releaseTemp(arrTmp, {
2013
+ kind: "ref",
2014
+ nullable: true,
2015
+ typeIndex: arrInfo.typeIndex,
2016
+ });
2017
+ this.releaseTemp(iTmp, "i32");
2018
+ this.releaseTemp(lenTmp, "i32");
2019
+ this.releaseTemp(strTmp, "externref");
2020
+ }
2021
+
2022
+ _emitByteSliceToString(fromType) {
2023
+ const elemGo = fromType.elem;
2024
+ const sliceInfo = this.mod.getSliceType(elemGo);
2025
+ const arrInfo = sliceInfo.arrInfo;
2026
+ const sliceW = {
2027
+ kind: "ref",
2028
+ nullable: true,
2029
+ typeIndex: sliceInfo.typeIndex,
2030
+ };
2031
+ const arrW = {
2032
+ kind: "ref",
2033
+ nullable: true,
2034
+ typeIndex: arrInfo.typeIndex,
2035
+ };
2036
+ const sTmp = this.acquireTemp(sliceW);
2037
+ this.pushInstruction({ op: "local.set", index: sTmp });
2038
+ const arrTmp = this.acquireTemp(arrW);
2039
+ const offTmp = this.acquireTemp("i32");
2040
+ const lenTmp = this.acquireTemp("i32");
2041
+ const capTmp = this.acquireTemp("i32");
2042
+ this._emitSliceUnpack(sTmp, sliceInfo, {
2043
+ arr: arrTmp,
2044
+ off: offTmp,
2045
+ len: lenTmp,
2046
+ cap: capTmp,
2047
+ });
2048
+ this.releaseTemp(sTmp, sliceW);
2049
+ this.releaseTemp(capTmp, "i32");
2050
+
2051
+ this.pushInstruction({ op: "local.get", index: lenTmp });
2052
+ this.pushInstruction({
2053
+ op: "call",
2054
+ funcIndex: this.mod.getBytesToStrInitImportIndex(),
2055
+ });
2056
+
2057
+ const iTmp = this.acquireTemp("i32");
2058
+ this.pushInstruction({ op: "i32.const", value: 0 });
2059
+ this.pushInstruction({ op: "local.set", index: iTmp });
2060
+
2061
+ this.pushInstruction({ op: "block", blockType: "void" });
2062
+ this.pushInstruction({ op: "loop", blockType: "void" });
2063
+ this.pushInstruction({ op: "local.get", index: iTmp });
2064
+ this.pushInstruction({ op: "local.get", index: lenTmp });
2065
+ this.pushInstruction("i32.ge_s");
2066
+ this.pushInstruction({ op: "br_if", depth: 1 });
2067
+
2068
+ this.pushInstruction({ op: "local.get", index: arrTmp });
2069
+ this.pushInstruction({ op: "local.get", index: offTmp });
2070
+ this.pushInstruction({ op: "local.get", index: iTmp });
2071
+ this.pushInstruction("i32.add");
2072
+ this.pushInstruction({ op: "array.get", typeIndex: arrInfo.typeIndex });
2073
+ this.pushInstruction({
2074
+ op: "call",
2075
+ funcIndex: this.mod.getBytesToStrPushImportIndex(),
2076
+ });
2077
+
2078
+ this.pushInstruction({ op: "local.get", index: iTmp });
2079
+ this.pushInstruction({ op: "i32.const", value: 1 });
2080
+ this.pushInstruction("i32.add");
2081
+ this.pushInstruction({ op: "local.set", index: iTmp });
2082
+ this.pushInstruction({ op: "br", depth: 0 });
2083
+ this.pushInstruction("end");
2084
+ this.pushInstruction("end");
2085
+
2086
+ this.pushInstruction({
2087
+ op: "call",
2088
+ funcIndex: this.mod.getBytesToStrFinishImportIndex(),
2089
+ });
2090
+
2091
+ this.releaseTemp(iTmp, "i32");
2092
+ this.releaseTemp(lenTmp, "i32");
2093
+ this.releaseTemp(offTmp, "i32");
2094
+ this.releaseTemp(arrTmp, arrW);
2095
+ }
1908
2096
  }