@kernlang/core 3.5.9-canary.220.1.c398cd95 → 4.0.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/dist/capability-matrix.js +8 -6
- package/dist/capability-matrix.js.map +1 -1
- package/dist/closure-eligibility.d.ts +104 -0
- package/dist/closure-eligibility.js +443 -0
- package/dist/closure-eligibility.js.map +1 -0
- package/dist/closure-python-lowering.d.ts +26 -0
- package/dist/closure-python-lowering.js +132 -22
- package/dist/closure-python-lowering.js.map +1 -1
- package/dist/codegen/body-ts.js +13 -0
- package/dist/codegen/body-ts.js.map +1 -1
- package/dist/codegen/type-system.js +50 -3
- package/dist/codegen/type-system.js.map +1 -1
- package/dist/codegen-core.js +29 -0
- package/dist/codegen-core.js.map +1 -1
- package/dist/codegen-expression.js +7 -0
- package/dist/codegen-expression.js.map +1 -1
- package/dist/constructor-super.d.ts +34 -0
- package/dist/constructor-super.js +122 -0
- package/dist/constructor-super.js.map +1 -0
- package/dist/core-contracts/boolean.d.ts +147 -0
- package/dist/core-contracts/boolean.js +110 -0
- package/dist/core-contracts/boolean.js.map +1 -0
- package/dist/core-contracts/function.d.ts +6 -0
- package/dist/core-contracts/function.js +7 -0
- package/dist/core-contracts/function.js.map +1 -0
- package/dist/core-contracts/index.d.ts +1032 -0
- package/dist/core-contracts/index.js +30 -0
- package/dist/core-contracts/index.js.map +1 -0
- package/dist/core-contracts/list.d.ts +87 -0
- package/dist/core-contracts/list.js +54 -0
- package/dist/core-contracts/list.js.map +1 -0
- package/dist/core-contracts/nullish.d.ts +12 -0
- package/dist/core-contracts/nullish.js +13 -0
- package/dist/core-contracts/nullish.js.map +1 -0
- package/dist/core-contracts/number.d.ts +291 -0
- package/dist/core-contracts/number.js +214 -0
- package/dist/core-contracts/number.js.map +1 -0
- package/dist/core-contracts/record.d.ts +60 -0
- package/dist/core-contracts/record.js +32 -0
- package/dist/core-contracts/record.js.map +1 -0
- package/dist/core-contracts/schema.d.ts +71 -0
- package/dist/core-contracts/schema.js +98 -0
- package/dist/core-contracts/schema.js.map +1 -0
- package/dist/core-contracts/semantics.d.ts +7 -0
- package/dist/core-contracts/semantics.js +221 -0
- package/dist/core-contracts/semantics.js.map +1 -0
- package/dist/core-contracts/string.d.ts +414 -0
- package/dist/core-contracts/string.js +335 -0
- package/dist/core-contracts/string.js.map +1 -0
- package/dist/core-runtime/contract-adapter.d.ts +8 -0
- package/dist/core-runtime/contract-adapter.js +73 -0
- package/dist/core-runtime/contract-adapter.js.map +1 -0
- package/dist/core-runtime/index.d.ts +109 -0
- package/dist/core-runtime/index.js +1946 -0
- package/dist/core-runtime/index.js.map +1 -0
- package/dist/core-runtime/shape-validator.d.ts +61 -0
- package/dist/core-runtime/shape-validator.js +564 -0
- package/dist/core-runtime/shape-validator.js.map +1 -0
- package/dist/core-runtime/value-brand.d.ts +2 -0
- package/dist/core-runtime/value-brand.js +6 -0
- package/dist/core-runtime/value-brand.js.map +1 -0
- package/dist/decompiler.js +46 -0
- package/dist/decompiler.js.map +1 -1
- package/dist/eligibility-taxonomy.d.ts +58 -0
- package/dist/eligibility-taxonomy.generated.d.ts +472 -0
- package/dist/eligibility-taxonomy.generated.js +566 -0
- package/dist/eligibility-taxonomy.generated.js.map +1 -0
- package/dist/eligibility-taxonomy.js +55 -0
- package/dist/eligibility-taxonomy.js.map +1 -0
- package/dist/index.d.ts +19 -2
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/instanceof-rhs.d.ts +53 -0
- package/dist/instanceof-rhs.js +74 -0
- package/dist/instanceof-rhs.js.map +1 -0
- package/dist/ir/semantics/lambda.js +9 -1
- package/dist/ir/semantics/lambda.js.map +1 -1
- package/dist/native-eligibility-ast.d.ts +5 -0
- package/dist/native-eligibility-ast.js +242 -63
- package/dist/native-eligibility-ast.js.map +1 -1
- package/dist/parser-expression.d.ts +1 -1
- package/dist/parser-expression.js +90 -1
- package/dist/parser-expression.js.map +1 -1
- package/dist/rag-assertions.d.ts +3 -0
- package/dist/rag-assertions.js +15 -0
- package/dist/rag-assertions.js.map +1 -0
- package/dist/rag-runtime.d.ts +169 -0
- package/dist/rag-runtime.js +823 -0
- package/dist/rag-runtime.js.map +1 -0
- package/dist/schema.d.ts +1 -0
- package/dist/schema.js +197 -2
- package/dist/schema.js.map +1 -1
- package/dist/semantic-substrate.d.ts +108 -0
- package/dist/semantic-substrate.js +263 -0
- package/dist/semantic-substrate.js.map +1 -0
- package/dist/semantic-validator.d.ts +332 -0
- package/dist/semantic-validator.js +3387 -0
- package/dist/semantic-validator.js.map +1 -1
- package/dist/spec.d.ts +2 -2
- package/dist/spec.js +15 -1
- package/dist/spec.js.map +1 -1
- package/dist/value-ir.d.ts +11 -1
- package/dist/value-ir.js.map +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1,1946 @@
|
|
|
1
|
+
import { hasDirectSuperCtorCall } from '../constructor-super.js';
|
|
2
|
+
import { CORE_TYPE_CONTRACTS, CoreContractEvaluationError, evaluateCoreContractOperation, } from '../core-contracts/index.js';
|
|
3
|
+
import { parseExpression } from '../parser-expression.js';
|
|
4
|
+
import { splitPortableExpressionList } from '../portable-expression-list.js';
|
|
5
|
+
import { CoreRuntimeContractAdapterError, coreFixtureValueToKernValue, kernValueToCoreFixtureValue, } from './contract-adapter.js';
|
|
6
|
+
import { collectCoreShapeFacts, validateCoreShape } from './shape-validator.js';
|
|
7
|
+
import { brandValue, KERN_VALUE_BRAND } from './value-brand.js';
|
|
8
|
+
const INTEGER_INDEX_RE = /^(0|[1-9]\d*)$/;
|
|
9
|
+
const ACTIVE_INSTANCE_SETTERS = new WeakMap();
|
|
10
|
+
const ACTIVE_CLASS_SETTERS = new WeakMap();
|
|
11
|
+
const ACTIVE_CONSTRUCTORS = new WeakMap();
|
|
12
|
+
export class CoreRuntimeEnv {
|
|
13
|
+
parent;
|
|
14
|
+
bindings = new Map();
|
|
15
|
+
runtimeRootContext;
|
|
16
|
+
constructor(parent) {
|
|
17
|
+
this.parent = parent;
|
|
18
|
+
}
|
|
19
|
+
define(name, value) {
|
|
20
|
+
if (this.bindings.has(name))
|
|
21
|
+
throw new Error(`KERN core runtime binding already defined: ${name}`);
|
|
22
|
+
this.bindings.set(name, value);
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
assign(name, value) {
|
|
26
|
+
if (this.bindings.has(name)) {
|
|
27
|
+
this.bindings.set(name, value);
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
if (this.parent)
|
|
31
|
+
return this.parent.assign(name, value);
|
|
32
|
+
throw new Error(`KERN core runtime binding not found: ${name}`);
|
|
33
|
+
}
|
|
34
|
+
lookup(name) {
|
|
35
|
+
if (this.bindings.has(name))
|
|
36
|
+
return this.bindings.get(name) ?? kUndefined();
|
|
37
|
+
if (this.parent)
|
|
38
|
+
return this.parent.lookup(name);
|
|
39
|
+
throw new Error(`KERN core runtime binding not found: ${name}`);
|
|
40
|
+
}
|
|
41
|
+
has(name) {
|
|
42
|
+
return this.bindings.has(name) || (this.parent?.has(name) ?? false);
|
|
43
|
+
}
|
|
44
|
+
child() {
|
|
45
|
+
return new CoreRuntimeEnv(this);
|
|
46
|
+
}
|
|
47
|
+
setRuntimeRootContext(root) {
|
|
48
|
+
this.runtimeRootContext = root;
|
|
49
|
+
}
|
|
50
|
+
getRuntimeRootContext() {
|
|
51
|
+
return this.runtimeRootContext ?? this.parent?.getRuntimeRootContext();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export const kNull = () => brandValue({ kind: 'null' });
|
|
55
|
+
export const kUndefined = () => brandValue({ kind: 'undefined' });
|
|
56
|
+
export const kBoolean = (value) => brandValue({ kind: 'boolean', value });
|
|
57
|
+
export const kNumber = (value) => {
|
|
58
|
+
if (!Number.isFinite(value))
|
|
59
|
+
throw new Error('KERN core runtime number must be finite.');
|
|
60
|
+
return brandValue({ kind: 'number', value });
|
|
61
|
+
};
|
|
62
|
+
export const kString = (value) => brandValue({ kind: 'string', value });
|
|
63
|
+
export function createCoreRuntimeEnv(options = {}) {
|
|
64
|
+
const env = new CoreRuntimeEnv(options.parent);
|
|
65
|
+
for (const [name, value] of Object.entries(options.globals ?? {}))
|
|
66
|
+
env.define(name, fromHostValue(value));
|
|
67
|
+
installPortableBuiltins(env);
|
|
68
|
+
return env;
|
|
69
|
+
}
|
|
70
|
+
function installPortableBuiltins(env) {
|
|
71
|
+
for (const builtin of [
|
|
72
|
+
{
|
|
73
|
+
kind: 'builtin',
|
|
74
|
+
name: 'String',
|
|
75
|
+
call: (args) => {
|
|
76
|
+
if (args.length !== 1)
|
|
77
|
+
throw new Error('KERN core runtime String() expects exactly one argument.');
|
|
78
|
+
return kString(kernStringCoerce(args[0]));
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
]) {
|
|
82
|
+
if (!env.has(builtin.name))
|
|
83
|
+
env.define(builtin.name, brandValue(builtin));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function fromHostValue(value) {
|
|
87
|
+
if (isKernValue(value))
|
|
88
|
+
return value;
|
|
89
|
+
if (value === null)
|
|
90
|
+
return kNull();
|
|
91
|
+
if (value === undefined)
|
|
92
|
+
return kUndefined();
|
|
93
|
+
if (typeof value === 'boolean')
|
|
94
|
+
return kBoolean(value);
|
|
95
|
+
if (typeof value === 'number')
|
|
96
|
+
return kNumber(value);
|
|
97
|
+
if (typeof value === 'string')
|
|
98
|
+
return kString(value);
|
|
99
|
+
if (Array.isArray(value))
|
|
100
|
+
return brandValue({ kind: 'array', items: Array.from(value, fromHostValue) });
|
|
101
|
+
if (isPlainRecord(value)) {
|
|
102
|
+
const entries = createRecordEntries();
|
|
103
|
+
for (const [key, entry] of Object.entries(value))
|
|
104
|
+
entries[key] = fromHostValue(entry);
|
|
105
|
+
return brandValue({
|
|
106
|
+
kind: 'record',
|
|
107
|
+
entries,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
throw new Error(`Unsupported host value for KERN core runtime: ${typeof value}`);
|
|
111
|
+
}
|
|
112
|
+
export function toHostValue(value) {
|
|
113
|
+
if (value === undefined)
|
|
114
|
+
return undefined;
|
|
115
|
+
switch (value.kind) {
|
|
116
|
+
case 'null':
|
|
117
|
+
return null;
|
|
118
|
+
case 'undefined':
|
|
119
|
+
return undefined;
|
|
120
|
+
case 'boolean':
|
|
121
|
+
case 'number':
|
|
122
|
+
case 'string':
|
|
123
|
+
return value.value;
|
|
124
|
+
case 'array':
|
|
125
|
+
return value.items.map(toHostValue);
|
|
126
|
+
case 'record':
|
|
127
|
+
return Object.fromEntries(Object.entries(value.entries).map(([key, entry]) => [key, toHostValue(entry)]));
|
|
128
|
+
case 'instance':
|
|
129
|
+
return Object.fromEntries(Object.entries(value.fields).map(([key, entry]) => [key, toHostValue(entry)]));
|
|
130
|
+
case 'function':
|
|
131
|
+
case 'builtin':
|
|
132
|
+
case 'class':
|
|
133
|
+
case 'bound-method':
|
|
134
|
+
return `[KERN ${value.kind}${value.name ? ` ${value.name}` : ''}]`;
|
|
135
|
+
case 'super':
|
|
136
|
+
return `[KERN super ${value.ownerClass.name}]`;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export function kernTruthy(value) {
|
|
140
|
+
switch (value.kind) {
|
|
141
|
+
case 'null':
|
|
142
|
+
case 'undefined':
|
|
143
|
+
return false;
|
|
144
|
+
case 'boolean':
|
|
145
|
+
return value.value;
|
|
146
|
+
case 'number':
|
|
147
|
+
return value.value !== 0;
|
|
148
|
+
case 'string':
|
|
149
|
+
return value.value.length > 0;
|
|
150
|
+
case 'array':
|
|
151
|
+
case 'record':
|
|
152
|
+
case 'function':
|
|
153
|
+
case 'builtin':
|
|
154
|
+
case 'class':
|
|
155
|
+
case 'instance':
|
|
156
|
+
case 'bound-method':
|
|
157
|
+
case 'super':
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
export function evalCoreExpression(expr, env = createCoreRuntimeEnv()) {
|
|
162
|
+
installPortableBuiltins(env);
|
|
163
|
+
const valueIR = typeof expr === 'string' ? parseExpression(expr) : expr;
|
|
164
|
+
return evalValueIR(valueIR, env);
|
|
165
|
+
}
|
|
166
|
+
export function runCoreRuntime(nodeOrNodes, env = createCoreRuntimeEnv()) {
|
|
167
|
+
env.setRuntimeRootContext(nodeOrNodes);
|
|
168
|
+
const nodes = isIRNodeArray(nodeOrNodes) ? nodeOrNodes : runtimeChildren(nodeOrNodes);
|
|
169
|
+
return { completion: executeSequence(nodes, env), env };
|
|
170
|
+
}
|
|
171
|
+
export function callCoreFunction(fnNode, args, env = createCoreRuntimeEnv()) {
|
|
172
|
+
if (fnNode.type !== 'fn')
|
|
173
|
+
throw new Error('KERN core runtime callCoreFunction expects an fn node.');
|
|
174
|
+
const fn = makeFunction(fnNode, env);
|
|
175
|
+
return callFunctionValue(fn, args);
|
|
176
|
+
}
|
|
177
|
+
function executeSequence(nodes, env) {
|
|
178
|
+
for (let i = 0; i < nodes.length; i += 1) {
|
|
179
|
+
const node = nodes[i];
|
|
180
|
+
if (node.type === 'else')
|
|
181
|
+
throw new Error('KERN core runtime `else` must immediately follow an `if`.');
|
|
182
|
+
if (node.type === 'if') {
|
|
183
|
+
const completion = executeIf(node, nodes[i + 1], env);
|
|
184
|
+
if (nodes[i + 1]?.type === 'else')
|
|
185
|
+
i += 1;
|
|
186
|
+
if (completion.kind !== 'normal')
|
|
187
|
+
return completion;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const completion = executeNode(node, env);
|
|
191
|
+
if (completion.kind !== 'normal')
|
|
192
|
+
return completion;
|
|
193
|
+
}
|
|
194
|
+
return { kind: 'normal', value: kUndefined() };
|
|
195
|
+
}
|
|
196
|
+
function executeNode(node, env) {
|
|
197
|
+
switch (node.type) {
|
|
198
|
+
case 'interface':
|
|
199
|
+
case 'import':
|
|
200
|
+
case 'use':
|
|
201
|
+
return { kind: 'normal', value: kUndefined() };
|
|
202
|
+
case 'handler':
|
|
203
|
+
case '__block':
|
|
204
|
+
return executeSequence(node.children ?? [], env);
|
|
205
|
+
case 'let':
|
|
206
|
+
case 'expression-v1': {
|
|
207
|
+
const name = requiredString(node.props?.name, `${node.type} name=`);
|
|
208
|
+
const rawExpr = node.type === 'let' ? node.props?.value : node.props?.expr;
|
|
209
|
+
env.define(name, evalCoreExpression(unwrapExpr(rawExpr, `${node.type} expression`), env));
|
|
210
|
+
return { kind: 'normal', value: kUndefined() };
|
|
211
|
+
}
|
|
212
|
+
case 'return': {
|
|
213
|
+
if (node.props && Object.hasOwn(node.props, 'value')) {
|
|
214
|
+
return { kind: 'return', value: evalCoreExpression(unwrapExpr(node.props.value, 'return value='), env) };
|
|
215
|
+
}
|
|
216
|
+
return { kind: 'return', value: kUndefined() };
|
|
217
|
+
}
|
|
218
|
+
case 'fn': {
|
|
219
|
+
const fn = makeFunction(node, env);
|
|
220
|
+
env.define(requiredString(node.props?.name, 'fn name='), fn);
|
|
221
|
+
return { kind: 'normal', value: kUndefined() };
|
|
222
|
+
}
|
|
223
|
+
case 'class': {
|
|
224
|
+
const klass = makeClass(node, env);
|
|
225
|
+
env.define(klass.name, klass);
|
|
226
|
+
initializeClassStaticFields(klass);
|
|
227
|
+
validateImplementedClassStaticProtocols(klass);
|
|
228
|
+
return { kind: 'normal', value: kUndefined() };
|
|
229
|
+
}
|
|
230
|
+
case 'assign':
|
|
231
|
+
executeAssign(node, env);
|
|
232
|
+
return { kind: 'normal', value: kUndefined() };
|
|
233
|
+
case 'do': {
|
|
234
|
+
evalCoreExpression(unwrapExpr(node.props?.value, 'do value='), env);
|
|
235
|
+
return { kind: 'normal', value: kUndefined() };
|
|
236
|
+
}
|
|
237
|
+
case 'coalesce':
|
|
238
|
+
case 'firstDefined':
|
|
239
|
+
return executeCoalesce(node, env);
|
|
240
|
+
case 'firstTruthy':
|
|
241
|
+
return executeFirstTruthy(node, env);
|
|
242
|
+
default:
|
|
243
|
+
throw new Error(`KERN core runtime unsupported node type: ${node.type}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function executeIf(node, maybeElse, env) {
|
|
247
|
+
const cond = evalCoreExpression(unwrapExpr(node.props?.cond, 'if cond='), env);
|
|
248
|
+
if (kernTruthy(cond))
|
|
249
|
+
return executeSequence(node.children ?? [], env.child());
|
|
250
|
+
if (maybeElse?.type === 'else')
|
|
251
|
+
return executeSequence(maybeElse.children ?? [], env.child());
|
|
252
|
+
return { kind: 'normal', value: kUndefined() };
|
|
253
|
+
}
|
|
254
|
+
function executeCoalesce(node, env) {
|
|
255
|
+
const name = requiredString(node.props?.name, `${node.type} name=`);
|
|
256
|
+
const values = splitPortableExpressionList(requiredString(node.props?.values, `${node.type} values=`), `${node.type} values=`);
|
|
257
|
+
if (values.length < 2)
|
|
258
|
+
throw new Error(`KERN core runtime ${node.type} requires at least two values.`);
|
|
259
|
+
let winner = kUndefined();
|
|
260
|
+
for (const value of values) {
|
|
261
|
+
const candidate = evalCoreExpression(value, env);
|
|
262
|
+
if (!isNullish(candidate)) {
|
|
263
|
+
winner = candidate;
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
env.define(name, winner);
|
|
268
|
+
return { kind: 'normal', value: kUndefined() };
|
|
269
|
+
}
|
|
270
|
+
function executeFirstTruthy(node, env) {
|
|
271
|
+
const name = requiredString(node.props?.name, 'firstTruthy name=');
|
|
272
|
+
const values = splitPortableExpressionList(requiredString(node.props?.values, 'firstTruthy values='), 'firstTruthy values=');
|
|
273
|
+
if (values.length < 2)
|
|
274
|
+
throw new Error('KERN core runtime firstTruthy requires at least two values.');
|
|
275
|
+
let winner = kUndefined();
|
|
276
|
+
for (const value of values) {
|
|
277
|
+
const candidate = evalCoreExpression(value, env);
|
|
278
|
+
if (kernTruthy(candidate)) {
|
|
279
|
+
winner = candidate;
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
env.define(name, winner);
|
|
284
|
+
return { kind: 'normal', value: kUndefined() };
|
|
285
|
+
}
|
|
286
|
+
function evalValueIR(node, env) {
|
|
287
|
+
switch (node.kind) {
|
|
288
|
+
case 'numLit':
|
|
289
|
+
if (node.bigint)
|
|
290
|
+
throw new Error('KERN core runtime bigint literals are not supported yet.');
|
|
291
|
+
return kNumber(node.value);
|
|
292
|
+
case 'strLit':
|
|
293
|
+
return kString(node.value);
|
|
294
|
+
case 'boolLit':
|
|
295
|
+
return kBoolean(node.value);
|
|
296
|
+
case 'nullLit':
|
|
297
|
+
return kNull();
|
|
298
|
+
case 'undefLit':
|
|
299
|
+
return kUndefined();
|
|
300
|
+
case 'ident': {
|
|
301
|
+
const value = env.lookup(node.name);
|
|
302
|
+
if (node.name === 'this' && value.kind === 'instance')
|
|
303
|
+
guardConstructedInstanceAccess(value);
|
|
304
|
+
return value;
|
|
305
|
+
}
|
|
306
|
+
case 'tmplLit':
|
|
307
|
+
return kString(node.quasis.reduce((out, quasi, index) => {
|
|
308
|
+
const expr = index < node.expressions.length ? kernStringCoerce(evalValueIR(node.expressions[index], env)) : '';
|
|
309
|
+
return out + quasi + expr;
|
|
310
|
+
}, ''));
|
|
311
|
+
case 'arrayLit':
|
|
312
|
+
return brandValue({ kind: 'array', items: node.items.map((item) => evalValueIR(item, env)) });
|
|
313
|
+
case 'objectLit':
|
|
314
|
+
return evalObjectLiteral(node, env);
|
|
315
|
+
case 'unary':
|
|
316
|
+
return evalUnary(node, env);
|
|
317
|
+
case 'binary':
|
|
318
|
+
return evalBinary(node, env);
|
|
319
|
+
case 'conditional':
|
|
320
|
+
return kernTruthy(evalValueIR(node.test, env))
|
|
321
|
+
? evalValueIR(node.consequent, env)
|
|
322
|
+
: evalValueIR(node.alternate, env);
|
|
323
|
+
case 'typeAssert':
|
|
324
|
+
case 'nonNull':
|
|
325
|
+
return evalValueIR(node.expression, env);
|
|
326
|
+
case 'member':
|
|
327
|
+
return evalMember(node, env);
|
|
328
|
+
case 'index':
|
|
329
|
+
return evalIndex(node, env);
|
|
330
|
+
case 'call':
|
|
331
|
+
return evalCall(node, env);
|
|
332
|
+
case 'lambda':
|
|
333
|
+
throw new Error('KERN core runtime lambda expressions are not supported in the first runtime slice.');
|
|
334
|
+
case 'new':
|
|
335
|
+
return evalNew(node, env);
|
|
336
|
+
default:
|
|
337
|
+
throw new Error(`KERN core runtime unsupported expression kind: ${node.kind}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function evalObjectLiteral(node, env) {
|
|
341
|
+
const entries = createRecordEntries();
|
|
342
|
+
for (const entry of node.entries) {
|
|
343
|
+
if (isObjectSpreadEntry(entry)) {
|
|
344
|
+
const spread = evalValueIR(entry.argument, env);
|
|
345
|
+
if (spread.kind !== 'record')
|
|
346
|
+
throw new Error('KERN core runtime object spread requires a record.');
|
|
347
|
+
for (const [key, value] of Object.entries(spread.entries))
|
|
348
|
+
entries[key] = value;
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
entries[entry.key] = evalValueIR(entry.value, env);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return brandValue({ kind: 'record', entries });
|
|
355
|
+
}
|
|
356
|
+
function evalUnary(node, env) {
|
|
357
|
+
const arg = evalValueIR(node.argument, env);
|
|
358
|
+
if (node.op === '!') {
|
|
359
|
+
if (arg.kind !== 'boolean')
|
|
360
|
+
throw new Error('KERN core runtime unary ! requires a boolean.');
|
|
361
|
+
return dispatchCoreContractOperation('Boolean.not', [arg.value]);
|
|
362
|
+
}
|
|
363
|
+
if (node.op === '-' || node.op === '+') {
|
|
364
|
+
if (arg.kind !== 'number')
|
|
365
|
+
throw new Error(`KERN core runtime unary ${node.op} requires a number.`);
|
|
366
|
+
return node.op === '-' ? dispatchCoreContractOperation('Number.negate', [arg.value]) : arg;
|
|
367
|
+
}
|
|
368
|
+
throw new Error(`KERN core runtime unsupported unary operator: ${node.op}`);
|
|
369
|
+
}
|
|
370
|
+
function evalBinary(node, env) {
|
|
371
|
+
if (node.op === '&&') {
|
|
372
|
+
const left = evalValueIR(node.left, env);
|
|
373
|
+
return kernTruthy(left) ? evalValueIR(node.right, env) : left;
|
|
374
|
+
}
|
|
375
|
+
if (node.op === '||') {
|
|
376
|
+
const left = evalValueIR(node.left, env);
|
|
377
|
+
return kernTruthy(left) ? left : evalValueIR(node.right, env);
|
|
378
|
+
}
|
|
379
|
+
if (node.op === '??') {
|
|
380
|
+
const left = evalValueIR(node.left, env);
|
|
381
|
+
return isNullish(left) ? evalValueIR(node.right, env) : left;
|
|
382
|
+
}
|
|
383
|
+
const left = evalValueIR(node.left, env);
|
|
384
|
+
const right = evalValueIR(node.right, env);
|
|
385
|
+
switch (node.op) {
|
|
386
|
+
case '+':
|
|
387
|
+
if (left.kind === 'number' && right.kind === 'number') {
|
|
388
|
+
return dispatchCoreContractOperation('Number.add', [left.value, right.value]);
|
|
389
|
+
}
|
|
390
|
+
if (left.kind === 'string' && right.kind === 'string') {
|
|
391
|
+
return dispatchCoreContractOperation('String.concat', [left.value, right.value]);
|
|
392
|
+
}
|
|
393
|
+
throw new Error('KERN core runtime + requires two numbers or two strings.');
|
|
394
|
+
case '-':
|
|
395
|
+
case '*':
|
|
396
|
+
case '/':
|
|
397
|
+
case '%':
|
|
398
|
+
return evalNumberBinary(node.op, left, right);
|
|
399
|
+
case '===':
|
|
400
|
+
case '==':
|
|
401
|
+
return kBoolean(kernEquals(left, right));
|
|
402
|
+
case '!==':
|
|
403
|
+
case '!=':
|
|
404
|
+
return kBoolean(!kernEquals(left, right));
|
|
405
|
+
case '<':
|
|
406
|
+
case '<=':
|
|
407
|
+
case '>':
|
|
408
|
+
case '>=':
|
|
409
|
+
return evalOrderedComparison(node.op, left, right);
|
|
410
|
+
default:
|
|
411
|
+
throw new Error(`KERN core runtime unsupported binary operator: ${node.op}`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
function evalNumberBinary(op, left, right) {
|
|
415
|
+
if (left.kind !== 'number' || right.kind !== 'number') {
|
|
416
|
+
throw new Error(`KERN core runtime ${op} requires two numbers.`);
|
|
417
|
+
}
|
|
418
|
+
switch (op) {
|
|
419
|
+
case '-':
|
|
420
|
+
return dispatchCoreContractOperation('Number.subtract', [left.value, right.value]);
|
|
421
|
+
case '*':
|
|
422
|
+
return dispatchCoreContractOperation('Number.multiply', [left.value, right.value]);
|
|
423
|
+
case '/':
|
|
424
|
+
return dispatchCoreContractOperation('Number.divide', [left.value, right.value]);
|
|
425
|
+
case '%':
|
|
426
|
+
return dispatchCoreContractOperation('Number.remainder', [left.value, right.value]);
|
|
427
|
+
default:
|
|
428
|
+
throw new Error(`KERN core runtime unsupported numeric operator: ${op}`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
function evalOrderedComparison(op, left, right) {
|
|
432
|
+
if (!((left.kind === 'number' && right.kind === 'number') || (left.kind === 'string' && right.kind === 'string'))) {
|
|
433
|
+
throw new Error(`KERN core runtime ${op} requires same-kind number or string operands.`);
|
|
434
|
+
}
|
|
435
|
+
if (left.kind === 'number' && right.kind === 'number') {
|
|
436
|
+
if (op === '<')
|
|
437
|
+
return dispatchCoreContractOperation('Number.lessThan', [left.value, right.value]);
|
|
438
|
+
if (op === '<=')
|
|
439
|
+
return dispatchCoreContractOperation('Number.lessThanOrEqual', [left.value, right.value]);
|
|
440
|
+
if (op === '>')
|
|
441
|
+
return dispatchCoreContractOperation('Number.greaterThan', [left.value, right.value]);
|
|
442
|
+
return dispatchCoreContractOperation('Number.greaterThanOrEqual', [left.value, right.value]);
|
|
443
|
+
}
|
|
444
|
+
if (op === '<')
|
|
445
|
+
return dispatchCoreContractOperation('String.lessThan', [left.value, right.value]);
|
|
446
|
+
if (op === '<=')
|
|
447
|
+
return dispatchCoreContractOperation('String.lessThanOrEqual', [left.value, right.value]);
|
|
448
|
+
if (op === '>')
|
|
449
|
+
return dispatchCoreContractOperation('String.greaterThan', [left.value, right.value]);
|
|
450
|
+
return dispatchCoreContractOperation('String.greaterThanOrEqual', [left.value, right.value]);
|
|
451
|
+
}
|
|
452
|
+
function evalMember(node, env) {
|
|
453
|
+
const object = evalValueIR(node.object, env);
|
|
454
|
+
if (isNullish(object)) {
|
|
455
|
+
if (node.optional)
|
|
456
|
+
return kUndefined();
|
|
457
|
+
throw new Error(`KERN core runtime cannot read .${node.property} from ${object.kind}.`);
|
|
458
|
+
}
|
|
459
|
+
if (object.kind === 'record') {
|
|
460
|
+
return evalRecordGet(object, node.property);
|
|
461
|
+
}
|
|
462
|
+
if (object.kind === 'instance')
|
|
463
|
+
return evalInstanceMember(object, node.property);
|
|
464
|
+
if (object.kind === 'super')
|
|
465
|
+
return evalSuperMember(object, node.property);
|
|
466
|
+
if (object.kind === 'class')
|
|
467
|
+
return evalClassMember(object, node.property);
|
|
468
|
+
if (object.kind === 'array' && node.property === 'length') {
|
|
469
|
+
return kNumber(object.items.length);
|
|
470
|
+
}
|
|
471
|
+
if (object.kind === 'string')
|
|
472
|
+
return evalStringMember(object, node.property);
|
|
473
|
+
if (object.kind === 'boolean')
|
|
474
|
+
return evalBooleanMember(object, node.property);
|
|
475
|
+
return kUndefined();
|
|
476
|
+
}
|
|
477
|
+
function evalIndex(node, env) {
|
|
478
|
+
const object = evalValueIR(node.object, env);
|
|
479
|
+
if (isNullish(object)) {
|
|
480
|
+
if (node.optional)
|
|
481
|
+
return kUndefined();
|
|
482
|
+
throw new Error(`KERN core runtime cannot index ${object.kind}.`);
|
|
483
|
+
}
|
|
484
|
+
const index = evalValueIR(node.index, env);
|
|
485
|
+
if (object.kind === 'array') {
|
|
486
|
+
if (index.kind !== 'number')
|
|
487
|
+
throw new Error('KERN core runtime array index must be a number.');
|
|
488
|
+
return evalListIndex(object, index.value);
|
|
489
|
+
}
|
|
490
|
+
if (object.kind === 'record' || object.kind === 'string') {
|
|
491
|
+
if (index.kind !== 'string' && index.kind !== 'number') {
|
|
492
|
+
throw new Error('KERN core runtime record/string index must be a string or number.');
|
|
493
|
+
}
|
|
494
|
+
const key = String(index.value);
|
|
495
|
+
if (object.kind === 'record')
|
|
496
|
+
return evalRecordGet(object, key);
|
|
497
|
+
const charIndex = index.kind === 'number' ? index.value : INTEGER_INDEX_RE.test(index.value) ? Number(index.value) : NaN;
|
|
498
|
+
if (!Number.isFinite(charIndex) && index.kind !== 'number')
|
|
499
|
+
return kUndefined();
|
|
500
|
+
return dispatchCoreContractOperation('String.index', [object.value, charIndex]);
|
|
501
|
+
}
|
|
502
|
+
return kUndefined();
|
|
503
|
+
}
|
|
504
|
+
function evalStringMember(object, property) {
|
|
505
|
+
if (property === 'length')
|
|
506
|
+
return dispatchCoreContractOperation('String.length', [object.value]);
|
|
507
|
+
const operation = stringMemberOperation(property);
|
|
508
|
+
if (!operation)
|
|
509
|
+
return kUndefined();
|
|
510
|
+
return boundCoreContractOperation(`String.${operation}`, [object.value]);
|
|
511
|
+
}
|
|
512
|
+
function evalBooleanMember(object, property) {
|
|
513
|
+
const operation = booleanMemberOperation(property);
|
|
514
|
+
if (!operation)
|
|
515
|
+
return kUndefined();
|
|
516
|
+
return boundCoreContractOperation(`Boolean.${operation}`, [object.value]);
|
|
517
|
+
}
|
|
518
|
+
function stringMemberOperation(property) {
|
|
519
|
+
switch (property) {
|
|
520
|
+
case 'includes':
|
|
521
|
+
case 'index':
|
|
522
|
+
case 'startsWith':
|
|
523
|
+
case 'endsWith':
|
|
524
|
+
case 'slice':
|
|
525
|
+
case 'trim':
|
|
526
|
+
case 'lower':
|
|
527
|
+
case 'upper':
|
|
528
|
+
case 'concat':
|
|
529
|
+
case 'equals':
|
|
530
|
+
case 'toString':
|
|
531
|
+
return property;
|
|
532
|
+
default:
|
|
533
|
+
return undefined;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
function booleanMemberOperation(property) {
|
|
537
|
+
switch (property) {
|
|
538
|
+
case 'not':
|
|
539
|
+
case 'and':
|
|
540
|
+
case 'or':
|
|
541
|
+
case 'equals':
|
|
542
|
+
case 'toString':
|
|
543
|
+
return property;
|
|
544
|
+
default:
|
|
545
|
+
return undefined;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
function boundCoreContractOperation(operationId, receiverArgs) {
|
|
549
|
+
return brandValue({
|
|
550
|
+
kind: 'builtin',
|
|
551
|
+
name: operationId,
|
|
552
|
+
call: (args) => {
|
|
553
|
+
try {
|
|
554
|
+
return dispatchCoreContractOperation(operationId, [...receiverArgs, ...args.map(kernValueToCoreFixtureValue)]);
|
|
555
|
+
}
|
|
556
|
+
catch (error) {
|
|
557
|
+
if (error instanceof CoreRuntimeContractAdapterError) {
|
|
558
|
+
throw new CoreContractEvaluationError('strict-type', coreOperationStrictTypeMessage(operationId));
|
|
559
|
+
}
|
|
560
|
+
throw error;
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
function dispatchCoreContractOperation(operationId, args) {
|
|
566
|
+
return coreFixtureValueToKernValue(evaluateCoreContractOperation(operationId, args));
|
|
567
|
+
}
|
|
568
|
+
function evalListIndex(object, index) {
|
|
569
|
+
if (!Number.isFinite(index) || !Number.isInteger(index) || index < 0 || index >= object.items.length) {
|
|
570
|
+
return kUndefined();
|
|
571
|
+
}
|
|
572
|
+
return object.items[index] ?? kUndefined();
|
|
573
|
+
}
|
|
574
|
+
function evalRecordGet(object, key) {
|
|
575
|
+
if (!Object.hasOwn(object.entries, key))
|
|
576
|
+
return dispatchCoreContractOperation('Record.get', [recordShapeFixture(object), key]);
|
|
577
|
+
return object.entries[key] ?? kUndefined();
|
|
578
|
+
}
|
|
579
|
+
function recordShapeFixture(object) {
|
|
580
|
+
const shape = Object.create(null);
|
|
581
|
+
for (const key of Object.keys(object.entries))
|
|
582
|
+
shape[key] = null;
|
|
583
|
+
return shape;
|
|
584
|
+
}
|
|
585
|
+
function coreOperationStrictTypeMessage(operationId) {
|
|
586
|
+
for (const contract of Object.values(CORE_TYPE_CONTRACTS.types)) {
|
|
587
|
+
const operation = contract.operations.find((operation) => operation.id === operationId);
|
|
588
|
+
if (operation)
|
|
589
|
+
return `${operationId} expects ${operation.args.join(', ')}.`;
|
|
590
|
+
}
|
|
591
|
+
return `${operationId} received an unsupported runtime value.`;
|
|
592
|
+
}
|
|
593
|
+
function evalCall(node, env) {
|
|
594
|
+
const callee = evalValueIR(node.callee, env);
|
|
595
|
+
if (isNullish(callee)) {
|
|
596
|
+
if (node.optional)
|
|
597
|
+
return kUndefined();
|
|
598
|
+
throw new Error(`KERN core runtime cannot call ${callee.kind}.`);
|
|
599
|
+
}
|
|
600
|
+
const args = node.args.map((arg) => evalValueIR(arg, env));
|
|
601
|
+
if (callee.kind === 'builtin')
|
|
602
|
+
return callee.call(args);
|
|
603
|
+
if (callee.kind === 'function')
|
|
604
|
+
return callFunctionValue(callee, args).value;
|
|
605
|
+
if (callee.kind === 'class')
|
|
606
|
+
return constructClassValue(callee, args);
|
|
607
|
+
if (callee.kind === 'bound-method')
|
|
608
|
+
return callBoundMethodValue(callee, args).value;
|
|
609
|
+
if (callee.kind === 'super')
|
|
610
|
+
return callSuperConstructor(callee, args);
|
|
611
|
+
throw new Error(`KERN core runtime cannot call ${callee.kind}.`);
|
|
612
|
+
}
|
|
613
|
+
function evalNew(node, env) {
|
|
614
|
+
if (node.argument.kind === 'member') {
|
|
615
|
+
return evalValueIR({ ...node.argument, object: { kind: 'new', argument: node.argument.object } }, env);
|
|
616
|
+
}
|
|
617
|
+
if (node.argument.kind === 'index') {
|
|
618
|
+
return evalValueIR({ ...node.argument, object: { kind: 'new', argument: node.argument.object } }, env);
|
|
619
|
+
}
|
|
620
|
+
if (node.argument.kind === 'call' &&
|
|
621
|
+
(node.argument.callee.kind === 'member' || node.argument.callee.kind === 'index')) {
|
|
622
|
+
return evalValueIR({
|
|
623
|
+
...node.argument,
|
|
624
|
+
callee: {
|
|
625
|
+
...node.argument.callee,
|
|
626
|
+
object: { kind: 'new', argument: node.argument.callee.object },
|
|
627
|
+
},
|
|
628
|
+
}, env);
|
|
629
|
+
}
|
|
630
|
+
if (node.argument.kind !== 'call')
|
|
631
|
+
throw new Error('KERN core runtime new expects a constructor call.');
|
|
632
|
+
const callee = evalValueIR(node.argument.callee, env);
|
|
633
|
+
if (callee.kind !== 'class')
|
|
634
|
+
throw new Error('KERN core runtime new expects a class value.');
|
|
635
|
+
return constructClassValue(callee, node.argument.args.map((arg) => evalValueIR(arg, env)));
|
|
636
|
+
}
|
|
637
|
+
function makeClass(node, env) {
|
|
638
|
+
if (node.type !== 'class')
|
|
639
|
+
throw new Error('KERN core runtime makeClass expects a class node.');
|
|
640
|
+
return brandValue({
|
|
641
|
+
kind: 'class',
|
|
642
|
+
name: requiredString(node.props?.name, 'class name='),
|
|
643
|
+
node,
|
|
644
|
+
env,
|
|
645
|
+
staticFields: createRecordEntries(),
|
|
646
|
+
...(env.getRuntimeRootContext() ? { runtimeRootContext: env.getRuntimeRootContext() } : {}),
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
function initializeClassStaticFields(klass) {
|
|
650
|
+
for (const field of runtimeChildNodes(klass.node, 'field')) {
|
|
651
|
+
if (field.props?.static !== true && field.props?.static !== 'true')
|
|
652
|
+
continue;
|
|
653
|
+
const name = requiredString(field.props?.name, 'field name=');
|
|
654
|
+
const value = Object.hasOwn(field.props ?? {}, 'value') || Object.hasOwn(field.props ?? {}, 'default')
|
|
655
|
+
? evalCoreExpression(runtimeFieldInitializerExpr(field), classStaticEnv(klass))
|
|
656
|
+
: kUndefined();
|
|
657
|
+
klass.staticFields[name] = value;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
function constructClassValue(klass, args) {
|
|
661
|
+
const instance = brandValue({
|
|
662
|
+
kind: 'instance',
|
|
663
|
+
classValue: klass,
|
|
664
|
+
fields: createRecordEntries(),
|
|
665
|
+
initializedClasses: new Set(),
|
|
666
|
+
});
|
|
667
|
+
initializeClassLayer(instance, klass, args, true);
|
|
668
|
+
validateImplementedClassProtocols(instance, klass);
|
|
669
|
+
return instance;
|
|
670
|
+
}
|
|
671
|
+
function validateImplementedClassProtocols(instance, klass) {
|
|
672
|
+
const factsByRoot = new Map();
|
|
673
|
+
for (const layer of classHierarchyFromBase(klass)) {
|
|
674
|
+
const root = layer.runtimeRootContext ?? layer.env.getRuntimeRootContext();
|
|
675
|
+
if (!root)
|
|
676
|
+
continue;
|
|
677
|
+
const facts = factsByRoot.get(root) ?? collectCoreShapeFacts(root);
|
|
678
|
+
factsByRoot.set(root, facts);
|
|
679
|
+
const shapeByName = new Map(facts.interfaces.map((shape) => [shape.name, shape]));
|
|
680
|
+
const importedProtocolNames = runtimeImportedProtocolNames(root);
|
|
681
|
+
for (const interfaceName of runtimeClassReferenceNames(layer.node.props?.implements)) {
|
|
682
|
+
const shape = shapeByName.get(interfaceName);
|
|
683
|
+
if (!shape) {
|
|
684
|
+
if (importedProtocolNames.has(interfaceName))
|
|
685
|
+
continue;
|
|
686
|
+
throw new Error(`KERN core runtime class '${klass.name}' implements unknown interface '${interfaceName}'.`);
|
|
687
|
+
}
|
|
688
|
+
if (!shape.validatorAvailable || shape.indexers.length > 0) {
|
|
689
|
+
throw new Error(`KERN core runtime class '${klass.name}' implements interface '${interfaceName}' that is not executable as a class protocol in v1.`);
|
|
690
|
+
}
|
|
691
|
+
const projection = classProtocolProjection(instance, shape.fields.map((field) => field.name));
|
|
692
|
+
const result = validateCoreShape(projection, interfaceName, root);
|
|
693
|
+
if (!result.passed) {
|
|
694
|
+
throw new Error(`KERN core runtime class '${klass.name}' violates implemented interface '${interfaceName}':\n${result.diagnostics
|
|
695
|
+
.map((diagnostic) => diagnostic.message)
|
|
696
|
+
.join('\n')}`);
|
|
697
|
+
}
|
|
698
|
+
const missingMethods = runtimeInterfaceProtocolMethods(root, interfaceName)
|
|
699
|
+
.filter((method) => !classHasRuntimeProtocolMethod(instance.classValue, method))
|
|
700
|
+
.map((method) => method.name);
|
|
701
|
+
if (missingMethods.length > 0) {
|
|
702
|
+
throw new Error(`KERN core runtime class '${klass.name}' violates implemented interface '${interfaceName}': missing or incompatible method(s): ${missingMethods.join(', ')}.`);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
function validateImplementedClassStaticProtocols(klass) {
|
|
708
|
+
const root = klass.runtimeRootContext ?? klass.env.getRuntimeRootContext();
|
|
709
|
+
if (!root)
|
|
710
|
+
return;
|
|
711
|
+
const facts = collectCoreShapeFacts(root);
|
|
712
|
+
const shapeByName = new Map(facts.interfaces.map((shape) => [shape.name, shape]));
|
|
713
|
+
const importedProtocolNames = runtimeImportedProtocolNames(root);
|
|
714
|
+
for (const interfaceName of runtimeClassReferenceNames(klass.node.props?.implements)) {
|
|
715
|
+
const shape = shapeByName.get(interfaceName);
|
|
716
|
+
if (!shape) {
|
|
717
|
+
if (importedProtocolNames.has(interfaceName))
|
|
718
|
+
continue;
|
|
719
|
+
throw new Error(`KERN core runtime class '${klass.name}' implements unknown interface '${interfaceName}'.`);
|
|
720
|
+
}
|
|
721
|
+
if (!shape.validatorAvailable || shape.indexers.length > 0) {
|
|
722
|
+
throw new Error(`KERN core runtime class '${klass.name}' implements interface '${interfaceName}' that is not executable as a class protocol in v1.`);
|
|
723
|
+
}
|
|
724
|
+
const staticFields = runtimeInterfaceProtocolFields(root, interfaceName, true);
|
|
725
|
+
if (staticFields.length > 0) {
|
|
726
|
+
const missingFields = staticFields.filter((field) => !classHasRuntimeProtocolField(klass, field));
|
|
727
|
+
if (missingFields.length > 0) {
|
|
728
|
+
throw new Error(`KERN core runtime class '${klass.name}' violates implemented interface '${interfaceName}': missing or incompatible static member(s): ${missingFields
|
|
729
|
+
.map((field) => field.name)
|
|
730
|
+
.join(', ')}.`);
|
|
731
|
+
}
|
|
732
|
+
const fieldBackedFields = staticFields.filter((field) => findReadableClassShapeMember(klass, field.name, true)?.kind === 'field');
|
|
733
|
+
const projection = classStaticProtocolProjection(klass, fieldBackedFields);
|
|
734
|
+
const result = validateProjectedProtocolFields(projection, interfaceName, fieldBackedFields, root);
|
|
735
|
+
if (!result.passed) {
|
|
736
|
+
throw new Error(`KERN core runtime class '${klass.name}' violates implemented interface '${interfaceName}' static field contract:\n${result.diagnostics
|
|
737
|
+
.map((diagnostic) => diagnostic.message)
|
|
738
|
+
.join('\n')}`);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
const missingMethods = runtimeInterfaceProtocolMethods(root, interfaceName, true)
|
|
742
|
+
.filter((method) => !classHasRuntimeProtocolMethod(klass, method, true))
|
|
743
|
+
.map((method) => method.name);
|
|
744
|
+
if (missingMethods.length > 0) {
|
|
745
|
+
throw new Error(`KERN core runtime class '${klass.name}' violates implemented interface '${interfaceName}': missing or incompatible static member(s): ${missingMethods.join(', ')}.`);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
function classProtocolProjection(instance, fieldNames) {
|
|
750
|
+
const entries = createRecordEntries();
|
|
751
|
+
for (const fieldName of fieldNames) {
|
|
752
|
+
if (Object.hasOwn(instance.fields, fieldName)) {
|
|
753
|
+
entries[fieldName] = instance.fields[fieldName] ?? kUndefined();
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
const member = findReadableClassShapeMember(instance.classValue, fieldName, false);
|
|
757
|
+
if (member?.kind !== 'getter')
|
|
758
|
+
continue;
|
|
759
|
+
entries[fieldName] = evalInstanceMember(instance, fieldName);
|
|
760
|
+
}
|
|
761
|
+
return brandValue({ kind: 'record', entries });
|
|
762
|
+
}
|
|
763
|
+
function classStaticProtocolProjection(klass, fields) {
|
|
764
|
+
const entries = createRecordEntries();
|
|
765
|
+
for (const field of fields) {
|
|
766
|
+
const member = findReadableClassShapeMember(klass, field.name, true);
|
|
767
|
+
if (member?.kind !== 'field')
|
|
768
|
+
continue;
|
|
769
|
+
entries[field.name] = evalClassMember(klass, field.name);
|
|
770
|
+
}
|
|
771
|
+
return brandValue({ kind: 'record', entries });
|
|
772
|
+
}
|
|
773
|
+
function validateProjectedProtocolFields(projection, interfaceName, fields, rootOrNodes) {
|
|
774
|
+
const syntheticName = `__KernStaticProtocol_${interfaceName}`;
|
|
775
|
+
const syntheticInterface = {
|
|
776
|
+
type: 'interface',
|
|
777
|
+
props: { name: syntheticName },
|
|
778
|
+
children: fields.map((field) => ({
|
|
779
|
+
type: 'field',
|
|
780
|
+
props: {
|
|
781
|
+
name: field.name,
|
|
782
|
+
optional: field.optional,
|
|
783
|
+
...(field.type ? { type: field.type } : {}),
|
|
784
|
+
},
|
|
785
|
+
})),
|
|
786
|
+
};
|
|
787
|
+
const roots = [...(isIRNodeArray(rootOrNodes) ? rootOrNodes : [rootOrNodes]), syntheticInterface];
|
|
788
|
+
return validateCoreShape(projection, syntheticName, roots);
|
|
789
|
+
}
|
|
790
|
+
function runtimeInterfaceProtocolFields(rootOrNodes, interfaceName, staticOnly) {
|
|
791
|
+
const interfaceByName = runtimeInterfaceNodesByName(rootOrNodes);
|
|
792
|
+
const resolve = (name, seen) => {
|
|
793
|
+
if (seen.has(name))
|
|
794
|
+
return [];
|
|
795
|
+
const node = interfaceByName.get(name);
|
|
796
|
+
if (!node)
|
|
797
|
+
return [];
|
|
798
|
+
const nextSeen = new Set(seen);
|
|
799
|
+
nextSeen.add(name);
|
|
800
|
+
const fields = new Map();
|
|
801
|
+
for (const baseName of runtimeClassReferenceNames(node.props?.extends)) {
|
|
802
|
+
for (const field of resolve(baseName, nextSeen))
|
|
803
|
+
fields.set(runtimeInterfaceMemberShapeKey(field), field);
|
|
804
|
+
}
|
|
805
|
+
for (const child of node.children ?? []) {
|
|
806
|
+
if (child.type !== 'field')
|
|
807
|
+
continue;
|
|
808
|
+
const name = runtimeStringProp(child.props?.name);
|
|
809
|
+
if (!name)
|
|
810
|
+
continue;
|
|
811
|
+
const isStatic = runtimeBooleanProp(child.props?.static);
|
|
812
|
+
if (isStatic !== staticOnly)
|
|
813
|
+
continue;
|
|
814
|
+
fields.set(runtimeInterfaceMemberShapeKey({ name, static: isStatic }), {
|
|
815
|
+
name,
|
|
816
|
+
optional: runtimeBooleanProp(child.props?.optional),
|
|
817
|
+
static: isStatic,
|
|
818
|
+
...(runtimeStringProp(child.props?.type) ? { type: runtimeStringProp(child.props?.type) } : {}),
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
return [...fields.values()];
|
|
822
|
+
};
|
|
823
|
+
return resolve(interfaceName, new Set());
|
|
824
|
+
}
|
|
825
|
+
function runtimeInterfaceProtocolMethods(rootOrNodes, interfaceName, staticOnly = false) {
|
|
826
|
+
const interfaceByName = runtimeInterfaceNodesByName(rootOrNodes);
|
|
827
|
+
const resolve = (name, seen) => {
|
|
828
|
+
if (seen.has(name))
|
|
829
|
+
return [];
|
|
830
|
+
const node = interfaceByName.get(name);
|
|
831
|
+
if (!node)
|
|
832
|
+
return [];
|
|
833
|
+
const nextSeen = new Set(seen);
|
|
834
|
+
nextSeen.add(name);
|
|
835
|
+
const methods = new Map();
|
|
836
|
+
for (const baseName of runtimeClassReferenceNames(node.props?.extends)) {
|
|
837
|
+
for (const method of resolve(baseName, nextSeen))
|
|
838
|
+
methods.set(runtimeInterfaceMemberShapeKey(method), method);
|
|
839
|
+
}
|
|
840
|
+
for (const child of node.children ?? []) {
|
|
841
|
+
if (child.type !== 'method')
|
|
842
|
+
continue;
|
|
843
|
+
const name = runtimeStringProp(child.props?.name);
|
|
844
|
+
if (!name)
|
|
845
|
+
continue;
|
|
846
|
+
const isStatic = runtimeBooleanProp(child.props?.static);
|
|
847
|
+
if (isStatic !== staticOnly)
|
|
848
|
+
continue;
|
|
849
|
+
methods.set(runtimeInterfaceMemberShapeKey({ name, static: isStatic }), {
|
|
850
|
+
name,
|
|
851
|
+
arity: runtimeParams(child).length,
|
|
852
|
+
paramTypes: runtimeParams(child).map((param) => param.type ?? ''),
|
|
853
|
+
async: runtimeBooleanProp(child.props?.async),
|
|
854
|
+
stream: runtimeBooleanProp(child.props?.stream),
|
|
855
|
+
generator: runtimeBooleanProp(child.props?.generator),
|
|
856
|
+
static: isStatic,
|
|
857
|
+
...(runtimeStringProp(child.props?.returns) ? { returns: runtimeStringProp(child.props?.returns) } : {}),
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
return [...methods.values()];
|
|
861
|
+
};
|
|
862
|
+
return resolve(interfaceName, new Set());
|
|
863
|
+
}
|
|
864
|
+
function runtimeInterfaceNodesByName(rootOrNodes) {
|
|
865
|
+
const interfaceByName = new Map();
|
|
866
|
+
const visit = (node) => {
|
|
867
|
+
if (node.type === 'interface') {
|
|
868
|
+
const name = runtimeStringProp(node.props?.name);
|
|
869
|
+
if (name && !interfaceByName.has(name))
|
|
870
|
+
interfaceByName.set(name, node);
|
|
871
|
+
}
|
|
872
|
+
for (const child of node.children ?? [])
|
|
873
|
+
visit(child);
|
|
874
|
+
};
|
|
875
|
+
for (const node of isIRNodeArray(rootOrNodes) ? rootOrNodes : [rootOrNodes])
|
|
876
|
+
visit(node);
|
|
877
|
+
return interfaceByName;
|
|
878
|
+
}
|
|
879
|
+
function runtimeInterfaceMemberShapeKey(member) {
|
|
880
|
+
return `${member.static ? 'static' : 'instance'}:${member.name}`;
|
|
881
|
+
}
|
|
882
|
+
function classHasRuntimeProtocolMethod(klass, method, staticOnly = false) {
|
|
883
|
+
const member = findReadableClassShapeMember(klass, method.name, staticOnly);
|
|
884
|
+
if (member?.kind !== 'method')
|
|
885
|
+
return false;
|
|
886
|
+
if (runtimeBooleanProp(member.node.props?.private))
|
|
887
|
+
return false;
|
|
888
|
+
const params = runtimeParams(member.node);
|
|
889
|
+
if (params.length !== method.arity)
|
|
890
|
+
return false;
|
|
891
|
+
if (!runtimeProtocolParamTypesCompatible(params.map((param) => param.type ?? ''), method.paramTypes))
|
|
892
|
+
return false;
|
|
893
|
+
if (runtimeBooleanProp(member.node.props?.async) !== method.async)
|
|
894
|
+
return false;
|
|
895
|
+
if (runtimeBooleanProp(member.node.props?.stream) !== method.stream)
|
|
896
|
+
return false;
|
|
897
|
+
if (runtimeBooleanProp(member.node.props?.generator) !== method.generator)
|
|
898
|
+
return false;
|
|
899
|
+
const returns = runtimeStringProp(member.node.props?.returns);
|
|
900
|
+
return runtimeProtocolReturnTypesCompatible(returns, {
|
|
901
|
+
async: runtimeBooleanProp(member.node.props?.async),
|
|
902
|
+
stream: runtimeBooleanProp(member.node.props?.stream),
|
|
903
|
+
generator: runtimeBooleanProp(member.node.props?.generator),
|
|
904
|
+
}, method.returns, method);
|
|
905
|
+
}
|
|
906
|
+
function classHasRuntimeProtocolField(klass, field) {
|
|
907
|
+
const member = findReadableClassShapeMember(klass, field.name, true);
|
|
908
|
+
if (!member)
|
|
909
|
+
return field.optional;
|
|
910
|
+
if (member.kind !== 'field' && member.kind !== 'getter')
|
|
911
|
+
return false;
|
|
912
|
+
if (runtimeBooleanProp(member.node.props?.private))
|
|
913
|
+
return false;
|
|
914
|
+
const actualType = member.kind === 'getter'
|
|
915
|
+
? runtimeStringProp(member.node.props?.returns)
|
|
916
|
+
: runtimeStringProp(member.node.props?.type);
|
|
917
|
+
return !field.type || normalizeRuntimeProtocolType(actualType) === normalizeRuntimeProtocolType(field.type);
|
|
918
|
+
}
|
|
919
|
+
function runtimeProtocolParamTypesCompatible(actual, expected) {
|
|
920
|
+
return expected.every((type, index) => !type || normalizeRuntimeProtocolType(actual[index]) === normalizeRuntimeProtocolType(type));
|
|
921
|
+
}
|
|
922
|
+
function normalizeRuntimeProtocolType(type) {
|
|
923
|
+
return compactRuntimeProtocolTypeWhitespace(type);
|
|
924
|
+
}
|
|
925
|
+
function compactRuntimeProtocolTypeWhitespace(type) {
|
|
926
|
+
let out = '';
|
|
927
|
+
let quote = null;
|
|
928
|
+
for (let index = 0; index < (type ?? '').length; index += 1) {
|
|
929
|
+
const ch = (type ?? '')[index];
|
|
930
|
+
if (quote !== null) {
|
|
931
|
+
out += ch;
|
|
932
|
+
if (ch === '\\' && index + 1 < (type ?? '').length)
|
|
933
|
+
out += (type ?? '')[++index];
|
|
934
|
+
else if (ch === quote)
|
|
935
|
+
quote = null;
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
939
|
+
quote = ch;
|
|
940
|
+
out += ch;
|
|
941
|
+
continue;
|
|
942
|
+
}
|
|
943
|
+
if (!/\s/.test(ch))
|
|
944
|
+
out += ch;
|
|
945
|
+
}
|
|
946
|
+
return out;
|
|
947
|
+
}
|
|
948
|
+
function runtimeProtocolReturnTypesCompatible(actual, actualFlags, expected, expectedFlags) {
|
|
949
|
+
return (normalizeRuntimeProtocolReturnType(actual, actualFlags) ===
|
|
950
|
+
normalizeRuntimeProtocolReturnType(expected, expectedFlags));
|
|
951
|
+
}
|
|
952
|
+
function normalizeRuntimeProtocolReturnType(returns, flags) {
|
|
953
|
+
if (flags.stream) {
|
|
954
|
+
if (returns?.startsWith('AsyncGenerator<'))
|
|
955
|
+
return returns;
|
|
956
|
+
return `AsyncGenerator<${returns || 'unknown'}>`;
|
|
957
|
+
}
|
|
958
|
+
if (flags.generator) {
|
|
959
|
+
if (returns?.startsWith('Generator<') || returns?.startsWith('AsyncGenerator<'))
|
|
960
|
+
return returns;
|
|
961
|
+
return `${flags.async ? 'AsyncGenerator' : 'Generator'}<${returns || 'unknown'}>`;
|
|
962
|
+
}
|
|
963
|
+
return !returns || returns === 'void' ? 'void' : returns;
|
|
964
|
+
}
|
|
965
|
+
function classHierarchyFromBase(klass) {
|
|
966
|
+
const base = resolveBaseClass(klass);
|
|
967
|
+
return base ? [...classHierarchyFromBase(base), klass] : [klass];
|
|
968
|
+
}
|
|
969
|
+
function runtimeImportedProtocolNames(rootOrNodes) {
|
|
970
|
+
const names = new Set();
|
|
971
|
+
const visit = (node) => {
|
|
972
|
+
if (node.type === 'import') {
|
|
973
|
+
for (const name of runtimeImportLocalNames(node))
|
|
974
|
+
names.add(name);
|
|
975
|
+
}
|
|
976
|
+
if (node.type === 'use') {
|
|
977
|
+
for (const child of node.children ?? []) {
|
|
978
|
+
if (child.type !== 'from')
|
|
979
|
+
continue;
|
|
980
|
+
const kind = runtimeStringProp(child.props?.kind);
|
|
981
|
+
if (kind && kind !== 'interface' && kind !== 'type')
|
|
982
|
+
continue;
|
|
983
|
+
const localName = runtimeStringProp(child.props?.as) ?? runtimeStringProp(child.props?.name);
|
|
984
|
+
if (localName)
|
|
985
|
+
names.add(localName);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
for (const child of node.children ?? [])
|
|
989
|
+
visit(child);
|
|
990
|
+
};
|
|
991
|
+
for (const node of isIRNodeArray(rootOrNodes) ? rootOrNodes : [rootOrNodes])
|
|
992
|
+
visit(node);
|
|
993
|
+
return names;
|
|
994
|
+
}
|
|
995
|
+
function runtimeImportLocalNames(node) {
|
|
996
|
+
const names = [];
|
|
997
|
+
const props = node.props ?? {};
|
|
998
|
+
const defaultName = runtimeStringProp(props.default);
|
|
999
|
+
if (defaultName && defaultName !== 'true')
|
|
1000
|
+
names.push(defaultName);
|
|
1001
|
+
const rawNames = runtimeStringProp(props.names);
|
|
1002
|
+
if (rawNames) {
|
|
1003
|
+
for (const raw of rawNames.split(',')) {
|
|
1004
|
+
const name = raw.trim();
|
|
1005
|
+
const aliasMatch = /^([A-Za-z_$][\w$]*)(?:\s+as\s+([A-Za-z_$][\w$]*))?$/u.exec(name);
|
|
1006
|
+
if (aliasMatch)
|
|
1007
|
+
names.push(aliasMatch[2] ?? aliasMatch[1]);
|
|
1008
|
+
else if (/^[A-Za-z_$][\w$]*$/u.test(name))
|
|
1009
|
+
names.push(name);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
return names;
|
|
1013
|
+
}
|
|
1014
|
+
function initializeClassLayer(instance, klass, args, receivesConstructorArgs) {
|
|
1015
|
+
if (instance.initializedClasses.has(klass.name)) {
|
|
1016
|
+
throw new Error(`KERN core runtime class already initialized: ${klass.name}`);
|
|
1017
|
+
}
|
|
1018
|
+
const base = resolveBaseClass(klass);
|
|
1019
|
+
const ctor = firstRuntimeChild(klass.node, 'constructor');
|
|
1020
|
+
if (base && !ctor)
|
|
1021
|
+
initializeClassLayer(instance, base, [], false);
|
|
1022
|
+
if (!base || !ctor)
|
|
1023
|
+
initializeClassFields(instance, klass);
|
|
1024
|
+
if (!ctor) {
|
|
1025
|
+
if (receivesConstructorArgs && args.length > 0) {
|
|
1026
|
+
throw new Error(`KERN core runtime class ${klass.name} has no constructor.`);
|
|
1027
|
+
}
|
|
1028
|
+
instance.initializedClasses.add(klass.name);
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
if (base && !hasDirectSuperCtorCall(ctor)) {
|
|
1032
|
+
// Implicit-super mode (KERN Option C): a derived constructor that omits a
|
|
1033
|
+
// direct super(...) gets base init injected FIRST, then its own field
|
|
1034
|
+
// defaults, then its body — identical to what both codegen targets emit, so
|
|
1035
|
+
// the interpreter and generated TS/Python agree. The frame starts with
|
|
1036
|
+
// superCalled=true so this/super access inside the body is unguarded; an
|
|
1037
|
+
// unexpected late super(...) would still trip the double-init guard. The same
|
|
1038
|
+
// `hasDirectSuperCtorCall` predicate decides this mode in the validator and
|
|
1039
|
+
// both codegens, so all four layers classify the constructor identically.
|
|
1040
|
+
initializeClassLayer(instance, base, [], false);
|
|
1041
|
+
initializeClassFields(instance, klass);
|
|
1042
|
+
withConstructionFrame(instance, klass, true, () => {
|
|
1043
|
+
callClassMemberBody(ctor, klass, instance, receivesConstructorArgs ? args : []).value;
|
|
1044
|
+
});
|
|
1045
|
+
instance.initializedClasses.add(klass.name);
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
if (base) {
|
|
1049
|
+
withConstructionFrame(instance, klass, false, () => {
|
|
1050
|
+
callClassMemberBody(ctor, klass, instance, receivesConstructorArgs ? args : []).value;
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
else {
|
|
1054
|
+
callClassMemberBody(ctor, klass, instance, receivesConstructorArgs ? args : []).value;
|
|
1055
|
+
}
|
|
1056
|
+
if (base && !instance.initializedClasses.has(base.name)) {
|
|
1057
|
+
throw new Error(`KERN core runtime constructor ${klass.name} must call super(...).`);
|
|
1058
|
+
}
|
|
1059
|
+
instance.initializedClasses.add(klass.name);
|
|
1060
|
+
}
|
|
1061
|
+
function initializeClassFields(instance, klass) {
|
|
1062
|
+
for (const field of runtimeChildNodes(klass.node, 'field')) {
|
|
1063
|
+
const name = requiredString(field.props?.name, 'field name=');
|
|
1064
|
+
if (field.props?.static === true || field.props?.static === 'true')
|
|
1065
|
+
continue;
|
|
1066
|
+
const value = Object.hasOwn(field.props ?? {}, 'value') || Object.hasOwn(field.props ?? {}, 'default')
|
|
1067
|
+
? evalCoreExpression(runtimeFieldInitializerExpr(field), classThisEnv(klass, instance))
|
|
1068
|
+
: kUndefined();
|
|
1069
|
+
instance.fields[name] = value;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
function runtimeFieldInitializerExpr(node) {
|
|
1073
|
+
const propName = Object.hasOwn(node.props ?? {}, 'value') ? 'value' : 'default';
|
|
1074
|
+
const rawValue = propName === 'value' ? node.props?.value : node.props?.default;
|
|
1075
|
+
if (typeof rawValue === 'string' && (node.__quotedProps ?? []).includes(propName))
|
|
1076
|
+
return JSON.stringify(rawValue);
|
|
1077
|
+
return unwrapExpr(rawValue, 'field value=');
|
|
1078
|
+
}
|
|
1079
|
+
function evalInstanceMember(object, property) {
|
|
1080
|
+
guardConstructedInstanceAccess(object);
|
|
1081
|
+
const member = findReadableClassShapeMember(object.classValue, property, false);
|
|
1082
|
+
if (!member)
|
|
1083
|
+
throw new Error(`KERN core runtime unknown instance property: ${object.classValue.name}.${property}.`);
|
|
1084
|
+
switch (member.kind) {
|
|
1085
|
+
case 'field':
|
|
1086
|
+
return object.fields[property] ?? kUndefined();
|
|
1087
|
+
case 'getter':
|
|
1088
|
+
return callClassMemberBody(member.node, member.owner, object, []).value;
|
|
1089
|
+
case 'method':
|
|
1090
|
+
return brandValue({
|
|
1091
|
+
kind: 'bound-method',
|
|
1092
|
+
name: `${object.classValue.name}.${property}`,
|
|
1093
|
+
receiver: object,
|
|
1094
|
+
methodNode: member.node,
|
|
1095
|
+
ownerClass: member.owner,
|
|
1096
|
+
});
|
|
1097
|
+
case 'setter':
|
|
1098
|
+
throw new Error(`KERN core runtime cannot read setter-only property: ${property}.`);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
function evalSuperMember(object, property) {
|
|
1102
|
+
const base = resolveBaseClass(object.ownerClass);
|
|
1103
|
+
if (!base)
|
|
1104
|
+
return kUndefined();
|
|
1105
|
+
if (object.receiver.kind === 'class')
|
|
1106
|
+
return evalClassMemberFrom(base, property, object.receiver);
|
|
1107
|
+
guardConstructedSuperAccess(object.receiver);
|
|
1108
|
+
const member = findReadableClassShapeMember(base, property, false);
|
|
1109
|
+
if (!member)
|
|
1110
|
+
throw new Error(`KERN core runtime unknown super property: ${object.ownerClass.name}.${property}.`);
|
|
1111
|
+
switch (member.kind) {
|
|
1112
|
+
case 'field':
|
|
1113
|
+
return object.receiver.fields[property] ?? kUndefined();
|
|
1114
|
+
case 'getter':
|
|
1115
|
+
return callClassMemberBody(member.node, member.owner, object.receiver, []).value;
|
|
1116
|
+
case 'method':
|
|
1117
|
+
return brandValue({
|
|
1118
|
+
kind: 'bound-method',
|
|
1119
|
+
name: `${base.name}.${property}`,
|
|
1120
|
+
receiver: object.receiver,
|
|
1121
|
+
methodNode: member.node,
|
|
1122
|
+
ownerClass: member.owner,
|
|
1123
|
+
});
|
|
1124
|
+
case 'setter':
|
|
1125
|
+
throw new Error(`KERN core runtime cannot read setter-only super property: ${property}.`);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
function evalClassMember(object, property) {
|
|
1129
|
+
return evalClassMemberFrom(object, property, object);
|
|
1130
|
+
}
|
|
1131
|
+
function evalClassMemberFrom(owner, property, receiver) {
|
|
1132
|
+
const member = findReadableClassShapeMember(owner, property, true);
|
|
1133
|
+
if (!member)
|
|
1134
|
+
throw new Error(`KERN core runtime unknown static property: ${receiver.name}.${property}.`);
|
|
1135
|
+
switch (member.kind) {
|
|
1136
|
+
case 'field':
|
|
1137
|
+
return member.owner === receiver
|
|
1138
|
+
? (receiver.staticFields[property] ?? kUndefined())
|
|
1139
|
+
: evalClassStaticField(member.owner, receiver, property);
|
|
1140
|
+
case 'getter':
|
|
1141
|
+
return callStaticClassMemberBody(member.node, member.owner, receiver, []).value;
|
|
1142
|
+
case 'method':
|
|
1143
|
+
return brandValue({
|
|
1144
|
+
kind: 'builtin',
|
|
1145
|
+
name: `${receiver.name}.${property}`,
|
|
1146
|
+
call: (args) => callStaticClassMemberBody(member.node, member.owner, receiver, args).value,
|
|
1147
|
+
});
|
|
1148
|
+
case 'setter':
|
|
1149
|
+
throw new Error(`KERN core runtime cannot read setter-only static property: ${property}.`);
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
function assignInstanceMember(object, property, value) {
|
|
1153
|
+
guardConstructedInstanceAccess(object);
|
|
1154
|
+
const member = findWritableClassShapeMember(object.classValue, property, false);
|
|
1155
|
+
if (!member)
|
|
1156
|
+
throw new Error(`KERN core runtime cannot assign undeclared instance property: ${property}.`);
|
|
1157
|
+
switch (member.kind) {
|
|
1158
|
+
case 'field':
|
|
1159
|
+
object.fields[property] = value;
|
|
1160
|
+
return;
|
|
1161
|
+
case 'setter':
|
|
1162
|
+
callSetterBody(object, member.node, member.owner, property, value);
|
|
1163
|
+
return;
|
|
1164
|
+
case 'getter':
|
|
1165
|
+
throw new Error(`KERN core runtime cannot assign getter-only property: ${property}.`);
|
|
1166
|
+
case 'method':
|
|
1167
|
+
throw new Error(`KERN core runtime cannot assign method property: ${property}.`);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
function assignSuperMember(object, property, value) {
|
|
1171
|
+
const base = resolveBaseClass(object.ownerClass);
|
|
1172
|
+
if (!base)
|
|
1173
|
+
throw new Error(`KERN core runtime class ${object.ownerClass.name} has no base class.`);
|
|
1174
|
+
if (object.receiver.kind === 'class') {
|
|
1175
|
+
assignClassMemberFrom(base, object.receiver, property, value);
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
guardConstructedSuperAccess(object.receiver);
|
|
1179
|
+
const member = findWritableClassShapeMember(base, property, false);
|
|
1180
|
+
if (!member)
|
|
1181
|
+
throw new Error(`KERN core runtime cannot assign undeclared super property: ${property}.`);
|
|
1182
|
+
switch (member.kind) {
|
|
1183
|
+
case 'field':
|
|
1184
|
+
object.receiver.fields[property] = value;
|
|
1185
|
+
return;
|
|
1186
|
+
case 'setter':
|
|
1187
|
+
callSetterBody(object.receiver, member.node, member.owner, property, value);
|
|
1188
|
+
return;
|
|
1189
|
+
case 'getter':
|
|
1190
|
+
throw new Error(`KERN core runtime cannot assign getter-only property: ${property}.`);
|
|
1191
|
+
case 'method':
|
|
1192
|
+
throw new Error(`KERN core runtime cannot assign method property: ${property}.`);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
function assignClassMember(object, property, value) {
|
|
1196
|
+
assignClassMemberFrom(object, object, property, value);
|
|
1197
|
+
}
|
|
1198
|
+
function assignClassMemberFrom(owner, receiver, property, value) {
|
|
1199
|
+
const member = findWritableClassShapeMember(owner, property, true);
|
|
1200
|
+
if (!member)
|
|
1201
|
+
throw new Error(`KERN core runtime cannot assign undeclared static property: ${property}.`);
|
|
1202
|
+
switch (member.kind) {
|
|
1203
|
+
case 'field':
|
|
1204
|
+
receiver.staticFields[property] = value;
|
|
1205
|
+
return;
|
|
1206
|
+
case 'setter':
|
|
1207
|
+
callStaticSetterBody(receiver, member.node, member.owner, property, value);
|
|
1208
|
+
return;
|
|
1209
|
+
case 'getter':
|
|
1210
|
+
throw new Error(`KERN core runtime cannot assign getter-only static property: ${property}.`);
|
|
1211
|
+
case 'method':
|
|
1212
|
+
throw new Error(`KERN core runtime cannot assign static method property: ${property}.`);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
function callSetterBody(receiver, setterNode, ownerClass, property, value) {
|
|
1216
|
+
const key = `${ownerClass.name}.${property}`;
|
|
1217
|
+
const activeSetters = ACTIVE_INSTANCE_SETTERS.get(receiver) ?? new Set();
|
|
1218
|
+
if (activeSetters.has(key)) {
|
|
1219
|
+
throw new Error(`KERN core runtime recursive setter assignment: ${key}.`);
|
|
1220
|
+
}
|
|
1221
|
+
activeSetters.add(key);
|
|
1222
|
+
ACTIVE_INSTANCE_SETTERS.set(receiver, activeSetters);
|
|
1223
|
+
try {
|
|
1224
|
+
callClassMemberBody(setterNode, ownerClass, receiver, [value]);
|
|
1225
|
+
}
|
|
1226
|
+
finally {
|
|
1227
|
+
activeSetters.delete(key);
|
|
1228
|
+
if (activeSetters.size === 0)
|
|
1229
|
+
ACTIVE_INSTANCE_SETTERS.delete(receiver);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
function callStaticSetterBody(receiver, setterNode, ownerClass, property, value) {
|
|
1233
|
+
const key = `${ownerClass.name}.${property}`;
|
|
1234
|
+
const activeSetters = ACTIVE_CLASS_SETTERS.get(receiver) ?? new Set();
|
|
1235
|
+
if (activeSetters.has(key)) {
|
|
1236
|
+
throw new Error(`KERN core runtime recursive static setter assignment: ${key}.`);
|
|
1237
|
+
}
|
|
1238
|
+
activeSetters.add(key);
|
|
1239
|
+
ACTIVE_CLASS_SETTERS.set(receiver, activeSetters);
|
|
1240
|
+
try {
|
|
1241
|
+
callStaticClassMemberBody(setterNode, ownerClass, receiver, [value]);
|
|
1242
|
+
}
|
|
1243
|
+
finally {
|
|
1244
|
+
activeSetters.delete(key);
|
|
1245
|
+
if (activeSetters.size === 0)
|
|
1246
|
+
ACTIVE_CLASS_SETTERS.delete(receiver);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
function callBoundMethodValue(method, args) {
|
|
1250
|
+
return callClassMemberBody(method.methodNode, method.ownerClass, method.receiver, args);
|
|
1251
|
+
}
|
|
1252
|
+
function callSuperConstructor(value, args) {
|
|
1253
|
+
if (value.mode !== 'constructor') {
|
|
1254
|
+
throw new Error('KERN core runtime super(...) is only valid inside a constructor.');
|
|
1255
|
+
}
|
|
1256
|
+
if (value.receiver.kind !== 'instance') {
|
|
1257
|
+
throw new Error('KERN core runtime super(...) requires an instance receiver.');
|
|
1258
|
+
}
|
|
1259
|
+
const base = resolveBaseClass(value.ownerClass);
|
|
1260
|
+
if (!base)
|
|
1261
|
+
throw new Error(`KERN core runtime class ${value.ownerClass.name} has no base class.`);
|
|
1262
|
+
const frame = activeConstructionFrame(value.receiver);
|
|
1263
|
+
if (!frame || frame.ownerClass !== value.ownerClass) {
|
|
1264
|
+
throw new Error(`KERN core runtime super(...) is not active for constructor ${value.ownerClass.name}.`);
|
|
1265
|
+
}
|
|
1266
|
+
if (frame.superCalled || value.receiver.initializedClasses.has(base.name)) {
|
|
1267
|
+
throw new Error(`KERN core runtime constructor ${value.ownerClass.name} called super(...) more than once.`);
|
|
1268
|
+
}
|
|
1269
|
+
frame.superCalled = true;
|
|
1270
|
+
initializeClassLayer(value.receiver, base, args, true);
|
|
1271
|
+
initializeClassFields(value.receiver, value.ownerClass);
|
|
1272
|
+
return value.receiver;
|
|
1273
|
+
}
|
|
1274
|
+
function withConstructionFrame(instance, ownerClass, initialSuperCalled, run) {
|
|
1275
|
+
const stack = ACTIVE_CONSTRUCTORS.get(instance) ?? [];
|
|
1276
|
+
const frame = { ownerClass, superCalled: initialSuperCalled };
|
|
1277
|
+
stack.push(frame);
|
|
1278
|
+
ACTIVE_CONSTRUCTORS.set(instance, stack);
|
|
1279
|
+
try {
|
|
1280
|
+
run();
|
|
1281
|
+
}
|
|
1282
|
+
finally {
|
|
1283
|
+
stack.pop();
|
|
1284
|
+
if (stack.length === 0)
|
|
1285
|
+
ACTIVE_CONSTRUCTORS.delete(instance);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
function activeConstructionFrame(instance) {
|
|
1289
|
+
const stack = ACTIVE_CONSTRUCTORS.get(instance);
|
|
1290
|
+
return stack?.[stack.length - 1];
|
|
1291
|
+
}
|
|
1292
|
+
function guardConstructedInstanceAccess(instance) {
|
|
1293
|
+
const frame = activeConstructionFrame(instance);
|
|
1294
|
+
if (!frame || frame.superCalled)
|
|
1295
|
+
return;
|
|
1296
|
+
if (!resolveBaseClass(frame.ownerClass))
|
|
1297
|
+
return;
|
|
1298
|
+
throw new Error(`KERN core runtime cannot access this before super(...) in ${frame.ownerClass.name}.`);
|
|
1299
|
+
}
|
|
1300
|
+
function guardConstructedSuperAccess(instance) {
|
|
1301
|
+
const frame = activeConstructionFrame(instance);
|
|
1302
|
+
if (!frame || frame.superCalled)
|
|
1303
|
+
return;
|
|
1304
|
+
if (!resolveBaseClass(frame.ownerClass))
|
|
1305
|
+
return;
|
|
1306
|
+
throw new Error(`KERN core runtime cannot access super members before super(...) in ${frame.ownerClass.name}.`);
|
|
1307
|
+
}
|
|
1308
|
+
function callClassMemberBody(memberNode, ownerClass, receiver, args) {
|
|
1309
|
+
const callEnv = ownerClass.env.child();
|
|
1310
|
+
if (receiver) {
|
|
1311
|
+
callEnv.define('this', receiver);
|
|
1312
|
+
if (resolveBaseClass(ownerClass)) {
|
|
1313
|
+
callEnv.define('super', brandValue({
|
|
1314
|
+
kind: 'super',
|
|
1315
|
+
receiver,
|
|
1316
|
+
ownerClass,
|
|
1317
|
+
mode: memberNode.type === 'constructor' ? 'constructor' : 'method',
|
|
1318
|
+
}));
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
const params = runtimeParams(memberNode);
|
|
1322
|
+
validateRuntimeArgs(`${ownerClass.name}.${memberNode.type}`, params, args);
|
|
1323
|
+
params.forEach((param, index) => {
|
|
1324
|
+
const provided = args[index];
|
|
1325
|
+
const value = provided === undefined || (provided.kind === 'undefined' && param.defaultExpr)
|
|
1326
|
+
? param.defaultExpr
|
|
1327
|
+
? evalCoreExpression(param.defaultExpr, callEnv)
|
|
1328
|
+
: kUndefined()
|
|
1329
|
+
: provided;
|
|
1330
|
+
callEnv.define(param.name, value);
|
|
1331
|
+
});
|
|
1332
|
+
const completion = executeSequence(runtimeFunctionBody(memberNode), callEnv);
|
|
1333
|
+
return { value: completion.value, env: callEnv };
|
|
1334
|
+
}
|
|
1335
|
+
function callStaticClassMemberBody(memberNode, ownerClass, receiver, args) {
|
|
1336
|
+
const callEnv = ownerClass.env.child();
|
|
1337
|
+
callEnv.define('this', receiver);
|
|
1338
|
+
if (resolveBaseClass(ownerClass)) {
|
|
1339
|
+
callEnv.define('super', brandValue({
|
|
1340
|
+
kind: 'super',
|
|
1341
|
+
receiver,
|
|
1342
|
+
ownerClass,
|
|
1343
|
+
mode: 'static',
|
|
1344
|
+
}));
|
|
1345
|
+
}
|
|
1346
|
+
const params = runtimeParams(memberNode);
|
|
1347
|
+
validateRuntimeArgs(`${ownerClass.name}.${memberNode.type}`, params, args);
|
|
1348
|
+
params.forEach((param, index) => {
|
|
1349
|
+
const provided = args[index];
|
|
1350
|
+
const value = provided === undefined || (provided.kind === 'undefined' && param.defaultExpr)
|
|
1351
|
+
? param.defaultExpr
|
|
1352
|
+
? evalCoreExpression(param.defaultExpr, callEnv)
|
|
1353
|
+
: kUndefined()
|
|
1354
|
+
: provided;
|
|
1355
|
+
callEnv.define(param.name, value);
|
|
1356
|
+
});
|
|
1357
|
+
const completion = executeSequence(runtimeFunctionBody(memberNode), callEnv);
|
|
1358
|
+
return { value: completion.value, env: callEnv };
|
|
1359
|
+
}
|
|
1360
|
+
function findOwnClassMember(klass, type, name, staticOnly = false) {
|
|
1361
|
+
for (const child of klass.node.children ?? []) {
|
|
1362
|
+
if (child.type !== type || child.props?.name !== name)
|
|
1363
|
+
continue;
|
|
1364
|
+
const isStatic = child.props?.static === true || child.props?.static === 'true';
|
|
1365
|
+
if (staticOnly !== isStatic)
|
|
1366
|
+
continue;
|
|
1367
|
+
return { node: child, owner: klass };
|
|
1368
|
+
}
|
|
1369
|
+
return undefined;
|
|
1370
|
+
}
|
|
1371
|
+
function findReadableClassShapeMember(klass, name, staticOnly) {
|
|
1372
|
+
return findClassShapeMember(klass, name, staticOnly, ['field', 'getter', 'method', 'setter']);
|
|
1373
|
+
}
|
|
1374
|
+
function findWritableClassShapeMember(klass, name, staticOnly) {
|
|
1375
|
+
return findClassShapeMember(klass, name, staticOnly, ['field', 'setter', 'getter', 'method']);
|
|
1376
|
+
}
|
|
1377
|
+
function findClassShapeMember(klass, name, staticOnly, precedence) {
|
|
1378
|
+
for (const kind of precedence) {
|
|
1379
|
+
const member = kind === 'field'
|
|
1380
|
+
? findOwnClassField(klass, name, staticOnly)
|
|
1381
|
+
: findOwnClassMethodShapeMember(klass, kind, name, staticOnly);
|
|
1382
|
+
if (member)
|
|
1383
|
+
return member;
|
|
1384
|
+
}
|
|
1385
|
+
const base = resolveBaseClass(klass);
|
|
1386
|
+
return base ? findClassShapeMember(base, name, staticOnly, precedence) : undefined;
|
|
1387
|
+
}
|
|
1388
|
+
function findOwnClassMethodShapeMember(klass, kind, name, staticOnly) {
|
|
1389
|
+
const member = findOwnClassMember(klass, kind, name, staticOnly);
|
|
1390
|
+
return member ? { kind, node: member.node, owner: member.owner } : undefined;
|
|
1391
|
+
}
|
|
1392
|
+
function findOwnClassField(klass, name, staticOnly) {
|
|
1393
|
+
for (const child of klass.node.children ?? []) {
|
|
1394
|
+
if (child.type !== 'field' || child.props?.name !== name)
|
|
1395
|
+
continue;
|
|
1396
|
+
const isStatic = child.props?.static === true || child.props?.static === 'true';
|
|
1397
|
+
if (staticOnly !== isStatic)
|
|
1398
|
+
continue;
|
|
1399
|
+
return { kind: 'field', node: child, owner: klass };
|
|
1400
|
+
}
|
|
1401
|
+
return undefined;
|
|
1402
|
+
}
|
|
1403
|
+
function evalClassStaticField(owner, receiver, property) {
|
|
1404
|
+
if (Object.hasOwn(receiver.staticFields, property))
|
|
1405
|
+
return receiver.staticFields[property] ?? kUndefined();
|
|
1406
|
+
return owner.staticFields[property] ?? kUndefined();
|
|
1407
|
+
}
|
|
1408
|
+
function resolveBaseClass(klass) {
|
|
1409
|
+
const baseName = classBaseName(klass.node.props?.extends);
|
|
1410
|
+
if (!baseName)
|
|
1411
|
+
return undefined;
|
|
1412
|
+
const base = klass.env.lookup(baseName);
|
|
1413
|
+
if (base.kind !== 'class')
|
|
1414
|
+
throw new Error(`KERN core runtime base class is not a class: ${baseName}`);
|
|
1415
|
+
return base;
|
|
1416
|
+
}
|
|
1417
|
+
function classBaseName(value) {
|
|
1418
|
+
if (typeof value !== 'string' || !value.trim())
|
|
1419
|
+
return undefined;
|
|
1420
|
+
const match = /^([A-Za-z_$][\w$]*)/.exec(value.trim());
|
|
1421
|
+
return match?.[1];
|
|
1422
|
+
}
|
|
1423
|
+
function runtimeStringProp(value) {
|
|
1424
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
1425
|
+
}
|
|
1426
|
+
function runtimeBooleanProp(value) {
|
|
1427
|
+
return value === true || (typeof value === 'string' && value.trim().toLowerCase() === 'true');
|
|
1428
|
+
}
|
|
1429
|
+
function runtimeClassReferenceNames(value) {
|
|
1430
|
+
if (typeof value !== 'string' || !value.trim())
|
|
1431
|
+
return [];
|
|
1432
|
+
const parts = splitRuntimeClassReferenceList(value);
|
|
1433
|
+
const names = new Set();
|
|
1434
|
+
for (const part of parts) {
|
|
1435
|
+
const name = runtimeClassReferenceName(part);
|
|
1436
|
+
if (!name)
|
|
1437
|
+
throw new Error(`implements= contains an invalid reference: ${part}.`);
|
|
1438
|
+
names.add(name);
|
|
1439
|
+
}
|
|
1440
|
+
return [...names];
|
|
1441
|
+
}
|
|
1442
|
+
function runtimeClassReferenceName(value) {
|
|
1443
|
+
const trimmed = value.trim();
|
|
1444
|
+
const match = /^([A-Za-z_$][\w$]*)(?:\s*<[\s\S]*>)?$/u.exec(trimmed);
|
|
1445
|
+
return match?.[1];
|
|
1446
|
+
}
|
|
1447
|
+
function splitRuntimeClassReferenceList(raw) {
|
|
1448
|
+
const out = [];
|
|
1449
|
+
let current = '';
|
|
1450
|
+
let depth = 0;
|
|
1451
|
+
let angleDepth = 0;
|
|
1452
|
+
let quote = null;
|
|
1453
|
+
for (let index = 0; index < raw.length; index += 1) {
|
|
1454
|
+
const ch = raw[index];
|
|
1455
|
+
if (quote !== null) {
|
|
1456
|
+
current += ch;
|
|
1457
|
+
if (ch === '\\' && index + 1 < raw.length)
|
|
1458
|
+
current += raw[++index];
|
|
1459
|
+
else if (ch === quote)
|
|
1460
|
+
quote = null;
|
|
1461
|
+
continue;
|
|
1462
|
+
}
|
|
1463
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
1464
|
+
quote = ch;
|
|
1465
|
+
current += ch;
|
|
1466
|
+
continue;
|
|
1467
|
+
}
|
|
1468
|
+
if (ch === '(' || ch === '[' || ch === '{')
|
|
1469
|
+
depth += 1;
|
|
1470
|
+
else if (ch === ')' || ch === ']' || ch === '}')
|
|
1471
|
+
depth -= 1;
|
|
1472
|
+
else if (ch === '<')
|
|
1473
|
+
angleDepth += 1;
|
|
1474
|
+
else if (ch === '>' && angleDepth > 0)
|
|
1475
|
+
angleDepth -= 1;
|
|
1476
|
+
if (depth < 0 || angleDepth < 0)
|
|
1477
|
+
throw new Error('implements= has unbalanced delimiters.');
|
|
1478
|
+
if (ch === ',' && depth === 0 && angleDepth === 0) {
|
|
1479
|
+
const part = current.trim();
|
|
1480
|
+
if (part.length === 0)
|
|
1481
|
+
throw new Error('implements= contains an empty reference.');
|
|
1482
|
+
out.push(part);
|
|
1483
|
+
current = '';
|
|
1484
|
+
continue;
|
|
1485
|
+
}
|
|
1486
|
+
current += ch;
|
|
1487
|
+
}
|
|
1488
|
+
if (quote !== null || depth !== 0 || angleDepth !== 0)
|
|
1489
|
+
throw new Error('implements= has unbalanced delimiters.');
|
|
1490
|
+
const tail = current.trim();
|
|
1491
|
+
if (tail.length === 0 && raw.trim().endsWith(','))
|
|
1492
|
+
throw new Error('implements= contains an empty reference.');
|
|
1493
|
+
if (tail.length > 0)
|
|
1494
|
+
out.push(tail);
|
|
1495
|
+
return out;
|
|
1496
|
+
}
|
|
1497
|
+
function runtimeAngleClosesBeforeNextTopLevelComma(raw, start) {
|
|
1498
|
+
let depth = 0;
|
|
1499
|
+
let quote = null;
|
|
1500
|
+
for (let index = start; index < raw.length; index += 1) {
|
|
1501
|
+
const ch = raw[index];
|
|
1502
|
+
if (quote !== null) {
|
|
1503
|
+
if (ch === '\\' && index + 1 < raw.length)
|
|
1504
|
+
index += 1;
|
|
1505
|
+
else if (ch === quote)
|
|
1506
|
+
quote = null;
|
|
1507
|
+
continue;
|
|
1508
|
+
}
|
|
1509
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
1510
|
+
quote = ch;
|
|
1511
|
+
continue;
|
|
1512
|
+
}
|
|
1513
|
+
if (ch === '(' || ch === '[' || ch === '{')
|
|
1514
|
+
depth += 1;
|
|
1515
|
+
else if ((ch === ')' || ch === ']' || ch === '}') && depth > 0)
|
|
1516
|
+
depth -= 1;
|
|
1517
|
+
else if (ch === '>' && depth === 0)
|
|
1518
|
+
return true;
|
|
1519
|
+
else if (ch === ',' && depth === 0)
|
|
1520
|
+
return false;
|
|
1521
|
+
}
|
|
1522
|
+
return false;
|
|
1523
|
+
}
|
|
1524
|
+
function classThisEnv(klass, receiver) {
|
|
1525
|
+
const env = klass.env.child();
|
|
1526
|
+
env.define('this', receiver);
|
|
1527
|
+
return env;
|
|
1528
|
+
}
|
|
1529
|
+
function classStaticEnv(klass) {
|
|
1530
|
+
const env = klass.env.child();
|
|
1531
|
+
env.define('this', klass);
|
|
1532
|
+
if (resolveBaseClass(klass)) {
|
|
1533
|
+
env.define('super', brandValue({
|
|
1534
|
+
kind: 'super',
|
|
1535
|
+
receiver: klass,
|
|
1536
|
+
ownerClass: klass,
|
|
1537
|
+
mode: 'static',
|
|
1538
|
+
}));
|
|
1539
|
+
}
|
|
1540
|
+
return env;
|
|
1541
|
+
}
|
|
1542
|
+
function makeFunction(node, env) {
|
|
1543
|
+
return brandValue({
|
|
1544
|
+
kind: 'function',
|
|
1545
|
+
name: requiredString(node.props?.name, 'fn name='),
|
|
1546
|
+
params: runtimeParams(node),
|
|
1547
|
+
body: runtimeFunctionBody(node),
|
|
1548
|
+
env,
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
function callFunctionValue(fn, args) {
|
|
1552
|
+
const callEnv = fn.env.child();
|
|
1553
|
+
validateRuntimeArgs(fn.name ?? 'anonymous function', fn.params, args);
|
|
1554
|
+
fn.params.forEach((param, index) => {
|
|
1555
|
+
const provided = args[index];
|
|
1556
|
+
const value = provided === undefined || (provided.kind === 'undefined' && param.defaultExpr)
|
|
1557
|
+
? param.defaultExpr
|
|
1558
|
+
? evalCoreExpression(param.defaultExpr, callEnv)
|
|
1559
|
+
: kUndefined()
|
|
1560
|
+
: provided;
|
|
1561
|
+
callEnv.define(param.name, value);
|
|
1562
|
+
});
|
|
1563
|
+
const completion = executeSequence(fn.body, callEnv);
|
|
1564
|
+
return { value: completion.value, env: callEnv };
|
|
1565
|
+
}
|
|
1566
|
+
function validateRuntimeArgs(label, params, args) {
|
|
1567
|
+
if (args.length > params.length) {
|
|
1568
|
+
throw new Error(`KERN core runtime ${label} received too many arguments.`);
|
|
1569
|
+
}
|
|
1570
|
+
params.forEach((param, index) => {
|
|
1571
|
+
if (index >= args.length && !param.defaultExpr) {
|
|
1572
|
+
throw new Error(`KERN core runtime ${label} missing required argument: ${param.name}.`);
|
|
1573
|
+
}
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
function executeAssign(node, env) {
|
|
1577
|
+
const target = requiredString(node.props?.target, 'assign target=');
|
|
1578
|
+
if (Object.hasOwn(node.props ?? {}, 'op') && node.props?.op !== '=') {
|
|
1579
|
+
throw new Error('KERN core runtime assign supports only direct assignment in this slice.');
|
|
1580
|
+
}
|
|
1581
|
+
const value = evalCoreExpression(unwrapExpr(node.props?.value, 'assign value='), env);
|
|
1582
|
+
assignRuntimeTarget(target, value, env);
|
|
1583
|
+
}
|
|
1584
|
+
function assignRuntimeTarget(target, value, env) {
|
|
1585
|
+
const parsed = parseExpression(target);
|
|
1586
|
+
if (parsed.kind === 'ident') {
|
|
1587
|
+
env.assign(parsed.name, value);
|
|
1588
|
+
return;
|
|
1589
|
+
}
|
|
1590
|
+
if (parsed.kind === 'member') {
|
|
1591
|
+
const object = evalValueIR(parsed.object, env);
|
|
1592
|
+
if (object.kind === 'instance') {
|
|
1593
|
+
assignInstanceMember(object, parsed.property, value);
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
if (object.kind === 'super') {
|
|
1597
|
+
assignSuperMember(object, parsed.property, value);
|
|
1598
|
+
return;
|
|
1599
|
+
}
|
|
1600
|
+
if (object.kind === 'record') {
|
|
1601
|
+
object.entries[parsed.property] = value;
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
if (object.kind === 'class') {
|
|
1605
|
+
assignClassMember(object, parsed.property, value);
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
throw new Error(`KERN core runtime cannot assign member on ${object.kind}.`);
|
|
1609
|
+
}
|
|
1610
|
+
if (parsed.kind === 'index') {
|
|
1611
|
+
const object = evalValueIR(parsed.object, env);
|
|
1612
|
+
const index = evalValueIR(parsed.index, env);
|
|
1613
|
+
if (object.kind === 'array') {
|
|
1614
|
+
if (index.kind !== 'number' || !Number.isInteger(index.value) || index.value < 0) {
|
|
1615
|
+
throw new Error('KERN core runtime array assignment index must be a non-negative integer.');
|
|
1616
|
+
}
|
|
1617
|
+
object.items[index.value] = value;
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
if (object.kind === 'record') {
|
|
1621
|
+
if (index.kind !== 'string')
|
|
1622
|
+
throw new Error('KERN core runtime record assignment key must be a string.');
|
|
1623
|
+
object.entries[index.value] = value;
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
throw new Error(`KERN core runtime cannot assign index on ${object.kind}.`);
|
|
1627
|
+
}
|
|
1628
|
+
throw new Error('KERN core runtime assign target must be an identifier, member, or index expression.');
|
|
1629
|
+
}
|
|
1630
|
+
function runtimeFunctionBody(node) {
|
|
1631
|
+
const handler = node.children?.find((child) => child.type === 'handler');
|
|
1632
|
+
const body = handler ? (handler.children ?? []) : (node.children ?? []);
|
|
1633
|
+
return body.filter((child) => child.type !== 'param' && child.type !== 'decorator');
|
|
1634
|
+
}
|
|
1635
|
+
function firstRuntimeChild(node, type) {
|
|
1636
|
+
return node.children?.find((child) => child.type === type);
|
|
1637
|
+
}
|
|
1638
|
+
function runtimeChildNodes(node, type) {
|
|
1639
|
+
return node.children?.filter((child) => child.type === type) ?? [];
|
|
1640
|
+
}
|
|
1641
|
+
function runtimeChildren(node) {
|
|
1642
|
+
if (node.type === 'document' || node.type === 'handler' || node.type === '__block')
|
|
1643
|
+
return node.children ?? [];
|
|
1644
|
+
return [node];
|
|
1645
|
+
}
|
|
1646
|
+
function runtimeParams(node) {
|
|
1647
|
+
const childParams = node.children
|
|
1648
|
+
?.filter((child) => child.type === 'param')
|
|
1649
|
+
.map((child) => ({
|
|
1650
|
+
name: requiredString(child.props?.name, 'param name='),
|
|
1651
|
+
type: typeof child.props?.type === 'string' ? child.props.type : undefined,
|
|
1652
|
+
defaultExpr: runtimeParamDefaultExpr(child),
|
|
1653
|
+
})) ?? [];
|
|
1654
|
+
if (childParams.length > 0)
|
|
1655
|
+
return childParams;
|
|
1656
|
+
const raw = typeof node.props?.params === 'string' ? node.props.params : '';
|
|
1657
|
+
if (!raw.trim())
|
|
1658
|
+
return [];
|
|
1659
|
+
return splitRuntimeParamList(raw, 'fn params=').map((part) => {
|
|
1660
|
+
const defaultIndex = findRuntimeDefaultSeparator(part);
|
|
1661
|
+
const beforeDefault = defaultIndex >= 0 ? part.slice(0, defaultIndex) : part;
|
|
1662
|
+
const defaultExpr = defaultIndex >= 0 ? part.slice(defaultIndex + 1).trim() : undefined;
|
|
1663
|
+
const typeIndex = beforeDefault.indexOf(':');
|
|
1664
|
+
const name = typeIndex >= 0 ? beforeDefault.slice(0, typeIndex) : beforeDefault;
|
|
1665
|
+
const type = typeIndex >= 0 ? beforeDefault.slice(typeIndex + 1) : '';
|
|
1666
|
+
return {
|
|
1667
|
+
name: requiredString(name.trim(), 'param name='),
|
|
1668
|
+
type: type.trim() || undefined,
|
|
1669
|
+
defaultExpr: defaultExpr || undefined,
|
|
1670
|
+
};
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1673
|
+
function splitRuntimeParamList(raw, propName) {
|
|
1674
|
+
const out = [];
|
|
1675
|
+
let current = '';
|
|
1676
|
+
let depth = 0;
|
|
1677
|
+
let angleDepth = 0;
|
|
1678
|
+
let inDefault = false;
|
|
1679
|
+
let quote = null;
|
|
1680
|
+
for (let index = 0; index < raw.length; index += 1) {
|
|
1681
|
+
const ch = raw[index];
|
|
1682
|
+
if (quote !== null) {
|
|
1683
|
+
current += ch;
|
|
1684
|
+
if (ch === '\\' && index + 1 < raw.length)
|
|
1685
|
+
current += raw[++index];
|
|
1686
|
+
else if (ch === quote)
|
|
1687
|
+
quote = null;
|
|
1688
|
+
continue;
|
|
1689
|
+
}
|
|
1690
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
1691
|
+
quote = ch;
|
|
1692
|
+
current += ch;
|
|
1693
|
+
continue;
|
|
1694
|
+
}
|
|
1695
|
+
if (ch === '(' || ch === '[' || ch === '{')
|
|
1696
|
+
depth += 1;
|
|
1697
|
+
else if (ch === ')' || ch === ']' || ch === '}')
|
|
1698
|
+
depth -= 1;
|
|
1699
|
+
else if (ch === '=' && depth === 0 && angleDepth === 0 && raw[index + 1] !== '>')
|
|
1700
|
+
inDefault = true;
|
|
1701
|
+
else if (ch === '<' && (!inDefault || runtimeAngleClosesBeforeNextTopLevelComma(raw, index + 1)))
|
|
1702
|
+
angleDepth += 1;
|
|
1703
|
+
else if (ch === '>' && angleDepth > 0)
|
|
1704
|
+
angleDepth -= 1;
|
|
1705
|
+
if (depth < 0 || angleDepth < 0)
|
|
1706
|
+
throw new Error(`${propName} has unbalanced delimiters.`);
|
|
1707
|
+
if (ch === ',' && depth === 0 && angleDepth === 0) {
|
|
1708
|
+
const part = current.trim();
|
|
1709
|
+
if (part.length === 0)
|
|
1710
|
+
throw new Error(`${propName} contains an empty expression.`);
|
|
1711
|
+
out.push(part);
|
|
1712
|
+
current = '';
|
|
1713
|
+
inDefault = false;
|
|
1714
|
+
continue;
|
|
1715
|
+
}
|
|
1716
|
+
current += ch;
|
|
1717
|
+
}
|
|
1718
|
+
if (quote !== null || depth !== 0 || angleDepth !== 0)
|
|
1719
|
+
throw new Error(`${propName} has unbalanced delimiters.`);
|
|
1720
|
+
const tail = current.trim();
|
|
1721
|
+
if (tail.length === 0 && raw.trim().endsWith(','))
|
|
1722
|
+
throw new Error(`${propName} contains an empty expression.`);
|
|
1723
|
+
if (tail.length > 0)
|
|
1724
|
+
out.push(tail);
|
|
1725
|
+
return out;
|
|
1726
|
+
}
|
|
1727
|
+
function runtimeParamDefaultExpr(node) {
|
|
1728
|
+
const propName = Object.hasOwn(node.props ?? {}, 'value') ? 'value' : 'default';
|
|
1729
|
+
const rawValue = propName === 'value' ? node.props?.value : node.props?.default;
|
|
1730
|
+
if (rawValue === undefined || rawValue === null)
|
|
1731
|
+
return undefined;
|
|
1732
|
+
if (typeof rawValue === 'string' && (node.__quotedProps ?? []).includes(propName))
|
|
1733
|
+
return JSON.stringify(rawValue);
|
|
1734
|
+
return unwrapExpr(rawValue, 'param value=');
|
|
1735
|
+
}
|
|
1736
|
+
function unwrapExpr(value, label) {
|
|
1737
|
+
if (typeof value === 'string')
|
|
1738
|
+
return value;
|
|
1739
|
+
if (isExprObject(value))
|
|
1740
|
+
return value.code;
|
|
1741
|
+
if (value === undefined || value === null)
|
|
1742
|
+
throw new Error(`KERN core runtime missing ${label}.`);
|
|
1743
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
1744
|
+
return String(value);
|
|
1745
|
+
throw new Error(`KERN core runtime ${label} must be a string expression.`);
|
|
1746
|
+
}
|
|
1747
|
+
function requiredString(value, label) {
|
|
1748
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
1749
|
+
throw new Error(`KERN core runtime requires ${label}.`);
|
|
1750
|
+
return value;
|
|
1751
|
+
}
|
|
1752
|
+
function kernStringCoerce(value) {
|
|
1753
|
+
if (value.kind === 'null')
|
|
1754
|
+
return 'null';
|
|
1755
|
+
if (value.kind === 'undefined')
|
|
1756
|
+
return 'undefined';
|
|
1757
|
+
if (value.kind === 'boolean')
|
|
1758
|
+
return value.value ? 'true' : 'false';
|
|
1759
|
+
if (value.kind === 'number')
|
|
1760
|
+
return String(value.value);
|
|
1761
|
+
if (value.kind === 'string')
|
|
1762
|
+
return value.value;
|
|
1763
|
+
return String(toHostValue(value));
|
|
1764
|
+
}
|
|
1765
|
+
function kernEquals(left, right) {
|
|
1766
|
+
if (left.kind !== right.kind)
|
|
1767
|
+
return false;
|
|
1768
|
+
switch (left.kind) {
|
|
1769
|
+
case 'null':
|
|
1770
|
+
case 'undefined':
|
|
1771
|
+
return true;
|
|
1772
|
+
case 'boolean':
|
|
1773
|
+
return left.value === right.value;
|
|
1774
|
+
case 'number':
|
|
1775
|
+
return left.value === right.value;
|
|
1776
|
+
case 'string':
|
|
1777
|
+
return left.value === right.value;
|
|
1778
|
+
case 'array': {
|
|
1779
|
+
const rightArray = right;
|
|
1780
|
+
return (left.items.length === rightArray.items.length &&
|
|
1781
|
+
left.items.every((item, i) => kernEquals(item, rightArray.items[i])));
|
|
1782
|
+
}
|
|
1783
|
+
case 'record': {
|
|
1784
|
+
const rightRecord = right;
|
|
1785
|
+
const leftKeys = Object.keys(left.entries);
|
|
1786
|
+
const rightKeys = Object.keys(rightRecord.entries);
|
|
1787
|
+
return (leftKeys.length === rightKeys.length &&
|
|
1788
|
+
leftKeys.every((key) => Object.hasOwn(rightRecord.entries, key) && kernEquals(left.entries[key], rightRecord.entries[key])));
|
|
1789
|
+
}
|
|
1790
|
+
case 'function':
|
|
1791
|
+
case 'builtin':
|
|
1792
|
+
case 'class':
|
|
1793
|
+
case 'instance':
|
|
1794
|
+
case 'bound-method':
|
|
1795
|
+
case 'super':
|
|
1796
|
+
return left === right;
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
function isNullish(value) {
|
|
1800
|
+
return value.kind === 'null' || value.kind === 'undefined';
|
|
1801
|
+
}
|
|
1802
|
+
function isKernValue(value) {
|
|
1803
|
+
return isKernValueShape(value, new WeakSet());
|
|
1804
|
+
}
|
|
1805
|
+
function isKernValueShape(value, seen) {
|
|
1806
|
+
if (!isPlainRecord(value) ||
|
|
1807
|
+
value[KERN_VALUE_BRAND] !== true ||
|
|
1808
|
+
typeof value.kind !== 'string') {
|
|
1809
|
+
return false;
|
|
1810
|
+
}
|
|
1811
|
+
if (seen.has(value))
|
|
1812
|
+
return true;
|
|
1813
|
+
seen.add(value);
|
|
1814
|
+
switch (value.kind) {
|
|
1815
|
+
case 'null':
|
|
1816
|
+
case 'undefined':
|
|
1817
|
+
return hasOnlyKeys(value, ['kind']);
|
|
1818
|
+
case 'boolean':
|
|
1819
|
+
return hasOnlyKeys(value, ['kind', 'value']) && typeof value.value === 'boolean';
|
|
1820
|
+
case 'number':
|
|
1821
|
+
return hasOnlyKeys(value, ['kind', 'value']) && typeof value.value === 'number' && Number.isFinite(value.value);
|
|
1822
|
+
case 'string':
|
|
1823
|
+
return hasOnlyKeys(value, ['kind', 'value']) && typeof value.value === 'string';
|
|
1824
|
+
case 'array':
|
|
1825
|
+
return (hasOnlyKeys(value, ['kind', 'items']) &&
|
|
1826
|
+
Array.isArray(value.items) &&
|
|
1827
|
+
!hasArrayHoles(value.items) &&
|
|
1828
|
+
value.items.every((item) => isKernValueShape(item, seen)));
|
|
1829
|
+
case 'record':
|
|
1830
|
+
return (hasOnlyKeys(value, ['kind', 'entries']) &&
|
|
1831
|
+
isPlainRecord(value.entries) &&
|
|
1832
|
+
Object.values(value.entries).every((entry) => isKernValueShape(entry, seen)));
|
|
1833
|
+
case 'function':
|
|
1834
|
+
return (hasOnlyKeys(value, ['kind', 'params', 'body', 'env'], ['name']) &&
|
|
1835
|
+
(value.name === undefined || typeof value.name === 'string') &&
|
|
1836
|
+
Array.isArray(value.params) &&
|
|
1837
|
+
Array.isArray(value.body) &&
|
|
1838
|
+
value.env instanceof CoreRuntimeEnv);
|
|
1839
|
+
case 'builtin':
|
|
1840
|
+
return (hasOnlyKeys(value, ['kind', 'name', 'call']) &&
|
|
1841
|
+
typeof value.name === 'string' &&
|
|
1842
|
+
typeof value.call === 'function');
|
|
1843
|
+
case 'class':
|
|
1844
|
+
return (hasOnlyKeys(value, ['kind', 'name', 'node', 'env', 'staticFields'], ['runtimeRootContext']) &&
|
|
1845
|
+
typeof value.name === 'string' &&
|
|
1846
|
+
isPlainRecord(value.node) &&
|
|
1847
|
+
value.env instanceof CoreRuntimeEnv &&
|
|
1848
|
+
isPlainRecord(value.staticFields) &&
|
|
1849
|
+
Object.values(value.staticFields).every((entry) => isKernValueShape(entry, seen)));
|
|
1850
|
+
case 'instance':
|
|
1851
|
+
return (hasOnlyKeys(value, ['kind', 'classValue', 'fields', 'initializedClasses']) &&
|
|
1852
|
+
isKernValueShape(value.classValue, seen) &&
|
|
1853
|
+
value.classValue.kind === 'class' &&
|
|
1854
|
+
isPlainRecord(value.fields) &&
|
|
1855
|
+
Object.values(value.fields).every((entry) => isKernValueShape(entry, seen)) &&
|
|
1856
|
+
value.initializedClasses instanceof Set);
|
|
1857
|
+
case 'bound-method':
|
|
1858
|
+
return (hasOnlyKeys(value, ['kind', 'name', 'receiver', 'methodNode', 'ownerClass']) &&
|
|
1859
|
+
typeof value.name === 'string' &&
|
|
1860
|
+
isKernValueShape(value.receiver, seen) &&
|
|
1861
|
+
value.receiver.kind === 'instance' &&
|
|
1862
|
+
isPlainRecord(value.methodNode) &&
|
|
1863
|
+
isKernValueShape(value.ownerClass, seen) &&
|
|
1864
|
+
value.ownerClass.kind === 'class');
|
|
1865
|
+
case 'super':
|
|
1866
|
+
return (hasOnlyKeys(value, ['kind', 'receiver', 'ownerClass', 'mode']) &&
|
|
1867
|
+
isKernValueShape(value.receiver, seen) &&
|
|
1868
|
+
(value.receiver.kind === 'instance' || value.receiver.kind === 'class') &&
|
|
1869
|
+
isKernValueShape(value.ownerClass, seen) &&
|
|
1870
|
+
value.ownerClass.kind === 'class' &&
|
|
1871
|
+
(value.mode === 'constructor' || value.mode === 'method' || value.mode === 'static'));
|
|
1872
|
+
default:
|
|
1873
|
+
return false;
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
function hasArrayHoles(value) {
|
|
1877
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
1878
|
+
if (!Object.hasOwn(value, i))
|
|
1879
|
+
return true;
|
|
1880
|
+
}
|
|
1881
|
+
return false;
|
|
1882
|
+
}
|
|
1883
|
+
function hasOnlyKeys(value, required, optional = []) {
|
|
1884
|
+
const allowed = new Set([...required, ...optional]);
|
|
1885
|
+
const keys = Object.keys(value);
|
|
1886
|
+
return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => allowed.has(key));
|
|
1887
|
+
}
|
|
1888
|
+
function createRecordEntries() {
|
|
1889
|
+
return Object.create(null);
|
|
1890
|
+
}
|
|
1891
|
+
function isPlainRecord(value) {
|
|
1892
|
+
if (typeof value !== 'object' || value === null)
|
|
1893
|
+
return false;
|
|
1894
|
+
const proto = Object.getPrototypeOf(value);
|
|
1895
|
+
return proto === Object.prototype || proto === null;
|
|
1896
|
+
}
|
|
1897
|
+
function findRuntimeDefaultSeparator(value) {
|
|
1898
|
+
let depth = 0;
|
|
1899
|
+
let quote = '';
|
|
1900
|
+
let escaped = false;
|
|
1901
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
1902
|
+
const ch = value[i];
|
|
1903
|
+
if (quote) {
|
|
1904
|
+
if (escaped) {
|
|
1905
|
+
escaped = false;
|
|
1906
|
+
}
|
|
1907
|
+
else if (ch === '\\') {
|
|
1908
|
+
escaped = true;
|
|
1909
|
+
}
|
|
1910
|
+
else if (ch === quote) {
|
|
1911
|
+
quote = '';
|
|
1912
|
+
}
|
|
1913
|
+
continue;
|
|
1914
|
+
}
|
|
1915
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
1916
|
+
quote = ch;
|
|
1917
|
+
continue;
|
|
1918
|
+
}
|
|
1919
|
+
if (ch === '<' || ch === '(' || ch === '{' || ch === '[')
|
|
1920
|
+
depth += 1;
|
|
1921
|
+
else if ((ch === '>' || ch === ')' || ch === '}' || ch === ']') && depth > 0)
|
|
1922
|
+
depth -= 1;
|
|
1923
|
+
else if (ch === '=' && depth === 0) {
|
|
1924
|
+
if (value[i + 1] === '>' ||
|
|
1925
|
+
value[i + 1] === '=' ||
|
|
1926
|
+
value[i - 1] === '=' ||
|
|
1927
|
+
value[i - 1] === '<' ||
|
|
1928
|
+
value[i - 1] === '>' ||
|
|
1929
|
+
value[i - 1] === '!') {
|
|
1930
|
+
continue;
|
|
1931
|
+
}
|
|
1932
|
+
return i;
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
return -1;
|
|
1936
|
+
}
|
|
1937
|
+
function isExprObject(value) {
|
|
1938
|
+
return isPlainRecord(value) && value.__expr === true && typeof value.code === 'string';
|
|
1939
|
+
}
|
|
1940
|
+
function isIRNodeArray(value) {
|
|
1941
|
+
return Array.isArray(value);
|
|
1942
|
+
}
|
|
1943
|
+
function isObjectSpreadEntry(entry) {
|
|
1944
|
+
return 'kind' in entry && entry.kind === 'spread';
|
|
1945
|
+
}
|
|
1946
|
+
//# sourceMappingURL=index.js.map
|