jseption 0.0.3 → 0.0.5

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.
@@ -0,0 +1,576 @@
1
+ /* The membrane: live host objects for the guest.
2
+ *
3
+ * `toGuest`/`expose` (src/embed.ts) copy plain data and wrap functions; a
4
+ * host class instance is refused. The membrane instead hands the guest a
5
+ * live view of a host object graph: every host object or function the guest
6
+ * reaches from a root crosses as a host `Proxy` whose handler lives here.
7
+ * Each trap forwards the operation to the host object and converts what
8
+ * comes back the same way, so nested objects, method results and
9
+ * constructed instances are proxied lazily.
10
+ *
11
+ * A membrane proxy is not a guest Proxy (src/builtins/proxy.ts): the guest
12
+ * Proxy calls guest handler code, while this handler is host code that must
13
+ * never return a host value. The proxy's target is a private dummy (an empty
14
+ * null-prototype object, or an empty function for callables) so the host
15
+ * Proxy invariants never force a host value out, and the proxy has no engine
16
+ * metadata, so the engine treats it as an ordinary object or host function
17
+ * (`Reflect.get`, `Reflect.apply`, ... on it run the traps).
18
+ *
19
+ * The sandbox rules (always on; a policy can only narrow them):
20
+ * 1. host intrinsics (Function and its async/generator variants, eval,
21
+ * globalThis, Reflect, Proxy, Object, the Error constructors, the
22
+ * intrinsic prototypes, the methods of Object, Reflect, Object.prototype
23
+ * and Function.prototype) are never wrapped: they read as undefined, or
24
+ * raise a guest TypeError where a value is required;
25
+ * 2. constructor, prototype, __proto__, caller, callee, arguments and the
26
+ * legacy __define/lookup{G,S}etter__ read as undefined; the prototype of
27
+ * a proxy is the guest Object.prototype (Function.prototype for
28
+ * callables); writes are refused unless the policy is writable, and
29
+ * setPrototypeOf / preventExtensions are always refused;
30
+ * 3. lookups stop at the host Object.prototype / Function.prototype and
31
+ * continue on the guest's own, so call/apply/bind/toString/hasOwnProperty
32
+ * are the guest intrinsics operating on the proxy, never host methods;
33
+ * 4. getters run on the host, their results are converted like any value;
34
+ * 5. only the well-known symbols iterator, asyncIterator, toStringTag and
35
+ * toPrimitive (and hasInstance, answered by the membrane) are forwarded;
36
+ * host symbols are neither listed nor returned;
37
+ * 6. `policy.allow(target, key)` filters every key-based operation.
38
+ *
39
+ * See docs/MEMBRANE.md. */
40
+ import { JS_IsException, JS_GetException, JS_NewCFunction, JS_Call, JS_Throw, JS_NewArrayFrom, JS_NewErrorNative, JS_SetPropertyStr, JS_GetClassID, js_meta, } from "./quickjs.js";
41
+ import { toGuest, drainJobs, guestErrorToHost, hostPromiseWith, guestPromiseToHostWith, fromGuestWith, FROM_GUEST_DEFAULT, } from "./embed.js";
42
+ import { JS_NewDate } from "./builtins/date.js";
43
+ /* ------------------------------------------------------------------ */
44
+ /* host intrinsics, collected once at module load */
45
+ const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;
46
+ const GeneratorFunction = Object.getPrototypeOf(function* () { }).constructor;
47
+ const AsyncGeneratorFunction = Object.getPrototypeOf(async function* () { }).constructor;
48
+ /* never wrapped, never returned to the guest */
49
+ const FORBIDDEN = new Set();
50
+ /* the host prototype walk stops here and continues on the guest prototype */
51
+ const WALK_STOP = new Set([Object.prototype, Function.prototype, globalThis]);
52
+ function forbid(v) {
53
+ if ((typeof v === 'object' && v !== null) || typeof v === 'function')
54
+ FORBIDDEN.add(v);
55
+ }
56
+ /* the functions stored on an object (values and accessors) */
57
+ function forbidMembers(o) {
58
+ for (const k of Reflect.ownKeys(o)) {
59
+ const d = Reflect.getOwnPropertyDescriptor(o, k);
60
+ if (!d)
61
+ continue;
62
+ if (typeof d.value === 'function')
63
+ forbid(d.value);
64
+ forbid(d.get);
65
+ forbid(d.set);
66
+ }
67
+ }
68
+ (function collectIntrinsics() {
69
+ const g = globalThis;
70
+ for (const v of [Function, AsyncFunction, GeneratorFunction, AsyncGeneratorFunction, eval, globalThis, Reflect, Proxy, Object])
71
+ forbid(v);
72
+ /* the host error constructors expose stack-trace hooks (captureStackTrace,
73
+ prepareStackTrace) that can hand out host frames */
74
+ for (const name of ['Error', 'EvalError', 'RangeError', 'ReferenceError', 'SyntaxError', 'TypeError', 'URIError',
75
+ 'AggregateError', 'SuppressedError'])
76
+ forbid(g[name]);
77
+ /* the prototype of every standard constructor */
78
+ const ctors = ['Object', 'Function', 'Array', 'String', 'Number', 'Boolean', 'Symbol', 'BigInt', 'Error', 'EvalError',
79
+ 'RangeError', 'ReferenceError', 'SyntaxError', 'TypeError', 'URIError', 'AggregateError', 'SuppressedError', 'Date',
80
+ 'RegExp', 'Map', 'Set', 'WeakMap', 'WeakSet', 'WeakRef', 'FinalizationRegistry', 'Promise', 'ArrayBuffer',
81
+ 'SharedArrayBuffer', 'DataView', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray', 'Int16Array', 'Uint16Array',
82
+ 'Int32Array', 'Uint32Array', 'Float16Array', 'Float32Array', 'Float64Array', 'BigInt64Array', 'BigUint64Array',
83
+ 'Iterator', 'AsyncIterator', 'DisposableStack', 'AsyncDisposableStack'];
84
+ for (const name of ctors) {
85
+ const c = g[name];
86
+ if (typeof c === 'function')
87
+ forbid(c.prototype);
88
+ }
89
+ const TypedArray = Object.getPrototypeOf(Int8Array);
90
+ forbid(TypedArray);
91
+ forbid(TypedArray.prototype);
92
+ for (const F of [AsyncFunction, GeneratorFunction, AsyncGeneratorFunction]) {
93
+ forbid(F.prototype); /* %AsyncFunction.prototype%, %GeneratorFunction.prototype%, ... */
94
+ forbid(F.prototype.prototype); /* %GeneratorPrototype%, %AsyncGeneratorPrototype% */
95
+ }
96
+ const arrayIter = Object.getPrototypeOf([][Symbol.iterator]());
97
+ forbid(arrayIter);
98
+ forbid(Object.getPrototypeOf(arrayIter)); /* %IteratorPrototype% */
99
+ forbid(Object.getPrototypeOf(Object.getPrototypeOf((async function* () { })()))); /* %AsyncGeneratorPrototype% */
100
+ forbid(Object.getPrototypeOf(Object.getPrototypeOf(Object.getPrototypeOf((async function* () { })())))); /* %AsyncIteratorPrototype% */
101
+ forbid(Object.getPrototypeOf(new Map().entries()));
102
+ forbid(Object.getPrototypeOf(new Set().values()));
103
+ forbid(Object.getPrototypeOf(''[Symbol.iterator]()));
104
+ forbid(Object.getPrototypeOf(/a/[Symbol.matchAll]('a')));
105
+ /* %ThrowTypeError% and the methods that reach code, prototypes or properties
106
+ of arbitrary objects */
107
+ forbid(Reflect.getOwnPropertyDescriptor(Function.prototype, 'caller')?.get);
108
+ for (const o of [Object, Reflect, Proxy, Object.prototype, Function.prototype])
109
+ forbidMembers(o);
110
+ })();
111
+ const BLOCKED_KEYS = new Set([
112
+ 'constructor', 'prototype', '__proto__', 'caller', 'callee', 'arguments',
113
+ '__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__',
114
+ ]);
115
+ const WELL_KNOWN_SYMBOLS = new Set(Reflect.ownKeys(Symbol).map(k => Symbol[k])
116
+ .filter((v) => typeof v === 'symbol'));
117
+ /* the well-known symbols forwarded as keys */
118
+ const FORWARDED_SYMBOLS = new Set([Symbol.iterator, Symbol.asyncIterator, Symbol.toStringTag, Symbol.toPrimitive]);
119
+ const ERROR_KINDS = [
120
+ [TypeError, 4 /* JSErrorEnum.TYPE_ERROR */], [RangeError, 1 /* JSErrorEnum.RANGE_ERROR */], [SyntaxError, 3 /* JSErrorEnum.SYNTAX_ERROR */],
121
+ [ReferenceError, 2 /* JSErrorEnum.REFERENCE_ERROR */], [URIError, 5 /* JSErrorEnum.URI_ERROR */], [EvalError, 0 /* JSErrorEnum.EVAL_ERROR */],
122
+ ];
123
+ const STANDARD_ERROR_NAMES = new Set(['Error', 'TypeError', 'RangeError', 'SyntaxError', 'ReferenceError', 'URIError', 'EvalError']);
124
+ function isHostConstructor(f) {
125
+ try {
126
+ Reflect.construct(String, [], f);
127
+ return true;
128
+ }
129
+ catch {
130
+ return false;
131
+ }
132
+ }
133
+ function isObjectLike(v) {
134
+ return (typeof v === 'object' && v !== null) || typeof v === 'function';
135
+ }
136
+ /* ------------------------------------------------------------------ */
137
+ /* registries shared by every membrane */
138
+ /* membrane proxy -> the host object it stands for */
139
+ const proxyHost = new WeakMap();
140
+ /* host callable made for a guest function -> that guest function */
141
+ const callableGuest = new WeakMap();
142
+ /* host Error thrown to host code for a guest exception -> the guest exception */
143
+ const thrownGuest = new WeakMap();
144
+ /* one membrane per context and policy */
145
+ const membranes = new WeakMap();
146
+ /* membrane traps running now, per runtime. Compiled guest code pushes no interpreter frame, so
147
+ when it calls into host code through a proxy, `rt.current_stack_frame` is null although guest
148
+ code is on the stack; this count is what tells a guest callback the host calls back that it is
149
+ not the outermost call */
150
+ const trapDepth = new WeakMap();
151
+ function membraneFor(ctx, policy) {
152
+ let e = membranes.get(ctx);
153
+ if (!e) {
154
+ e = { byPolicy: new WeakMap() };
155
+ membranes.set(ctx, e);
156
+ }
157
+ if (policy === undefined)
158
+ return (e.plain ??= new Membrane(ctx, {}));
159
+ let m = e.byPolicy.get(policy);
160
+ if (!m) {
161
+ m = new Membrane(ctx, policy);
162
+ e.byPolicy.set(policy, m);
163
+ }
164
+ return m;
165
+ }
166
+ /* ------------------------------------------------------------------ */
167
+ class Membrane {
168
+ ctx;
169
+ proxies = new WeakMap(); /* host object -> proxy */
170
+ callables = new WeakMap(); /* guest function -> host callable */
171
+ hasInstanceFns = new WeakMap();
172
+ guestSymbols = new WeakSet(); /* symbols the guest handed to the host */
173
+ writable;
174
+ allowFn;
175
+ constructor(ctx, policy) {
176
+ this.ctx = ctx;
177
+ this.writable = policy.writable === true;
178
+ this.allowFn = typeof policy.allow === 'function' ? policy.allow.bind(policy) : undefined;
179
+ }
180
+ /* ---------------- host -> guest ---------------- */
181
+ toGuest(v, seen) {
182
+ switch (typeof v) {
183
+ case 'undefined':
184
+ case 'boolean':
185
+ case 'number':
186
+ case 'string':
187
+ case 'bigint':
188
+ return v;
189
+ case 'symbol':
190
+ return this.symbolCrosses(v) ? v : undefined;
191
+ }
192
+ if (v === null)
193
+ return null;
194
+ const o = v;
195
+ if (FORBIDDEN.has(o))
196
+ return undefined;
197
+ const p = proxyHost.get(o);
198
+ if (p !== undefined)
199
+ return p.membrane.ctx === this.ctx ? o : this.toGuest(p.host, seen);
200
+ if (typeof o === 'function') {
201
+ const g = callableGuest.get(o);
202
+ if (g !== undefined)
203
+ return g;
204
+ }
205
+ if (this.isGuestOwned(o))
206
+ return o;
207
+ if (o instanceof Promise) {
208
+ return hostPromiseWith(this.ctx, o, r => this.toGuest(r), e => this.thrownToGuest(e));
209
+ }
210
+ if (Array.isArray(o)) {
211
+ /* arrays are snapshots: a fresh guest array whose elements are converted */
212
+ seen ??= new Map();
213
+ const cached = seen.get(o);
214
+ if (cached !== undefined)
215
+ return cached;
216
+ const arr = JS_NewArrayFrom(this.ctx, []);
217
+ seen.set(o, arr);
218
+ const len = o.length;
219
+ for (let i = 0; i < len; i++)
220
+ arr[i] = this.toGuest(o[i], seen);
221
+ return arr;
222
+ }
223
+ if (o instanceof Date)
224
+ return JS_NewDate(this.ctx, Date.prototype.getTime.call(o));
225
+ if (o instanceof Error)
226
+ return this.errorToGuest(o);
227
+ if (o instanceof RegExp || o instanceof ArrayBuffer || ArrayBuffer.isView(o))
228
+ return toGuest(this.ctx, o);
229
+ return this.wrap(o);
230
+ }
231
+ /* a well-known or registered symbol, or one the guest gave to the host */
232
+ symbolCrosses(s) {
233
+ return WELL_KNOWN_SYMBOLS.has(s) || Symbol.keyFor(s) !== undefined || this.guestSymbols.has(s);
234
+ }
235
+ /* an engine object, or an object whose prototype chain reaches this
236
+ context's guest Object.prototype / Function.prototype */
237
+ isGuestOwned(o) {
238
+ if (js_meta.has(o))
239
+ return true;
240
+ try {
241
+ return this.chainReachesGuest(o);
242
+ }
243
+ catch {
244
+ return false; /* a host proxy whose getPrototypeOf throws */
245
+ }
246
+ }
247
+ chainReachesGuest(o) {
248
+ const ctx = this.ctx;
249
+ const op = ctx.class_proto[1 /* JSClassID.OBJECT */], fp = ctx.function_proto;
250
+ let p = Reflect.getPrototypeOf(o);
251
+ for (let i = 0; i < 64 && p !== null; i++) {
252
+ if (p === op || p === fp)
253
+ return true;
254
+ if (WALK_STOP.has(p) || FORBIDDEN.has(p) || proxyHost.has(p))
255
+ return false;
256
+ p = Reflect.getPrototypeOf(p);
257
+ }
258
+ return false;
259
+ }
260
+ errorToGuest(e) {
261
+ let kind = 8 /* JSErrorEnum.PLAIN_ERROR */;
262
+ for (const [C, k] of ERROR_KINDS)
263
+ if (e instanceof C) {
264
+ kind = k;
265
+ break;
266
+ }
267
+ let message = '', name;
268
+ try {
269
+ const m = e.message;
270
+ message = typeof m === 'string' ? m : '';
271
+ name = e.name;
272
+ }
273
+ catch { /* hostile getter */ }
274
+ const g = JS_NewErrorNative(this.ctx, kind, message);
275
+ if (kind === 8 /* JSErrorEnum.PLAIN_ERROR */ && typeof name === 'string' && !STANDARD_ERROR_NAMES.has(name)) {
276
+ Reflect.defineProperty(g, 'name', { value: name, writable: true, enumerable: false, configurable: true });
277
+ }
278
+ return g;
279
+ }
280
+ /* convert anything the host threw into a value to throw in the guest; the
281
+ host exception object itself never crosses */
282
+ thrownToGuest(e) {
283
+ if (isObjectLike(e)) {
284
+ const g = thrownGuest.get(e);
285
+ if (g !== undefined)
286
+ return g; /* a guest exception that passed through host code */
287
+ if (proxyHost.has(e) || this.isGuestOwned(e))
288
+ return e; /* thrown by guest code */
289
+ if (e instanceof Error)
290
+ return this.errorToGuest(e);
291
+ let message = 'host exception';
292
+ try {
293
+ const m = e.message;
294
+ if (typeof m === 'string')
295
+ message = m;
296
+ }
297
+ catch { /* hostile getter */ }
298
+ return JS_NewErrorNative(this.ctx, 8 /* JSErrorEnum.PLAIN_ERROR */, message);
299
+ }
300
+ if (typeof e === 'symbol')
301
+ return this.symbolCrosses(e) ? e : undefined;
302
+ return e;
303
+ }
304
+ guestTypeError(message) {
305
+ return JS_NewErrorNative(this.ctx, 4 /* JSErrorEnum.TYPE_ERROR */, message);
306
+ }
307
+ /* the proxy for a host object (one per host object) */
308
+ wrap(host) {
309
+ const cached = this.proxies.get(host);
310
+ if (cached !== undefined)
311
+ return cached;
312
+ let target;
313
+ const callable = typeof host === 'function';
314
+ if (callable) {
315
+ /* constructible or not like the host function; no own properties, so
316
+ no target invariant constrains the traps */
317
+ const f = isHostConstructor(host) ? Function.prototype.bind.call(function () { }, null) : () => { };
318
+ Reflect.deleteProperty(f, 'name');
319
+ Reflect.deleteProperty(f, 'length');
320
+ Reflect.setPrototypeOf(f, null);
321
+ target = f;
322
+ }
323
+ else {
324
+ target = Object.create(null);
325
+ }
326
+ const proxy = new Proxy(target, this.handler(host, callable));
327
+ this.proxies.set(host, proxy);
328
+ proxyHost.set(proxy, { host, membrane: this });
329
+ return proxy;
330
+ }
331
+ keyAllowed(host, key) {
332
+ if (typeof key === 'symbol') {
333
+ if (WELL_KNOWN_SYMBOLS.has(key) && !FORWARDED_SYMBOLS.has(key))
334
+ return false;
335
+ }
336
+ else if (BLOCKED_KEYS.has(key)) {
337
+ return false;
338
+ }
339
+ return this.allowFn === undefined || this.allowFn(host, key) === true;
340
+ }
341
+ /* listed by ownKeys: host symbols other than the forwarded well-known ones stay hidden */
342
+ keyListed(host, key) {
343
+ if (typeof key === 'symbol' && !FORWARDED_SYMBOLS.has(key) && !this.guestSymbols.has(key))
344
+ return false;
345
+ return this.keyAllowed(host, key);
346
+ }
347
+ /* walk the host prototype chain up to (not including) the host
348
+ Object.prototype / Function.prototype */
349
+ lookup(host, key) {
350
+ let o = host;
351
+ for (let depth = 0; o !== null && depth < 10000; depth++) {
352
+ if (WALK_STOP.has(o))
353
+ return undefined;
354
+ const d = Reflect.getOwnPropertyDescriptor(o, key);
355
+ if (d !== undefined)
356
+ return d;
357
+ o = Reflect.getPrototypeOf(o);
358
+ }
359
+ return undefined;
360
+ }
361
+ hostOf(v, fallback) {
362
+ return isObjectLike(v) ? proxyHost.get(v)?.host ?? fallback : fallback;
363
+ }
364
+ hasInstance(host) {
365
+ let f = this.hasInstanceFns.get(host);
366
+ if (f === undefined) {
367
+ f = JS_NewCFunction(this.ctx, (ctx, _this, argv) => {
368
+ const v = argv[0];
369
+ const h = isObjectLike(v) ? proxyHost.get(v)?.host : undefined;
370
+ if (h === undefined)
371
+ return false;
372
+ try {
373
+ return h instanceof host;
374
+ }
375
+ catch (e) {
376
+ return JS_Throw(ctx, this.thrownToGuest(e));
377
+ }
378
+ }, '[Symbol.hasInstance]', 1);
379
+ this.hasInstanceFns.set(host, f);
380
+ }
381
+ return f;
382
+ }
383
+ handler(host, callable) {
384
+ const m = this;
385
+ const ctx = this.ctx;
386
+ const guestProto = () => (callable ? ctx.function_proto : ctx.class_proto[1 /* JSClassID.OBJECT */]);
387
+ /* every trap converts what the host throws, and counts as guest code on the stack while it
388
+ runs host code */
389
+ const rt = ctx.rt;
390
+ const guard = (fn) => (...args) => {
391
+ trapDepth.set(rt, (trapDepth.get(rt) ?? 0) + 1);
392
+ try {
393
+ return fn(...args);
394
+ }
395
+ catch (e) {
396
+ throw m.thrownToGuest(e);
397
+ }
398
+ finally {
399
+ trapDepth.set(rt, trapDepth.get(rt) - 1);
400
+ }
401
+ };
402
+ const refuse = (what, key) => {
403
+ const k = key === undefined ? '' : ` '${typeof key === 'symbol' ? key.description ?? 'symbol' : key}'`;
404
+ throw m.guestTypeError(`cannot ${what}${k}: host object is read-only`);
405
+ };
406
+ const proxyOf = () => m.proxies.get(host);
407
+ return {
408
+ get: guard((_t, key, receiver) => {
409
+ if (callable && key === Symbol.hasInstance)
410
+ return m.hasInstance(host);
411
+ if (!m.keyAllowed(host, key))
412
+ return undefined;
413
+ const d = m.lookup(host, key);
414
+ if (d === undefined)
415
+ return Reflect.get(guestProto(), key, receiver);
416
+ if ('value' in d)
417
+ return m.toGuest(d.value);
418
+ if (d.get === undefined)
419
+ return undefined;
420
+ return m.toGuest(Reflect.apply(d.get, m.hostOf(receiver, host), []));
421
+ }),
422
+ has: guard((_t, key) => {
423
+ if (callable && key === Symbol.hasInstance)
424
+ return true;
425
+ if (!m.keyAllowed(host, key))
426
+ return false;
427
+ if (m.lookup(host, key) !== undefined)
428
+ return true;
429
+ return Reflect.has(guestProto(), key);
430
+ }),
431
+ ownKeys: guard((_t) => Reflect.ownKeys(host).filter(k => m.keyListed(host, k))),
432
+ getOwnPropertyDescriptor: guard((_t, key) => {
433
+ if (!m.keyAllowed(host, key))
434
+ return undefined;
435
+ const d = Reflect.getOwnPropertyDescriptor(host, key);
436
+ if (d === undefined)
437
+ return undefined;
438
+ if ('value' in d) {
439
+ return { value: m.toGuest(d.value), writable: m.writable && d.writable === true, enumerable: d.enumerable === true, configurable: true };
440
+ }
441
+ return {
442
+ get: d.get === undefined ? undefined : m.toGuest(d.get),
443
+ set: d.set === undefined || !m.writable ? undefined : m.toGuest(d.set),
444
+ enumerable: d.enumerable === true, configurable: true,
445
+ };
446
+ }),
447
+ set: guard((_t, key, value, receiver) => {
448
+ if (receiver !== proxyOf() && !(isObjectLike(receiver) && proxyHost.has(receiver))) {
449
+ /* a guest object inheriting from the proxy: the property lands on it */
450
+ return Reflect.defineProperty(receiver, key, { value, writable: true, enumerable: true, configurable: true });
451
+ }
452
+ if (!m.writable || !m.keyAllowed(host, key))
453
+ refuse('assign to property', key);
454
+ return Reflect.set(host, key, m.fromGuest(value), m.hostOf(receiver, host));
455
+ }),
456
+ defineProperty: guard((_t, key, desc) => {
457
+ if (!m.writable || !m.keyAllowed(host, key))
458
+ refuse('define property', key);
459
+ if (desc.configurable === false)
460
+ throw m.guestTypeError('cannot define a non-configurable property on a host object');
461
+ const hd = {};
462
+ if ('value' in desc)
463
+ hd.value = m.fromGuest(desc.value);
464
+ if ('get' in desc)
465
+ hd.get = m.fromGuest(desc.get);
466
+ if ('set' in desc)
467
+ hd.set = m.fromGuest(desc.set);
468
+ if ('writable' in desc)
469
+ hd.writable = desc.writable;
470
+ if ('enumerable' in desc)
471
+ hd.enumerable = desc.enumerable;
472
+ hd.configurable = true;
473
+ return Reflect.defineProperty(host, key, hd);
474
+ }),
475
+ deleteProperty: guard((_t, key) => {
476
+ if (!m.writable || !m.keyAllowed(host, key))
477
+ refuse('delete property', key);
478
+ return Reflect.deleteProperty(host, key);
479
+ }),
480
+ getPrototypeOf: () => guestProto(),
481
+ setPrototypeOf: () => false,
482
+ isExtensible: () => true,
483
+ preventExtensions: () => false,
484
+ apply: guard((_t, thisArg, args) => {
485
+ const hostThis = thisArg === ctx.global_obj ? undefined : m.fromGuest(thisArg);
486
+ const hostArgs = args.map(a => m.fromGuest(a));
487
+ return m.toGuest(Reflect.apply(host, hostThis, hostArgs));
488
+ }),
489
+ construct: guard((_t, args, newTarget) => {
490
+ if (newTarget !== proxyOf())
491
+ throw m.guestTypeError('host constructors cannot be subclassed from the guest');
492
+ const r = m.toGuest(Reflect.construct(host, args.map(a => m.fromGuest(a))));
493
+ if (!isObjectLike(r))
494
+ throw m.guestTypeError('host constructor returned a value that cannot cross the membrane');
495
+ return r;
496
+ }),
497
+ };
498
+ }
499
+ /* ---------------- guest -> host ---------------- */
500
+ fromGuest(v) {
501
+ return fromGuestWith(this.ctx, v, x => this.fromGuestHook(x));
502
+ }
503
+ fromGuestHook(v) {
504
+ if (typeof v === 'symbol') {
505
+ if (!WELL_KNOWN_SYMBOLS.has(v) && Symbol.keyFor(v) === undefined)
506
+ this.guestSymbols.add(v);
507
+ return v;
508
+ }
509
+ if (!isObjectLike(v))
510
+ return FROM_GUEST_DEFAULT;
511
+ const p = proxyHost.get(v);
512
+ if (p !== undefined)
513
+ return p.host;
514
+ if (typeof v === 'function')
515
+ return this.callable(v);
516
+ if (JS_GetClassID(v) === 45 /* JSClassID.PROMISE */) {
517
+ return guestPromiseToHostWith(this.ctx, v, x => this.fromGuest(x), e => (JS_GetClassID(e) === 3 /* JSClassID.ERROR */ ? guestErrorToHost(this.ctx, e) : this.fromGuest(e)));
518
+ }
519
+ return FROM_GUEST_DEFAULT;
520
+ }
521
+ /* a host function that runs the guest function (one per guest function) */
522
+ callable(fn) {
523
+ const cached = this.callables.get(fn);
524
+ if (cached !== undefined)
525
+ return cached;
526
+ const m = this;
527
+ const ctx = this.ctx;
528
+ const c = {
529
+ guestCallback(...args) {
530
+ /* outermost: no guest code below this call, neither an interpreter frame nor a membrane
531
+ trap (the way compiled guest code reaches host code) */
532
+ const outermost = ctx.rt.current_stack_frame === null && (trapDepth.get(ctx.rt) ?? 0) === 0;
533
+ try {
534
+ const r = JS_Call(ctx, fn, m.toGuest(this), args.map(a => m.toGuest(a)));
535
+ if (JS_IsException(r)) {
536
+ const exc = JS_GetException(ctx);
537
+ const he = guestErrorToHost(ctx, exc);
538
+ thrownGuest.set(he, exc);
539
+ throw he;
540
+ }
541
+ return m.fromGuest(r);
542
+ }
543
+ finally {
544
+ /* called from host code outside any guest call (an event, a timer, a
545
+ promise reaction): run the guest jobs the callback queued */
546
+ if (outermost)
547
+ drainJobs(ctx);
548
+ }
549
+ },
550
+ }.guestCallback;
551
+ this.callables.set(fn, c);
552
+ callableGuest.set(c, fn);
553
+ return c;
554
+ }
555
+ }
556
+ /* ------------------------------------------------------------------ */
557
+ /* public API */
558
+ /** the guest view of a host value: primitives as is, host objects and
559
+ * functions as live membrane proxies (see docs/MEMBRANE.md) */
560
+ export function toGuestLive(ctx, value, policy) {
561
+ if (FORBIDDEN.has(value))
562
+ throw new TypeError('toGuestLive: refusing to hand a host intrinsic to the guest');
563
+ return membraneFor(ctx, policy).toGuest(value);
564
+ }
565
+ /** define the global `name` in the guest as a live view of `root` */
566
+ export function exposeLive(ctx, name, root, policy) {
567
+ const g = toGuestLive(ctx, root, policy);
568
+ if (JS_SetPropertyStr(ctx, ctx.global_obj, name, g) < 0) {
569
+ throw guestErrorToHost(ctx, JS_GetException(ctx));
570
+ }
571
+ }
572
+ /** true if `value` is a membrane proxy */
573
+ export function isLiveProxy(value) {
574
+ return isObjectLike(value) && proxyHost.has(value);
575
+ }
576
+ //# sourceMappingURL=membrane.js.map