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
@@ -0,0 +1,205 @@
1
+ // Compile-time folding of package-level constant expressions for the wasm
2
+ // backend. Folds literals, other already-folded constants, unary/binary
3
+ // operators and `len("literal")` into a single BasicLit. Anything it cannot
4
+ // reduce returns null and the caller falls back to a runtime-initialised
5
+ // global (see `_collectPackageConsts`).
6
+
7
+ const INT = "INT";
8
+ const FLOAT = "FLOAT";
9
+ const STRING = "STRING";
10
+ const BOOL = "BOOL";
11
+
12
+ function fromLit(lit) {
13
+ switch (lit.litKind) {
14
+ case INT: {
15
+ const s = String(lit.value);
16
+ const clean = /^0[0-7]+$/.test(s) ? `0o${s.slice(1)}` : s;
17
+ return { k: INT, v: BigInt(clean) };
18
+ }
19
+ case FLOAT: {
20
+ const n = Number(lit.value);
21
+ return Number.isFinite(n) ? { k: FLOAT, v: n } : null;
22
+ }
23
+ case STRING:
24
+ return { k: STRING, v: String(lit.value ?? "") };
25
+ case BOOL:
26
+ return { k: BOOL, v: lit.value === "true" };
27
+ default:
28
+ return null;
29
+ }
30
+ }
31
+
32
+ function toLit(val) {
33
+ switch (val.k) {
34
+ case INT:
35
+ return { kind: "BasicLit", litKind: INT, value: val.v.toString() };
36
+ case FLOAT:
37
+ return {
38
+ kind: "BasicLit",
39
+ litKind: FLOAT,
40
+ value: Object.is(val.v, -0) ? "-0" : String(val.v),
41
+ };
42
+ case STRING:
43
+ return { kind: "BasicLit", litKind: STRING, value: val.v };
44
+ default:
45
+ return {
46
+ kind: "BasicLit",
47
+ litKind: BOOL,
48
+ value: val.v ? "true" : "false",
49
+ };
50
+ }
51
+ }
52
+
53
+ function asFloat(val) {
54
+ return val.k === INT ? Number(val.v) : val.v;
55
+ }
56
+
57
+ function foldUnary(op, x) {
58
+ if (op === "-" && x.k === INT) return { k: INT, v: -x.v };
59
+ if (op === "-" && x.k === FLOAT) return { k: FLOAT, v: -x.v };
60
+ if (op === "+" && (x.k === INT || x.k === FLOAT)) return x;
61
+ if (op === "!" && x.k === BOOL) return { k: BOOL, v: !x.v };
62
+ if (op === "^" && x.k === INT) return { k: INT, v: ~x.v };
63
+ return null;
64
+ }
65
+
66
+ function compare(op, a, b) {
67
+ switch (op) {
68
+ case "==":
69
+ return a === b;
70
+ case "!=":
71
+ return a !== b;
72
+ case "<":
73
+ return a < b;
74
+ case "<=":
75
+ return a <= b;
76
+ case ">":
77
+ return a > b;
78
+ case ">=":
79
+ return a >= b;
80
+ default:
81
+ return null;
82
+ }
83
+ }
84
+
85
+ function foldInts(op, a, b) {
86
+ switch (op) {
87
+ case "+":
88
+ return { k: INT, v: a + b };
89
+ case "-":
90
+ return { k: INT, v: a - b };
91
+ case "*":
92
+ return { k: INT, v: a * b };
93
+ case "/":
94
+ if (b === 0n) throw new Error("division by zero");
95
+ return { k: INT, v: a / b };
96
+ case "%":
97
+ if (b === 0n) throw new Error("division by zero");
98
+ return { k: INT, v: a % b };
99
+ case "&":
100
+ return { k: INT, v: a & b };
101
+ case "|":
102
+ return { k: INT, v: a | b };
103
+ case "^":
104
+ return { k: INT, v: a ^ b };
105
+ case "&^":
106
+ return { k: INT, v: a & ~b };
107
+ case "<<":
108
+ if (b < 0n) throw new Error("negative shift count");
109
+ return b > 1024n ? null : { k: INT, v: a << b };
110
+ case ">>":
111
+ if (b < 0n) throw new Error("negative shift count");
112
+ return b > 1024n ? null : { k: INT, v: a >> b };
113
+ default: {
114
+ const r = compare(op, a, b);
115
+ return r === null ? null : { k: BOOL, v: r };
116
+ }
117
+ }
118
+ }
119
+
120
+ function foldFloats(op, a, b) {
121
+ let v;
122
+ switch (op) {
123
+ case "+":
124
+ v = a + b;
125
+ break;
126
+ case "-":
127
+ v = a - b;
128
+ break;
129
+ case "*":
130
+ v = a * b;
131
+ break;
132
+ case "/":
133
+ if (b === 0) throw new Error("division by zero");
134
+ v = a / b;
135
+ break;
136
+ default: {
137
+ const r = compare(op, a, b);
138
+ return r === null ? null : { k: BOOL, v: r };
139
+ }
140
+ }
141
+ return Number.isFinite(v) ? { k: FLOAT, v } : null;
142
+ }
143
+
144
+ function foldBinary(op, a, b) {
145
+ if (a.k === BOOL && b.k === BOOL) {
146
+ if (op === "&&") return { k: BOOL, v: a.v && b.v };
147
+ if (op === "||") return { k: BOOL, v: a.v || b.v };
148
+ const r = op === "==" || op === "!=" ? compare(op, a.v, b.v) : null;
149
+ return r === null ? null : { k: BOOL, v: r };
150
+ }
151
+ if (a.k === STRING && b.k === STRING) {
152
+ if (op === "+") return { k: STRING, v: a.v + b.v };
153
+ const r = compare(op, a.v, b.v);
154
+ return r === null ? null : { k: BOOL, v: r };
155
+ }
156
+ if (a.k === INT && b.k === INT) return foldInts(op, a.v, b.v);
157
+ if (
158
+ (a.k === INT || a.k === FLOAT) &&
159
+ (b.k === INT || b.k === FLOAT) &&
160
+ op !== "<<" &&
161
+ op !== ">>"
162
+ ) {
163
+ return foldFloats(op, asFloat(a), asFloat(b));
164
+ }
165
+ return null;
166
+ }
167
+
168
+ function foldExpr(expr, resolve) {
169
+ switch (expr?.kind) {
170
+ case "BasicLit":
171
+ return fromLit(expr);
172
+ case "Ident": {
173
+ if (expr.name === "true" || expr.name === "false")
174
+ return { k: BOOL, v: expr.name === "true" };
175
+ const lit = resolve(expr.name);
176
+ return lit ? fromLit(lit) : null;
177
+ }
178
+ case "UnaryExpr": {
179
+ const x = foldExpr(expr.operand, resolve);
180
+ return x ? foldUnary(expr.op, x) : null;
181
+ }
182
+ case "BinaryExpr": {
183
+ const a = foldExpr(expr.left, resolve);
184
+ const b = a ? foldExpr(expr.right, resolve) : null;
185
+ return a && b ? foldBinary(expr.op, a, b) : null;
186
+ }
187
+ case "CallExpr": {
188
+ if (expr.func?.kind !== "Ident" || expr.func.name !== "len") return null;
189
+ if (expr.args?.length !== 1) return null;
190
+ const s = foldExpr(expr.args[0], resolve);
191
+ if (s?.k !== STRING) return null;
192
+ // Gofront strings across JS and WASM runtime use UTF-16 code units (s.length).
193
+ return { k: INT, v: BigInt(s.v.length) };
194
+ }
195
+ default:
196
+ return null;
197
+ }
198
+ }
199
+
200
+ // Folds `expr` into a BasicLit, or returns null when it needs the runtime.
201
+ // `resolve(name)` yields the BasicLit of an already-folded constant, if any.
202
+ export function foldConstExpr(expr, resolve) {
203
+ const val = foldExpr(expr, resolve);
204
+ return val ? toLit(val) : null;
205
+ }
@@ -65,18 +65,6 @@ export class BuiltinsEmitter {
65
65
  return null;
66
66
  }
