jseption 0.0.4 → 0.0.6
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/README.md +56 -4
- package/dist/atoms.d.ts +1 -0
- package/dist/atoms.js +2 -1
- package/dist/atoms.js.map +1 -1
- package/dist/builtins/base64.d.ts +14 -0
- package/dist/builtins/base64.js +219 -0
- package/dist/builtins/base64.js.map +1 -0
- package/dist/builtins/bigint.js +3 -3
- package/dist/builtins/bigint.js.map +1 -1
- package/dist/builtins/date.d.ts +1 -0
- package/dist/builtins/date.js +26 -12
- package/dist/builtins/date.js.map +1 -1
- package/dist/builtins/error.js +9 -0
- package/dist/builtins/error.js.map +1 -1
- package/dist/builtins/float16.d.ts +3 -0
- package/dist/builtins/float16.js +69 -0
- package/dist/builtins/float16.js.map +1 -0
- package/dist/builtins/function.js +2 -2
- package/dist/builtins/index.js +9 -6
- package/dist/builtins/index.js.map +1 -1
- package/dist/builtins/iterator.d.ts +2 -0
- package/dist/builtins/iterator.js +624 -0
- package/dist/builtins/iterator.js.map +1 -0
- package/dist/builtins/json.d.ts +13 -1
- package/dist/builtins/json.js +283 -211
- package/dist/builtins/json.js.map +1 -1
- package/dist/builtins/mapset.js +365 -6
- package/dist/builtins/mapset.js.map +1 -1
- package/dist/builtins/math.js +203 -3
- package/dist/builtins/math.js.map +1 -1
- package/dist/builtins/native.js +6 -6
- package/dist/builtins/object.js +9 -2
- package/dist/builtins/object.js.map +1 -1
- package/dist/builtins/promise.js +31 -11
- package/dist/builtins/promise.js.map +1 -1
- package/dist/builtins/proxy.js +2 -2
- package/dist/builtins/reflect.js +6 -2
- package/dist/builtins/reflect.js.map +1 -1
- package/dist/builtins/regexp.js +74 -3
- package/dist/builtins/regexp.js.map +1 -1
- package/dist/builtins/string.js +1 -1
- package/dist/builtins/symbol.js +1 -1
- package/dist/builtins/symbol.js.map +1 -1
- package/dist/builtins/typedarray.d.ts +1 -0
- package/dist/builtins/typedarray.js +302 -32
- package/dist/builtins/typedarray.js.map +1 -1
- package/dist/builtins/weakref.js +7 -7
- package/dist/embed.d.ts +5 -0
- package/dist/embed.js +47 -15
- package/dist/embed.js.map +1 -1
- package/dist/interpreter.js +15 -14
- package/dist/interpreter.js.map +1 -1
- package/dist/jit.d.ts +1 -0
- package/dist/jit.js +51 -13
- package/dist/jit.js.map +1 -1
- package/dist/jit_hook.d.ts +1 -0
- package/dist/jit_hook.js.map +1 -1
- package/dist/jit_runtime.d.ts +1 -1
- package/dist/jit_runtime.js +6 -6
- package/dist/jit_runtime.js.map +1 -1
- package/dist/jit_stack.d.ts +1 -0
- package/dist/jit_stack.js +2 -1
- package/dist/jit_stack.js.map +1 -1
- package/dist/jseption.d.ts +4 -0
- package/dist/jseption.js +2 -0
- package/dist/jseption.js.map +1 -1
- package/dist/membrane.d.ts +16 -0
- package/dist/membrane.js +576 -0
- package/dist/membrane.js.map +1 -0
- package/dist/module.js +5 -3
- package/dist/module.js.map +1 -1
- package/dist/parser.js +26 -9
- package/dist/parser.js.map +1 -1
- package/dist/print.d.ts +12 -0
- package/dist/print.js +494 -0
- package/dist/print.js.map +1 -0
- package/dist/qjs.js +13 -10
- package/dist/qjs.js.map +1 -1
- package/dist/quickjs.d.ts +8 -0
- package/dist/quickjs.js +132 -53
- package/dist/quickjs.js.map +1 -1
- package/dist/std.d.ts +4 -2
- package/dist/std.js +42 -8
- package/dist/std.js.map +1 -1
- package/dist/values.d.ts +31 -27
- package/dist/values.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
package/dist/jseption.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jseption.js","sourceRoot":"","sources":["../src/jseption.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,kBAAkB,CAAC;AAC1B,OAAO,aAAa,CAAC;AACrB,OAAO,qBAAqB,CAAC;AAC7B,OAAO,UAAU,CAAC;AAClB,OAAO,aAAa,CAAC;AAErB,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAM,WAAW,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACnF,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,eAAe,EAAE,qBAAqB,EAAE,eAAe,EACjG,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACnG,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC/I,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,YAAY,EAAO,MAAM,aAAa,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"jseption.js","sourceRoot":"","sources":["../src/jseption.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * jseption: TypeScript port of QuickJS - public entry point\n *\n * Importing this module loads the interpreter, the compiler and the\n * builtin objects and re-exports the core API.\n */\nimport './interpreter.ts';\nimport './parser.ts';\nimport './builtins/index.ts';\nimport './jit.ts';\nimport './module.ts';\n\nexport * from './quickjs.ts';\nexport { OP, opcode_info } from './opcodes.ts';\nexport { JS_WriteObject, JS_ReadObject, JS_WRITE_OBJ_SOURCE } from './bytecode.ts';\nexport { JSEPTION_VERSION } from './version.ts';\nexport { JS_PrintValue, JS_PrintValueSetDefaultOptions, JS_PrintValueToString } from './print.ts';\nexport type { JSPrintValueOptions, JSPrintValueWrite } from './print.ts';\nexport { JS_SetModuleLoader, JS_EvalModule, JS_ImportModule, JS_GetModuleNamespace, JS_DetectModule,\n js_default_module_resolve } from './module.ts';\nexport type { JSModuleLoader, JSModuleSource, JSModuleLoadResult, JSModuleSourceType } from './module.ts';\nexport { JS_PromiseState, JS_PromiseResult, JS_NewPromiseCapability } from './builtins/promise.ts';\nexport { JS_CallInternal, js_closure } from './interpreter.ts';\nexport { jit_config, jit_configure, jit_compile, jit_generate_source, jit_stats, jit_probe, jit_is_wrapper, jit_default_mode } from './jit.ts';\nexport { JS_EvalInternal, next_token, JSParseState, TOK } from './parser.ts';\nexport { toGuest, fromGuest, expose, hostFunction, hostPromise, guestPromiseToHost, guestErrorToHost, isGuestValue, drainJobs } from './embed.ts';\nexport type { ToGuestOptions } from './embed.ts';\nexport { exposeLive, toGuestLive, isLiveProxy } from './membrane.ts';\nexport type { MembranePolicy } from './membrane.ts';\n\nimport { JSContext, JSValue, JS_Eval, JS_IsException, JS_GetException, JS_ToString, JS_GetPropertyStr, JS_IsError,\n JS_EVAL_TYPE_GLOBAL, JSRuntime, JS_ExecutePendingJob, JS_IsJobPending } from './quickjs.ts';\n\n/* convenience helpers for tests and the CLI */\n\n/* evaluate 'src' and return the result or throw a host Error describing the JS exception */\nexport function evalOrThrow(ctx: JSContext, src: string, filename = '<eval>', flags: number = JS_EVAL_TYPE_GLOBAL): JSValue {\n const r = JS_Eval(ctx, src, filename, flags);\n if (JS_IsException(r)) {\n const exc = JS_GetException(ctx);\n throw new Error(formatException(ctx, exc));\n }\n return r;\n}\n\nexport function formatException(ctx: JSContext, exc: JSValue): string {\n let msg: string;\n const s = JS_ToString(ctx, exc);\n if (JS_IsException(s)) { JS_GetException(ctx); msg = '[exception]'; }\n else msg = s as string;\n if (JS_IsError(ctx, exc)) {\n const stack = JS_GetPropertyStr(ctx, exc, 'stack');\n if (typeof stack === 'string' && stack.length > 0) msg += '\\n' + stack;\n }\n return msg;\n}\n\n/* run all pending promise jobs; returns the number of jobs executed */\nexport function runPendingJobs(rt: JSRuntime): number {\n let n = 0;\n const pctx = { ctx: null as JSContext | null };\n while (JS_IsJobPending(rt)) {\n const r = JS_ExecutePendingJob(rt, pctx);\n if (r < 0) {\n /* the exception is left in the context */\n if (pctx.ctx) {\n const exc = JS_GetException(pctx.ctx);\n throw new Error('uncaught exception in job: ' + formatException(pctx.ctx, exc));\n }\n }\n n++;\n }\n return n;\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { JSContext, JSValue } from './quickjs.ts';
|
|
2
|
+
export interface MembranePolicy {
|
|
3
|
+
/** called for every key-based operation (read, `in`, own keys, descriptors,
|
|
4
|
+
* writes) on the host object `target`; any result but `true` hides the key */
|
|
5
|
+
allow?(target: object, key: PropertyKey): boolean;
|
|
6
|
+
/** let the guest set, define and delete properties on host objects
|
|
7
|
+
* (default false: read-only) */
|
|
8
|
+
writable?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/** the guest view of a host value: primitives as is, host objects and
|
|
11
|
+
* functions as live membrane proxies (see docs/MEMBRANE.md) */
|
|
12
|
+
export declare function toGuestLive(ctx: JSContext, value: unknown, policy?: MembranePolicy): JSValue;
|
|
13
|
+
/** define the global `name` in the guest as a live view of `root` */
|
|
14
|
+
export declare function exposeLive(ctx: JSContext, name: string, root: unknown, policy?: MembranePolicy): void;
|
|
15
|
+
/** true if `value` is a membrane proxy */
|
|
16
|
+
export declare function isLiveProxy(value: unknown): boolean;
|
package/dist/membrane.js
ADDED
|
@@ -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) === 46 /* 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
|