@scriptc/compiler 0.2.0 → 0.2.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.
- package/ambient/scriptc-node-fallback.d.ts +25 -15
- package/dist/backend/cycle-analysis.js +1 -0
- package/dist/backend/cycle-analysis.js.map +1 -1
- package/dist/backend/llvm/classes.js +21 -5
- package/dist/backend/llvm/classes.js.map +1 -1
- package/dist/backend/llvm/dyn.d.ts +5 -0
- package/dist/backend/llvm/dyn.js +166 -99
- package/dist/backend/llvm/dyn.js.map +1 -1
- package/dist/backend/llvm/emitter.d.ts +2 -2
- package/dist/backend/llvm/emitter.js +79 -20
- package/dist/backend/llvm/emitter.js.map +1 -1
- package/dist/backend/llvm/expr-bytes.d.ts +1 -1
- package/dist/backend/llvm/expr-bytes.js +28 -17
- package/dist/backend/llvm/expr-bytes.js.map +1 -1
- package/dist/backend/llvm/expr-control.js +1 -1
- package/dist/backend/llvm/expr-control.js.map +1 -1
- package/dist/backend/llvm/expr-dynamic.js +18 -9
- package/dist/backend/llvm/expr-dynamic.js.map +1 -1
- package/dist/backend/llvm/expr-primitives.js +11 -1
- package/dist/backend/llvm/expr-primitives.js.map +1 -1
- package/dist/backend/llvm/expr-records.js +11 -3
- package/dist/backend/llvm/expr-records.js.map +1 -1
- package/dist/backend/llvm/expr-stream-bridges.js +125 -17
- package/dist/backend/llvm/expr-stream-bridges.js.map +1 -1
- package/dist/backend/llvm/lib-process.js +23 -3
- package/dist/backend/llvm/lib-process.js.map +1 -1
- package/dist/backend/llvm/lib-shared.js +45 -2
- package/dist/backend/llvm/lib-shared.js.map +1 -1
- package/dist/backend/llvm/shapes.js +8 -2
- package/dist/backend/llvm/shapes.js.map +1 -1
- package/dist/backend/native-toolchain.d.ts +3 -6
- package/dist/backend/native-toolchain.js +53 -23
- package/dist/backend/native-toolchain.js.map +1 -1
- package/dist/backend/native-toolchain.test.js +72 -0
- package/dist/backend/native-toolchain.test.js.map +1 -1
- package/dist/backend/runtime-abi.d.ts +2 -2
- package/dist/backend/runtime-abi.js +2 -2
- package/dist/backend/runtime-pack-native.js +4 -1
- package/dist/backend/runtime-pack-native.js.map +1 -1
- package/dist/backend/runtime-pack-targets.test.js +1 -1
- package/dist/backend/runtime-pack.test.js +1 -1
- package/dist/backend/targets.js +10 -6
- package/dist/backend/targets.js.map +1 -1
- package/dist/backend/targets.test.js +7 -0
- package/dist/backend/targets.test.js.map +1 -1
- package/dist/compat/fetch-profile.js +34 -4
- package/dist/compat/fetch-profile.js.map +1 -1
- package/dist/diagnostics/diagnostic.js +2 -7
- package/dist/diagnostics/diagnostic.js.map +1 -1
- package/dist/frontend/lowering/class-callbacks.d.ts +3 -3
- package/dist/frontend/lowering/class-callbacks.js +26 -15
- package/dist/frontend/lowering/class-callbacks.js.map +1 -1
- package/dist/frontend/lowering/class-construction.d.ts +12 -0
- package/dist/frontend/lowering/class-construction.js +156 -0
- package/dist/frontend/lowering/class-construction.js.map +1 -0
- package/dist/frontend/lowering/class-descriptors.d.ts +1 -0
- package/dist/frontend/lowering/class-descriptors.js +46 -5
- package/dist/frontend/lowering/class-descriptors.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.d.ts +11 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.js +403 -48
- package/dist/frontend/lowering/class-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.test.js +2 -1
- package/dist/frontend/lowering/class-dynamic-dispatch.test.js.map +1 -1
- package/dist/frontend/lowering/class-instance-constructor.js +4 -1
- package/dist/frontend/lowering/class-instance-constructor.js.map +1 -1
- package/dist/frontend/lowering/class-method-values.d.ts +4 -1
- package/dist/frontend/lowering/class-method-values.js +72 -7
- package/dist/frontend/lowering/class-method-values.js.map +1 -1
- package/dist/frontend/lowering/class-prototypes.d.ts +5 -2
- package/dist/frontend/lowering/class-prototypes.js +121 -13
- package/dist/frontend/lowering/class-prototypes.js.map +1 -1
- package/dist/frontend/lowering/class-runtime-statics.d.ts +8 -0
- package/dist/frontend/lowering/class-runtime-statics.js +66 -0
- package/dist/frontend/lowering/class-runtime-statics.js.map +1 -0
- package/dist/frontend/lowering/class-static-data.js +2 -2
- package/dist/frontend/lowering/class-static-data.js.map +1 -1
- package/dist/frontend/lowering/containers/bytes.js +49 -13
- package/dist/frontend/lowering/containers/bytes.js.map +1 -1
- package/dist/frontend/lowering/containers/string-and-regexp.js +19 -47
- package/dist/frontend/lowering/containers/string-and-regexp.js.map +1 -1
- package/dist/frontend/lowering/error-methods.d.ts +4 -1
- package/dist/frontend/lowering/error-methods.js +100 -1
- package/dist/frontend/lowering/error-methods.js.map +1 -1
- package/dist/frontend/lowering/expressions/native-proxy.js +3 -1
- package/dist/frontend/lowering/expressions/native-proxy.js.map +1 -1
- package/dist/frontend/lowering/expressions/nullish-assignment.js +19 -11
- package/dist/frontend/lowering/expressions/nullish-assignment.js.map +1 -1
- package/dist/frontend/lowering/expressions/object-literals.js +38 -7
- package/dist/frontend/lowering/expressions/object-literals.js.map +1 -1
- package/dist/frontend/lowering/expressions/optional-chains.d.ts +3 -4
- package/dist/frontend/lowering/expressions/optional-chains.js +69 -17
- package/dist/frontend/lowering/expressions/optional-chains.js.map +1 -1
- package/dist/frontend/lowering/function-context.d.ts +11 -3
- package/dist/frontend/lowering/function-context.js +11 -5
- package/dist/frontend/lowering/function-context.js.map +1 -1
- package/dist/frontend/lowering/generator-dynamic-dispatch.d.ts +1 -0
- package/dist/frontend/lowering/generator-dynamic-dispatch.js +39 -1
- package/dist/frontend/lowering/generator-dynamic-dispatch.js.map +1 -1
- package/dist/frontend/lowering/lib-boundary.d.ts +2 -0
- package/dist/frontend/lowering/lib-boundary.js +54 -1
- package/dist/frontend/lowering/lib-boundary.js.map +1 -1
- package/dist/frontend/lowering/lower-builtin-values.d.ts +7 -1
- package/dist/frontend/lowering/lower-builtin-values.js +113 -3
- package/dist/frontend/lowering/lower-builtin-values.js.map +1 -1
- package/dist/frontend/lowering/lower-builtins.d.ts +6 -5
- package/dist/frontend/lowering/lower-builtins.js +156 -41
- package/dist/frontend/lowering/lower-builtins.js.map +1 -1
- package/dist/frontend/lowering/lower-calls.d.ts +8 -5
- package/dist/frontend/lowering/lower-calls.js +448 -83
- package/dist/frontend/lowering/lower-calls.js.map +1 -1
- package/dist/frontend/lowering/lower-classes.d.ts +13 -13
- package/dist/frontend/lowering/lower-classes.js +650 -176
- package/dist/frontend/lowering/lower-classes.js.map +1 -1
- package/dist/frontend/lowering/lower-containers.d.ts +9 -0
- package/dist/frontend/lowering/lower-containers.js +135 -36
- package/dist/frontend/lowering/lower-containers.js.map +1 -1
- package/dist/frontend/lowering/lower-dgram.js +3 -2
- package/dist/frontend/lowering/lower-dgram.js.map +1 -1
- package/dist/frontend/lowering/lower-exprs.d.ts +2 -2
- package/dist/frontend/lowering/lower-exprs.js +531 -126
- package/dist/frontend/lowering/lower-exprs.js.map +1 -1
- package/dist/frontend/lowering/lower-generators.js +12 -1
- package/dist/frontend/lowering/lower-generators.js.map +1 -1
- package/dist/frontend/lowering/lower-island.d.ts +2 -0
- package/dist/frontend/lowering/lower-island.js +32 -10
- package/dist/frontend/lowering/lower-island.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.js +220 -17
- package/dist/frontend/lowering/lower-modules.js.map +1 -1
- package/dist/frontend/lowering/lower-modules.test.d.ts +1 -0
- package/dist/frontend/lowering/lower-modules.test.js +39 -0
- package/dist/frontend/lowering/lower-modules.test.js.map +1 -0
- package/dist/frontend/lowering/lower-stmts.d.ts +7 -3
- package/dist/frontend/lowering/lower-stmts.js +135 -26
- package/dist/frontend/lowering/lower-stmts.js.map +1 -1
- package/dist/frontend/lowering/lower-stream.js +1 -1
- package/dist/frontend/lowering/lower-stream.js.map +1 -1
- package/dist/frontend/lowering/lowerer.d.ts +27 -1
- package/dist/frontend/lowering/lowerer.js +172 -50
- package/dist/frontend/lowering/lowerer.js.map +1 -1
- package/dist/frontend/lowering/lowerer.test.js +149 -3
- package/dist/frontend/lowering/lowerer.test.js.map +1 -1
- package/dist/frontend/lowering/surfaces.d.ts +2 -0
- package/dist/frontend/lowering/surfaces.js +25 -8
- package/dist/frontend/lowering/surfaces.js.map +1 -1
- package/dist/frontend/lowering/text-decoder-encoding.d.ts +2 -0
- package/dist/frontend/lowering/text-decoder-encoding.js +5 -0
- package/dist/frontend/lowering/text-decoder-encoding.js.map +1 -1
- package/dist/frontend/lowering/unregistered-class-types.test.js +3 -3
- package/dist/frontend/lowering/unregistered-class-types.test.js.map +1 -1
- package/dist/frontend/npm-static-prune.js +116 -34
- package/dist/frontend/npm-static-prune.js.map +1 -1
- package/dist/frontend/npm-static-prune.test.js +8 -0
- package/dist/frontend/npm-static-prune.test.js.map +1 -1
- package/dist/frontend/program.d.ts +1 -1
- package/dist/frontend/program.js +53 -24
- package/dist/frontend/program.js.map +1 -1
- package/dist/frontend/program.test.js +18 -0
- package/dist/frontend/program.test.js.map +1 -1
- package/dist/frontend/ts7/ast.js +4 -3
- package/dist/frontend/ts7/ast.js.map +1 -1
- package/dist/frontend/ts7/semantic-json.js +5 -10
- package/dist/frontend/ts7/semantic-json.js.map +1 -1
- package/dist/frontend/type-mapper.d.ts +9 -1
- package/dist/frontend/type-mapper.js +90 -13
- package/dist/frontend/type-mapper.js.map +1 -1
- package/dist/frontend/type-mapper.test.js +23 -0
- package/dist/frontend/type-mapper.test.js.map +1 -1
- package/dist/frontend/union-discriminants.d.ts +1 -1
- package/dist/frontend/union-discriminants.js +2 -2
- package/dist/frontend/union-discriminants.js.map +1 -1
- package/dist/ir/analysis.js +2 -0
- package/dist/ir/analysis.js.map +1 -1
- package/dist/ir/ir.d.ts +52 -33
- package/dist/ir/ir.js +142 -42
- package/dist/ir/ir.js.map +1 -1
- package/dist/ir/ir.test.js +35 -0
- package/dist/ir/ir.test.js.map +1 -1
- package/dist/ir/serialize.js +15 -3
- package/dist/ir/serialize.js.map +1 -1
- package/dist/ir/serialize.test.js +5 -1
- package/dist/ir/serialize.test.js.map +1 -1
- package/dist/ir/validate.js +80 -11
- package/dist/ir/validate.js.map +1 -1
- package/dist/ir/validate.test.js +61 -1
- package/dist/ir/validate.test.js.map +1 -1
- package/dist/native/sanitizer.js +3 -1
- package/dist/native/sanitizer.js.map +1 -1
- package/dist/native/toolchain.d.ts +1 -1
- package/dist/native/toolchain.js +26 -6
- package/dist/native/toolchain.js.map +1 -1
- package/dist/native/toolchain.test.js +53 -1
- package/dist/native/toolchain.test.js.map +1 -1
- package/package.json +20 -20
- package/surface-manifest.json +56 -81
|
@@ -1,26 +1,48 @@
|
|
|
1
|
-
import { BOOL, DYN, DYN_CLASS_PROPERTIES as PROPERTY_BAG, STRING, VOID, canConvertToDyn, canDynCheckTo, isClassOwnEnumerableFieldName, isDynTypedRefType, isUnitType, typeEquals, typeKey } from "../../ir/ir.js";
|
|
1
|
+
import { BOOL, DYN, F64, DYN_CLASS_PROPERTIES as PROPERTY_BAG, STRING, VOID, canConvertToDyn, canDynCheckTo, isClassOwnEnumerableFieldName, isDynTypedRefType, isUnitType, typeEquals, typeKey } from "../../ir/ir.js";
|
|
2
2
|
import { streamTypedRefEligible } from "../../ir/analysis.js";
|
|
3
3
|
import { varRef } from "../../ir/build.js";
|
|
4
|
-
import { everyStmtList, transformStmtList } from "../../ir/traverse.js";
|
|
4
|
+
import { everyStmtList, transformExpr, transformStmtList } from "../../ir/traverse.js";
|
|
5
5
|
import { dynUndefinedExpr, PoisonError } from "./lowerer.js";
|
|
6
6
|
import { implicitDefaultInstance } from "./lower-calls.js";
|
|
7
|
-
import { accessorCall, findGenericMethodOn, findMethodOn, genericOverrideBelow, upcastTo } from "./lower-classes.js";
|
|
8
|
-
import { classPrototypeData, hasClassPrototypeData } from "./class-prototypes.js";
|
|
9
|
-
import { errorToStringCall, errorToStringMethod, refreshErrorMethodDispatch } from "./error-methods.js";
|
|
7
|
+
import { accessorCall, classValueRef, findGenericMethodOn, findMethodOn, findStaticOn, genericOverrideBelow, upcastTo } from "./lower-classes.js";
|
|
8
|
+
import { classPrototypeData, hasClassPrototypeData, reflectClassPrototype } from "./class-prototypes.js";
|
|
9
|
+
import { errorPropertyRead, errorPropertyWrite, errorToStringCall, errorToStringMethod, refreshErrorPropertyDispatch, refreshErrorMethodDispatch } from "./error-methods.js";
|
|
10
10
|
import { GeneratorDynamicDispatch } from "./generator-dynamic-dispatch.js";
|
|
11
11
|
import { classMethodValue } from "./class-method-values.js";
|
|
12
12
|
import * as ts from "../ts7/adapter.js";
|
|
13
13
|
import { SYMBOL_T } from "../../ir/ir.js";
|
|
14
14
|
import { isClassCallback } from "./class-callbacks.js";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
import { refreshDescriptorGuards } from "./class-descriptors.js";
|
|
16
|
+
import { ClassConstructionDispatch } from "./class-construction.js";
|
|
17
|
+
const STREAM_METHODS = new Set(["on", "addListener", "once", "prependListener", "prependOnceListener", "off", "removeListener", "read", "pause", "resume", "isPaused", "destroy"]);
|
|
18
|
+
const STREAM_BOOL_PROPERTIES = new Set(["readable", "readableEnded", "writable", "writableEnded", "writableFinished", "writableNeedDrain", "destroyed", "closed", "readableObjectMode", "writableObjectMode", "allowHalfOpen"]);
|
|
19
|
+
const STREAM_NUM_PROPERTIES = new Set(["readableLength", "readableHighWaterMark", "writableLength", "writableHighWaterMark", "writableCorked"]);
|
|
20
|
+
const streamProperty = (name) => STREAM_BOOL_PROPERTIES.has(name) || STREAM_NUM_PROPERTIES.has(name);
|
|
21
|
+
export function classInstanceOf(lowerer, value, info, loc, classValue) {
|
|
22
|
+
const name = `%dyn.class.${classValue ? "valueInstanceof" : "instanceof"}:${info.def.name}`;
|
|
23
|
+
const params = [{ localId: "value", name: "value", type: DYN },
|
|
24
|
+
...(classValue ? [{ localId: "class", name: "class", type: classValue.type }] : [])];
|
|
25
|
+
if (!lowerer.liftedFns.some((fn) => fn.name === name)) {
|
|
26
|
+
const candidate = varRef("candidate", DYN, loc);
|
|
27
|
+
const prototype = classPrototypeData(lowerer, info, loc, classValue ? varRef("class", classValue.type, loc) : undefined);
|
|
28
|
+
const body = prototype ? [
|
|
29
|
+
{ kind: "varDecl", localId: "candidate", init: varRef("value", DYN, loc), loc },
|
|
30
|
+
{ kind: "while", cond: { kind: "ternary", cond: { kind: "dynTest", test: "nullish", value: candidate, type: BOOL, loc },
|
|
31
|
+
then: { kind: "boolLit", value: false, type: BOOL, loc }, else_: { kind: "dynTest", test: "object", value: candidate, type: BOOL, loc }, type: BOOL, loc }, body: [
|
|
32
|
+
{ kind: "assign", localId: "candidate", value: { kind: "libCall", fn: "dyn.getPrototype", args: [candidate], type: DYN, loc }, loc },
|
|
33
|
+
{ kind: "if", cond: { kind: "dynScalarEq", left: candidate, right: prototype, type: BOOL, loc }, then: [
|
|
34
|
+
{ kind: "return", value: { kind: "boolLit", value: true, type: BOOL, loc }, loc },
|
|
35
|
+
], else_: null, loc },
|
|
36
|
+
], loc },
|
|
37
|
+
] : [];
|
|
38
|
+
body.push({ kind: "return", value: { kind: "boolLit", value: false, type: BOOL, loc }, loc });
|
|
18
39
|
lowerer.liftedFns.push({
|
|
19
|
-
name, params
|
|
20
|
-
locals: [{ id:
|
|
21
|
-
|
|
40
|
+
name, params, returnType: BOOL,
|
|
41
|
+
locals: [...params.map((p) => ({ id: p.localId, name: p.name, type: p.type, mutable: false })),
|
|
42
|
+
...(prototype ? [{ id: "candidate", name: "candidate", type: DYN, mutable: true }] : [])], body, loc,
|
|
22
43
|
});
|
|
23
|
-
|
|
44
|
+
}
|
|
45
|
+
return { kind: "call", callee: name, args: [value, ...(classValue ? [classValue] : [])], type: BOOL, loc };
|
|
24
46
|
}
|
|
25
47
|
export function classPropertiesHelper(lowerer, loc) {
|
|
26
48
|
const name = "%dyn.class.properties";
|
|
@@ -45,7 +67,9 @@ export class ClassDynamicDispatch {
|
|
|
45
67
|
this.asyncFree = asyncFree;
|
|
46
68
|
}
|
|
47
69
|
generatorDispatch = new GeneratorDynamicDispatch();
|
|
70
|
+
constructionDispatch = new ClassConstructionDispatch();
|
|
48
71
|
boxed = new Set();
|
|
72
|
+
boxedConstructors = new Set();
|
|
49
73
|
dispatches = new Map();
|
|
50
74
|
properties = new Map();
|
|
51
75
|
computed = new Map();
|
|
@@ -55,10 +79,20 @@ export class ClassDynamicDispatch {
|
|
|
55
79
|
bagInitializers = new Map();
|
|
56
80
|
typedPropertyBags = new Map();
|
|
57
81
|
generated = new Set();
|
|
82
|
+
constructDispatch = null;
|
|
83
|
+
constructors = new Map();
|
|
84
|
+
constructed = new Set();
|
|
58
85
|
instanceTests = new Map();
|
|
86
|
+
jsonPrototypes = new Set();
|
|
87
|
+
descriptorHelper = null;
|
|
88
|
+
prototypeHelper = null;
|
|
89
|
+
prototypeClasses = new Set();
|
|
90
|
+
instancePrototypes = new Map();
|
|
59
91
|
process(lowerer, functions) {
|
|
60
92
|
let changed = refreshErrorMethodDispatch(lowerer, functions);
|
|
93
|
+
changed = refreshErrorPropertyDispatch(lowerer, functions) || changed;
|
|
61
94
|
changed = this.generatorDispatch.process(lowerer, functions, this.generated) || changed;
|
|
95
|
+
changed = this.constructionDispatch.process(lowerer, functions) || changed;
|
|
62
96
|
const seenTypes = new Set();
|
|
63
97
|
const rewrite = new Set();
|
|
64
98
|
const discover = (type) => {
|
|
@@ -66,8 +100,12 @@ export class ClassDynamicDispatch {
|
|
|
66
100
|
if (seenTypes.has(key))
|
|
67
101
|
return;
|
|
68
102
|
seenTypes.add(key);
|
|
69
|
-
if (
|
|
70
|
-
|
|
103
|
+
if (type.kind === "object") {
|
|
104
|
+
if (isDynTypedRefType(type))
|
|
105
|
+
this.boxed.add(type.className);
|
|
106
|
+
}
|
|
107
|
+
else if (type.kind === "classval")
|
|
108
|
+
this.boxedConstructors.add(type.className);
|
|
71
109
|
else if (type.kind === "array")
|
|
72
110
|
discover(type.elem);
|
|
73
111
|
else if (type.kind === "record") {
|
|
@@ -100,8 +138,8 @@ export class ClassDynamicDispatch {
|
|
|
100
138
|
rewrite.add(fn);
|
|
101
139
|
break;
|
|
102
140
|
case "libCall":
|
|
103
|
-
if (expr.fn === "dyn.keySet" || expr.fn === "dyn.keySetComputed" || (expr.fn === "dyn.iterator" || expr.fn === "dyn.arrayFromIterator") || expr.fn === "dyn.toString" || expr.fn === "dyn.stringConstructor" ||
|
|
104
|
-
expr.fn === "dyn.hasKeyComputed" || expr.fn === "dyn.hasOwnComputed" || expr.fn === "dyn.propertyIsEnumerableComputed")
|
|
141
|
+
if (expr.fn === "bytes.construct" || expr.fn === "dyn.keySet" || expr.fn === "dyn.keySetComputed" || (expr.fn === "dyn.iterator" || expr.fn === "dyn.arrayFromIterator") || expr.fn === "dyn.toString" || expr.fn === "dyn.stringConstructor" ||
|
|
142
|
+
expr.fn === "dyn.hasKeyComputed" || expr.fn === "dyn.hasOwnComputed" || expr.fn === "dyn.propertyIsEnumerableComputed" || expr.fn === "dyn.defineProperty" || expr.fn === "dyn.getPrototype")
|
|
105
143
|
rewrite.add(fn);
|
|
106
144
|
break;
|
|
107
145
|
}
|
|
@@ -111,9 +149,9 @@ export class ClassDynamicDispatch {
|
|
|
111
149
|
if (this.boxed.size === 0 && rewrite.size === 0)
|
|
112
150
|
return changed;
|
|
113
151
|
for (const fn of functions) {
|
|
114
|
-
if (!fn.name.startsWith("%dyn.class.instanceof:"))
|
|
152
|
+
if (!fn.name.startsWith("%dyn.class.instanceof:") && !fn.name.startsWith("%dyn.class.valueInstanceof:"))
|
|
115
153
|
continue;
|
|
116
|
-
const target = fn.name.slice(
|
|
154
|
+
const target = fn.name.slice(fn.name.indexOf(":") + 1);
|
|
117
155
|
let checked = this.instanceTests.get(fn.name);
|
|
118
156
|
if (!checked) {
|
|
119
157
|
checked = new Set();
|
|
@@ -129,8 +167,10 @@ export class ClassDynamicDispatch {
|
|
|
129
167
|
const loc = fn.loc;
|
|
130
168
|
const value = varRef("value", DYN, loc);
|
|
131
169
|
const type = { kind: "object", className };
|
|
132
|
-
const result = subtype
|
|
133
|
-
|
|
170
|
+
const result = subtype && fn.params[1] && lowerer.classes.get(target)?.localClass
|
|
171
|
+
? { kind: "instanceOfValue", value: { kind: "dynCheck", value, type, loc }, classValue: varRef("class", fn.params[1].type, loc), type: BOOL, loc }
|
|
172
|
+
: subtype ? { kind: "boolLit", value: true, type: BOOL, loc }
|
|
173
|
+
: { kind: "instanceOf", value: { kind: "dynCheck", value, type, loc }, className: target, type: BOOL, loc };
|
|
134
174
|
fn.body.unshift({ kind: "if", cond: { kind: "libCall", fn: "dyn.typedRefIs", args: [value,
|
|
135
175
|
{ kind: "strLit", value: typeKey(type), type: STRING, loc }], type: BOOL, loc }, then: [{ kind: "return", value: result, loc }], else_: null, loc });
|
|
136
176
|
changed = true;
|
|
@@ -145,33 +185,120 @@ export class ClassDynamicDispatch {
|
|
|
145
185
|
const info = lowerer.classes.get(className);
|
|
146
186
|
if (!info || info.builtinEmitter || info.builtinStream || info.builtinError)
|
|
147
187
|
continue;
|
|
188
|
+
if (info.symbolFields && info.def.symbolFields === undefined) {
|
|
189
|
+
info.def.symbolFields = [...info.symbolFields].flatMap(([symbol, field]) => {
|
|
190
|
+
const global = lowerer.globalsBySymbol.get(symbol);
|
|
191
|
+
return global?.type.kind === "symbol" ? [{ field, globalId: global.id }] : [];
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (this.prototypeHelper && !this.prototypeClasses.has(className)) {
|
|
195
|
+
this.prototypeClasses.add(className);
|
|
196
|
+
const loc = this.prototypeHelper.loc;
|
|
197
|
+
const type = { kind: "object", className };
|
|
198
|
+
const value = varRef("value", DYN, loc);
|
|
199
|
+
const prototype = reflectClassPrototype(lowerer, info, loc, { kind: "dynCheck", value, type, loc });
|
|
200
|
+
if (prototype)
|
|
201
|
+
this.prototypeHelper.body.unshift({ kind: "if", cond: { kind: "libCall", fn: "dyn.typedRefIs", args: [value,
|
|
202
|
+
{ kind: "strLit", value: typeKey(type), type: STRING, loc }], type: BOOL, loc },
|
|
203
|
+
then: [{ kind: "return", value: prototype, loc }], else_: null, loc });
|
|
204
|
+
changed = true;
|
|
205
|
+
}
|
|
206
|
+
if (!this.jsonPrototypes.has(className) && info.decl && findMethodOn(lowerer, info, "toJSON")) {
|
|
207
|
+
this.jsonPrototypes.add(className);
|
|
208
|
+
const loc = this.propertyBag.loc;
|
|
209
|
+
const method = classMethodValue(lowerer, info.decl, info, "toJSON", loc);
|
|
210
|
+
const prototype = method ? classPrototypeData(lowerer, info, loc) : null;
|
|
211
|
+
const helper = lowerer.liftedFns.find((fn) => fn.name === info.def.prototypeDataHelper);
|
|
212
|
+
const init = helper?.body[0];
|
|
213
|
+
if (!info.prototypeReflectionReady && method && prototype && init?.kind === "if" && canConvertToDyn(method.type, (id) => lowerer.shapes.get(id), (id) => lowerer.unions.get(id))) {
|
|
214
|
+
const descriptor = { kind: "exprStmt", expr: {
|
|
215
|
+
kind: "libCall", fn: "dyn.defineProperty", args: [prototype,
|
|
216
|
+
{ kind: "dynFrom", value: { kind: "strLit", value: "toJSON", type: STRING, loc }, type: DYN, loc },
|
|
217
|
+
{ kind: "dynObjLit", fields: [
|
|
218
|
+
{ key: { kind: "strLit", value: "value", type: STRING, loc }, value: { kind: "dynFrom", value: method, type: DYN, loc } },
|
|
219
|
+
...["writable", "configurable"].map((key) => ({ key: { kind: "strLit", value: key, type: STRING, loc }, value: { kind: "dynFrom", value: { kind: "boolLit", value: true, type: BOOL, loc }, type: DYN, loc } })),
|
|
220
|
+
], type: DYN, loc },
|
|
221
|
+
], type: DYN, loc
|
|
222
|
+
}, loc };
|
|
223
|
+
init.then.push(descriptor);
|
|
224
|
+
changed = true;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
for (const plan of this.propertyReceivers(lowerer)) {
|
|
229
|
+
const info = plan.info;
|
|
230
|
+
const className = info.def.name;
|
|
231
|
+
let instancePrototype = this.instancePrototypes.get(plan.capsule.def.name);
|
|
232
|
+
if (!instancePrototype && hasClassPrototypeData(info)) {
|
|
233
|
+
const name = `%class.instancePrototype:${plan.capsule.def.name}`;
|
|
234
|
+
const type = { kind: "object", className: plan.capsule.def.name };
|
|
235
|
+
const loc = this.propertyBag.loc;
|
|
236
|
+
const fn = { name, params: [{ localId: "this", name: "this", type }],
|
|
237
|
+
locals: [{ id: "this", name: "this", type, mutable: false }], returnType: DYN,
|
|
238
|
+
body: [{ kind: "return", value: { kind: "libCall", fn: "dyn.getPrototype",
|
|
239
|
+
args: [{ kind: "dynObjLit", fields: [], type: DYN, loc }], type: DYN, loc }, loc }], loc };
|
|
240
|
+
plan.capsule.def.instancePrototypeHelper = name;
|
|
241
|
+
instancePrototype = { fn, classes: new Set() };
|
|
242
|
+
this.instancePrototypes.set(plan.capsule.def.name, instancePrototype);
|
|
243
|
+
this.generated.add(fn);
|
|
244
|
+
lowerer.liftedFns.push(fn);
|
|
245
|
+
}
|
|
246
|
+
if (instancePrototype && hasClassPrototypeData(info) && !instancePrototype.classes.has(className)) {
|
|
247
|
+
instancePrototype.classes.add(className);
|
|
248
|
+
const loc = instancePrototype.fn.loc;
|
|
249
|
+
const receiver = varRef("this", instancePrototype.fn.params[0].type, loc);
|
|
250
|
+
const prototype = classPrototypeData(lowerer, info, loc, info === plan.capsule ? receiver
|
|
251
|
+
: { kind: "downcast", value: receiver, type: { kind: "object", className }, loc });
|
|
252
|
+
const returned = { kind: "return", value: prototype ?? { kind: "libCall", fn: "dyn.getPrototype",
|
|
253
|
+
args: [{ kind: "dynObjLit", fields: [], type: DYN, loc }], type: DYN, loc }, loc };
|
|
254
|
+
if (info === plan.capsule)
|
|
255
|
+
instancePrototype.fn.body[instancePrototype.fn.body.length - 1] = returned;
|
|
256
|
+
else
|
|
257
|
+
instancePrototype.fn.body.unshift({ kind: "if",
|
|
258
|
+
cond: { kind: "instanceOf", value: receiver, className, type: BOOL, loc }, then: [returned], else_: null, loc });
|
|
259
|
+
changed = true;
|
|
260
|
+
}
|
|
261
|
+
const needsPrototype = (current) => hasClassPrototypeData(current) || current.subclasses.some(needsPrototype);
|
|
262
|
+
const prototypeFor = (current, receiver, loc) => {
|
|
263
|
+
const currentReceiver = receiver.type.kind === "object" && receiver.type.className !== current.def.name
|
|
264
|
+
? { kind: "downcast", value: receiver, type: { kind: "object", className: current.def.name }, loc }
|
|
265
|
+
: receiver;
|
|
266
|
+
let result = classPrototypeData(lowerer, current, loc, currentReceiver);
|
|
267
|
+
for (const child of current.subclasses) {
|
|
268
|
+
const selected = prototypeFor(child, receiver, loc);
|
|
269
|
+
if (selected && result)
|
|
270
|
+
result = { kind: "ternary", cond: { kind: "instanceOf", value: receiver, className: child.def.name, type: BOOL, loc },
|
|
271
|
+
then: selected, else_: result, type: DYN, loc };
|
|
272
|
+
}
|
|
273
|
+
return result;
|
|
274
|
+
};
|
|
148
275
|
for (const existingInit of this.bagInitializers.get(className) ?? []) {
|
|
149
|
-
if (existingInit.value.kind === "dynObjLit" &&
|
|
150
|
-
const prototype =
|
|
276
|
+
if (existingInit.value.kind === "dynObjLit" && needsPrototype(info)) {
|
|
277
|
+
const prototype = prototypeFor(info, existingInit.obj, existingInit.loc);
|
|
151
278
|
if (prototype) {
|
|
152
279
|
existingInit.value = { kind: "libCall", fn: "dyn.objCreate", args: [prototype], type: DYN, loc: existingInit.loc };
|
|
153
280
|
changed = true;
|
|
154
281
|
}
|
|
155
282
|
}
|
|
156
283
|
}
|
|
157
|
-
if (this.bagClasses.has(
|
|
284
|
+
if (this.bagClasses.has(plan.key))
|
|
158
285
|
continue;
|
|
159
|
-
this.bagClasses.add(
|
|
286
|
+
this.bagClasses.add(plan.key);
|
|
160
287
|
this.ensurePropertyBag(info);
|
|
161
288
|
const loc = this.propertyBag.loc;
|
|
162
|
-
const
|
|
163
|
-
const value = varRef("p.0", DYN, loc);
|
|
164
|
-
const receiver = { kind: "dynCheck", value, type, loc };
|
|
289
|
+
const { receiver, condition } = this.propertyReceiver(plan, loc);
|
|
165
290
|
const bag = { kind: "fieldGet", obj: receiver, className, field: PROPERTY_BAG, type: DYN, loc };
|
|
166
|
-
const prototype =
|
|
291
|
+
const prototype = needsPrototype(info) ? prototypeFor(info, receiver, loc) : null;
|
|
167
292
|
const initialize = {
|
|
168
293
|
kind: "fieldSet", obj: receiver, className, field: PROPERTY_BAG, value: prototype
|
|
169
294
|
? { kind: "libCall", fn: "dyn.objCreate", args: [prototype], type: DYN, loc }
|
|
170
295
|
: { kind: "dynObjLit", fields: [], type: DYN, loc }, loc,
|
|
171
296
|
};
|
|
172
|
-
this.bagInitializers.
|
|
297
|
+
const initializers = this.bagInitializers.get(className) ?? [];
|
|
298
|
+
initializers.push(initialize);
|
|
299
|
+
this.bagInitializers.set(className, initializers);
|
|
173
300
|
this.propertyBag.body.unshift({
|
|
174
|
-
kind: "if", cond:
|
|
301
|
+
kind: "if", cond: condition,
|
|
175
302
|
then: [
|
|
176
303
|
{ kind: "if", cond: { kind: "dynTest", test: "undefined", value: bag, type: BOOL, loc }, then: [
|
|
177
304
|
initialize,
|
|
@@ -181,6 +308,7 @@ export class ClassDynamicDispatch {
|
|
|
181
308
|
});
|
|
182
309
|
changed = true;
|
|
183
310
|
}
|
|
311
|
+
refreshDescriptorGuards(lowerer);
|
|
184
312
|
const byMethod = new Map();
|
|
185
313
|
const candidates = (method) => {
|
|
186
314
|
const found = byMethod.get(method);
|
|
@@ -188,8 +316,10 @@ export class ClassDynamicDispatch {
|
|
|
188
316
|
return found;
|
|
189
317
|
const matching = [...this.boxed].flatMap((name) => {
|
|
190
318
|
const info = lowerer.classes.get(name);
|
|
191
|
-
if (!info || info.fields.has(method) || info.builtinEmitter || info.
|
|
319
|
+
if (!info || info.fields.has(method) || info.builtinEmitter || info.builtinError)
|
|
192
320
|
return [];
|
|
321
|
+
if (info.builtinStream)
|
|
322
|
+
return STREAM_METHODS.has(method) ? [info] : [];
|
|
193
323
|
return findMethodOn(lowerer, info, method) || findGenericMethodOn(lowerer, info, method) ? [info] : [];
|
|
194
324
|
});
|
|
195
325
|
byMethod.set(method, matching);
|
|
@@ -204,6 +334,18 @@ export class ClassDynamicDispatch {
|
|
|
204
334
|
fn.body = transformStmtList(fn.body, {
|
|
205
335
|
stmt: (stmt) => stmt,
|
|
206
336
|
expr: (expr) => {
|
|
337
|
+
if (expr.kind === "libCall" && expr.fn === "dyn.getPrototype") {
|
|
338
|
+
if (!this.prototypeHelper) {
|
|
339
|
+
const loc = expr.loc;
|
|
340
|
+
this.prototypeHelper = { name: "%dyn.class.getPrototype", params: [{ localId: "value", name: "value", type: DYN }],
|
|
341
|
+
locals: [{ id: "value", name: "value", type: DYN, mutable: false }], returnType: DYN, loc,
|
|
342
|
+
body: [{ kind: "return", value: { ...expr, args: [varRef("value", DYN, loc)] }, loc }] };
|
|
343
|
+
lowerer.liftedFns.push(this.prototypeHelper);
|
|
344
|
+
this.generated.add(this.prototypeHelper);
|
|
345
|
+
changed = true;
|
|
346
|
+
}
|
|
347
|
+
return { kind: "call", callee: this.prototypeHelper.name, args: expr.args, type: DYN, loc: expr.loc };
|
|
348
|
+
}
|
|
207
349
|
if (expr.kind === "libCall" && (expr.fn === "dyn.toString" || expr.fn === "dyn.stringConstructor") && lowerer.classes.has("%Error")) {
|
|
208
350
|
const key = JSON.stringify([expr.fn, ...expr.args.map((arg) => typeKey(arg.type))]);
|
|
209
351
|
let errorString = this.errorStrings.get(key);
|
|
@@ -239,6 +381,43 @@ export class ClassDynamicDispatch {
|
|
|
239
381
|
}
|
|
240
382
|
}
|
|
241
383
|
}
|
|
384
|
+
if (expr.kind === "libCall" && expr.fn === "bytes.construct") {
|
|
385
|
+
if (!this.constructDispatch) {
|
|
386
|
+
const loc = expr.loc;
|
|
387
|
+
const params = [DYN, DYN, STRING].map((type, i) => ({ localId: `p.${i}`, name: `p${i}`, type }));
|
|
388
|
+
this.constructDispatch = { name: "%dyn.class.construct", params, returnType: DYN,
|
|
389
|
+
locals: params.map((p) => ({ id: p.localId, name: p.name, type: p.type, mutable: false })),
|
|
390
|
+
body: [{ kind: "return", value: { ...expr, args: params.map((p) => varRef(p.localId, p.type, loc)) }, loc }], loc };
|
|
391
|
+
this.generated.add(this.constructDispatch);
|
|
392
|
+
lowerer.liftedFns.push(this.constructDispatch);
|
|
393
|
+
changed = true;
|
|
394
|
+
}
|
|
395
|
+
return { kind: "call", callee: this.constructDispatch.name, args: expr.args, type: DYN, loc: expr.loc };
|
|
396
|
+
}
|
|
397
|
+
if (expr.kind === "libCall" && expr.fn === "dyn.defineProperty") {
|
|
398
|
+
const loc = expr.loc;
|
|
399
|
+
if (!this.descriptorHelper) {
|
|
400
|
+
const params = ["target", "key", "descriptor"].map((name) => ({ localId: name, name, type: DYN }));
|
|
401
|
+
const target = varRef("target", DYN, loc);
|
|
402
|
+
const key = varRef("key", DYN, loc);
|
|
403
|
+
const descriptor = varRef("descriptor", DYN, loc);
|
|
404
|
+
this.descriptorHelper = {
|
|
405
|
+
name: "%dyn.class.defineProperty", params, returnType: DYN,
|
|
406
|
+
locals: params.map((param) => ({ id: param.localId, name: param.name, type: DYN, mutable: false })),
|
|
407
|
+
body: [
|
|
408
|
+
{ kind: "if", cond: { kind: "dynTest", test: "symbol", value: key, type: BOOL, loc }, then: [
|
|
409
|
+
{ kind: "exprStmt", expr: { ...expr, args: [{ kind: "call", callee: this.propertyBag.name, args: [target], type: DYN, loc }, key, descriptor] }, loc },
|
|
410
|
+
{ kind: "return", value: target, loc },
|
|
411
|
+
], else_: null, loc },
|
|
412
|
+
{ kind: "return", value: { ...expr, args: [target, key, descriptor] }, loc },
|
|
413
|
+
], loc,
|
|
414
|
+
};
|
|
415
|
+
this.generated.add(this.descriptorHelper);
|
|
416
|
+
lowerer.liftedFns.push(this.descriptorHelper);
|
|
417
|
+
changed = true;
|
|
418
|
+
}
|
|
419
|
+
return { kind: "call", callee: this.descriptorHelper.name, args: expr.args, type: DYN, loc };
|
|
420
|
+
}
|
|
242
421
|
const computedRead = expr.kind === "dynKeyGet" && expr.key.kind !== "strLit" ? expr : null;
|
|
243
422
|
const computedWrite = expr.kind === "libCall" && (expr.fn === "dyn.keySetComputed" || expr.fn === "dyn.keySet" && expr.args[1]?.kind !== "strLit") ? expr : null;
|
|
244
423
|
const computedProbe = expr.kind === "libCall" && ["dyn.hasKeyComputed", "dyn.hasOwnComputed", "dyn.propertyIsEnumerableComputed"].includes(expr.fn) ? expr : null;
|
|
@@ -385,11 +564,16 @@ export class ClassDynamicDispatch {
|
|
|
385
564
|
const type = { kind: "object", className: info.def.name };
|
|
386
565
|
const receiver = varRef("p.0", DYN, loc);
|
|
387
566
|
if (isClassCallback(lowerer, info, dispatch.source.method)) {
|
|
567
|
+
const prototypeMethod = lowerer.prototypeMethodAccesses.has(dispatch.source.method);
|
|
568
|
+
if (prototypeMethod)
|
|
569
|
+
classPrototypeData(lowerer, info, loc);
|
|
388
570
|
const bag = { kind: "call", callee: this.propertyBag.name, args: [receiver], type: DYN, loc };
|
|
389
571
|
dispatch.callback.body.unshift({ kind: "if", cond: {
|
|
390
572
|
kind: "libCall", fn: "dyn.typedRefIs", args: [receiver, { kind: "strLit", value: typeKey(type), type: STRING, loc }], type: BOOL, loc,
|
|
391
|
-
}, then: [{ kind: "return", value: { kind: "libCall", fn: "dyn.
|
|
392
|
-
|
|
573
|
+
}, then: [{ kind: "return", value: { kind: "ternary", cond: prototypeMethod ? { kind: "boolLit", value: true, type: BOOL, loc } : { kind: "libCall", fn: "dyn.hasKey", args: [bag,
|
|
574
|
+
{ kind: "strLit", value: dispatch.source.method, type: STRING, loc }], type: BOOL, loc }, then: { kind: "dynObjLit", fields: [
|
|
575
|
+
{ key: { kind: "strLit", value: "value", type: STRING, loc }, value: { kind: "dynKeyGet", value: bag, key: { kind: "strLit", value: dispatch.source.method, type: STRING, loc }, type: DYN, loc } },
|
|
576
|
+
], type: DYN, loc }, else_: dynUndefinedExpr(loc), type: DYN, loc }, loc }], else_: null, loc });
|
|
393
577
|
}
|
|
394
578
|
const before = lowerer.diags.length;
|
|
395
579
|
let branch;
|
|
@@ -413,11 +597,27 @@ export class ClassDynamicDispatch {
|
|
|
413
597
|
}
|
|
414
598
|
}
|
|
415
599
|
for (const dispatch of this.properties.values()) {
|
|
416
|
-
|
|
600
|
+
if (!dispatch.write)
|
|
601
|
+
for (const name of this.boxedConstructors) {
|
|
602
|
+
const info = lowerer.classes.get(name);
|
|
603
|
+
const field = info && findStaticOn(lowerer, info, dispatch.name)?.field;
|
|
604
|
+
const key = `constructor:${name}`;
|
|
605
|
+
if (!field || dispatch.name.startsWith("#") || dispatch.classes.has(key))
|
|
606
|
+
continue;
|
|
607
|
+
dispatch.classes.add(key);
|
|
608
|
+
const loc = dispatch.fn.loc;
|
|
609
|
+
const value = varRef("p.0", DYN, loc);
|
|
610
|
+
const type = { kind: "classval", className: name };
|
|
611
|
+
dispatch.fn.body.unshift({ kind: "if", cond: { kind: "libCall", fn: "dyn.classIs", args: [value,
|
|
612
|
+
{ kind: "strLit", value: typeKey(type), type: STRING, loc }], type: BOOL, loc }, then: [
|
|
613
|
+
{ kind: "return", value: lowerer.coerceToExpected(varRef(field.globalId, field.type, loc), DYN), loc },
|
|
614
|
+
], else_: null, loc });
|
|
615
|
+
changed = true;
|
|
616
|
+
}
|
|
617
|
+
for (const plan of this.propertyReceivers(lowerer, true)) {
|
|
417
618
|
const { info } = plan;
|
|
418
619
|
if (!isClassOwnEnumerableFieldName(dispatch.name) ||
|
|
419
|
-
!info.fields.has(dispatch.name) && !findMethodOn(lowerer, info, `get:${dispatch.name}`) && !findMethodOn(lowerer, info, `set:${dispatch.name}`)
|
|
420
|
-
!findMethodOn(lowerer, info, dispatch.name) && !findGenericMethodOn(lowerer, info, dispatch.name))
|
|
620
|
+
!(info.builtinStream !== undefined && streamProperty(dispatch.name)) && !findMethodOn(lowerer, info, dispatch.name) && !findGenericMethodOn(lowerer, info, dispatch.name) && dispatch.name !== "constructor" && !info.fields.has(dispatch.name) && !findMethodOn(lowerer, info, `get:${dispatch.name}`) && !findMethodOn(lowerer, info, `set:${dispatch.name}`))
|
|
421
621
|
continue;
|
|
422
622
|
if (dispatch.classes.has(plan.key))
|
|
423
623
|
continue;
|
|
@@ -444,16 +644,37 @@ export class ClassDynamicDispatch {
|
|
|
444
644
|
}
|
|
445
645
|
}
|
|
446
646
|
for (const dispatch of this.computed.values()) {
|
|
447
|
-
for (const plan of this.propertyReceivers(lowerer)) {
|
|
647
|
+
for (const plan of this.propertyReceivers(lowerer, true)) {
|
|
448
648
|
if (dispatch.classes.has(plan.key))
|
|
449
649
|
continue;
|
|
450
650
|
const { info } = plan;
|
|
451
651
|
dispatch.classes.add(plan.key);
|
|
452
652
|
const loc = dispatch.fn.loc;
|
|
453
653
|
const { receiver, condition } = this.propertyReceiver(plan, loc);
|
|
454
|
-
const memberBody = (name) =>
|
|
455
|
-
|
|
456
|
-
|
|
654
|
+
const memberBody = (name) => {
|
|
655
|
+
if (dispatch.probe && (name === "message" || name === "name") && lowerer.isSubclassOf(info.def.name, "%Error") && dispatch.probe !== "in")
|
|
656
|
+
return [{
|
|
657
|
+
kind: "return", value: dispatch.probe === "own"
|
|
658
|
+
? { kind: "fieldGet", obj: lowerer.upcastTo(receiver, "%Error"), className: "%Error", field: `%${name}Present`, type: BOOL, loc }
|
|
659
|
+
: { kind: "fieldGet", obj: lowerer.upcastTo(receiver, "%Error"), className: "%Error", field: `%${name}Enumerable`, type: BOOL, loc }, loc,
|
|
660
|
+
}];
|
|
661
|
+
if (info.builtinStream !== undefined && streamProperty(name) && dispatch.probe)
|
|
662
|
+
return [{ kind: "return", value: { kind: "boolLit", value: dispatch.probe === "in", type: BOOL, loc }, loc }];
|
|
663
|
+
if (!dispatch.probe)
|
|
664
|
+
return this.propertyBody(lowerer, { ...dispatch, name }, info, receiver);
|
|
665
|
+
const prototypeMethod = lowerer.prototypeMethodAccesses.has(name);
|
|
666
|
+
if (info.fields.has(name) || dispatch.probe === "in" && !prototypeMethod) {
|
|
667
|
+
return [{ kind: "return", value: { kind: "boolLit", value: true, type: BOOL, loc }, loc }];
|
|
668
|
+
}
|
|
669
|
+
if (prototypeMethod)
|
|
670
|
+
classPrototypeData(lowerer, info, loc, receiver);
|
|
671
|
+
const bag = { kind: "call", callee: this.propertyBag.name,
|
|
672
|
+
args: [lowerer.coerceToExpected(receiver, DYN)], type: DYN, loc };
|
|
673
|
+
const symbol = name.startsWith("sym:") || name.startsWith("%symbol:");
|
|
674
|
+
const fn = dispatch.probe === "in" ? "dyn.hasKey" : dispatch.probe === "own" ? "dyn.hasOwn" : "dyn.propertyIsEnumerable";
|
|
675
|
+
return [{ kind: "return", value: { kind: "libCall", fn: symbol ? `${fn}Computed` : fn,
|
|
676
|
+
args: [bag, symbol ? varRef("p.key", DYN, loc) : { kind: "strLit", value: name, type: STRING, loc }], type: BOOL, loc }, loc }];
|
|
677
|
+
};
|
|
457
678
|
if (dispatch.dynamicKey) {
|
|
458
679
|
const symbols = new Map();
|
|
459
680
|
const symbolEntries = [];
|
|
@@ -480,6 +701,16 @@ export class ClassDynamicDispatch {
|
|
|
480
701
|
}
|
|
481
702
|
}
|
|
482
703
|
const branches = [];
|
|
704
|
+
if (!dispatch.write) {
|
|
705
|
+
const value = varRef("p.0", DYN, loc);
|
|
706
|
+
const key = varRef("p.key", DYN, loc);
|
|
707
|
+
const bag = { kind: "call", callee: this.propertyBag.name, args: [value], type: DYN, loc };
|
|
708
|
+
branches.push({ kind: "if", cond: { kind: "libCall", fn: "dyn.hasOwnComputed", args: [bag, key], type: BOOL, loc }, then: [
|
|
709
|
+
{ kind: "return", value: dispatch.probe ? {
|
|
710
|
+
kind: "libCall", fn: dispatch.probe === "enumerable" ? "dyn.propertyIsEnumerableComputed" : "dyn.hasOwnComputed", args: [bag, key], type: BOOL, loc,
|
|
711
|
+
} : { kind: "dynKeyGet", value: bag, key, type: DYN, loc }, loc },
|
|
712
|
+
], else_: null, loc });
|
|
713
|
+
}
|
|
483
714
|
for (const [name, symbol] of symbols) {
|
|
484
715
|
const before = lowerer.diags.length;
|
|
485
716
|
let then;
|
|
@@ -504,6 +735,9 @@ export class ClassDynamicDispatch {
|
|
|
504
735
|
}
|
|
505
736
|
}
|
|
506
737
|
const names = new Set([...info.fields.keys()].filter(isClassOwnEnumerableFieldName));
|
|
738
|
+
if (info.builtinStream)
|
|
739
|
+
for (const name of [...STREAM_BOOL_PROPERTIES, ...STREAM_NUM_PROPERTIES])
|
|
740
|
+
names.add(name);
|
|
507
741
|
for (let owner = info; owner; owner = owner.base) {
|
|
508
742
|
const methodNames = [...owner.methods.keys()];
|
|
509
743
|
if (owner.genericMethods)
|
|
@@ -533,16 +767,71 @@ export class ClassDynamicDispatch {
|
|
|
533
767
|
kind: "strEq", left: varRef(dispatch.keyLocal, STRING, loc), right: { kind: "strLit", value: name, type: STRING, loc }, negated: false, type: BOOL, loc,
|
|
534
768
|
}, then: body, else_: null, loc });
|
|
535
769
|
}
|
|
536
|
-
|
|
770
|
+
// Keep each class's key table in a separate function. A single
|
|
771
|
+
// function containing every boxed class grows quadratically during
|
|
772
|
+
// LLVM's control-flow optimization for large library workloads.
|
|
773
|
+
const params = [...dispatch.fn.params];
|
|
774
|
+
if (dispatch.keyLocal !== "p.key")
|
|
775
|
+
params.push({ localId: dispatch.keyLocal, name: "key", type: STRING });
|
|
776
|
+
const helper = {
|
|
777
|
+
name: `%dyn.class.keys.${lowerer.liftedFns.length}`, params, returnType: dispatch.fn.returnType,
|
|
778
|
+
locals: params.map((p) => ({ id: p.localId, name: p.name, type: p.type, mutable: false })),
|
|
779
|
+
body: [...branch, ...dispatch.fn.body.slice(dispatch.write ? -2 : -1)], loc,
|
|
780
|
+
};
|
|
781
|
+
this.generated.add(helper);
|
|
782
|
+
lowerer.liftedFns.push(helper);
|
|
783
|
+
const call = { kind: "call", callee: helper.name,
|
|
784
|
+
args: params.map((p) => varRef(p.localId, p.type, loc)), type: helper.returnType, loc };
|
|
785
|
+
const then = dispatch.write
|
|
786
|
+
? [{ kind: "exprStmt", expr: call, loc }, { kind: "return", value: null, loc }]
|
|
787
|
+
: [{ kind: "return", value: call, loc }];
|
|
788
|
+
dispatch.fn.body.splice(dispatch.branchIndex, 0, { kind: "if", cond: condition, then, else_: null, loc });
|
|
537
789
|
changed = true;
|
|
538
790
|
}
|
|
539
791
|
}
|
|
792
|
+
if (this.constructDispatch)
|
|
793
|
+
for (const [name, info] of this.constructors) {
|
|
794
|
+
if (this.constructed.has(name))
|
|
795
|
+
continue;
|
|
796
|
+
this.constructed.add(name);
|
|
797
|
+
const loc = this.constructDispatch.loc;
|
|
798
|
+
const callee = varRef("p.0", DYN, loc), input = varRef("p.1", DYN, loc);
|
|
799
|
+
const args = [];
|
|
800
|
+
let supported = true;
|
|
801
|
+
for (let i = 0; i < info.ctorParams.length; i++) {
|
|
802
|
+
const param = info.ctorParams[i];
|
|
803
|
+
if (param.mode !== "required" && param.mode !== "omittable") {
|
|
804
|
+
supported = false;
|
|
805
|
+
break;
|
|
806
|
+
}
|
|
807
|
+
const value = { kind: "dynKeyGet", value: input, key: { kind: "strLit", value: String(i), type: STRING, loc }, type: DYN, loc };
|
|
808
|
+
const arg = lowerer.coerceToExpected(value, param.type);
|
|
809
|
+
if (!typeEquals(arg.type, param.type)) {
|
|
810
|
+
supported = false;
|
|
811
|
+
break;
|
|
812
|
+
}
|
|
813
|
+
args.push(arg);
|
|
814
|
+
}
|
|
815
|
+
if (!supported || !info.decl)
|
|
816
|
+
continue;
|
|
817
|
+
const cls = classValueRef(lowerer, info, info.decl);
|
|
818
|
+
const result = { kind: "new", className: name, args, type: { kind: "object", className: name }, loc };
|
|
819
|
+
this.constructDispatch.body.unshift({ kind: "if", cond: { kind: "dynScalarEq", left: callee,
|
|
820
|
+
right: lowerer.coerceToExpected(cls, DYN), type: BOOL, loc }, then: [
|
|
821
|
+
{ kind: "return", value: lowerer.coerceToExpected(result, DYN), loc },
|
|
822
|
+
], else_: null, loc });
|
|
823
|
+
changed = true;
|
|
824
|
+
}
|
|
540
825
|
return changed;
|
|
541
826
|
}
|
|
542
827
|
/** A known native class can access its shared bag directly. Keep the
|
|
543
828
|
* receiver as an owned parameter so calls and temporary instances retain
|
|
544
829
|
* their ordinary evaluation and lifetime rules without a boxed capsule. */
|
|
545
830
|
typedPropertyBag(lowerer, type, loc) {
|
|
831
|
+
// A superclass constructor can be the first code to access this bag.
|
|
832
|
+
// Its receiver still needs the actual subclass's prototype.
|
|
833
|
+
if (lowerer.classes.get(type.className)?.subclasses.length)
|
|
834
|
+
return null;
|
|
546
835
|
const existing = this.typedPropertyBags.get(type.className);
|
|
547
836
|
if (existing)
|
|
548
837
|
return existing;
|
|
@@ -551,11 +840,8 @@ export class ClassDynamicDispatch {
|
|
|
551
840
|
return null;
|
|
552
841
|
const receiver = varRef("p.0", type, loc);
|
|
553
842
|
const bag = { kind: "fieldGet", obj: receiver, className: type.className, field: PROPERTY_BAG, type: DYN, loc };
|
|
554
|
-
const
|
|
555
|
-
|
|
556
|
-
const initialize = { ...initializers[0], obj: receiver, value: prototype
|
|
557
|
-
? { kind: "libCall", fn: "dyn.objCreate", args: [prototype], type: DYN, loc }
|
|
558
|
-
: { kind: "dynObjLit", fields: [], type: DYN, loc }, loc };
|
|
843
|
+
const initialize = { ...initializers[0], obj: receiver,
|
|
844
|
+
value: transformExpr(initializers[0].value, { stmt: (stmt) => stmt, expr: (expr) => expr.kind === "dynCheck" && expr.value.kind === "varRef" && expr.value.localId === "p.0" ? receiver : expr }), loc };
|
|
559
845
|
const helper = {
|
|
560
846
|
name: `%class.properties:${type.className}`, params: [{ localId: "p.0", name: "value", type }], returnType: DYN,
|
|
561
847
|
locals: [{ id: "p.0", name: "value", type, mutable: false }], loc,
|
|
@@ -592,11 +878,11 @@ export class ClassDynamicDispatch {
|
|
|
592
878
|
}
|
|
593
879
|
/** A capsule retains its static type, while its object may be a subclass.
|
|
594
880
|
* Preorder insertion lets later branches test the most derived layout first. */
|
|
595
|
-
propertyReceivers(lowerer) {
|
|
881
|
+
propertyReceivers(lowerer, streams = false) {
|
|
596
882
|
const plans = [];
|
|
597
883
|
for (const name of this.boxed) {
|
|
598
884
|
const capsule = lowerer.classes.get(name);
|
|
599
|
-
if (!capsule || capsule.builtinEmitter || capsule.builtinStream || capsule.builtinError)
|
|
885
|
+
if (!capsule || capsule.builtinEmitter || capsule.builtinStream && !streams || capsule.builtinError)
|
|
600
886
|
continue;
|
|
601
887
|
const visit = (info) => {
|
|
602
888
|
plans.push({ info, capsule, key: JSON.stringify([name, info.def.name]) });
|
|
@@ -624,6 +910,15 @@ export class ClassDynamicDispatch {
|
|
|
624
910
|
propertyBody(lowerer, dispatch, info, receiver) {
|
|
625
911
|
const { name, write } = dispatch;
|
|
626
912
|
const loc = dispatch.fn.loc;
|
|
913
|
+
if (info.builtinStream !== undefined && streamProperty(name)) {
|
|
914
|
+
if (write)
|
|
915
|
+
return [{ kind: "runtimeFence", code: "SC2020", message: `writing stream property '${name}' through an untyped value is not supported`, loc }];
|
|
916
|
+
return [{ kind: "return", value: lowerer.coerceToExpected({ kind: "libCall", fn: "stream.prop", args: [receiver, { kind: "strLit", value: name, type: STRING, loc }], type: STREAM_BOOL_PROPERTIES.has(name) ? BOOL : F64, loc }, DYN), loc }];
|
|
917
|
+
}
|
|
918
|
+
if ((name === "message" || name === "name") && lowerer.isSubclassOf(info.def.name, "%Error"))
|
|
919
|
+
return write
|
|
920
|
+
? [errorPropertyWrite(lowerer, receiver, lowerer.coerceToExpected(varRef("p.1", DYN, loc), STRING), name), { kind: "return", value: null, loc }]
|
|
921
|
+
: [{ kind: "return", value: lowerer.coerceToExpected(errorPropertyRead(lowerer, receiver, name), DYN), loc }];
|
|
627
922
|
const field = info.fields.get(name);
|
|
628
923
|
const member = `${write ? "set" : "get"}:${name}`;
|
|
629
924
|
const accessor = field ? null : findMethodOn(lowerer, info, member);
|
|
@@ -631,6 +926,14 @@ export class ClassDynamicDispatch {
|
|
|
631
926
|
const getRecord = (id) => lowerer.shapes.get(id);
|
|
632
927
|
const getUnion = (id) => lowerer.unions.get(id);
|
|
633
928
|
if (!write) {
|
|
929
|
+
if (name === "constructor" && !field && !accessor && info.decl && !info.generic && !info.localClass && !info.classDecorators) {
|
|
930
|
+
this.constructors.set(info.def.name, info);
|
|
931
|
+
const cls = lowerer.coerceToExpected(classValueRef(lowerer, info, info.decl), DYN);
|
|
932
|
+
const bag = { kind: "call", callee: this.propertyBag.name, args: [lowerer.coerceToExpected(receiver, DYN)], type: DYN, loc };
|
|
933
|
+
const key = { kind: "strLit", value: name, type: STRING, loc };
|
|
934
|
+
return [{ kind: "return", value: { kind: "ternary", cond: { kind: "libCall", fn: "dyn.hasKey", args: [bag, key], type: BOOL, loc },
|
|
935
|
+
then: { kind: "dynKeyGet", value: bag, key, type: DYN, loc }, else_: cls, type: DYN, loc }, loc }];
|
|
936
|
+
}
|
|
634
937
|
if (name === "toString" && findMethodOn(lowerer, info, name)?.declarer.builtinError) {
|
|
635
938
|
return [{ kind: "return", value: errorToStringMethod(lowerer, receiver), loc }];
|
|
636
939
|
}
|
|
@@ -659,6 +962,20 @@ export class ClassDynamicDispatch {
|
|
|
659
962
|
(candidate.methods.has(name) || candidate.genericMethods?.has(name)));
|
|
660
963
|
overrides.sort((a, b) => lowerer.isSubclassOf(a.def.name, b.def.name) ? -1 : lowerer.isSubclassOf(b.def.name, a.def.name) ? 1 : 0);
|
|
661
964
|
const body = [];
|
|
965
|
+
if (isClassOwnEnumerableFieldName(name) && !name.startsWith("sym:")) {
|
|
966
|
+
const prototypeMethod = lowerer.prototypeMethodAccesses.has(name);
|
|
967
|
+
if (prototypeMethod)
|
|
968
|
+
classPrototypeData(lowerer, info, loc, receiver);
|
|
969
|
+
const bag = { kind: "call", callee: this.propertyBag.name,
|
|
970
|
+
args: [lowerer.coerceToExpected(receiver, DYN)], type: DYN, loc };
|
|
971
|
+
const key = { kind: "strLit", value: name, type: STRING, loc };
|
|
972
|
+
body.push({ kind: "if", cond: { kind: "libCall", fn: "dyn.hasKey", args: [bag, key], type: BOOL, loc },
|
|
973
|
+
then: [{ kind: "return", value: { kind: "dynKeyGet", value: bag, key, type: DYN, loc }, loc }], else_: null, loc });
|
|
974
|
+
if (prototypeMethod) {
|
|
975
|
+
body.push({ kind: "return", value: dynUndefinedExpr(loc), loc });
|
|
976
|
+
return body;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
662
979
|
for (const candidate of overrides) {
|
|
663
980
|
const selected = classMethodValue(lowerer, info.decl, candidate, name, loc);
|
|
664
981
|
const branch = selected && canConvertToDyn(selected.type, getRecord, getUnion)
|
|
@@ -683,8 +1000,23 @@ export class ClassDynamicDispatch {
|
|
|
683
1000
|
(t.kind === "union" && (getUnion(t.unionId)?.arms.some(mutable) ?? false));
|
|
684
1001
|
if (boxed.kind === "dynFrom" && mutable(boxed.value.type))
|
|
685
1002
|
boxed.liveRef = true;
|
|
1003
|
+
if (accessor) {
|
|
1004
|
+
const bag = { kind: "call", callee: this.propertyBag.name,
|
|
1005
|
+
args: [lowerer.coerceToExpected(receiver, DYN)], type: DYN, loc };
|
|
1006
|
+
const key = { kind: "strLit", value: name, type: STRING, loc };
|
|
1007
|
+
return [{ kind: "if", cond: { kind: "libCall", fn: "dyn.hasOwn", args: [bag, key], type: BOOL, loc },
|
|
1008
|
+
then: [{ kind: "return", value: { kind: "dynKeyGet", value: bag, key, type: DYN, loc }, loc }], else_: null, loc },
|
|
1009
|
+
{ kind: "return", value: boxed, loc }];
|
|
1010
|
+
}
|
|
686
1011
|
return [{ kind: "return", value: boxed, loc }];
|
|
687
1012
|
}
|
|
1013
|
+
if (!field && !accessor && isClassOwnEnumerableFieldName(name) && !name.startsWith("sym:") && !findMethodOn(lowerer, info, `get:${name}`)) {
|
|
1014
|
+
const bag = { kind: "call", callee: this.propertyBag.name,
|
|
1015
|
+
args: [lowerer.coerceToExpected(receiver, DYN)], type: DYN, loc };
|
|
1016
|
+
return [{ kind: "exprStmt", expr: { kind: "libCall", fn: "dyn.keySet", args: [bag,
|
|
1017
|
+
{ kind: "strLit", value: name, type: STRING, loc }, varRef("p.1", DYN, loc)], type: VOID, loc }, loc },
|
|
1018
|
+
{ kind: "return", value: null, loc }];
|
|
1019
|
+
}
|
|
688
1020
|
if (!field && !accessor)
|
|
689
1021
|
return [{ kind: "throw", value: {
|
|
690
1022
|
kind: "libCall", fn: "error.new", args: [{
|
|
@@ -707,6 +1039,29 @@ export class ClassDynamicDispatch {
|
|
|
707
1039
|
methodBody(lowerer, dispatch, info, receiver) {
|
|
708
1040
|
const { method, loc } = dispatch.source;
|
|
709
1041
|
const fence = () => [{ kind: "runtimeFence", code: "SC2020", message: `calling '${method}' on this native class through an untyped value is not supported yet`, loc }];
|
|
1042
|
+
if (info.builtinStream) {
|
|
1043
|
+
const incoming = dispatch.source.args.map((_, i) => varRef(`p.${i + 1}`, DYN, loc));
|
|
1044
|
+
const registering = ["on", "addListener", "once", "prependListener", "prependOnceListener"].includes(method);
|
|
1045
|
+
const removing = method === "off" || method === "removeListener";
|
|
1046
|
+
const ret = (value) => [{ kind: "return", value: lowerer.coerceToExpected(value, DYN), loc }];
|
|
1047
|
+
if (registering || removing) {
|
|
1048
|
+
if (incoming.length < 2)
|
|
1049
|
+
return fence();
|
|
1050
|
+
const event = lowerer.coerceToExpected(incoming[0], STRING);
|
|
1051
|
+
const cb = incoming[1];
|
|
1052
|
+
return [{ kind: "exprStmt", expr: { kind: "libCall", fn: "emitter.checkListener", args: [cb], type: VOID, loc }, loc }, ...ret({ kind: "libCall", fn: registering ? "stream.onDyn" : "emitter.offDyn", args: [receiver, event, cb, ...(registering ? [
|
|
1053
|
+
{ kind: "boolLit", value: method === "once" || method === "prependOnceListener", type: BOOL, loc },
|
|
1054
|
+
{ kind: "boolLit", value: method.startsWith("prepend"), type: BOOL, loc },
|
|
1055
|
+
] : [])], type: receiver.type, loc })];
|
|
1056
|
+
}
|
|
1057
|
+
if (method === "read")
|
|
1058
|
+
return ret({ kind: "libCall", fn: "readable.readDyn", args: [receiver, incoming[0] ?? dynUndefinedExpr(loc)], type: DYN, loc });
|
|
1059
|
+
if (method === "pause" || method === "resume" || method === "isPaused")
|
|
1060
|
+
return ret({ kind: "libCall", fn: method === "pause" ? "readable.pause" : method === "resume" ? "readable.resume" : "readable.isPaused", args: [receiver], type: method === "isPaused" ? BOOL : receiver.type, loc });
|
|
1061
|
+
if (method === "destroy" && (!incoming.length || incoming[0]?.kind === "unitLit"))
|
|
1062
|
+
return ret({ kind: "libCall", fn: "stream.destroy", args: [receiver], type: receiver.type, loc });
|
|
1063
|
+
return fence();
|
|
1064
|
+
}
|
|
710
1065
|
const methodInfo = findMethodOn(lowerer, info, method);
|
|
711
1066
|
if (this.asyncFree && (methodInfo?.sig.gen || methodInfo?.sig.async)) {
|
|
712
1067
|
return [{ kind: "runtimeFence", code: "SC4005", message: `calling async or generator method '${method}' through an untyped library value is not supported`, loc }];
|