67
67
 
68
- _getArrayElemType(t) {
69
- if (!t) return null;
70
- if (t.elem) return t.elem;
71
- if (t.underlying?.elem) return t.underlying.elem;
72
- if (t.kind === "ArrayType") return t.elem;
73
- if (t.name && this.mod.checker?.types?.has(t.name)) {
74
- const resolved = this.mod.checker.types.get(t.name);
75
- return this._getArrayElemType(resolved);
76
- }
77
- return null;
78
- }
79
-
80
68
  _emitBuiltinLen(call, targetWasmType) {
81
69
  const arg = call.args[0];
82
70
  const argType = arg._type ?? this._resolveExprGoType(arg);
@@ -92,12 +80,7 @@ export class BuiltinsEmitter {
92
80
  op: "call",
93
81
  funcIndex: this.mod.resolveFuncIndex(mapInfo.lenFuncName),
94
82
  });
95
- if ((targetWasmType ?? "i64") === "i64") {
96
- this.pushInstruction("i64.extend_i32_u");
97
- }
98
- return;
99
- }
100
- if (isSliceType(argType, this.mod.checker)) {
83
+ } else if (isSliceType(argType, this.mod.checker)) {
101
84
  const elemType = this._getSliceElemType(argType);
102
85
  const sliceInfo = this.mod.getSliceType(elemType);
103
86
  const sliceWType = {
@@ -111,12 +94,7 @@ export class BuiltinsEmitter {
111
94
  typeIndex: sliceInfo.typeIndex,
112
95
  fieldIndex: 2,
113
96
  });
114
- if ((targetWasmType ?? "i64") === "i64") {
115
- this.pushInstruction("i64.extend_i32_u");
116
- }
117
- return;
118
- }
119
- if (
97
+ } else if (
120
98
  isArrayType(argType, this.mod.checker) ||
121
99
  (argType?.kind === "pointer" &&
122
100
  isArrayType(argType.base, this.mod.checker))
@@ -130,19 +108,13 @@ export class BuiltinsEmitter {
130
108
  };
131
109
  this.emitExpr(arg, arrWType);
132
110
  this.pushInstruction("array.len");
133
- if ((targetWasmType ?? "i64") === "i64") {
134
- this.pushInstruction("i64.extend_i32_u");
135
- }
136
- return;
137
- }
138
- if (isStringType(argType, this.mod.checker)) {
111
+ } else if (isStringType(argType, this.mod.checker)) {
139
112
  this.emitExpr(arg, "externref");
140
113
  const lenIdx = this.mod.getStringLenImportIndex();
141
114
  this.pushInstruction({ op: "call", funcIndex: lenIdx });
142
- if ((targetWasmType ?? "i64") === "i64") {
143
- this.pushInstruction("i64.extend_i32_u");
144
- }
145
- return;
115
+ }
116
+ if ((targetWasmType ?? "i64") === "i64") {
117
+ this.pushInstruction("i64.extend_i32_u");
146
118
  }
