gofront 1.7.0 → 1.9.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.
- package/README.md +14 -14
- package/package.json +10 -38
- package/src/backend/js/expressions.js +2060 -389
- package/src/backend/js/index.js +586 -174
- package/src/backend/js/natives.js +383 -0
- package/src/backend/js/runtime.js +936 -1644
- package/src/backend/js/statements.js +1349 -132
- package/src/backend/js/stdlib/index.js +51 -94
- package/src/backend/js/templ.js +67 -118
- package/src/backend/js/type-info.js +436 -0
- package/src/backend/shared/fixed-float.js +20 -0
- package/src/backend/shared/fmt-types.js +80 -0
- package/src/backend/shared/host-unicode.js +36 -0
- package/src/backend/wasm/boundary.js +753 -872
- package/src/backend/wasm/emit-builtins.js +914 -237
- package/src/backend/wasm/emit-exprs.js +1456 -195
- package/src/backend/wasm/emit-maps.js +78 -380
- package/src/backend/wasm/emit-stmts.js +1659 -214
- package/src/backend/wasm/emit.js +614 -88
- package/src/backend/wasm/encode.js +43 -13
- package/src/backend/wasm/globals.js +491 -0
- package/src/backend/wasm/glue.js +2 -4
- package/src/backend/wasm/index.js +406 -3195
- package/src/backend/wasm/method-expr.js +392 -0
- package/src/backend/wasm/module-emitter.js +1615 -0
- package/src/backend/wasm/monomorph.js +164 -9
- package/src/backend/wasm/natives.js +596 -0
- package/src/backend/wasm/prescan.js +249 -0
- package/src/backend/wasm/prune-funcs.js +89 -0
- package/src/backend/wasm/stdlib/index.js +2 -37
- package/src/backend/wasm/stdlib/simd.js +293 -28
- package/src/backend/wasm/types.js +260 -27
- package/src/backend/wasm/variadic.js +93 -0
- package/src/backend/wasm/wat.js +47 -20
- package/src/{cli-core.js → cli/cli-core.js} +241 -11
- package/src/{test-runner.js → cli/test-runner.js} +188 -122
- package/src/{vendor.js → cli/vendor.js} +3 -4
- package/src/compiler.js +202 -386
- package/src/index.js +15 -169
- package/src/{lexer.js → lexer/lexer.js} +203 -62
- package/src/{templ-lexer.js → lexer/templ-lexer.js} +61 -18
- package/src/{tokens.js → lexer/tokens.js} +2 -0
- package/src/link/sourcemap.js +97 -0
- package/src/link/stdlib.js +737 -0
- package/src/link/type-names.js +148 -0
- package/src/link/wasm.js +318 -0
- package/src/lower/bounds.js +547 -0
- package/src/lower/boxing.js +1 -1
- package/src/lower/captures.js +267 -64
- package/src/lower/embedding.js +32 -3
- package/src/lower/errors-as.js +196 -0
- package/src/lower/fmt-formatters.js +905 -0
- package/src/lower/functions.js +2 -0
- package/src/lower/gen-types.js +274 -0
- package/src/lower/index.js +33 -19
- package/src/lower/json-codecs.js +1379 -0
- package/src/lower/ownership.js +47 -8
- package/src/lower/range.js +86 -1
- package/src/lower/reachability.js +150 -0
- package/src/lower/walk.js +12 -0
- package/src/parser/declarations.js +83 -15
- package/src/parser/expressions.js +110 -18
- package/src/parser/index.js +2 -1
- package/src/parser/statements.js +38 -22
- package/src/{templ-parser.js → parser/templ.js} +179 -17
- package/src/parser/types.js +70 -9
- package/src/resolver.js +21 -1
- package/src/typechecker/assignability.js +291 -19
- package/src/typechecker/const-fold.js +399 -0
- package/src/typechecker/constants.js +282 -0
- package/src/typechecker/expressions.js +1220 -183
- package/src/typechecker/index.js +659 -92
- package/src/typechecker/init-order.js +426 -0
- package/src/typechecker/operand-text.js +40 -0
- package/src/typechecker/resolve.js +613 -62
- package/src/typechecker/statements.js +212 -20
- package/src/typechecker/stdlib/excluded.js +902 -0
- package/src/typechecker/stdlib/index.js +38 -0
- package/src/typechecker/stdlib/simd.js +65 -15
- package/src/typechecker/termination.js +11 -2
- package/src/typechecker/types.js +268 -514
- package/stdlib/bytes/buffer.go +479 -0
- package/stdlib/bytes/bytes.go +1010 -0
- package/stdlib/bytes/reader.go +161 -0
- package/stdlib/cmp/cmp.go +65 -0
- package/stdlib/context/context.go +115 -0
- package/stdlib/crypto/rand/rand.go +34 -0
- package/stdlib/crypto/sha256/sha256.go +249 -0
- package/stdlib/encoding/base64/base64.go +331 -0
- package/stdlib/encoding/binary/binary.go +332 -0
- package/stdlib/encoding/hex/hex.go +140 -0
- package/stdlib/encoding/json/errors.go +31 -0
- package/stdlib/encoding/json/indent.go +71 -0
- package/stdlib/encoding/json/json.go +62 -0
- package/stdlib/encoding/json/number.go +12 -0
- package/stdlib/encoding/json/stream.go +173 -0
- package/stdlib/errors/errors.go +174 -0
- package/stdlib/fmt/errors.go +110 -0
- package/stdlib/fmt/format.go +732 -0
- package/stdlib/fmt/print.go +1232 -0
- package/stdlib/gom/gom.go +293 -0
- package/stdlib/hash/hash.go +53 -0
- package/stdlib/html/html.go +251 -0
- package/stdlib/internal/fmtrt/fmtrt.go +163 -0
- package/stdlib/internal/jsonrt/dec.go +871 -0
- package/stdlib/internal/jsonrt/enc.go +516 -0
- package/stdlib/internal/jsonrt/indent.go +135 -0
- package/stdlib/internal/jsonrt/number.go +27 -0
- package/stdlib/internal/jsonrt/reader.go +73 -0
- package/stdlib/internal/jsonrt/scanner.go +658 -0
- package/stdlib/io/io.go +502 -0
- package/stdlib/iter/iter.go +18 -0
- package/stdlib/maps/maps.go +131 -0
- package/stdlib/math/bits/bits.go +507 -0
- package/stdlib/math/extra.go +272 -0
- package/stdlib/math/math.go +439 -0
- package/stdlib/math/rand/rand.go +297 -0
- package/stdlib/os/os.go +93 -0
- package/stdlib/path/filepath/filepath.go +47 -0
- package/stdlib/path/path.go +394 -0
- package/stdlib/regexp/regexp.go +231 -0
- package/stdlib/slices/slices.go +526 -0
- package/stdlib/slices/sort.go +209 -0
- package/stdlib/slices/zsort_cmp.go +480 -0
- package/stdlib/slices/zsort_ordered.go +482 -0
- package/stdlib/sort/sort.go +500 -0
- package/stdlib/sort/zsort_func.go +480 -0
- package/stdlib/sort/zsort_iface.go +480 -0
- package/stdlib/strconv/float.go +1699 -0
- package/stdlib/strconv/isprint.go +752 -0
- package/stdlib/strconv/quote.go +526 -0
- package/stdlib/strconv/strconv.go +616 -0
- package/stdlib/strings/builder.go +95 -0
- package/stdlib/strings/reader.go +163 -0
- package/stdlib/strings/replacer.go +102 -0
- package/stdlib/strings/strings.go +807 -0
- package/stdlib/sync/sync.go +268 -0
- package/stdlib/testing/async_js.go +61 -0
- package/stdlib/testing/benchmark.go +297 -0
- package/stdlib/testing/testing.go +523 -0
- package/stdlib/time/duration.go +233 -0
- package/stdlib/time/format.go +745 -0
- package/stdlib/time/parse.go +790 -0
- package/stdlib/time/sleep_js.go +63 -0
- package/stdlib/time/time.go +834 -0
- package/stdlib/unicode/unicode.go +250 -0
- package/stdlib/unicode/utf8/utf8.go +464 -0
- package/src/backend/js/stdlib/base64.js +0 -12
- package/src/backend/js/stdlib/binary.js +0 -102
- package/src/backend/js/stdlib/bits.js +0 -46
- package/src/backend/js/stdlib/builder.js +0 -63
- package/src/backend/js/stdlib/bytes.js +0 -122
- package/src/backend/js/stdlib/errors.js +0 -44
- package/src/backend/js/stdlib/fmt.js +0 -178
- package/src/backend/js/stdlib/gom.js +0 -155
- package/src/backend/js/stdlib/hex.js +0 -17
- package/src/backend/js/stdlib/html.js +0 -20
- package/src/backend/js/stdlib/io.js +0 -137
- package/src/backend/js/stdlib/json.js +0 -972
- package/src/backend/js/stdlib/maps.js +0 -45
- package/src/backend/js/stdlib/math.js +0 -69
- package/src/backend/js/stdlib/os.js +0 -13
- package/src/backend/js/stdlib/path.js +0 -48
- package/src/backend/js/stdlib/rand.js +0 -32
- package/src/backend/js/stdlib/regexp.js +0 -54
- package/src/backend/js/stdlib/sha256.js +0 -12
- package/src/backend/js/stdlib/shared.js +0 -17
- package/src/backend/js/stdlib/simd.js +0 -24
- package/src/backend/js/stdlib/slices.js +0 -77
- package/src/backend/js/stdlib/sort.js +0 -30
- package/src/backend/js/stdlib/strconv.js +0 -74
- package/src/backend/js/stdlib/strings.js +0 -222
- package/src/backend/js/stdlib/sync.js +0 -13
- package/src/backend/js/stdlib/testing.js +0 -34
- package/src/backend/js/stdlib/time.js +0 -128
- package/src/backend/js/stdlib/unicode.js +0 -25
- package/src/backend/js/stdlib/utf8.js +0 -26
- package/src/backend/wasm/const-fold.js +0 -205
- package/src/backend/wasm/emit-stdlib.js +0 -13
- package/src/backend/wasm/stdlib/bytes.js +0 -5039
- package/src/backend/wasm/stdlib/core.js +0 -8903
- package/src/backend/wasm/stdlib/io.js +0 -2161
- package/src/backend/wasm/stdlib/packages.js +0 -2400
- package/src/backend/wasm/stdlib/pure.js +0 -720
- package/src/backend/wasm/stdlib/time.js +0 -1200
- package/src/typechecker/stdlib/core.js +0 -801
- package/src/typechecker/stdlib/extended.js +0 -914
- package/src/typechecker/stdlib.js +0 -22
- /package/src/{asset-manager.js → cli/asset-manager.js} +0 -0
- /package/src/{colors.js → cli/colors.js} +0 -0
- /package/src/{dev-server.js → cli/dev-server.js} +0 -0
- /package/src/{minifier.js → cli/minifier.js} +0 -0
- /package/src/{project-config.js → cli/project-config.js} +0 -0
- /package/src/{pwa.js → cli/pwa.js} +0 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
// src/backend/js/type-info.js
|
|
2
|
+
// Run-time type information for erased generics. A generic function (or the
|
|
3
|
+
// instance of a generic type) receives one zero-value factory per type
|
|
4
|
+
// parameter; when its body needs to know the instantiated type (type
|
|
5
|
+
// assertions to a type parameter, conversions to it, `==` on it, building an
|
|
6
|
+
// instance of another generic type from it) the factory also carries the
|
|
7
|
+
// type's kind, identity and a type test (see `__ti` in HELPER_GENERICS).
|
|
8
|
+
|
|
9
|
+
import { forEachChild } from "../../lower/index.js";
|
|
10
|
+
import {
|
|
11
|
+
isAny,
|
|
12
|
+
isTypedArraySlice,
|
|
13
|
+
typedArrayConstructorForElem,
|
|
14
|
+
typeStr,
|
|
15
|
+
} from "../../typechecker/types.js";
|
|
16
|
+
import { mentionsTypeParam } from "./expressions.js";
|
|
17
|
+
|
|
18
|
+
// True when type node `node` names one of the type parameters `names`.
|
|
19
|
+
function typeNodeMentions(node, names) {
|
|
20
|
+
if (Array.isArray(node)) return node.some((n) => typeNodeMentions(n, names));
|
|
21
|
+
if (!node || typeof node !== "object") return false;
|
|
22
|
+
if (
|
|
23
|
+
(node.kind === "TypeName" || node.kind === "Ident") &&
|
|
24
|
+
names.has(node.name)
|
|
25
|
+
)
|
|
26
|
+
return true;
|
|
27
|
+
let found = false;
|
|
28
|
+
forEachChild(node, (c) => {
|
|
29
|
+
if (!found && typeNodeMentions(c, names)) found = true;
|
|
30
|
+
});
|
|
31
|
+
return found;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function typeArgsMention(node) {
|
|
35
|
+
const args = node._typeArgs ?? node._type?.typeArgs;
|
|
36
|
+
return Array.isArray(args) && args.some((t) => mentionsTypeParam(t));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const typeInfoGenMethods = {
|
|
40
|
+
// Whether the body of generic `decl` (a function or method declaration)
|
|
41
|
+
// needs the instantiated types of `names`.
|
|
42
|
+
_declNeedsTypeInfo(decl, names) {
|
|
43
|
+
this._typeInfoMemo ??= new Map();
|
|
44
|
+
this._typeInfoActive ??= new Set();
|
|
45
|
+
if (this._typeInfoMemo.has(decl)) return this._typeInfoMemo.get(decl);
|
|
46
|
+
if (this._typeInfoActive.has(decl)) {
|
|
47
|
+
this._typeInfoCycle = true; // answer depends on an unfinished scan
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const outer = this._typeInfoCycle;
|
|
51
|
+
this._typeInfoCycle = false;
|
|
52
|
+
this._typeInfoActive.add(decl);
|
|
53
|
+
const result = this._scanTypeInfoUse(decl.body, new Set(names));
|
|
54
|
+
this._typeInfoActive.delete(decl);
|
|
55
|
+
// A false that leaned on an in-progress scan is provisional: only cache
|
|
56
|
+
// it once no scan is in progress, otherwise a mutually recursive
|
|
57
|
+
// function would keep a stale answer.
|
|
58
|
+
if (result || !this._typeInfoCycle || this._typeInfoActive.size === 0)
|
|
59
|
+
this._typeInfoMemo.set(decl, result);
|
|
60
|
+
this._typeInfoCycle = outer || this._typeInfoCycle;
|
|
61
|
+
return result;
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
_scanTypeInfoUse(node, names) {
|
|
65
|
+
if (Array.isArray(node))
|
|
66
|
+
return node.some((n) => this._scanTypeInfoUse(n, names));
|
|
67
|
+
if (!node || typeof node !== "object") return false;
|
|
68
|
+
if (this._nodeUsesTypeInfo(node, names)) return true;
|
|
69
|
+
let found = false;
|
|
70
|
+
forEachChild(node, (c) => {
|
|
71
|
+
if (!found && this._scanTypeInfoUse(c, names)) found = true;
|
|
72
|
+
});
|
|
73
|
+
return found;
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
// `x.M(...)` where x has a type parameter type: a raw value of a named
|
|
77
|
+
// non-struct type argument must be boxed to reach its methods.
|
|
78
|
+
_isTypeParamMethodCall(call, names) {
|
|
79
|
+
const t = call.func?.expr?._type;
|
|
80
|
+
return (
|
|
81
|
+
call.func?.kind === "SelectorExpr" &&
|
|
82
|
+
!call.func._isMethodExpr &&
|
|
83
|
+
t?.kind === "typeParam" &&
|
|
84
|
+
(!names || names.has(t.name))
|
|
85
|
+
);
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
_nodeUsesTypeInfo(node, names) {
|
|
89
|
+
if (node._ifaceKeyDyn && names.has(node._ifaceKeyParam)) return true;
|
|
90
|
+
if (node._ifaceSrc?.kind === "typeParam" && names.has(node._ifaceSrc.name))
|
|
91
|
+
return true;
|
|
92
|
+
switch (node.kind) {
|
|
93
|
+
case "TypeAssertExpr":
|
|
94
|
+
return typeNodeMentions(node.type, names);
|
|
95
|
+
case "TypeSwitchStmt":
|
|
96
|
+
return node.cases.some((c) => typeNodeMentions(c.types, names));
|
|
97
|
+
case "TypeConversion":
|
|
98
|
+
return typeNodeMentions(node.targetType, names);
|
|
99
|
+
case "BinaryExpr":
|
|
100
|
+
return (
|
|
101
|
+
((node.op === "==" || node.op === "!=") &&
|
|
102
|
+
node.left._type?.kind === "typeParam") ||
|
|
103
|
+
(node._type?.kind === "typeParam" && names.has(node._type.name))
|
|
104
|
+
);
|
|
105
|
+
case "AssignStmt":
|
|
106
|
+
return (
|
|
107
|
+
node.op !== "=" &&
|
|
108
|
+
node.lhs?.[0]?._type?.kind === "typeParam" &&
|
|
109
|
+
names.has(node.lhs[0]._type.name)
|
|
110
|
+
);
|
|
111
|
+
case "IncDecStmt":
|
|
112
|
+
return (
|
|
113
|
+
node.expr?._type?.kind === "typeParam" &&
|
|
114
|
+
names.has(node.expr._type.name)
|
|
115
|
+
);
|
|
116
|
+
case "CallExpr":
|
|
117
|
+
return this._callUsesTypeInfo(node, names);
|
|
118
|
+
case "CompositeLit":
|
|
119
|
+
case "GenericTypeName":
|
|
120
|
+
return typeArgsMention(node);
|
|
121
|
+
default:
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
_callUsesTypeInfo(call, names) {
|
|
127
|
+
if (this._isTypeParamMethodCall(call, names)) return true;
|
|
128
|
+
if (call._isTypeConversion)
|
|
129
|
+
return call._conversionTargetType?.kind === "typeParam";
|
|
130
|
+
// A fmt operand of a type parameter type is formatted by its instantiated
|
|
131
|
+
// type (a Stringer type argument), see _fmtArgJs.
|
|
132
|
+
if (
|
|
133
|
+
call.func?.kind === "SelectorExpr" &&
|
|
134
|
+
call.func.expr?.name === "fmt" &&
|
|
135
|
+
call.args.some((a) => a?._type?.kind === "typeParam")
|
|
136
|
+
)
|
|
137
|
+
return true;
|
|
138
|
+
const site = call._genericFnType ? call : call.func;
|
|
139
|
+
if (!site?._genericFnType) return false;
|
|
140
|
+
if (!site._typeArgs?.some((t) => mentionsTypeParam(t))) return false;
|
|
141
|
+
return this._genericFnNeedsTypeInfo(site._genericFnType);
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
// Whether callers of generic function `generic` pass type information.
|
|
145
|
+
_genericFnNeedsTypeInfo(generic) {
|
|
146
|
+
const decl = generic?.declNode;
|
|
147
|
+
if (!decl) return false;
|
|
148
|
+
return this._declNeedsTypeInfo(
|
|
149
|
+
decl,
|
|
150
|
+
(decl.typeParams ?? []).map((tp) => tp.name),
|
|
151
|
+
);
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
// Whether instances of generic type `named` carry type information: any
|
|
155
|
+
// of its methods needs it.
|
|
156
|
+
_genericTypeNeedsTypeInfo(named) {
|
|
157
|
+
const generic = named?._generic;
|
|
158
|
+
if (!generic?.methods) return false;
|
|
159
|
+
const names = generic.typeParams.map((tp) => tp.name);
|
|
160
|
+
for (const m of generic.methods.values())
|
|
161
|
+
if (m?._decl && this._declNeedsTypeInfo(m._decl, names)) return true;
|
|
162
|
+
return false;
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
// The factory a generic callee receives for type argument `t`, carrying
|
|
166
|
+
// the type's kind, identity and type test.
|
|
167
|
+
_typeInfoFactory(t) {
|
|
168
|
+
if (t?.kind === "typeParam") {
|
|
169
|
+
const bound = this._typeParamZeroFactory(t.name);
|
|
170
|
+
if (bound) return bound;
|
|
171
|
+
}
|
|
172
|
+
this.useHelper("generics");
|
|
173
|
+
const base = this.zeroFactoryFor(t);
|
|
174
|
+
const u = this._underlyingOf(t);
|
|
175
|
+
let k = "";
|
|
176
|
+
if (isAny(u) || u?.kind === "interface") k = "iface";
|
|
177
|
+
else if (u?.kind === "basic") k = u.name;
|
|
178
|
+
const check = this._typeCheckForType(t, "__v");
|
|
179
|
+
const is = check ? `(__v) => ${check}` : "null";
|
|
180
|
+
const unbox = this._boxesInInterface(t) ? "true" : "false";
|
|
181
|
+
const box = this._ifaceKeyBox(t) ?? this._ptrMethodBox(t);
|
|
182
|
+
const fmtBox = this._fmtBoxFor(t);
|
|
183
|
+
const tail = fmtBox
|
|
184
|
+
? `, ${box ?? "null"}, ${fmtBox}`
|
|
185
|
+
: box
|
|
186
|
+
? `, ${box}`
|
|
187
|
+
: "";
|
|
188
|
+
return `__ti(${base}, ${JSON.stringify(k)}, ${JSON.stringify(typeStr(t))}, ${is}, ${unbox}${tail})`;
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
// How `__ik` tells the dynamic type of an interface map key apart where
|
|
192
|
+
// the JS value cannot: the numeric type name for types sharing a JS
|
|
193
|
+
// representation with `int`/`float64`/`int64`, `*` for pointers (keyed by
|
|
194
|
+
// identity) and `!type` for types Go cannot hash.
|
|
195
|
+
_ifaceKeyTag(t) {
|
|
196
|
+
if (!t || t.kind === "named" || mentionsTypeParam(t)) return null;
|
|
197
|
+
if (t.kind === "pointer") return "*";
|
|
198
|
+
if (t.kind === "slice" || t.kind === "map" || t.kind === "func")
|
|
199
|
+
return `!${this._dynTypeName(t)}`;
|
|
200
|
+
if (t.kind !== "basic") return null;
|
|
201
|
+
const name = this._dynTypeName(t);
|
|
202
|
+
if (["int", "float64", "int64", "string", "bool"].includes(name))
|
|
203
|
+
return null;
|
|
204
|
+
return /^(u?int\d*|uintptr|float32)$/.test(name) ? name : null;
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
// A function boxing a plain value of named basic type `t`, as converting
|
|
208
|
+
// it to an interface does; null when `t` is not boxed.
|
|
209
|
+
_ifaceKeyBox(t) {
|
|
210
|
+
if (!this._boxesInInterface(t) || t?.kind !== "named") return null;
|
|
211
|
+
return `(__v) => ${this._genNamedIfaceBox("__v", t, false)}`;
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
// A function boxing a pointer to a named non-struct type with methods, as
|
|
215
|
+
// converting it to an interface does; null for any other type.
|
|
216
|
+
_ptrMethodBox(t) {
|
|
217
|
+
if (t?.kind !== "pointer" || !this._boxesInInterface(t)) return null;
|
|
218
|
+
return `(__v) => ${this._genNamedIfaceBox("__v", t.base, true)}`;
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
// A function giving a plain value of type `t` the type information fmt
|
|
222
|
+
// needs once it is an `any` operand; null when it needs none.
|
|
223
|
+
_fmtBoxFor(t) {
|
|
224
|
+
const base = this._fmtBoxBase(t);
|
|
225
|
+
// An unnamed slice, map or pointer also carries its type tag, as
|
|
226
|
+
// converting it to an `any` gives it, so a type switch tells it apart.
|
|
227
|
+
const tag = this._dynTypeTag(t);
|
|
228
|
+
if (!tag) return base;
|
|
229
|
+
this.useHelper("ifaceBox");
|
|
230
|
+
const ptr = t.kind === "pointer" ? ", true" : "";
|
|
231
|
+
return `(__v) => __ity(${base ? `(${base})(__v)` : "__v"}, ${JSON.stringify(tag)}${ptr})`;
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
_fmtBoxBase(t) {
|
|
235
|
+
if (!t || t.kind === "typeParam" || mentionsTypeParam(t)) return null;
|
|
236
|
+
if (this._ifaceKeyBox(t)) return null; // boxed by the key box already
|
|
237
|
+
const u = this._underlyingOf(t);
|
|
238
|
+
if (t.kind === "pointer" && this._underlyingOf(t.base)?.kind === "struct") {
|
|
239
|
+
this.useHelper("ifaceBox");
|
|
240
|
+
return "(__v) => __ifp(__v)";
|
|
241
|
+
}
|
|
242
|
+
if (u?.kind === "map") {
|
|
243
|
+
this.useHelper("ifaceBox");
|
|
244
|
+
return "(__v) => __ifm(__v)";
|
|
245
|
+
}
|
|
246
|
+
if (u?.kind === "slice") return "(__v) => (__v ?? [])";
|
|
247
|
+
return null;
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
// The run-time type tag of an unnamed slice, map or non-struct pointer
|
|
251
|
+
// entering an interface, or null when the value needs none. The tag lets
|
|
252
|
+
// assertions and type switches tell `[]int32` from `[]uint8`, `*int` from
|
|
253
|
+
// `*string`, and a nil `*int` from the nil interface.
|
|
254
|
+
_dynTypeTag(t) {
|
|
255
|
+
if (!t || mentionsTypeParam(t)) return null;
|
|
256
|
+
if (
|
|
257
|
+
t.kind === "slice" ||
|
|
258
|
+
t.kind === "map" ||
|
|
259
|
+
t.kind === "array" ||
|
|
260
|
+
t.kind === "struct" ||
|
|
261
|
+
t.kind === "func"
|
|
262
|
+
)
|
|
263
|
+
return this._dynTypeName(t);
|
|
264
|
+
if (this._isNamedComposite(t)) return t.name;
|
|
265
|
+
if (t.kind === "pointer") {
|
|
266
|
+
const base = t.base;
|
|
267
|
+
if (base?.kind === "struct" && !base.name) return this._dynTypeName(t);
|
|
268
|
+
if (this._underlyingOf(base)?.kind === "struct") return null;
|
|
269
|
+
if (this._boxesInInterface(t)) return null;
|
|
270
|
+
return this._dynTypeName(t);
|
|
271
|
+
}
|
|
272
|
+
return null;
|
|
273
|
+
},
|
|
274
|
+
|
|
275
|
+
// A named slice, array or map type: its values are plain JS arrays and
|
|
276
|
+
// objects, told apart in an interface by a tag carrying the type's name.
|
|
277
|
+
_isNamedComposite(t) {
|
|
278
|
+
if (t?.kind !== "named" || this._namedBoxInfo(t)) return false;
|
|
279
|
+
const kind = this._underlyingOf(t)?.kind;
|
|
280
|
+
return kind === "slice" || kind === "array" || kind === "map";
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
// `typeStr` with the spellings of identical types unified.
|
|
284
|
+
_dynTypeName(t) {
|
|
285
|
+
return this._dynTypeStr(t)
|
|
286
|
+
.replace(/\bbyte\b/g, "uint8")
|
|
287
|
+
.replace(/\brune\b/g, "int32")
|
|
288
|
+
.replace(/\bany\b/g, "interface {}")
|
|
289
|
+
.replace(/interface\{\.\.\.\}/g, "interface {}");
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
// `typeStr`, but an unnamed struct lists its fields, so two anonymous
|
|
293
|
+
// structs do not share a tag.
|
|
294
|
+
_dynTypeStr(t) {
|
|
295
|
+
const s = (x) => this._dynTypeStr(x);
|
|
296
|
+
switch (t?.kind) {
|
|
297
|
+
case "slice":
|
|
298
|
+
return `[]${s(t.elem)}`;
|
|
299
|
+
case "array":
|
|
300
|
+
return `[${t.size?.value ?? t.size ?? "..."}]${s(t.elem)}`;
|
|
301
|
+
case "map":
|
|
302
|
+
return `map[${s(t.key)}]${s(t.value)}`;
|
|
303
|
+
case "pointer":
|
|
304
|
+
return `*${s(t.base)}`;
|
|
305
|
+
case "struct":
|
|
306
|
+
if (t.name || !t._structFields) return typeStr(t);
|
|
307
|
+
return `struct{${t._structFields
|
|
308
|
+
.map(
|
|
309
|
+
(f) =>
|
|
310
|
+
`${(f.names ?? []).join(",")} ${s(f.type)}${f.tag ? ` ${JSON.stringify(f.tag)}` : ""}${f.embedded ? " embedded" : ""}`,
|
|
311
|
+
)
|
|
312
|
+
.join("; ")}}`;
|
|
313
|
+
default:
|
|
314
|
+
return typeStr(t);
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
|
|
318
|
+
// Run-time test that `val` holds a value of the unnamed composite type
|
|
319
|
+
// (or named func type) of assertion target `typeNode`; null when the
|
|
320
|
+
// checker recorded no type for it.
|
|
321
|
+
_dynTypeCheck(typeNode, val) {
|
|
322
|
+
const t = typeNode?._assertType;
|
|
323
|
+
if (!t) return null;
|
|
324
|
+
const u = this._underlyingOf(t);
|
|
325
|
+
const shape = mentionsTypeParam(t);
|
|
326
|
+
const tag = JSON.stringify(shape ? "" : this._dynTypeName(t));
|
|
327
|
+
this.useHelper("ifaceBox");
|
|
328
|
+
const args = (kind, ctor = "null", n = "0") =>
|
|
329
|
+
`__isty(${val}, ${tag}, "${kind}", ${ctor}, ${n})`;
|
|
330
|
+
if (u?.kind === "func") return args("func");
|
|
331
|
+
if (t.kind === "named") return null;
|
|
332
|
+
if (u?.kind === "map") return args("map");
|
|
333
|
+
if (u?.kind === "struct") return args("struct");
|
|
334
|
+
if (u?.kind === "array")
|
|
335
|
+
return args("array", "null", String(u.size?.value ?? u.size ?? 0));
|
|
336
|
+
if (u?.kind === "slice") {
|
|
337
|
+
const ctor = isTypedArraySlice(u)
|
|
338
|
+
? typedArrayConstructorForElem(u.elem)
|
|
339
|
+
: null;
|
|
340
|
+
return args("slice", ctor ?? "null");
|
|
341
|
+
}
|
|
342
|
+
if (u?.kind === "pointer") {
|
|
343
|
+
if (u.base?.kind === "struct" && !u.base.name) return args("struct");
|
|
344
|
+
if (this._underlyingOf(u.base)?.kind === "struct") return null;
|
|
345
|
+
if (this._boxesInInterface(u)) return null;
|
|
346
|
+
return args("ptr");
|
|
347
|
+
}
|
|
348
|
+
return null;
|
|
349
|
+
},
|
|
350
|
+
|
|
351
|
+
_underlyingOf(t) {
|
|
352
|
+
let u = t;
|
|
353
|
+
for (let i = 0; i < 8 && u?.kind === "named"; i++) u = u.underlying;
|
|
354
|
+
return u;
|
|
355
|
+
},
|
|
356
|
+
|
|
357
|
+
// A named non-struct value (or pointer to one) is boxed in an interface.
|
|
358
|
+
_boxesInInterface(t) {
|
|
359
|
+
const base = t?.kind === "pointer" ? t.base : t;
|
|
360
|
+
return Boolean(this._namedBoxInfo(base));
|
|
361
|
+
},
|
|
362
|
+
|
|
363
|
+
// JS test that `val` holds a value of type `t`, or null when it cannot be
|
|
364
|
+
// told at run time.
|
|
365
|
+
_typeCheckForType(t, val) {
|
|
366
|
+
switch (t?.kind) {
|
|
367
|
+
case "basic":
|
|
368
|
+
return this._typeNameCheckExpr(t.name, val);
|
|
369
|
+
case "named":
|
|
370
|
+
return this._namedTypeCheck(t, val);
|
|
371
|
+
case "pointer":
|
|
372
|
+
return this._pointerTypeCheck(t.base, val);
|
|
373
|
+
case "interface":
|
|
374
|
+
return t.methods?.size
|
|
375
|
+
? this._ifaceMethodCheck(
|
|
376
|
+
val,
|
|
377
|
+
[...t.methods.keys()],
|
|
378
|
+
this._unwrapIsMulti(t.methods.get("Unwrap")),
|
|
379
|
+
)
|
|
380
|
+
: null;
|
|
381
|
+
case "typeParam": {
|
|
382
|
+
const f = this._typeParamZeroFactory(t.name);
|
|
383
|
+
return f ? `(${f}?.is?.(${val}) ?? true)` : null;
|
|
384
|
+
}
|
|
385
|
+
default:
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
_namedTypeCheck(t, val) {
|
|
391
|
+
if (t.typeArgs?.length) {
|
|
392
|
+
const generic = this._genericInstanceCheck(
|
|
393
|
+
{ kind: "GenericTypeName", name: t.name, _typeArgs: t.typeArgs },
|
|
394
|
+
val,
|
|
395
|
+
);
|
|
396
|
+
if (generic) return generic;
|
|
397
|
+
}
|
|
398
|
+
const u = this._underlyingOf(t);
|
|
399
|
+
if (u?.kind === "interface" && t.name !== "error")
|
|
400
|
+
return this._ifaceMethodCheck(
|
|
401
|
+
val,
|
|
402
|
+
[...(u.methods?.keys() ?? [])],
|
|
403
|
+
this._unwrapIsMulti(u.methods?.get("Unwrap")),
|
|
404
|
+
);
|
|
405
|
+
return this._typeNameCheckExpr(t.name, val);
|
|
406
|
+
},
|
|
407
|
+
|
|
408
|
+
_pointerTypeCheck(base, val) {
|
|
409
|
+
if (base?.kind !== "named") return null;
|
|
410
|
+
const node = base.typeArgs?.length
|
|
411
|
+
? { kind: "GenericTypeName", name: base.name, _typeArgs: base.typeArgs }
|
|
412
|
+
: { kind: "TypeName", name: base.name };
|
|
413
|
+
const check = this._typeCheckExpr({ kind: "PointerType", base: node }, val);
|
|
414
|
+
return check === "true" ? null : check;
|
|
415
|
+
},
|
|
416
|
+
|
|
417
|
+
// Runtime instance id of a generic type's instantiation `typeArgs` that
|
|
418
|
+
// mention type parameters: the callee's type arguments joined as
|
|
419
|
+
// `instanceId` prints them; null when it cannot be built.
|
|
420
|
+
_runtimeInstanceId(typeArgs) {
|
|
421
|
+
if (!typeArgs?.length) return null;
|
|
422
|
+
const parts = [];
|
|
423
|
+
for (const t of typeArgs) {
|
|
424
|
+
if (!mentionsTypeParam(t)) {
|
|
425
|
+
parts.push(JSON.stringify(typeStr(t)));
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
const f =
|
|
429
|
+
t.kind === "typeParam" ? this._typeParamZeroFactory(t.name) : null;
|
|
430
|
+
if (!f) return null;
|
|
431
|
+
parts.push(`${f}?.id`);
|
|
432
|
+
}
|
|
433
|
+
this.useHelper("generics");
|
|
434
|
+
return `__tid(${parts.join(", ")})`;
|
|
435
|
+
},
|
|
436
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// The host half of strconv's fixed-point float formatting: a JS function,
|
|
2
|
+
// as source text, that both backends embed (src/backend/js/natives.js and the
|
|
3
|
+
// WASM host imports in src/backend/wasm/boundary.js).
|
|
4
|
+
//
|
|
5
|
+
// `toFixed` is exact except that it rounds a tie (a value whose decimal
|
|
6
|
+
// expansion ends in a 5 right after the digits kept) up, where Go rounds it
|
|
7
|
+
// to even; the extra digits of a second call tell the two apart. A tie is a
|
|
8
|
+
// multiple of 2^-(p+1), so any other value needs no second call. "" declines.
|
|
9
|
+
export const FIXED_FLOAT_JS = `(x, p) => {
|
|
10
|
+
if (!(x < 1e21)) return "";
|
|
11
|
+
const s = x.toFixed(p);
|
|
12
|
+
if (x % 1 === 0 || (x * 2 ** (p + 1)) % 1 !== 0) return s;
|
|
13
|
+
const t = x.toFixed(p + 30);
|
|
14
|
+
const dot = t.indexOf(".");
|
|
15
|
+
if (/^50*$/.test(t.slice(dot + 1 + p))) {
|
|
16
|
+
const down = t.slice(0, p === 0 ? dot : dot + 1 + p);
|
|
17
|
+
return (down.charCodeAt(down.length - 1) - 48) % 2 === 0 ? down : s;
|
|
18
|
+
}
|
|
19
|
+
return s;
|
|
20
|
+
}`;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Go's `reflect` spelling of a type used as a type argument (the monomorphiser
|
|
2
|
+
// records it for `%T` of a type-parameter operand), shared by the JS and WASM
|
|
3
|
+
// backends.
|
|
4
|
+
|
|
5
|
+
const qualify = (name) =>
|
|
6
|
+
name.split("[")[0].includes(".") ? name : `main.${name}`;
|
|
7
|
+
|
|
8
|
+
const PREDECLARED = new Set([
|
|
9
|
+
"bool",
|
|
10
|
+
"string",
|
|
11
|
+
"int",
|
|
12
|
+
"int8",
|
|
13
|
+
"int16",
|
|
14
|
+
"int32",
|
|
15
|
+
"int64",
|
|
16
|
+
"uint",
|
|
17
|
+
"uint8",
|
|
18
|
+
"uint16",
|
|
19
|
+
"uint32",
|
|
20
|
+
"uint64",
|
|
21
|
+
"uintptr",
|
|
22
|
+
"float32",
|
|
23
|
+
"float64",
|
|
24
|
+
"complex64",
|
|
25
|
+
"complex128",
|
|
26
|
+
"byte",
|
|
27
|
+
"rune",
|
|
28
|
+
"error",
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Go's `reflect` spelling of a type used as a type argument: `int`,
|
|
33
|
+
* `main.Foo`, `[]main.Foo`. Takes checker types and parsed type nodes;
|
|
34
|
+
* `specialized` maps the names of monomorphised instances to their spelling.
|
|
35
|
+
* @param {any} t
|
|
36
|
+
* @param {Map<string, string>} [specialized]
|
|
37
|
+
* @returns {string}
|
|
38
|
+
*/
|
|
39
|
+
export function goTypeName(t, specialized = null) {
|
|
40
|
+
const go = (x) => goTypeName(x, specialized);
|
|
41
|
+
switch (t?.kind) {
|
|
42
|
+
case "basic":
|
|
43
|
+
return t.name === "any" ? "interface {}" : t.name;
|
|
44
|
+
case "untyped":
|
|
45
|
+
return t.base;
|
|
46
|
+
case "TypeName":
|
|
47
|
+
case "Ident":
|
|
48
|
+
if (specialized?.has(t.name)) return qualify(specialized.get(t.name));
|
|
49
|
+
if (t.name === "any") return "interface {}";
|
|
50
|
+
return PREDECLARED.has(t.name) ? t.name : qualify(t.name);
|
|
51
|
+
case "GenericTypeName":
|
|
52
|
+
return qualify(`${t.name}[${t.typeArgs.map(go).join(",")}]`);
|
|
53
|
+
case "named":
|
|
54
|
+
return qualify(instanceName(t, specialized));
|
|
55
|
+
case "interface":
|
|
56
|
+
return t.name ? qualify(t.name) : "interface {}";
|
|
57
|
+
case "pointer":
|
|
58
|
+
case "PointerType":
|
|
59
|
+
return `*${go(t.base)}`;
|
|
60
|
+
case "slice":
|
|
61
|
+
case "SliceType":
|
|
62
|
+
return `[]${go(t.elem)}`;
|
|
63
|
+
case "array":
|
|
64
|
+
case "ArrayType":
|
|
65
|
+
return `[${t.size ?? 0}]${go(t.elem)}`;
|
|
66
|
+
case "map":
|
|
67
|
+
case "MapType":
|
|
68
|
+
return `map[${go(t.key)}]${go(t.value)}`;
|
|
69
|
+
default:
|
|
70
|
+
return "interface {}";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// `Box[int]` for a generic instance: the checker's type arguments on JS, the
|
|
75
|
+
// spelling the monomorphiser recorded on WASM.
|
|
76
|
+
function instanceName(t, specialized = null) {
|
|
77
|
+
if (t._goName) return t._goName;
|
|
78
|
+
if (!t.typeArgs?.length) return t.name ?? "";
|
|
79
|
+
return `${t.name}[${t.typeArgs.map((a) => goTypeName(a, specialized)).join(",")}]`;
|
|
80
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// JS bodies of the host side of the unicode intrinsics (unicode_is,
|
|
2
|
+
// unicode_to_upper, unicode_to_lower). The JS backend inlines them as the
|
|
3
|
+
// intrinsic bodies and the WASM boundary ships them as `env` imports, so
|
|
4
|
+
// both targets answer from the same tables. Each takes the names of the
|
|
5
|
+
// parameters it reads and returns statements ending in a `return`.
|
|
6
|
+
|
|
7
|
+
// Classes, in the order of the constants in stdlib/unicode/unicode.go: the
|
|
8
|
+
// test of the one-code-point string `c`.
|
|
9
|
+
const CLASS_TESTS = [
|
|
10
|
+
"/\\p{L}/u.test(c)", // Letter
|
|
11
|
+
"/\\p{Nd}/u.test(c)", // Digit
|
|
12
|
+
"/[ \\t\\n\\v\\f\\r\\u0085\\u00A0\\u1680\\u2000-\\u200A\\u2028\\u2029\\u202F\\u205F\\u3000]/.test(c)", // Space
|
|
13
|
+
"/\\p{Lu}/u.test(c)", // Upper
|
|
14
|
+
"/\\p{Ll}/u.test(c)", // Lower
|
|
15
|
+
"/\\p{P}/u.test(c)", // Punct
|
|
16
|
+
"/\\p{Cc}/u.test(c)", // Control
|
|
17
|
+
"!/\\p{Cc}/u.test(c)", // Print
|
|
18
|
+
"!/\\p{Cc}/u.test(c)", // Graphic
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export function unicodeIsJs(r, cls) {
|
|
22
|
+
const cases = CLASS_TESTS.map((test, i) => `case ${i}: return ${test};`).join(
|
|
23
|
+
" ",
|
|
24
|
+
);
|
|
25
|
+
return `const c = String.fromCodePoint(${r}); switch (${cls}) { ${cases} } return false;`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// A letter whose upper case spans several code points (the German sharp s)
|
|
29
|
+
// stays as it is.
|
|
30
|
+
export function unicodeToUpperJs(r) {
|
|
31
|
+
return `const m = String.fromCodePoint(${r}).toUpperCase(); const u = m.codePointAt(0); return m.length > (u > 0xffff ? 2 : 1) ? ${r} : u;`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function unicodeToLowerJs(r) {
|
|
35
|
+
return `return String.fromCodePoint(${r}).toLowerCase().codePointAt(0);`;
|
|
36
|
+
}
|