@opencode/codemode 2.0.1 → 2.0.2
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/data.d.ts +6 -5
- package/dist/data.js +44 -46
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/interpreter/errors.d.ts +3 -4
- package/dist/interpreter/errors.js +40 -17
- package/dist/interpreter/execute.js +5 -3
- package/dist/interpreter/generators.d.ts +4 -0
- package/dist/interpreter/generators.js +25 -0
- package/dist/interpreter/globals.js +67 -46
- package/dist/interpreter/intrinsics.d.ts +8 -5
- package/dist/interpreter/intrinsics.js +59 -18
- package/dist/interpreter/model.d.ts +2 -32
- package/dist/interpreter/model.js +4 -38
- package/dist/interpreter/native.d.ts +19 -0
- package/dist/interpreter/native.js +40 -0
- package/dist/interpreter/objects.d.ts +105 -12
- package/dist/interpreter/objects.js +190 -66
- package/dist/interpreter/promises.d.ts +12 -13
- package/dist/interpreter/promises.js +52 -46
- package/dist/interpreter/references.d.ts +1 -0
- package/dist/interpreter/references.js +20 -33
- package/dist/interpreter/runner.d.ts +15 -13
- package/dist/interpreter/runner.js +19 -19
- package/dist/interpreter/runtime.d.ts +3 -3
- package/dist/interpreter/runtime.js +150 -314
- package/dist/stdlib/array.d.ts +4 -2
- package/dist/stdlib/array.js +423 -31
- package/dist/stdlib/collections.d.ts +3 -7
- package/dist/stdlib/collections.js +286 -114
- package/dist/stdlib/console.d.ts +3 -2
- package/dist/stdlib/console.js +35 -30
- package/dist/stdlib/date.d.ts +1 -7
- package/dist/stdlib/date.js +93 -188
- package/dist/stdlib/json.d.ts +2 -2
- package/dist/stdlib/json.js +24 -24
- package/dist/stdlib/math.d.ts +2 -2
- package/dist/stdlib/math.js +96 -76
- package/dist/stdlib/number.d.ts +3 -4
- package/dist/stdlib/number.js +94 -59
- package/dist/stdlib/object.d.ts +4 -4
- package/dist/stdlib/object.js +123 -49
- package/dist/stdlib/regexp.d.ts +6 -8
- package/dist/stdlib/regexp.js +71 -63
- package/dist/stdlib/string.d.ts +2 -2
- package/dist/stdlib/string.js +213 -50
- package/dist/stdlib/url.d.ts +5 -13
- package/dist/stdlib/url.js +196 -96
- package/dist/stdlib/value.d.ts +5 -4
- package/dist/stdlib/value.js +16 -16
- package/dist/stdlib/web.d.ts +4 -4
- package/dist/stdlib/web.js +8 -7
- package/dist/tool-runtime.d.ts +2 -1
- package/dist/tool-runtime.js +2 -2
- package/package.json +1 -1
- package/dist/interpreter/host.d.ts +0 -41
- package/dist/interpreter/host.js +0 -44
- package/dist/interpreter/methods.d.ts +0 -4
- package/dist/interpreter/methods.js +0 -837
- package/dist/values.d.ts +0 -37
- package/dist/values.js +0 -56
|
@@ -1,42 +1,124 @@
|
|
|
1
1
|
import { AsyncIteratorSymbol, IteratorSymbol } from "./model.js";
|
|
2
|
+
/** Ordinary assignment: writable, enumerable, configurable. */
|
|
3
|
+
export const data = { writable: true, enumerable: true, configurable: true };
|
|
4
|
+
/** Built-in methods and `constructor`: writable and configurable but hidden from enumeration. */
|
|
5
|
+
export const hidden = { writable: true, enumerable: false, configurable: true };
|
|
6
|
+
/** Function `name` and `length`: read-only but deletable. */
|
|
7
|
+
export const readonly = { writable: false, enumerable: false, configurable: true };
|
|
8
|
+
/** Constants such as `Math.PI` and a constructor's `prototype`. */
|
|
9
|
+
export const frozen = { writable: false, enumerable: false, configurable: false };
|
|
2
10
|
/** An object owned by the program: own properties plus a prototype link. */
|
|
3
11
|
export class ProgramObject {
|
|
4
12
|
proto;
|
|
5
13
|
props = new Map();
|
|
6
|
-
constructor(proto
|
|
14
|
+
constructor(proto) {
|
|
7
15
|
this.proto = proto;
|
|
8
16
|
}
|
|
9
17
|
}
|
|
10
18
|
export class ProgramArray extends ProgramObject {
|
|
11
19
|
items;
|
|
12
|
-
constructor(items = []) {
|
|
13
|
-
super();
|
|
20
|
+
constructor(proto, items = []) {
|
|
21
|
+
super(proto);
|
|
14
22
|
this.items = items;
|
|
15
23
|
}
|
|
16
24
|
}
|
|
17
25
|
/** An object with the [[ErrorData]] slot: what `Error.prototype.toString` and the host boundary recognize as an error. */
|
|
18
26
|
export class ProgramError extends ProgramObject {
|
|
19
27
|
}
|
|
20
|
-
export class
|
|
21
|
-
name
|
|
28
|
+
export class Callable extends ProgramObject {
|
|
29
|
+
constructor(proto, name, length) {
|
|
30
|
+
super(proto);
|
|
31
|
+
define(this, "length", length, readonly);
|
|
32
|
+
define(this, "name", name, readonly);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export class ProgramFunction extends Callable {
|
|
22
36
|
parameters;
|
|
23
37
|
body;
|
|
24
38
|
capturedScopes;
|
|
25
39
|
async;
|
|
26
40
|
generator;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
super();
|
|
30
|
-
this.name = name;
|
|
41
|
+
constructor(proto, name, parameters, body, capturedScopes, async, generator) {
|
|
42
|
+
const optional = parameters.findIndex((p) => p.type === "AssignmentPattern" || p.type === "RestElement");
|
|
43
|
+
super(proto, name, optional === -1 ? parameters.length : optional);
|
|
31
44
|
this.parameters = parameters;
|
|
32
45
|
this.body = body;
|
|
33
46
|
this.capturedScopes = capturedScopes;
|
|
34
47
|
this.async = async;
|
|
35
48
|
this.generator = generator;
|
|
36
|
-
const optional = parameters.findIndex((p) => p.type === "AssignmentPattern" || p.type === "RestElement");
|
|
37
|
-
this.length = optional === -1 ? parameters.length : optional;
|
|
38
49
|
}
|
|
39
50
|
}
|
|
51
|
+
export class NativeFunction extends Callable {
|
|
52
|
+
call;
|
|
53
|
+
construct;
|
|
54
|
+
callback;
|
|
55
|
+
constructor(proto, options) {
|
|
56
|
+
super(proto, options.name, options.length ?? 0);
|
|
57
|
+
this.call = options.call;
|
|
58
|
+
this.construct = options.construct;
|
|
59
|
+
this.callback = options.callback ?? true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export class ProgramPromise extends ProgramObject {
|
|
63
|
+
fiber;
|
|
64
|
+
constructor(proto, fiber) {
|
|
65
|
+
super(proto);
|
|
66
|
+
this.fiber = fiber;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export class ProgramGenerator extends ProgramObject {
|
|
70
|
+
asynchronous;
|
|
71
|
+
request;
|
|
72
|
+
constructor(proto, asynchronous, request) {
|
|
73
|
+
super(proto);
|
|
74
|
+
this.asynchronous = asynchronous;
|
|
75
|
+
this.request = request;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export class ProgramDate extends ProgramObject {
|
|
79
|
+
time;
|
|
80
|
+
constructor(proto, time) {
|
|
81
|
+
super(proto);
|
|
82
|
+
this.time = time;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export class ProgramRegExp extends ProgramObject {
|
|
86
|
+
regex;
|
|
87
|
+
constructor(proto, pattern, flags) {
|
|
88
|
+
super(proto);
|
|
89
|
+
this.regex = new RegExp(pattern, flags);
|
|
90
|
+
define(this, "lastIndex", 0, { writable: true, enumerable: false, configurable: false });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export class ProgramMap extends ProgramObject {
|
|
94
|
+
map = new Map();
|
|
95
|
+
}
|
|
96
|
+
export class ProgramSet extends ProgramObject {
|
|
97
|
+
set = new Set();
|
|
98
|
+
}
|
|
99
|
+
export class ProgramURLSearchParams extends ProgramObject {
|
|
100
|
+
params;
|
|
101
|
+
constructor(proto, params) {
|
|
102
|
+
super(proto);
|
|
103
|
+
this.params = params;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export class ProgramURL extends ProgramObject {
|
|
107
|
+
url;
|
|
108
|
+
searchParams;
|
|
109
|
+
constructor(proto, searchParamsProto, url) {
|
|
110
|
+
super(proto);
|
|
111
|
+
this.url = url;
|
|
112
|
+
this.searchParams = new ProgramURLSearchParams(searchParamsProto, url.searchParams);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** Built-in objects that wrap a host value; data-like, but never plain data. */
|
|
116
|
+
export const isWrapper = (value) => value instanceof ProgramDate ||
|
|
117
|
+
value instanceof ProgramRegExp ||
|
|
118
|
+
value instanceof ProgramMap ||
|
|
119
|
+
value instanceof ProgramSet ||
|
|
120
|
+
value instanceof ProgramURL ||
|
|
121
|
+
value instanceof ProgramURLSearchParams;
|
|
40
122
|
const MAX_ARRAY_LENGTH = 4_294_967_295;
|
|
41
123
|
export const parseArrayIndex = (key) => {
|
|
42
124
|
const property = String(key);
|
|
@@ -47,79 +129,119 @@ export const parseArrayIndex = (key) => {
|
|
|
47
129
|
};
|
|
48
130
|
const canonical = (key) => (typeof key === "symbol" ? key : String(key));
|
|
49
131
|
const index = (target, key) => target instanceof ProgramArray && typeof key === "string" ? parseArrayIndex(key) : undefined;
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
((name === "name" || name === "length") && target instanceof ProgramFunction);
|
|
53
|
-
export const hasOwn = (target, key) => {
|
|
132
|
+
/** The own property under `key`, including an array's live indexes and `length`. */
|
|
133
|
+
export const own = (target, key) => {
|
|
54
134
|
const name = canonical(key);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
135
|
+
if (target instanceof ProgramArray) {
|
|
136
|
+
const at = index(target, name);
|
|
137
|
+
if (at !== undefined) {
|
|
138
|
+
return at in target.items ? { value: target.items[at], ...data } : undefined;
|
|
139
|
+
}
|
|
140
|
+
if (name === "length")
|
|
141
|
+
return { value: target.items.length, writable: true, enumerable: false, configurable: false };
|
|
142
|
+
}
|
|
143
|
+
return target.props.get(name);
|
|
59
144
|
};
|
|
145
|
+
const read = (slot, receiver) => "value" in slot ? slot.value : slot.get === undefined ? undefined : slot.get(receiver);
|
|
146
|
+
export const hasOwn = (target, key) => own(target, key) !== undefined;
|
|
60
147
|
export const getOwn = (target, key) => {
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
if (at !== undefined)
|
|
64
|
-
return target.items[at];
|
|
65
|
-
if (target instanceof ProgramArray && name === "length")
|
|
66
|
-
return target.items.length;
|
|
67
|
-
if (target instanceof ProgramFunction && name === "name")
|
|
68
|
-
return target.name;
|
|
69
|
-
if (target instanceof ProgramFunction && name === "length")
|
|
70
|
-
return target.length;
|
|
71
|
-
return target.props.get(name);
|
|
148
|
+
const slot = own(target, key);
|
|
149
|
+
return slot === undefined ? undefined : read(slot, target);
|
|
72
150
|
};
|
|
73
|
-
|
|
151
|
+
/** [[Get]]: walks the prototype chain; accessors see `receiver`, which is the primitive for wrapper prototypes. */
|
|
152
|
+
export const get = (target, key, receiver = target) => {
|
|
74
153
|
for (let current = target; current !== null; current = current.proto) {
|
|
75
|
-
|
|
76
|
-
|
|
154
|
+
const slot = own(current, key);
|
|
155
|
+
if (slot !== undefined)
|
|
156
|
+
return read(slot, receiver);
|
|
77
157
|
}
|
|
78
158
|
return undefined;
|
|
79
159
|
};
|
|
80
|
-
export const
|
|
81
|
-
for (let current =
|
|
82
|
-
if (current
|
|
160
|
+
export const has = (target, key) => {
|
|
161
|
+
for (let current = target; current !== null; current = current.proto) {
|
|
162
|
+
if (own(current, key) !== undefined)
|
|
83
163
|
return true;
|
|
84
164
|
}
|
|
85
165
|
return false;
|
|
86
166
|
};
|
|
87
|
-
export const
|
|
88
|
-
for (let current =
|
|
89
|
-
if (
|
|
167
|
+
export const hasPrototype = (value, proto) => {
|
|
168
|
+
for (let current = value instanceof ProgramObject ? value.proto : null; current !== null; current = current.proto) {
|
|
169
|
+
if (current === proto)
|
|
90
170
|
return true;
|
|
91
171
|
}
|
|
92
172
|
return false;
|
|
93
173
|
};
|
|
94
|
-
|
|
95
|
-
const name = canonical(key);
|
|
174
|
+
const writeArray = (target, name, value) => {
|
|
96
175
|
const at = index(target, name);
|
|
97
176
|
if (at !== undefined) {
|
|
98
|
-
;
|
|
99
177
|
target.items[at] = value;
|
|
100
178
|
return true;
|
|
101
179
|
}
|
|
102
|
-
if (name
|
|
103
|
-
|
|
104
|
-
|
|
180
|
+
if (name !== "length")
|
|
181
|
+
return undefined;
|
|
182
|
+
const length = typeof value === "number" ? value : Number(value);
|
|
183
|
+
if (!Number.isInteger(length) || length < 0 || length > MAX_ARRAY_LENGTH)
|
|
184
|
+
return false;
|
|
185
|
+
target.items.length = length;
|
|
186
|
+
return true;
|
|
187
|
+
};
|
|
188
|
+
/** [[Set]]: an inherited setter or read-only property decides before an own data property is created. */
|
|
189
|
+
export const set = (target, key, value) => {
|
|
190
|
+
const name = canonical(key);
|
|
191
|
+
for (let current = target; current !== null; current = current.proto) {
|
|
192
|
+
const slot = own(current, name);
|
|
193
|
+
if (slot === undefined)
|
|
194
|
+
continue;
|
|
195
|
+
if (!("value" in slot)) {
|
|
196
|
+
if (slot.set === undefined)
|
|
197
|
+
return false;
|
|
198
|
+
slot.set(target, value);
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
if (!slot.writable)
|
|
105
202
|
return false;
|
|
106
|
-
|
|
203
|
+
if (current !== target)
|
|
204
|
+
break;
|
|
205
|
+
if (target instanceof ProgramArray) {
|
|
206
|
+
const written = writeArray(target, name, value);
|
|
207
|
+
if (written !== undefined)
|
|
208
|
+
return written;
|
|
209
|
+
}
|
|
210
|
+
slot.value = value;
|
|
107
211
|
return true;
|
|
108
212
|
}
|
|
109
|
-
if (
|
|
110
|
-
|
|
111
|
-
|
|
213
|
+
if (target instanceof ProgramArray) {
|
|
214
|
+
const written = writeArray(target, name, value);
|
|
215
|
+
if (written !== undefined)
|
|
216
|
+
return written;
|
|
217
|
+
}
|
|
218
|
+
target.props.set(name, { value, ...data });
|
|
112
219
|
return true;
|
|
113
220
|
};
|
|
221
|
+
/** [[DefineOwnProperty]] for a data property, ignoring the chain. */
|
|
222
|
+
export const define = (target, key, value, attrs = data) => {
|
|
223
|
+
const name = canonical(key);
|
|
224
|
+
if (target instanceof ProgramArray && writeArray(target, name, value) !== undefined)
|
|
225
|
+
return;
|
|
226
|
+
target.props.set(name, { value, ...attrs });
|
|
227
|
+
};
|
|
228
|
+
export const defineAccessor = (target, key, get, set) => {
|
|
229
|
+
target.props.set(canonical(key), { get, set, enumerable: false, configurable: true });
|
|
230
|
+
};
|
|
114
231
|
export const remove = (target, key) => {
|
|
115
232
|
const name = canonical(key);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
233
|
+
if (target instanceof ProgramArray) {
|
|
234
|
+
const at = index(target, name);
|
|
235
|
+
if (at !== undefined)
|
|
236
|
+
return delete target.items[at];
|
|
237
|
+
if (name === "length")
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
const slot = target.props.get(name);
|
|
241
|
+
if (slot === undefined)
|
|
122
242
|
return true;
|
|
243
|
+
if (!slot.configurable)
|
|
244
|
+
return false;
|
|
123
245
|
target.props.delete(name);
|
|
124
246
|
return true;
|
|
125
247
|
};
|
|
@@ -128,27 +250,29 @@ export const ownKeys = (target) => {
|
|
|
128
250
|
const strings = [...target.props.keys()].filter((key) => typeof key === "string");
|
|
129
251
|
const symbols = [...target.props.keys()].filter((key) => typeof key === "symbol");
|
|
130
252
|
return [
|
|
131
|
-
...(target instanceof ProgramArray ? Object.keys(target.items) : []),
|
|
253
|
+
...(target instanceof ProgramArray ? [...Object.keys(target.items), "length"] : []),
|
|
132
254
|
...strings.filter((key) => parseArrayIndex(key) !== undefined).sort((a, b) => Number(a) - Number(b)),
|
|
133
255
|
...strings.filter((key) => parseArrayIndex(key) === undefined),
|
|
134
256
|
...symbols,
|
|
135
257
|
];
|
|
136
258
|
};
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
259
|
+
const enumerable = (target, key) => own(target, key)?.enumerable === true;
|
|
260
|
+
/** Own enumerable keys, including the iterator symbols; what spread and `Object.assign` copy. */
|
|
261
|
+
export const enumerableKeys = (target) => ownKeys(target).filter((key) => (typeof key === "string" || key === IteratorSymbol || key === AsyncIteratorSymbol) && enumerable(target, key));
|
|
262
|
+
/** Own enumerable string keys: `Object.keys`. */
|
|
263
|
+
export const keys = (target) => ownKeys(target).filter((key) => typeof key === "string" && enumerable(target, key));
|
|
264
|
+
/** Own enumerable string entries: `Object.entries` and serialization. */
|
|
265
|
+
export const entries = (target) => keys(target).map((key) => [key, getOwn(target, key)]);
|
|
266
|
+
export const record = (proto, fields) => {
|
|
267
|
+
const target = new ProgramObject(proto);
|
|
268
|
+
for (const [key, value] of Object.entries(fields))
|
|
269
|
+
define(target, key, value);
|
|
144
270
|
return target;
|
|
145
271
|
};
|
|
146
272
|
export const assign = (target, source, skip) => {
|
|
147
|
-
for (const key of
|
|
273
|
+
for (const key of enumerableKeys(source)) {
|
|
148
274
|
if (skip?.has(key))
|
|
149
275
|
continue;
|
|
150
|
-
if (typeof key === "symbol" && key !== IteratorSymbol && key !== AsyncIteratorSymbol)
|
|
151
|
-
continue;
|
|
152
276
|
set(target, key, getOwn(source, key));
|
|
153
277
|
}
|
|
154
278
|
};
|
|
@@ -1,31 +1,30 @@
|
|
|
1
1
|
import { Effect, Exit, Scope } from "effect";
|
|
2
2
|
import type { Diagnostic } from "../codemode.js";
|
|
3
|
-
import { type AstNode, InterpreterRuntimeError
|
|
4
|
-
import {
|
|
5
|
-
import { Values } from "../values.js";
|
|
3
|
+
import { type AstNode, InterpreterRuntimeError } from "./model.js";
|
|
4
|
+
import { ProgramObject, ProgramPromise } from "./objects.js";
|
|
6
5
|
import { type Runner } from "./runner.js";
|
|
7
6
|
export declare class PromiseRuntime<R> {
|
|
8
7
|
private readonly scope;
|
|
8
|
+
private readonly proto;
|
|
9
9
|
private readonly active;
|
|
10
10
|
private readonly ids;
|
|
11
11
|
private readonly observed;
|
|
12
12
|
private readonly failures;
|
|
13
13
|
private nextID;
|
|
14
|
-
constructor(scope: Scope.Scope);
|
|
14
|
+
constructor(scope: Scope.Scope, proto: ProgramObject);
|
|
15
15
|
createWithSelf(body: (self: {
|
|
16
|
-
promise?:
|
|
17
|
-
}) => Effect.Effect<unknown, unknown, R>): Effect.Effect<
|
|
18
|
-
create(effect: Effect.Effect<unknown, unknown, R>): Effect.Effect<
|
|
19
|
-
markObserved(promise:
|
|
20
|
-
await(promise:
|
|
16
|
+
promise?: ProgramPromise;
|
|
17
|
+
}) => Effect.Effect<unknown, unknown, R>): Effect.Effect<ProgramPromise, never, R>;
|
|
18
|
+
create(effect: Effect.Effect<unknown, unknown, R>): Effect.Effect<ProgramPromise, never, R>;
|
|
19
|
+
markObserved(promise: ProgramPromise): void;
|
|
20
|
+
await(promise: ProgramPromise): Effect.Effect<Exit.Exit<unknown, unknown>>;
|
|
21
21
|
fork(effect: Effect.Effect<unknown, unknown, R>): Effect.Effect<void, never, R>;
|
|
22
22
|
diagnostics(): Array<Diagnostic>;
|
|
23
23
|
interrupt(): Effect.Effect<Array<Diagnostic>>;
|
|
24
24
|
}
|
|
25
25
|
export declare const selfResolutionError: (node?: AstNode) => InterpreterRuntimeError;
|
|
26
26
|
export declare const resolvePromiseValue: <R>(runner: Runner<R>, value: unknown, node: AstNode, own?: {
|
|
27
|
-
promise?:
|
|
27
|
+
promise?: ProgramPromise;
|
|
28
28
|
}) => Effect.Effect<unknown, unknown, R>;
|
|
29
|
-
export declare const resolvePromise: <R>(runner: Runner<R>, promises: PromiseRuntime<R>, value: unknown, node: AstNode) => Effect.Effect<
|
|
30
|
-
export declare const
|
|
31
|
-
export declare const promiseGlobal: <R>(runner: Runner<R>, promises: PromiseRuntime<R>) => HostFunction<R>;
|
|
29
|
+
export declare const resolvePromise: <R>(runner: Runner<R>, promises: PromiseRuntime<R>, value: unknown, node: AstNode) => Effect.Effect<ProgramPromise, never, R>;
|
|
30
|
+
export declare const promiseGlobal: <R>(runner: Runner<R>, promises: PromiseRuntime<R>) => import("./objects.js").NativeFunction<R>;
|
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
import { Cause, Deferred, Effect, Exit, Fiber, Scope } from "effect";
|
|
2
|
-
import { InterpreterRuntimeError, ProgramThrow
|
|
3
|
-
import { get, ProgramArray, ProgramFunction, ProgramObject, record } from "./objects.js";
|
|
4
|
-
import {
|
|
2
|
+
import { InterpreterRuntimeError, ProgramThrow } from "./model.js";
|
|
3
|
+
import { Callable, define, get, hidden, ProgramArray, ProgramFunction, ProgramObject, ProgramPromise, record, } from "./objects.js";
|
|
4
|
+
import { constructor, fn, methods, native, receiver, requiresNew } from "./native.js";
|
|
5
5
|
import { caughtErrorValue, createAggregateErrorValue, normalizeError } from "./errors.js";
|
|
6
6
|
import { typeofValue } from "./references.js";
|
|
7
|
-
import { Values } from "../values.js";
|
|
8
7
|
import { applyCollectionCallback, isSupportedCallback } from "./runner.js";
|
|
9
8
|
// A `resolve`/`reject` handed to an executor or thenable: calling it settles the capability.
|
|
10
|
-
const capability = (name, settle) =>
|
|
9
|
+
const capability = (runner, name, settle) => fn(runner.prototypes, name, 1, (_, args) => {
|
|
11
10
|
settle(args[0]);
|
|
12
11
|
return undefined;
|
|
13
12
|
});
|
|
14
13
|
// Observation only controls rejection reporting; program completion interrupts all promise work.
|
|
15
14
|
export class PromiseRuntime {
|
|
16
15
|
scope;
|
|
16
|
+
proto;
|
|
17
17
|
active = new Set();
|
|
18
18
|
ids = new WeakMap();
|
|
19
19
|
observed = new WeakSet();
|
|
20
20
|
failures = new Map();
|
|
21
21
|
nextID = 0;
|
|
22
|
-
constructor(scope) {
|
|
22
|
+
constructor(scope, proto) {
|
|
23
23
|
this.scope = scope;
|
|
24
|
+
this.proto = proto;
|
|
24
25
|
}
|
|
25
26
|
// Resolution bodies need the promise's own identity to reject `resolve(promise)` self-resolution.
|
|
26
27
|
createWithSelf(body) {
|
|
@@ -35,7 +36,7 @@ export class PromiseRuntime {
|
|
|
35
36
|
// Allocate before forking so reruns get distinct IDs and diagnostics retain creation order.
|
|
36
37
|
const id = this.nextID++;
|
|
37
38
|
return Effect.map(Effect.forkIn(effect, this.scope, { startImmediately: true }), (fiber) => {
|
|
38
|
-
const promise = new
|
|
39
|
+
const promise = new ProgramPromise(this.proto, fiber);
|
|
39
40
|
this.active.add(promise);
|
|
40
41
|
this.ids.set(promise, id);
|
|
41
42
|
fiber.addObserver((exit) => {
|
|
@@ -86,7 +87,7 @@ export const selfResolutionError = (node) => new InterpreterRuntimeError("Chaini
|
|
|
86
87
|
export const resolvePromiseValue = (runner, value, node, own) => {
|
|
87
88
|
if (own?.promise !== undefined && value === own.promise)
|
|
88
89
|
return Effect.fail(selfResolutionError(node));
|
|
89
|
-
if (value instanceof
|
|
90
|
+
if (value instanceof ProgramPromise)
|
|
90
91
|
return runner.settlePromise(value);
|
|
91
92
|
if (!(value instanceof ProgramObject))
|
|
92
93
|
return Effect.succeed(value);
|
|
@@ -97,9 +98,9 @@ export const resolvePromiseValue = (runner, value, node, own) => {
|
|
|
97
98
|
// Promise resolution invokes a thenable's method in a later job.
|
|
98
99
|
yield* Effect.yieldNow;
|
|
99
100
|
const deferred = Deferred.makeUnsafe();
|
|
100
|
-
const resolve = capability("resolve", (result) => Deferred.doneUnsafe(deferred, Exit.succeed(result)));
|
|
101
|
-
const reject = capability("reject", (reason) => Deferred.doneUnsafe(deferred, Exit.fail(new ProgramThrow(reason))));
|
|
102
|
-
const executed = yield* Effect.exit(runner.invokeCallable(then, [resolve, reject], node));
|
|
101
|
+
const resolve = capability(runner, "resolve", (result) => Deferred.doneUnsafe(deferred, Exit.succeed(result)));
|
|
102
|
+
const reject = capability(runner, "reject", (reason) => Deferred.doneUnsafe(deferred, Exit.fail(new ProgramThrow(reason))));
|
|
103
|
+
const executed = yield* Effect.exit(runner.invokeCallable(then, value, [resolve, reject], node));
|
|
103
104
|
if (!Exit.isSuccess(executed)) {
|
|
104
105
|
if (Cause.hasInterruptsOnly(executed.cause))
|
|
105
106
|
return yield* Effect.failCause(executed.cause);
|
|
@@ -109,7 +110,7 @@ export const resolvePromiseValue = (runner, value, node, own) => {
|
|
|
109
110
|
});
|
|
110
111
|
};
|
|
111
112
|
export const resolvePromise = (runner, promises, value, node) => {
|
|
112
|
-
if (value instanceof
|
|
113
|
+
if (value instanceof ProgramPromise)
|
|
113
114
|
return Effect.succeed(value);
|
|
114
115
|
return promises.createWithSelf((self) => resolvePromiseValue(runner, value, node, self));
|
|
115
116
|
};
|
|
@@ -136,22 +137,25 @@ const invokePromiseMethod = (runner, promises, name, args, node) => {
|
|
|
136
137
|
items.push(item);
|
|
137
138
|
}
|
|
138
139
|
if (name === "all") {
|
|
139
|
-
return new ProgramArray(yield* settleAfterTurn(Effect.all(items.map((item) => Effect.flatten(promises.await(item))), { concurrency: "unbounded" })));
|
|
140
|
+
return new ProgramArray(runner.prototypes.Array, yield* settleAfterTurn(Effect.all(items.map((item) => Effect.flatten(promises.await(item))), { concurrency: "unbounded" })));
|
|
140
141
|
}
|
|
141
142
|
if (name === "allSettled") {
|
|
142
143
|
const outcomes = [];
|
|
143
144
|
for (const item of items) {
|
|
144
145
|
const exit = yield* promises.await(item);
|
|
145
146
|
if (Exit.isSuccess(exit)) {
|
|
146
|
-
outcomes.push(record({ status: "fulfilled", value: exit.value }));
|
|
147
|
+
outcomes.push(record(runner.prototypes.Object, { status: "fulfilled", value: exit.value }));
|
|
147
148
|
continue;
|
|
148
149
|
}
|
|
149
150
|
if (Cause.hasInterruptsOnly(exit.cause))
|
|
150
151
|
return yield* Effect.failCause(exit.cause);
|
|
151
|
-
outcomes.push(record(
|
|
152
|
+
outcomes.push(record(runner.prototypes.Object, {
|
|
153
|
+
status: "rejected",
|
|
154
|
+
reason: caughtErrorValue(runner, Cause.squash(exit.cause)),
|
|
155
|
+
}));
|
|
152
156
|
}
|
|
153
157
|
yield* Effect.yieldNow;
|
|
154
|
-
return new ProgramArray(outcomes);
|
|
158
|
+
return new ProgramArray(runner.prototypes.Array, outcomes);
|
|
155
159
|
}
|
|
156
160
|
if (name === "race") {
|
|
157
161
|
if (items.length === 0) {
|
|
@@ -169,15 +173,16 @@ const invokePromiseMethod = (runner, promises, name, args, node) => {
|
|
|
169
173
|
return yield* settleAfterTurn(Effect.all(flipped, { concurrency: "unbounded" }).pipe(Effect.flatMap((reasons) => Effect.fail(new ProgramThrow(createAggregateErrorValue(runner, reasons, "All promises were rejected")))), Effect.catch((error) => error instanceof PromiseAnyFulfilled ? Effect.succeed(error.value) : Effect.fail(error))));
|
|
170
174
|
}));
|
|
171
175
|
};
|
|
172
|
-
|
|
173
|
-
const method = `Promise.prototype.${
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
176
|
+
const instanceMethod = (runner, promises, name, thisValue, args, node) => {
|
|
177
|
+
const method = `Promise.prototype.${name}`;
|
|
178
|
+
const promise = receiver(ProgramPromise, thisValue, method, node);
|
|
179
|
+
promises.markObserved(promise);
|
|
180
|
+
if (name === "finally") {
|
|
181
|
+
return chainFinally(runner, promises, promise, reactionHandler(args[0], method, node), method, node);
|
|
177
182
|
}
|
|
178
|
-
const onFulfilled =
|
|
179
|
-
const onRejected = reactionHandler(
|
|
180
|
-
return chainReaction(runner, promises,
|
|
183
|
+
const onFulfilled = name === "then" ? reactionHandler(args[0], method, node) : undefined;
|
|
184
|
+
const onRejected = reactionHandler(name === "then" ? args[1] : args[0], method, node);
|
|
185
|
+
return chainReaction(runner, promises, promise, onFulfilled, onRejected, method, node);
|
|
181
186
|
};
|
|
182
187
|
const constructPromise = (runner, promises, executor, node) => {
|
|
183
188
|
if (!(executor instanceof ProgramFunction)) {
|
|
@@ -186,9 +191,9 @@ const constructPromise = (runner, promises, executor, node) => {
|
|
|
186
191
|
return Effect.gen(function* () {
|
|
187
192
|
const deferred = Deferred.makeUnsafe();
|
|
188
193
|
const promise = yield* promises.createWithSelf((self) => Effect.flatMap(Deferred.await(deferred), (value) => resolvePromiseValue(runner, value, node, self)));
|
|
189
|
-
const resolve = capability("resolve", (value) => Deferred.doneUnsafe(deferred, Exit.succeed(value)));
|
|
190
|
-
const reject = capability("reject", (value) => Deferred.doneUnsafe(deferred, Exit.fail(new ProgramThrow(value))));
|
|
191
|
-
const executed = yield* Effect.exit(runner.
|
|
194
|
+
const resolve = capability(runner, "resolve", (value) => Deferred.doneUnsafe(deferred, Exit.succeed(value)));
|
|
195
|
+
const reject = capability(runner, "reject", (value) => Deferred.doneUnsafe(deferred, Exit.fail(new ProgramThrow(value))));
|
|
196
|
+
const executed = yield* Effect.exit(runner.invokeCallable(executor, undefined, [resolve, reject], node));
|
|
192
197
|
if (!Exit.isSuccess(executed)) {
|
|
193
198
|
if (Cause.hasInterruptsOnly(executed.cause))
|
|
194
199
|
return yield* Effect.failCause(executed.cause);
|
|
@@ -245,26 +250,27 @@ const chainFinally = (runner, promises, source, cleanup, method, node) => promis
|
|
|
245
250
|
return yield* exit;
|
|
246
251
|
}));
|
|
247
252
|
export const promiseGlobal = (runner, promises) => {
|
|
248
|
-
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
new HostFunction({
|
|
252
|
-
name: `Promise.${name}`,
|
|
253
|
-
call: (args, node) => invokePromiseMethod(runner, promises, name, args, node),
|
|
254
|
-
callback: false,
|
|
255
|
-
}),
|
|
256
|
-
]));
|
|
257
|
-
return new HostFunction({
|
|
253
|
+
const protos = runner.prototypes;
|
|
254
|
+
const proto = protos.Promise;
|
|
255
|
+
const promise = constructor(protos, proto, {
|
|
258
256
|
name: "Promise",
|
|
257
|
+
length: 1,
|
|
259
258
|
call: requiresNew("Promise"),
|
|
260
|
-
construct: (args, node) => constructPromise(runner, promises, args[0], node),
|
|
261
|
-
instanceOf: (value) => value instanceof Values.Promise,
|
|
262
|
-
// Unknown statics fail loudly so a missing await cannot hide behind `undefined`.
|
|
263
|
-
members: (key, node) => {
|
|
264
|
-
const method = typeof key === "string" ? statics.get(key) : undefined;
|
|
265
|
-
if (method !== undefined)
|
|
266
|
-
return method;
|
|
267
|
-
throw new InterpreterRuntimeError(`Promise.${String(key)} is not available. Available: Promise.all, Promise.allSettled, Promise.race, Promise.any, Promise.resolve, and Promise.reject; consume promises with await.`, node);
|
|
268
|
-
},
|
|
259
|
+
construct: (args, _, node) => constructPromise(runner, promises, args[0], node),
|
|
269
260
|
});
|
|
261
|
+
// Combinators are not callbacks: `[p].map(Promise.resolve)` must ask for an arrow function.
|
|
262
|
+
for (const name of promiseStatics) {
|
|
263
|
+
define(promise, name, native(protos, {
|
|
264
|
+
name,
|
|
265
|
+
length: 1,
|
|
266
|
+
call: (_, args, node) => invokePromiseMethod(runner, promises, name, args, node),
|
|
267
|
+
callback: false,
|
|
268
|
+
}), hidden);
|
|
269
|
+
}
|
|
270
|
+
methods(protos, proto, [
|
|
271
|
+
["then", 2, (thisValue, args, node) => instanceMethod(runner, promises, "then", thisValue, args, node)],
|
|
272
|
+
["catch", 1, (thisValue, args, node) => instanceMethod(runner, promises, "catch", thisValue, args, node)],
|
|
273
|
+
["finally", 1, (thisValue, args, node) => instanceMethod(runner, promises, "finally", thisValue, args, node)],
|
|
274
|
+
]);
|
|
275
|
+
return promise;
|
|
270
276
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type AstNode } from "./model.js";
|
|
2
|
+
/** Values that cannot cross the data boundary. */
|
|
2
3
|
export declare const isRuntimeReference: (value: unknown) => boolean;
|
|
3
4
|
export declare const containsRuntimeReference: (value: unknown) => boolean;
|
|
4
5
|
export declare const containsOpaqueReference: (value: unknown) => boolean;
|