147
119
  }
148
120
 
@@ -177,12 +149,7 @@ export class BuiltinsEmitter {
177
149
  typeIndex: sliceInfo.typeIndex,
178
150
  fieldIndex: 3,
179
151
  });
180
- if ((targetWasmType ?? "i64") === "i64") {
181
- this.pushInstruction("i64.extend_i32_u");
182
- }
183
- return;
184
- }
185
- if (
152
+ } else if (
186
153
  isArrayType(argType, this.mod.checker) ||
187
154
  (argType?.kind === "pointer" &&
188
155
  isArrayType(argType.base, this.mod.checker))
@@ -196,10 +163,9 @@ export class BuiltinsEmitter {
196
163
  };
197
164
  this.emitExpr(arg, arrWType);
198
165
  this.pushInstruction("array.len");
199
- if ((targetWasmType ?? "i64") === "i64") {
200
- this.pushInstruction("i64.extend_i32_u");
201
- }
202
- return;
166
+ }
167
+ if ((targetWasmType ?? "i64") === "i64") {
168
+ this.pushInstruction("i64.extend_i32_u");
203
169
  }
204
170
  }
205
171
 
@@ -894,12 +860,24 @@ export class BuiltinsEmitter {
894
860
  this.emitSlicesCall(func.field, args, targetWasmType);
895
861
  return true;
896
862
  case "strings":
863
+ if (func.field === "NewReader") {
864
+ this.emitStringsNewReader(call, targetWasmType);
865
+ return true;
866
+ }
897
867
  this.emitStringsCall(func.field, args, targetWasmType);
898
868
  return true;
899
869
  case "strconv":
900
870
  this.emitStrconvCall(func.field, args);
901
871
  return true;
902
872
  case "fmt":
873
+ if (func.field.startsWith("Fprint")) {
874
+ this.emitFprintCall(func.field, call, targetWasmType);
875
+ return true;
876
+ }
877
+ if (func.field.startsWith("Sscan")) {
878
+ this.emitSscanCall(func.field, call, targetWasmType);
879
+ return true;
880
+ }
903
881
  this.emitFmtCall(func.field, args);
904
882
  return true;
905
883
  case "errors":
@@ -914,12 +892,50 @@ export class BuiltinsEmitter {
914
892
  case "shared":
915
893
  this.emitSharedNew(func.field, args);
916
894
  return true;
895
+ case "unicode":
896
+ this.emitUnicodeCall(func.field, args, targetWasmType);
897
+ return true;
898
+ case "rand":
899
+ this.emitRandCall(func.field, args, targetWasmType);
900
+ return true;
901
+ case "bytes":
902
+ if (func.field === "NewReader") {
903
+ this.emitBytesNewReader(call, targetWasmType);
904
+ return true;
905
+ }
906
+ this.emitBytesCall(func.field, args, targetWasmType);
907
+ return true;
908
+ case "path":
909
+ case "filepath":
910
+ this.emitPathCall(func.field, args);
911
+ return true;
912
+ case "html":
913
+ this.emitHtmlCall(func.field, args);
914
+ return true;
915
+ case "io":
916
+ this.emitIoCall(func.field, call, targetWasmType);
917
+ return true;
918
+ case "time":
919
+ this.emitTimeCall(func.field, call, targetWasmType);
920
+ return true;
921
+ case "binary":
922
+ this.emitBinaryCall(func.field, call, targetWasmType);
923
+ return true;
924
+ case "sha256":
925
+ this.emitSha256Call(func.field, call, targetWasmType);
926
+ return true;
927
+ case "hex":
928
+ this.emitHexCall(func.field, call, targetWasmType);
929
+ return true;
930
+ case "base64":
931
+ this.emitBase64Call(func.field, call, targetWasmType);
932
+ return true;
917
933
  default:
918
934
  return false;
919
935
  }
920
936
  }
