@opencode/codemode 2.0.15 → 2.0.17
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 +2 -2
- package/dist/data.js +1 -1
- package/dist/interpreter/callback.d.ts +10 -3
- package/dist/interpreter/callback.js +17 -4
- package/dist/interpreter/errors.js +8 -4
- package/dist/interpreter/globals.d.ts +1 -1
- package/dist/interpreter/globals.js +38 -3
- package/dist/interpreter/interpreter.js +229 -122
- package/dist/interpreter/intrinsics.d.ts +1 -1
- package/dist/interpreter/intrinsics.js +4 -0
- package/dist/interpreter/model.d.ts +1 -1
- package/dist/interpreter/model.js +1 -1
- package/dist/interpreter/objects.d.ts +29 -1
- package/dist/interpreter/objects.js +68 -9
- package/dist/stdlib/array.d.ts +7 -1
- package/dist/stdlib/array.js +93 -54
- package/dist/stdlib/bytes.js +19 -1
- package/dist/stdlib/collections.d.ts +2 -0
- package/dist/stdlib/collections.js +130 -3
- package/dist/stdlib/date.js +10 -3
- package/dist/stdlib/headers.js +1 -1
- package/dist/stdlib/json.js +1 -1
- package/dist/stdlib/math.js +18 -14
- package/dist/stdlib/number.js +23 -34
- package/dist/stdlib/object.js +64 -10
- package/dist/stdlib/string.js +98 -72
- package/dist/stdlib/url.js +1 -1
- package/dist/stdlib/value.d.ts +2 -1
- package/dist/stdlib/value.js +14 -11
- package/dist/stdlib/web.d.ts +1 -0
- package/dist/stdlib/web.js +63 -1
- package/package.json +1 -1
|
@@ -49,7 +49,7 @@ export const referenceError = failure("ReferenceError");
|
|
|
49
49
|
export const syntaxError = failure("SyntaxError");
|
|
50
50
|
export const uriError = failure("URIError");
|
|
51
51
|
// Orient the agent rather than enumerate JavaScript; interpreter-support.md is the full matrix.
|
|
52
|
-
export const supportedSyntaxMessage = "This is a restricted JavaScript-like language. Supported: plain and async functions, data literals, destructuring, standard control flow, await and Promise, and built-ins such as Array, Object, Math, JSON, Date, RegExp, Map, Set, and URL. Unsupported: classes,
|
|
52
|
+
export const supportedSyntaxMessage = "This is a restricted JavaScript-like language. Supported: plain and async functions, data literals, destructuring, standard control flow, await and Promise, and built-ins such as Array, Object, Math, JSON, Date, RegExp, Map, Set, and URL. Unsupported: classes, getters/setters, BigInt, and custom Symbols. Use plain functions and data objects instead.";
|
|
53
53
|
export const unsupportedSyntax = (kind, node) => new PendingThrow("SyntaxError", `Syntax '${kind}' is not supported. ${supportedSyntaxMessage}`, node, "UnsupportedSyntax", [supportedSyntaxMessage]);
|
|
54
54
|
// Acorn lines are 1-based and its columns are 0-based. Diagnostics use 1-based columns of the submitted source.
|
|
55
55
|
export const sourceLocation = (node) => ({
|
|
@@ -38,6 +38,8 @@ export declare const frozen: Attributes;
|
|
|
38
38
|
export declare class Obj {
|
|
39
39
|
proto: Obj | null;
|
|
40
40
|
readonly props: Map<string | symbol, Slot>;
|
|
41
|
+
/** [[Extensible]]: cleared by `Object.preventExtensions`, `seal`, and `freeze`. */
|
|
42
|
+
extensible: boolean;
|
|
41
43
|
constructor(proto: Obj | null);
|
|
42
44
|
/** The class name `Object.prototype.toString` reports: `[object Map]`. */
|
|
43
45
|
readonly tag: string;
|
|
@@ -59,6 +61,8 @@ export declare class Obj {
|
|
|
59
61
|
export declare class Arr extends Obj {
|
|
60
62
|
readonly items: Array<Value>;
|
|
61
63
|
readonly tag = "Array";
|
|
64
|
+
/** The attributes every live element shares; `seal` and `freeze` narrow them since elements have no slots. */
|
|
65
|
+
elements: Attributes;
|
|
62
66
|
constructor(proto: Obj, items?: Array<Value>);
|
|
63
67
|
get describe(): string;
|
|
64
68
|
toString(): string;
|
|
@@ -80,6 +84,7 @@ export declare abstract class Opaque extends Obj {
|
|
|
80
84
|
inspect(): string;
|
|
81
85
|
}
|
|
82
86
|
export declare abstract class Callable extends Opaque {
|
|
87
|
+
readonly length: number;
|
|
83
88
|
readonly tag = "Function";
|
|
84
89
|
constructor(proto: Obj, name: string, length: number);
|
|
85
90
|
get describe(): string;
|
|
@@ -90,7 +95,17 @@ export declare class Fn extends Callable {
|
|
|
90
95
|
readonly capturedScopes: Array<Map<string, Binding>>;
|
|
91
96
|
readonly async: boolean;
|
|
92
97
|
readonly generator: boolean;
|
|
93
|
-
|
|
98
|
+
/** Arrows have no `this` or `arguments` of their own; they read the enclosing function's. */
|
|
99
|
+
readonly arrow: boolean;
|
|
100
|
+
constructor(proto: Obj, name: string, parameters: ReadonlyArray<Pattern>, body: BlockStatement | Expression, capturedScopes: Array<Map<string, Binding>>, async: boolean, generator: boolean,
|
|
101
|
+
/** Arrows have no `this` or `arguments` of their own; they read the enclosing function's. */
|
|
102
|
+
arrow: boolean);
|
|
103
|
+
}
|
|
104
|
+
/** The strict `arguments` object: an ordinary object with indexed own properties and a hidden `length`. */
|
|
105
|
+
export declare class Arguments extends Obj {
|
|
106
|
+
readonly tag = "Arguments";
|
|
107
|
+
constructor(proto: Obj, args: Array<Value>);
|
|
108
|
+
iterator(): ArrayIterator<Value>;
|
|
94
109
|
}
|
|
95
110
|
export type NativeCall<R> = (thisValue: Value, args: Array<Value>) => Effect.Effect<Value, unknown, R>;
|
|
96
111
|
export type NativeConstruct<R> = (args: Array<Value>, newTarget: Callable) => Effect.Effect<Value, unknown, R>;
|
|
@@ -177,6 +192,17 @@ export declare class SetObj extends Wrapper {
|
|
|
177
192
|
toHost(item: (value: Value) => unknown): Set<unknown>;
|
|
178
193
|
iterator(): SetIterator<Value>;
|
|
179
194
|
}
|
|
195
|
+
/** Keys are program objects, so a host WeakMap gives the same lifetime rule as JavaScript without any bookkeeping. */
|
|
196
|
+
export declare class WeakMapObj extends Wrapper {
|
|
197
|
+
readonly tag = "WeakMap";
|
|
198
|
+
readonly map: WeakMap<Obj, Value>;
|
|
199
|
+
inspect(): string;
|
|
200
|
+
}
|
|
201
|
+
export declare class WeakSetObj extends Wrapper {
|
|
202
|
+
readonly tag = "WeakSet";
|
|
203
|
+
readonly set: WeakSet<Obj>;
|
|
204
|
+
inspect(): string;
|
|
205
|
+
}
|
|
180
206
|
export declare class URLSearchParamsObj extends Wrapper {
|
|
181
207
|
readonly params: URLSearchParams;
|
|
182
208
|
readonly tag = "URLSearchParams";
|
|
@@ -232,6 +258,8 @@ export declare const has: (target: Obj, key: PropertyKey) => boolean;
|
|
|
232
258
|
export declare const hasPrototype: (value: Value, proto: Obj) => boolean;
|
|
233
259
|
/** [[Set]]: an inherited setter or read-only property decides before an own data property is created. */
|
|
234
260
|
export declare const set: (target: Obj, key: PropertyKey, value: Value) => boolean;
|
|
261
|
+
/** Creating a property on a non-extensible object is the one [[Set]] failure with its own message. */
|
|
262
|
+
export declare const rejectAddition: (target: Obj, key: string | symbol | number) => void;
|
|
235
263
|
/** [[DefineOwnProperty]] for a data property, ignoring the chain. */
|
|
236
264
|
export declare const define: (target: Obj, key: PropertyKey, value: Value, attrs?: Attributes) => void;
|
|
237
265
|
export declare const defineAccessor: (target: Obj, key: PropertyKey, get: Getter | undefined, set?: Setter) => void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
2
|
import { ToolReference } from "../tool-runtime.js";
|
|
3
3
|
import { checkArrayLength } from "./limits.js";
|
|
4
|
-
import { AsyncIteratorSymbol, IteratorSymbol, } from "./model.js";
|
|
4
|
+
import { AsyncIteratorSymbol, IteratorSymbol, rangeError, typeError, } from "./model.js";
|
|
5
5
|
/** Ordinary assignment: writable, enumerable, configurable. */
|
|
6
6
|
export const data = { writable: true, enumerable: true, configurable: true };
|
|
7
7
|
/** Built-in methods and `constructor`: writable and configurable but hidden from enumeration. */
|
|
@@ -17,6 +17,8 @@ export const frozen = { writable: false, enumerable: false, configurable: false
|
|
|
17
17
|
export class Obj {
|
|
18
18
|
proto;
|
|
19
19
|
props = new Map();
|
|
20
|
+
/** [[Extensible]]: cleared by `Object.preventExtensions`, `seal`, and `freeze`. */
|
|
21
|
+
extensible = true;
|
|
20
22
|
constructor(proto) {
|
|
21
23
|
this.proto = proto;
|
|
22
24
|
}
|
|
@@ -60,6 +62,8 @@ export class Obj {
|
|
|
60
62
|
export class Arr extends Obj {
|
|
61
63
|
items;
|
|
62
64
|
tag = "Array";
|
|
65
|
+
/** The attributes every live element shares; `seal` and `freeze` narrow them since elements have no slots. */
|
|
66
|
+
elements = data;
|
|
63
67
|
constructor(proto, items = []) {
|
|
64
68
|
super(proto);
|
|
65
69
|
this.items = items;
|
|
@@ -108,9 +112,11 @@ export class Opaque extends Obj {
|
|
|
108
112
|
}
|
|
109
113
|
}
|
|
110
114
|
export class Callable extends Opaque {
|
|
115
|
+
length;
|
|
111
116
|
tag = "Function";
|
|
112
117
|
constructor(proto, name, length) {
|
|
113
118
|
super(proto);
|
|
119
|
+
this.length = length;
|
|
114
120
|
define(this, "length", length, readonly);
|
|
115
121
|
define(this, "name", name, readonly);
|
|
116
122
|
}
|
|
@@ -124,7 +130,10 @@ export class Fn extends Callable {
|
|
|
124
130
|
capturedScopes;
|
|
125
131
|
async;
|
|
126
132
|
generator;
|
|
127
|
-
|
|
133
|
+
arrow;
|
|
134
|
+
constructor(proto, name, parameters, body, capturedScopes, async, generator,
|
|
135
|
+
/** Arrows have no `this` or `arguments` of their own; they read the enclosing function's. */
|
|
136
|
+
arrow) {
|
|
128
137
|
const optional = parameters.findIndex((p) => p.type === "AssignmentPattern" || p.type === "RestElement");
|
|
129
138
|
super(proto, name, optional === -1 ? parameters.length : optional);
|
|
130
139
|
this.parameters = parameters;
|
|
@@ -132,6 +141,21 @@ export class Fn extends Callable {
|
|
|
132
141
|
this.capturedScopes = capturedScopes;
|
|
133
142
|
this.async = async;
|
|
134
143
|
this.generator = generator;
|
|
144
|
+
this.arrow = arrow;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** The strict `arguments` object: an ordinary object with indexed own properties and a hidden `length`. */
|
|
148
|
+
export class Arguments extends Obj {
|
|
149
|
+
tag = "Arguments";
|
|
150
|
+
constructor(proto, args) {
|
|
151
|
+
super(proto);
|
|
152
|
+
args.forEach((arg, index) => define(this, String(index), arg));
|
|
153
|
+
define(this, "length", args.length, hidden);
|
|
154
|
+
}
|
|
155
|
+
iterator() {
|
|
156
|
+
return keys(this)
|
|
157
|
+
.map((key) => get(this, key))
|
|
158
|
+
.values();
|
|
135
159
|
}
|
|
136
160
|
}
|
|
137
161
|
export class Native extends Callable {
|
|
@@ -257,6 +281,21 @@ export class SetObj extends Wrapper {
|
|
|
257
281
|
return this.set.values();
|
|
258
282
|
}
|
|
259
283
|
}
|
|
284
|
+
/** Keys are program objects, so a host WeakMap gives the same lifetime rule as JavaScript without any bookkeeping. */
|
|
285
|
+
export class WeakMapObj extends Wrapper {
|
|
286
|
+
tag = "WeakMap";
|
|
287
|
+
map = new WeakMap();
|
|
288
|
+
inspect() {
|
|
289
|
+
return "WeakMap { <items unknown> }";
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
export class WeakSetObj extends Wrapper {
|
|
293
|
+
tag = "WeakSet";
|
|
294
|
+
set = new WeakSet();
|
|
295
|
+
inspect() {
|
|
296
|
+
return "WeakSet { <items unknown> }";
|
|
297
|
+
}
|
|
298
|
+
}
|
|
260
299
|
export class URLSearchParamsObj extends Wrapper {
|
|
261
300
|
params;
|
|
262
301
|
tag = "URLSearchParams";
|
|
@@ -363,10 +402,12 @@ export const own = (target, key) => {
|
|
|
363
402
|
const at = index(target, name);
|
|
364
403
|
if (at !== undefined) {
|
|
365
404
|
const items = elements(target);
|
|
366
|
-
|
|
405
|
+
if (!(at in items))
|
|
406
|
+
return undefined;
|
|
407
|
+
return { value: items[at], ...(target instanceof Arr ? target.elements : data) };
|
|
367
408
|
}
|
|
368
409
|
if (target instanceof Arr && name === "length") {
|
|
369
|
-
return { value: target.items.length, writable:
|
|
410
|
+
return { value: target.items.length, writable: target.elements.writable, enumerable: false, configurable: false };
|
|
370
411
|
}
|
|
371
412
|
}
|
|
372
413
|
return target.props.get(name);
|
|
@@ -403,16 +444,22 @@ export const hasPrototype = (value, proto) => {
|
|
|
403
444
|
const writeElement = (target, name, value) => {
|
|
404
445
|
const at = index(target, name);
|
|
405
446
|
if (at !== undefined) {
|
|
406
|
-
if (target instanceof Bytes)
|
|
447
|
+
if (target instanceof Bytes) {
|
|
407
448
|
target.bytes[at] = typeof value === "number" ? value : Number(value);
|
|
408
|
-
|
|
409
|
-
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
if (!(at in target.items))
|
|
452
|
+
rejectAddition(target, at);
|
|
453
|
+
target.items[at] = value;
|
|
410
454
|
return true;
|
|
411
455
|
}
|
|
412
456
|
if (!(target instanceof Arr) || name !== "length")
|
|
413
457
|
return undefined;
|
|
414
458
|
const length = typeof value === "number" ? value : Number(value);
|
|
415
459
|
if (!Number.isInteger(length) || length < 0)
|
|
460
|
+
throw rangeError("Invalid array length");
|
|
461
|
+
// Shrinking deletes elements, which a sealed array forbids.
|
|
462
|
+
if (length < target.items.length && !target.elements.configurable)
|
|
416
463
|
return false;
|
|
417
464
|
checkArrayLength(length);
|
|
418
465
|
target.items.length = length;
|
|
@@ -448,9 +495,16 @@ export const set = (target, key, value) => {
|
|
|
448
495
|
if (written !== undefined)
|
|
449
496
|
return written;
|
|
450
497
|
}
|
|
498
|
+
rejectAddition(target, name);
|
|
451
499
|
target.props.set(name, { value, ...data });
|
|
452
500
|
return true;
|
|
453
501
|
};
|
|
502
|
+
/** Creating a property on a non-extensible object is the one [[Set]] failure with its own message. */
|
|
503
|
+
export const rejectAddition = (target, key) => {
|
|
504
|
+
if (target.extensible)
|
|
505
|
+
return;
|
|
506
|
+
throw typeError(`Cannot add property ${String(key)}, object is not extensible.`);
|
|
507
|
+
};
|
|
454
508
|
/** [[DefineOwnProperty]] for a data property, ignoring the chain. */
|
|
455
509
|
export const define = (target, key, value, attrs = data) => {
|
|
456
510
|
const name = canonical(key);
|
|
@@ -465,8 +519,13 @@ export const remove = (target, key) => {
|
|
|
465
519
|
const name = canonical(key);
|
|
466
520
|
if (isIndexed(target)) {
|
|
467
521
|
const at = index(target, name);
|
|
468
|
-
if (at !== undefined)
|
|
469
|
-
|
|
522
|
+
if (at !== undefined) {
|
|
523
|
+
if (target instanceof Bytes)
|
|
524
|
+
return !(at in target.bytes);
|
|
525
|
+
if (at in target.items && !target.elements.configurable)
|
|
526
|
+
return false;
|
|
527
|
+
return delete target.items[at];
|
|
528
|
+
}
|
|
470
529
|
if (target instanceof Arr && name === "length")
|
|
471
530
|
return false;
|
|
472
531
|
}
|
package/dist/stdlib/array.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
-
import { type
|
|
2
|
+
import { type Method } from "../interpreter/native.js";
|
|
3
|
+
import { Obj, type Value } from "../interpreter/objects.js";
|
|
3
4
|
import type { Interpreter } from "../interpreter/interpreter.js";
|
|
4
5
|
export declare const sortArray: <R>(ctx: Interpreter<R>, target: Array<Value>, comparator: Value, name: string) => Effect.Effect<Array<Value>, unknown, R>;
|
|
5
6
|
export declare const arrayGlobal: <R>(ctx: Interpreter<R>) => import("../interpreter/objects.js").Native<R>;
|
|
7
|
+
/**
|
|
8
|
+
* The callback methods Array and Uint8Array share. They fix the iteration length while reading existing elements
|
|
9
|
+
* live; `wrap` builds the collection `map` and `filter` return.
|
|
10
|
+
*/
|
|
11
|
+
export declare const callbackMethods: <R, T extends Obj>(ctx: Interpreter<R>, label: string, self: (thisValue: Value, name: string) => T, elements: (target: T) => ArrayLike<Value>, wrap: (values: Array<Value>) => Value) => Array<Method>;
|
package/dist/stdlib/array.js
CHANGED
|
@@ -2,9 +2,9 @@ import { Effect } from "effect";
|
|
|
2
2
|
import { constructor, methods, prototypeFrom, receiver } from "../interpreter/native.js";
|
|
3
3
|
import { checkArrayLength, checkStringLength, MAX_ARRAY_LENGTH } from "../interpreter/limits.js";
|
|
4
4
|
import { invalidData, IteratorSymbol, rangeError, typeError } from "../interpreter/model.js";
|
|
5
|
-
import { define, get, hidden, Arr, GeneratorObj, hostIterator, Obj, PromiseObj, coerceToInteger, coerceToNumber, coerceToString, } from "../interpreter/objects.js";
|
|
5
|
+
import { define, get, hidden, Arr, GeneratorObj, hostIterator, Obj, PromiseObj, coerceToInteger, coerceToNumber, coerceToString, rejectAddition, } from "../interpreter/objects.js";
|
|
6
6
|
import { describeValue, rejectCircularInsertion } from "../interpreter/references.js";
|
|
7
|
-
import { applyCollectionCallback, invoke, preserveConsumerError } from "../interpreter/callback.js";
|
|
7
|
+
import { applyCollectionCallback, invoke, preserveConsumerError, withPrimitives } from "../interpreter/callback.js";
|
|
8
8
|
import { compareText } from "../tool-runtime.js";
|
|
9
9
|
const arrayLikeSource = (source) => {
|
|
10
10
|
// JS would treat a promise as an empty array-like; that would hide a missing `await`.
|
|
@@ -29,7 +29,7 @@ const arrayFrom = (ctx, args) => {
|
|
|
29
29
|
const values = [];
|
|
30
30
|
for (let index = 0; index < arrayLike.length; index += 1) {
|
|
31
31
|
const item = get(arrayLike.source, index);
|
|
32
|
-
values.push(apply === undefined ? item : yield* apply([item, index]));
|
|
32
|
+
values.push(apply === undefined ? item : yield* apply([item, index], args[2]));
|
|
33
33
|
}
|
|
34
34
|
return new Arr(proto, values);
|
|
35
35
|
}
|
|
@@ -39,7 +39,9 @@ const arrayFrom = (ctx, args) => {
|
|
|
39
39
|
const step = yield* cursor.next;
|
|
40
40
|
if (step.done)
|
|
41
41
|
return new Arr(proto, values);
|
|
42
|
-
values.push(apply === undefined
|
|
42
|
+
values.push(apply === undefined
|
|
43
|
+
? step.value
|
|
44
|
+
: yield* preserveConsumerError(cursor.close, apply([step.value, index], args[2])));
|
|
43
45
|
index += 1;
|
|
44
46
|
}
|
|
45
47
|
});
|
|
@@ -102,34 +104,40 @@ export const arrayGlobal = (ctx) => {
|
|
|
102
104
|
["from", 1, (_, args) => arrayFrom(ctx, args)],
|
|
103
105
|
]);
|
|
104
106
|
const self = (thisValue, name) => receiver(Arr, thisValue, `Array.prototype.${name}`);
|
|
107
|
+
// A mutating method fails where its first element write, delete, or `length` write would on a frozen, sealed, or
|
|
108
|
+
// non-extensible array. `growth` is given by the methods that always write `length`, even when it is 0; holes a
|
|
109
|
+
// method would fill on a non-extensible array are not checked.
|
|
110
|
+
const mutable = (target, growth) => {
|
|
111
|
+
const length = target.items.length;
|
|
112
|
+
if ((growth ?? 0) > 0)
|
|
113
|
+
rejectAddition(target, length);
|
|
114
|
+
if ((growth ?? 0) < 0 && length > 0 && !target.elements.configurable) {
|
|
115
|
+
throw typeError(`Cannot delete property '${length - 1}'.`);
|
|
116
|
+
}
|
|
117
|
+
if (!target.elements.writable && (length > 0 || growth !== undefined)) {
|
|
118
|
+
throw typeError(`Cannot assign to read only property '${length > 0 ? 0 : "length"}'.`);
|
|
119
|
+
}
|
|
120
|
+
return target;
|
|
121
|
+
};
|
|
105
122
|
const optNumber = (value) => (value === undefined ? undefined : coerceToInteger(value));
|
|
106
|
-
// Callback methods fix the iteration length while reading existing elements live.
|
|
107
|
-
const iterate = (name, length, body) => [
|
|
108
|
-
name,
|
|
109
|
-
length,
|
|
110
|
-
(thisValue, args) => {
|
|
111
|
-
const target = self(thisValue, name);
|
|
112
|
-
return body(target.items, target, applyCollectionCallback(ctx, args[0], `Array.${name}`), args);
|
|
113
|
-
},
|
|
114
|
-
];
|
|
115
123
|
methods(builtins, proto, [
|
|
116
124
|
[
|
|
117
125
|
"join",
|
|
118
126
|
1,
|
|
119
127
|
(thisValue, args) => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
128
|
+
// .map would keep holes, which Effect.forEach would then hand to the body as undefined.
|
|
129
|
+
const parts = Array.from(self(thisValue, "join").items, (item) => item ?? "");
|
|
130
|
+
return withPrimitives(ctx, "string", [args[0] === undefined ? "," : args[0], ...parts], ([separator, ...items]) => {
|
|
131
|
+
const joined = items.map(coerceToString).join(coerceToString(separator));
|
|
132
|
+
checkStringLength(joined.length);
|
|
133
|
+
return joined;
|
|
134
|
+
});
|
|
125
135
|
},
|
|
126
136
|
],
|
|
127
137
|
[
|
|
128
138
|
"toString",
|
|
129
139
|
0,
|
|
130
|
-
(thisValue) => self(thisValue, "toString")
|
|
131
|
-
.items.map((item) => coerceToString(item ?? ""))
|
|
132
|
-
.join(","),
|
|
140
|
+
(thisValue) => withPrimitives(ctx, "string", Array.from(self(thisValue, "toString").items, (item) => item ?? ""), (items) => items.map(coerceToString).join(",")),
|
|
133
141
|
],
|
|
134
142
|
[
|
|
135
143
|
"includes",
|
|
@@ -144,7 +152,8 @@ export const arrayGlobal = (ctx) => {
|
|
|
144
152
|
1,
|
|
145
153
|
(thisValue, args) => {
|
|
146
154
|
const target = self(thisValue, "lastIndexOf").items;
|
|
147
|
-
|
|
155
|
+
// An explicit undefined is a fromIndex of 0, unlike omitting it.
|
|
156
|
+
return args.length < 2 ? target.lastIndexOf(args[0]) : target.lastIndexOf(args[0], optNumber(args[1]));
|
|
148
157
|
},
|
|
149
158
|
],
|
|
150
159
|
["at", 1, (thisValue, args) => self(thisValue, "at").items.at(optNumber(args[0]) ?? 0)],
|
|
@@ -177,6 +186,9 @@ export const arrayGlobal = (ctx) => {
|
|
|
177
186
|
0,
|
|
178
187
|
(thisValue) => {
|
|
179
188
|
const target = self(thisValue, "reverse");
|
|
189
|
+
// Fewer than two elements means no writes at all, so a frozen one-element array reverses fine.
|
|
190
|
+
if (target.items.length > 1)
|
|
191
|
+
mutable(target);
|
|
180
192
|
target.items.reverse();
|
|
181
193
|
return target;
|
|
182
194
|
},
|
|
@@ -185,7 +197,7 @@ export const arrayGlobal = (ctx) => {
|
|
|
185
197
|
"sort",
|
|
186
198
|
1,
|
|
187
199
|
(thisValue, args) => {
|
|
188
|
-
const target = self(thisValue, "sort");
|
|
200
|
+
const target = mutable(self(thisValue, "sort"));
|
|
189
201
|
const items = target.items;
|
|
190
202
|
const length = items.length;
|
|
191
203
|
const holeCount = Array.from({ length }, (_, index) => Object.hasOwn(items, index)).filter((o) => !o).length;
|
|
@@ -225,7 +237,7 @@ export const arrayGlobal = (ctx) => {
|
|
|
225
237
|
"push",
|
|
226
238
|
1,
|
|
227
239
|
(thisValue, args) => {
|
|
228
|
-
const target = self(thisValue, "push");
|
|
240
|
+
const target = mutable(self(thisValue, "push"), args.length);
|
|
229
241
|
// Validate all insertions before mutating to avoid partial cyclic updates.
|
|
230
242
|
for (const item of args)
|
|
231
243
|
rejectCircularInsertion(target, item, "Array.push result");
|
|
@@ -236,29 +248,28 @@ export const arrayGlobal = (ctx) => {
|
|
|
236
248
|
"unshift",
|
|
237
249
|
1,
|
|
238
250
|
(thisValue, args) => {
|
|
239
|
-
const target = self(thisValue, "unshift");
|
|
251
|
+
const target = mutable(self(thisValue, "unshift"), args.length);
|
|
240
252
|
for (const item of args)
|
|
241
253
|
rejectCircularInsertion(target, item, "Array.unshift result");
|
|
242
254
|
return target.items.unshift(...args);
|
|
243
255
|
},
|
|
244
256
|
],
|
|
245
|
-
["pop", 0, (thisValue) => self(thisValue, "pop").items.pop()],
|
|
246
|
-
["shift", 0, (thisValue) => self(thisValue, "shift").items.shift()],
|
|
257
|
+
["pop", 0, (thisValue) => mutable(self(thisValue, "pop"), -1).items.pop()],
|
|
258
|
+
["shift", 0, (thisValue) => mutable(self(thisValue, "shift"), -1).items.shift()],
|
|
247
259
|
[
|
|
248
260
|
"splice",
|
|
249
261
|
2,
|
|
250
262
|
(thisValue, args) => {
|
|
251
263
|
const target = self(thisValue, "splice");
|
|
252
|
-
|
|
253
|
-
return wrap(target.items.splice(0, 0));
|
|
264
|
+
const length = target.items.length;
|
|
254
265
|
const start = optNumber(args[0]) ?? 0;
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
const deleteCount = optNumber(args[1]) ?? 0;
|
|
266
|
+
const from = start < 0 ? Math.max(length + start, 0) : Math.min(start, length);
|
|
267
|
+
const deleteCount = args.length === 1 ? length - from : Math.min(Math.max(optNumber(args[1]) ?? 0, 0), length - from);
|
|
258
268
|
const inserted = args.slice(2);
|
|
259
269
|
for (const item of inserted)
|
|
260
270
|
rejectCircularInsertion(target, item, "Array.splice result");
|
|
261
|
-
|
|
271
|
+
mutable(target, inserted.length - deleteCount);
|
|
272
|
+
return wrap(target.items.splice(from, deleteCount, ...inserted));
|
|
262
273
|
},
|
|
263
274
|
],
|
|
264
275
|
[
|
|
@@ -280,7 +291,7 @@ export const arrayGlobal = (ctx) => {
|
|
|
280
291
|
"fill",
|
|
281
292
|
1,
|
|
282
293
|
(thisValue, args) => {
|
|
283
|
-
const target = self(thisValue, "fill");
|
|
294
|
+
const target = mutable(self(thisValue, "fill"));
|
|
284
295
|
rejectCircularInsertion(target, args[0], "Array.fill result");
|
|
285
296
|
target.items.fill(args[0], optNumber(args[1]), optNumber(args[2]));
|
|
286
297
|
return target;
|
|
@@ -290,7 +301,7 @@ export const arrayGlobal = (ctx) => {
|
|
|
290
301
|
"copyWithin",
|
|
291
302
|
2,
|
|
292
303
|
(thisValue, args) => {
|
|
293
|
-
const target = self(thisValue, "copyWithin");
|
|
304
|
+
const target = mutable(self(thisValue, "copyWithin"));
|
|
294
305
|
target.items.copyWithin(optNumber(args[0]) ?? 0, optNumber(args[1]) ?? 0, optNumber(args[2]));
|
|
295
306
|
return target;
|
|
296
307
|
},
|
|
@@ -311,6 +322,50 @@ export const arrayGlobal = (ctx) => {
|
|
|
311
322
|
.items.entries()
|
|
312
323
|
.map(([index, item]) => wrap([index, item]))),
|
|
313
324
|
],
|
|
325
|
+
[
|
|
326
|
+
"flatMap",
|
|
327
|
+
1,
|
|
328
|
+
(thisValue, args) => {
|
|
329
|
+
const target = self(thisValue, "flatMap");
|
|
330
|
+
const apply = applyCollectionCallback(ctx, args[0], "Array.flatMap");
|
|
331
|
+
return Effect.gen(function* () {
|
|
332
|
+
const length = target.items.length;
|
|
333
|
+
const values = [];
|
|
334
|
+
for (let index = 0; index < length; index += 1) {
|
|
335
|
+
if (!(index in target.items))
|
|
336
|
+
continue;
|
|
337
|
+
const mapped = yield* apply([target.items[index], index, target], args[1]);
|
|
338
|
+
if (mapped instanceof Arr)
|
|
339
|
+
values.push(...mapped.items);
|
|
340
|
+
else
|
|
341
|
+
values.push(mapped);
|
|
342
|
+
}
|
|
343
|
+
return wrap(values);
|
|
344
|
+
});
|
|
345
|
+
},
|
|
346
|
+
],
|
|
347
|
+
...callbackMethods(ctx, "Array", self, (target) => target.items, wrap),
|
|
348
|
+
]);
|
|
349
|
+
define(proto, IteratorSymbol, get(proto, "values"), hidden);
|
|
350
|
+
return array;
|
|
351
|
+
};
|
|
352
|
+
/**
|
|
353
|
+
* The callback methods Array and Uint8Array share. They fix the iteration length while reading existing elements
|
|
354
|
+
* live; `wrap` builds the collection `map` and `filter` return.
|
|
355
|
+
*/
|
|
356
|
+
export const callbackMethods = (ctx, label, self, elements, wrap) => {
|
|
357
|
+
const iterate = (name, length, body) => [
|
|
358
|
+
name,
|
|
359
|
+
length,
|
|
360
|
+
(thisValue, args) => {
|
|
361
|
+
const target = self(thisValue, name);
|
|
362
|
+
const call = applyCollectionCallback(ctx, args[0], `${label}.${name}`);
|
|
363
|
+
// reduce and reduceRight take an initial value where the others take a thisArg.
|
|
364
|
+
const thisArg = name.startsWith("reduce") ? undefined : args[1];
|
|
365
|
+
return body(elements(target), target, (callbackArgs) => call(callbackArgs, thisArg), args);
|
|
366
|
+
},
|
|
367
|
+
];
|
|
368
|
+
return [
|
|
314
369
|
iterate("map", 1, (target, receiver, apply) => Effect.gen(function* () {
|
|
315
370
|
const length = target.length;
|
|
316
371
|
const values = [];
|
|
@@ -322,20 +377,6 @@ export const arrayGlobal = (ctx) => {
|
|
|
322
377
|
}
|
|
323
378
|
return wrap(values);
|
|
324
379
|
})),
|
|
325
|
-
iterate("flatMap", 1, (target, receiver, apply) => Effect.gen(function* () {
|
|
326
|
-
const length = target.length;
|
|
327
|
-
const values = [];
|
|
328
|
-
for (let index = 0; index < length; index += 1) {
|
|
329
|
-
if (!(index in target))
|
|
330
|
-
continue;
|
|
331
|
-
const mapped = yield* apply([target[index], index, receiver]);
|
|
332
|
-
if (mapped instanceof Arr)
|
|
333
|
-
values.push(...mapped.items);
|
|
334
|
-
else
|
|
335
|
-
values.push(mapped);
|
|
336
|
-
}
|
|
337
|
-
return wrap(values);
|
|
338
|
-
})),
|
|
339
380
|
iterate("filter", 1, (target, receiver, apply) => Effect.gen(function* () {
|
|
340
381
|
const length = target.length;
|
|
341
382
|
const values = [];
|
|
@@ -416,7 +457,7 @@ export const arrayGlobal = (ctx) => {
|
|
|
416
457
|
while (start < length && !(start in target))
|
|
417
458
|
start += 1;
|
|
418
459
|
if (start === length) {
|
|
419
|
-
throw typeError(
|
|
460
|
+
throw typeError(`${label}.reduce of an empty array with no initial value.`);
|
|
420
461
|
}
|
|
421
462
|
accumulator = target[start];
|
|
422
463
|
start += 1;
|
|
@@ -435,7 +476,7 @@ export const arrayGlobal = (ctx) => {
|
|
|
435
476
|
while (start >= 0 && !(start in target))
|
|
436
477
|
start -= 1;
|
|
437
478
|
if (start < 0) {
|
|
438
|
-
throw typeError(
|
|
479
|
+
throw typeError(`${label}.reduceRight of an empty array with no initial value.`);
|
|
439
480
|
}
|
|
440
481
|
accumulator = target[start];
|
|
441
482
|
start -= 1;
|
|
@@ -447,7 +488,5 @@ export const arrayGlobal = (ctx) => {
|
|
|
447
488
|
}
|
|
448
489
|
return accumulator;
|
|
449
490
|
})),
|
|
450
|
-
]
|
|
451
|
-
define(proto, IteratorSymbol, get(proto, "values"), hidden);
|
|
452
|
-
return array;
|
|
491
|
+
];
|
|
453
492
|
};
|
package/dist/stdlib/bytes.js
CHANGED
|
@@ -4,6 +4,7 @@ import { constructor, methods, prototypeFrom, receiver, requiresNew } from "../i
|
|
|
4
4
|
import { IteratorSymbol, rangeError, syntaxError, typeError } from "../interpreter/model.js";
|
|
5
5
|
import { define, defineAccessor, get, hidden, Arr, Bytes, hostIterator, Obj, coerceToInteger, coerceToNumber, coerceToString, } from "../interpreter/objects.js";
|
|
6
6
|
import { describeValue } from "../interpreter/references.js";
|
|
7
|
+
import { callbackMethods, sortArray } from "./array.js";
|
|
7
8
|
/** The bytes a Uint8Array, array, or other iterable of numbers describes; the host array clamps each value. */
|
|
8
9
|
const collectBytes = (ctx, source, name) => {
|
|
9
10
|
if (source instanceof Bytes)
|
|
@@ -110,6 +111,22 @@ export const uint8ArrayGlobal = (ctx) => {
|
|
|
110
111
|
return target;
|
|
111
112
|
},
|
|
112
113
|
],
|
|
114
|
+
[
|
|
115
|
+
"sort",
|
|
116
|
+
1,
|
|
117
|
+
(thisValue, args) => {
|
|
118
|
+
const target = self(thisValue, "sort");
|
|
119
|
+
// Without a comparator, typed arrays sort numerically rather than by string.
|
|
120
|
+
if (args[0] === undefined) {
|
|
121
|
+
target.bytes.sort();
|
|
122
|
+
return target;
|
|
123
|
+
}
|
|
124
|
+
return Effect.map(sortArray(ctx, [...target.bytes], args[0], "Uint8Array.sort"), (sorted) => {
|
|
125
|
+
target.bytes.set(Uint8Array.from(sorted, coerceToNumber));
|
|
126
|
+
return target;
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
],
|
|
113
130
|
[
|
|
114
131
|
"indexOf",
|
|
115
132
|
1,
|
|
@@ -120,7 +137,7 @@ export const uint8ArrayGlobal = (ctx) => {
|
|
|
120
137
|
1,
|
|
121
138
|
(thisValue, args) => {
|
|
122
139
|
const target = self(thisValue, "lastIndexOf").bytes;
|
|
123
|
-
return args
|
|
140
|
+
return args.length < 2
|
|
124
141
|
? target.lastIndexOf(coerceToNumber(args[0]))
|
|
125
142
|
: target.lastIndexOf(coerceToNumber(args[0]), optNumber(args[1]));
|
|
126
143
|
},
|
|
@@ -151,6 +168,7 @@ export const uint8ArrayGlobal = (ctx) => {
|
|
|
151
168
|
.bytes.entries()
|
|
152
169
|
.map(([index, byte]) => wrapAll([index, byte]))),
|
|
153
170
|
],
|
|
171
|
+
...callbackMethods(ctx, "Uint8Array", self, (target) => target.bytes, (values) => wrap(Uint8Array.from(values, coerceToNumber))),
|
|
154
172
|
]);
|
|
155
173
|
define(proto, IteratorSymbol, get(proto, "values"), hidden);
|
|
156
174
|
return uint8Array;
|
|
@@ -3,3 +3,5 @@ import type { Interpreter } from "../interpreter/interpreter.js";
|
|
|
3
3
|
export declare const groupBy: <R>(ctx: Interpreter<R>, namespace: "Map" | "Object") => import("../interpreter/objects.js").Native<R>;
|
|
4
4
|
export declare const mapGlobal: <R>(ctx: Interpreter<R>) => import("../interpreter/objects.js").Native<R>;
|
|
5
5
|
export declare const setGlobal: <R>(ctx: Interpreter<R>) => import("../interpreter/objects.js").Native<R>;
|
|
6
|
+
export declare const weakMapGlobal: <R>(ctx: Interpreter<R>) => import("../interpreter/objects.js").Native<R>;
|
|
7
|
+
export declare const weakSetGlobal: <R>(ctx: Interpreter<R>) => import("../interpreter/objects.js").Native<R>;
|