921
937
 
922
- _emitMethodCall(call) {
938
+ _emitMethodCall(call, targetWasmType = null) {
923
939
  const { func, args } = call;
924
940
  if (func.kind !== "SelectorExpr") return false;
925
941
 
@@ -930,6 +946,25 @@ export class BuiltinsEmitter {
930
946
  return true;
931
947
  }
932
948
  const recvTypeName = this._getReceiverTypeName(recvType, func.expr);
949
+ if (
950
+ this._emitStdlibReceiverMethod(
951
+ recvTypeName,
952
+ func.field,
953
+ func.expr,
954
+ args,
955
+ targetWasmType,
956
+ )
957
+ ) {
958
+ return true;
959
+ }
960
+ if (recvTypeName === "strings.Builder") {
961
+ this.emitBuilderMethodCall(func.field, func.expr, args);
962
+ return true;
963
+ }
964
+ if (recvTypeName === "strings.Replacer") {
965
+ this.emitReplacerMethodCall(func.field, func.expr, args);
966
+ return true;
967
+ }
933
968
  if (isNonEmptyInterface(recvType, this.mod.checker)) {
934
969
  const ifaceName = recvTypeName ?? "anon";
935
970
  const dispatchName = `__dispatch_${ifaceName}_${func.field}`;
@@ -950,38 +985,15 @@ export class BuiltinsEmitter {
950
985
  }
951
986
  if (recvTypeName) {
952
987
  const methodName = `${recvTypeName}.${func.field}`;
953
- let targetFuncIdx = this.mod.resolveFuncIndex(methodName);
954
- let targetParamTypes = this.mod.getFuncParamTypes(methodName);
988
+ const targetFuncIdx = this.mod.resolveFuncIndex(methodName);
989
+ const targetParamTypes = this.mod.getFuncParamTypes(methodName);
955
990
 
956
991
  // If not found directly, check embedded structs
957
- if (targetFuncIdx === null) {
958
- const structInfo = this.mod.getStructType(recvTypeName);
959
- if (structInfo) {
960
- for (const embed of structInfo.embeds) {
961
- const embedMethodName = `${embed.name}.${func.field}`;
962
- const subIdx = this.mod.resolveFuncIndex(embedMethodName);
963
- if (subIdx !== null) {
964
- targetFuncIdx = subIdx;
965
- targetParamTypes = this.mod.getFuncParamTypes(embedMethodName);
966
- const baseWType = this.toWasmType(recvType);
967
- this.emitExpr(func.expr, baseWType);
968
- this.pushInstruction({
969
- op: "struct.get",
970
- typeIndex: structInfo.typeIndex,
971
- fieldIndex: embed.fieldIndex,
972
- });
973
- for (let i = 0; i < args.length; i++) {
974
- const pType = targetParamTypes[i + 1] ?? null;
975
- this.emitExpr(args[i], pType);
976
- }
977
- this.pushInstruction({
978
- op: "call",
979
- funcIndex: targetFuncIdx,
980
- });
981
- return true;
982
- }
983
- }
984
- }
992
+ if (
993
+ targetFuncIdx === null &&
994
+ this._emitEmbeddedMethodCall(recvTypeName, func, args, recvType)
995
+ ) {
996
+ return true;
985
997
  }
986
998
 
987
999
  if (targetFuncIdx !== null) {
@@ -1003,6 +1015,113 @@ export class BuiltinsEmitter {
1003
1015
  return false;
1004
1016
  }
1005
1017
 
1018
+ _emitStdlibReceiverMethod(
1019
+ recvTypeName,
1020
+ field,
1021
+ expr,
1022
+ args,
1023
+ targetWasmType = null,
1024
+ ) {
1025
+ if (recvTypeName === "strings.Builder") {
1026
+ this.emitBuilderMethodCall(field, expr, args, targetWasmType);
1027
+ return true;
1028
+ }
1029
+ if (recvTypeName === "strings.Replacer") {
1030
+ this.emitReplacerMethodCall(field, expr, args);
1031
+ return true;
1032
+ }
1033
+ if (recvTypeName === "bytes.Buffer") {
1034
+ this.emitBytesBufferMethodCall(field, expr, args, targetWasmType);
1035
+ return true;
1036
+ }
1037
+ if (recvTypeName === "strings.Reader") {
1038
+ this.emitStringsReaderMethodCall(field, expr, args, targetWasmType);
1039
+ return true;
1040
+ }
1041
+ if (recvTypeName === "bytes.Reader") {
1042
+ this.emitBytesReaderMethodCall(field, expr, args, targetWasmType);
1043
+ return true;
1044
+ }
1045
+ if (recvTypeName === "time.Time") {
1046
+ this.emitTimeMethodCall(field, expr, args, targetWasmType);
1047
+ return true;
1048
+ }
1049
+ if (recvTypeName === "time.Duration") {
1050
+ this.emitDurationMethodCall(field, expr, args, targetWasmType);
1051
+ return true;
1052
+ }
1053
+ if (recvTypeName === "io.LimitedReader") {
1054
+ this.emitIoLimitedReaderMethodCall(field, expr, args, targetWasmType);
1055
+ return true;
1056
+ }
1057
+ if (recvTypeName === "sync.Mutex" || recvTypeName === "sync.RWMutex") {
1058
+ this.emitSyncMutexMethodCall(field, expr, args, targetWasmType);
1059
+ return true;
1060
+ }
1061
+ if (recvTypeName === "sync.Once") {
1062
+ this.emitSyncOnceMethodCall(field, expr, args, targetWasmType);
1063
+ return true;
1064
+ }
1065
+ if (recvTypeName === "sync.WaitGroup") {
1066
+ this.emitSyncWaitGroupMethodCall(field, expr, args, targetWasmType);
1067
+ return true;
1068
+ }
1069
+ if (
1070
+ recvTypeName === "binary.LittleEndian" ||
1071
+ recvTypeName === "binary.BigEndian" ||
1072
+ recvTypeName === "encoding/binary.LittleEndian" ||
1073
+ recvTypeName === "encoding/binary.BigEndian" ||
1074
+ recvTypeName === "binary.ByteOrder" ||
1075
+ recvTypeName === "encoding/binary.ByteOrder"
1076
+ ) {
1077
+ this.emitBinaryOrderMethodCall(
1078
+ recvTypeName,
1079
+ field,
1080
+ expr,
1081
+ args,
1082
+ targetWasmType,
1083
+ );
1084
+ return true;
1085
+ }
1086
+ if (
1087
+ recvTypeName === "base64.Encoding" ||
1088
+ recvTypeName === "encoding/base64.Encoding"
1089
+ ) {
1090
+ this.emitBase64EncodingMethodCall(field, expr, args, targetWasmType);
1091
+ return true;
1092
+ }
1093
+ return false;
1094
+ }
1095
+
1096
+ _emitEmbeddedMethodCall(recvTypeName, func, args, recvType) {
1097
+ const structInfo = this.mod.getStructType(recvTypeName);
1098
+ if (!structInfo) return false;
1099
+ for (const embed of structInfo.embeds) {
1100
+ const embedMethodName = `${embed.name}.${func.field}`;
1101
+ const subIdx = this.mod.resolveFuncIndex(embedMethodName);
1102
+ if (subIdx !== null) {
1103
+ const targetParamTypes = this.mod.getFuncParamTypes(embedMethodName);
1104
+ const baseWType = this.toWasmType(recvType);
1105
+ this.emitExpr(func.expr, baseWType);
1106
+ this.pushInstruction({
1107
+ op: "struct.get",
1108
+ typeIndex: structInfo.typeIndex,
1109
+ fieldIndex: embed.fieldIndex,
1110
+ });
1111
+ for (let i = 0; i < args.length; i++) {
1112
+ const pType = targetParamTypes[i + 1] ?? null;
1113
+ this.emitExpr(args[i], pType);
1114
+ }
1115
+ this.pushInstruction({
1116
+ op: "call",
1117
+ funcIndex: subIdx,
1118
+ });
1119
+ return true;
1120
+ }
1121
+ }
1122
+ return false;
1123
+ }
1124
+
1006
1125
  _emitStaticCall(call) {
1007
1126
  const { func, args } = call;
1008
1127
  if (func.kind !== "Ident") return false;
@@ -1076,6 +1195,14 @@ export class BuiltinsEmitter {
1076
1195
  }
1077
1196
 
1078
1197
  emitCallExpr(call, targetWasmType = null) {
1198
+ if (call._isTypeConversion) {
1199
+ this.emitTypeConversion({
1200
+ targetType: call._conversionTargetType,
1201
+ expr: call.args[0],
1202
+ });
1203
+ return;
1204
+ }
1205
+
1079
1206
  const deqFunc = this._dequalify(call.func);
1080
1207
  if (deqFunc) {
1081
1208
  this.emitCallExpr({ ...call, func: deqFunc }, targetWasmType);
@@ -1090,7 +1217,7 @@ export class BuiltinsEmitter {
1090
1217
  return;
1091
1218
  }
1092
1219
 
1093
- if (this._emitMethodCall(call)) {
1220
+ if (this._emitMethodCall(call, targetWasmType)) {
1094
1221
  return;
1095
1222
  }
1096
1223