diff --git a/packages/xsnap/doc/XS Metering.md b/packages/xsnap/doc/XS Metering.md new file mode 100644 index 000000000000..28962936f814 --- /dev/null +++ b/packages/xsnap/doc/XS Metering.md @@ -0,0 +1,144 @@ +# XS Metering +Revised: November 10, 2020 + +Warning: These notes are preliminary. Omissions and errors are likely. If you encounter problems, please ask for assistance. + +## Introduction + +The objective is to allow runtime to constraint how much computation a machine can do. + +The technique is to count how many byte codes are executed and to ask the runtime if the limit has been reached. + +Asking the runtime at every byte code would be prohibitively slow. So XS only asks the runtime if the limit has been reached: + +- when branching backwards, +- when calling a function, +- when returning from a function, +- when catching an exception, +- when iterating a generator, +- when resuming an async function. + +To be faster, the runtime can also set a metering *interval*, a number of byte codes to wait for before asking again. + +When the runtime tells XS that the limit has been reached, XS aborts with a "*too much computation*" exit. Like for other exits ("*not enough memory*", "*unhandled exception*", etc), the runtime can then decide what to do: + +- throwing an exception, +- exiting the machine, +- exiting the process. + +> Both exiting the machine and exiting the process cannot be caught by the executed JavaScript code. + +## Programming Interface + +To begin metering use the `xsBeginMetering` macro: + + xsBeginMetering(xsMachine* machine, + xsBooleanValue (*ask)(xsMachine* the, xsUnsignedValue index), + xsUnsignedValue interval) + +- `machine`: the machine to meter. +- `ask`: the C function that XS will call to ask if the limit has been reached. +- `interval`: the metering interval. + +The macro uses `setjmp` and must be balanced with the `xsEndMetering` macro: + + xsEndMetering(xsMachine* machine) + +- `machine`: the metered machine. + +The `ask` callback gets the metered machine and the current index. It returns `1` to tell XS to continue, `0` to tell XS to abort. + +## Built-ins + +To fine tune the metering, runtimes can patch built-ins functions. + + xsPatchHostFunction(xsSlot function, xsCallback patch) + +- `function`: the function to patch. +- `patch`: the callback that replaces the original callback of the function. + +Patches must conclude by running their original callback with + + xsMeterHostFunction(xsUnsignedValue count) + +- `count`: the number to add to the metering index. + +### Example + +Here is a patch for `Array.prototype` functions that adds the length of the array to the metering index. + + void fx_Array_prototype_meter(xsMachine* the) + { + xsIntegerValue length = xsToInteger(xsGet(xsThis, xsID("length"))); + xsMeterHostFunction(length); + } + +The same patch can be installed into several `Array.prototype` functions, for instance here `Array.prototype.reverse` and `Array.prototype.sort` + + xsBeginHost(machine); + xsVars(2); + xsVar(0) = xsGet(xsGlobal, xsID("Array")); + xsVar(0) = xsGet(xsVar(0), xsID("prototype")); + xsVar(1) = xsGet(xsVar(0), xsID("reverse")); + xsPatchHostFunction(xsVar(1), fx_Array_prototype_meter); + xsVar(1) = xsGet(xsVar(0), xsID("sort")); + xsPatchHostFunction(xsVar(1), fx_Array_prototype_meter); + xsEndHost(machine); + +## Usage + +Here is the typical runtime sequence: + + static xsBooleanValue ask(xsMachine* machine, xsUnsignedValue index) + { + if (index > 10000) { + fprintf(stderr, "too much computation\n"); + return 0; + } + return 1; + } + + int main(int argc, char* argv[]) + { + //... + xsMachine* machine xsCreateMachine(creation, "main", NULL); + xsBeginMetering(machine, ask, 1000); + { + xsBeginHost(machine); + { + // execute scripts or modules + } + xsEndHost(machine); + } + xsEndMetering(machine); + xsDeleteMachine(machine); + //... + } + +The fxAbort function has to be supplied by all runtimes based on XS. Here the `xsTooMuchComputationExit` case exits the machine. + + void fxAbort(xsMachine* the, int exit) + { + if (exit == xsTooMuchComputationExit) { + fxExitToHost(the); + } + //... + } + +### In JavaScript + +The runtime must provide a C function for the `ask` callback. However the `ask` callback can use the XS in C programming interface to call a JavaScript function. Like a system callback, the `ask` callback has to use `xsBeginHost` and `xsEndHost`. + + static xsBooleanValue ask(xsMachine* machine, xsUnsignedValue index) + { + xsBooleanValue result; + xsBeginHost(machine); + { + result = xsToBoolean(xsCall1(xsGlobal, xsID_ask, xsNumber(index)); + } + xsEndHost(machine); + return result; + } + +The metering is suspended during the `ask` callback. + diff --git a/packages/xsnap/doc/XS Snapshots.md b/packages/xsnap/doc/XS Snapshots.md new file mode 100644 index 000000000000..1e21e71aacb6 --- /dev/null +++ b/packages/xsnap/doc/XS Snapshots.md @@ -0,0 +1,135 @@ +# XS Snapshots +Revised: September 24, 2020 + +Warning: These notes are preliminary. Omissions and errors are likely. If you encounter problems, please ask for assistance. + +## Format + +XS snapshots are atom based. Inside the `XS_M` container, there are nine atoms: + +- `VERS`: The version of XS and the architecture. +- `SIGN`: The runtime signature. +- `CREA`: The parameters to create the machine: block initial and incremental size, heap initial and incremental size, stack size, etc. +- `BLOC`: The chunks in the blocks. +- `HEAP`: The slots in the heaps. +- `STAC`: The slots in the stack. +- `KEYS`: The keys table. +- `NAME`: The names table. +- `SYMB`: The symbols table. + +XS snapshots are bound to: + +- the major and minor version numbers of XS, +- the architecture: 32-bit vs 64-bit, big endian vs little endian, +- the runtime signature. + +## Programming Interface + +The `xsSnapshot` structure is used to read and write snapshots. + + typedef struct { + char* signature; + int signatureLength; + xsCallback* callbacks; + int callbacksLength; + int (*read)(void* stream, void* ptr, size_t size); + int (*write)(void* stream, void* ptr, size_t size); + void* stream; + int error; + void* data[3]; + } xsSnapshot; + + +- `signature`: bytes to identify the runtime. +- `signatureLength`: the number of bytes in `signature`. +- `callbacks`: array of XS callbacks implemented by the runtime. +- `callbacksLength`: the number of XS callbacks in `callbacks`. +- `read`: the function that `xsReadSnapshot` will call to read the snapshot. +- `write`: the function that `xsWriteSnapshot` will call to write the snapshot. +- `stream`: the parameter passed to the `read` and `write` functions. +- `error`: the error that occurred when reading or writing the snapshot. +- `data`: pointers used internally by XS, must be set to `NULL`. + +The `signature` and `callbacks` fields are related. Runtimes must change the `signature` if the `callbacks` become incompatible. + +If the `stream` is a binary `FILE*`, the `read` and `write` functions are trivial: + + int read(void* stream, void* ptr, size_t size) + { + return (fread(ptr, size, 1, stream) == 1) ? 0 : errno; + } + + int write(void* stream, void* ptr, size_t size) + { + return (fwrite(ptr, size, 1, stream) == 1) ? 0 : errno; + } + +### Writing Snapshot + +Here is the typical runtime sequence: + +- create a new machine, +- run modules or scripts, +- wait for all jobs to complete, +- write the snapshot. + +To write the snapshot, fill the `xsSnapshot` structure and call `xsWriteSnapshot`. + + extern int xsWriteSnapshot(xsMachine* the, xsSnapshot* snapshot); + +- `xsWriteSnapshot` must be called outside of XS callbacks and outside of `xsBeginHost` `xsEndHost` blocks. +- There must be no host instances. + +`xsWriteSnapshot` returns `1` if successful, otherwise `xsWriteSnapshot` returns `0` and sets the `error` field. + +### Reading Snapshot + +Once you have a snapshot, instead of creating a new machine with `xsCreateMachine`, you can create a new machine with `xsReadSnapshot`. The new machine will be in the same state as the machine that was saved by `xsWriteSnapshot`. + +To read the snapshot, fill the `xsSnapshot` structure and call `xsReadSnapshot`. + + xsMachine* xsReadSnapshot(xsSnapshot* snapshot, xsStringValue name, void* context); + +- `snapshot`: The snapshot structure. +- `name`: The name of the machine to be displayed in **xsbug**. +- `context`: The initial context of the machine, or `NULL` + +`xsReadSnapshot` returns a machine if successful, otherwise `xsReadSnapshot` returns `NULL` and sets the `error` field. + +## Implementation Details + +To be able to write a snapshot, everything must be in chunk blocks, slot heaps and slot stack. There cannot be pointers to host data. + +That was mostly the case, except for a few optimizations that create JS strings pointing to C data. Such optimizations are now skipped if `mxSnapshot` is defined. We should investigate if such optimizations are still necessary. + +Interestingly enough snapshots are completely orthogonal to the shared machine prepared by the XS linker to flash micro-controllers. The strategy there is to have as many pointers to host data as possible... + +### Callbacks + +The only pointers that cannot be avoided are XS callbacks, i.e. pointers to host code. + +The major and minor version numbers of XS change when byte codes evolve and when built-ins get new features. New features are always implemented with new callbacks. + +Snapshots are obviously bound to major and minor version numbers of XS. For the sake of snapshots, XS maintains an array of callbacks. It is then enough to project XS callbacks into array indexes. + +Similarly, thanks to a signature and an array of callbacks, runtimes can add new callbacks that will be projected into array indexes. + +### Strictly Deterministic + +If two machines with the same allocations perform the same operations with the same results in the same order, their snapshots will be the same. + +The XS garbage collector is always complete and introduces no variations. + +But asynchronous features can of course alter the order. Then the snapshots will not be the same, even if they are functionally equivalent. + +### Tests + +A lot of tests remain to be done to verify how various built-ins survive the snapshot process. + + + + + + + + diff --git a/packages/xsnap/doc/XSNAP - CLI.md b/packages/xsnap/doc/XSNAP - CLI.md new file mode 100644 index 000000000000..a32788c89d9d --- /dev/null +++ b/packages/xsnap/doc/XSNAP - CLI.md @@ -0,0 +1,183 @@ +# xsnap + +Revised: November 10, 2020 + +Warning: These notes are preliminary. Omissions and errors are likely. If you encounter problems, please ask for assistance. + +## About + +`xsnap` is a custom XS runtime that read and write XS snapshots. See [XS snapshots](./documentation/XS Snapshots.md) for details and the C programming interface. + +`xsnap` can create a machine from scratch or from a snapshot. In both cases, the machine can be frozen prior to scripts or modules execution. + +When a machine is frozen, all intrinsics become immutable. Similarly to the [XS linker](https://github.com/Moddable-OpenSource/moddable/blob/public/documentation/xs/XS%20linker%20warnings.md), `xsnap` reports warning about mutable closures or objects. +`xsnap` cannot write a snapshot if the machine has been frozen. + +`xsnap` also uses the metering version of XS. There are options to constraint how much computation a machine can do. See [XS metering](./documentation/XS Metering.md) + +## Build + +### Linux + + cd ./makefiles/lin + make + +The debug version is built in `$MODDABLE/build/bin/lin/debug` +The release version is built in `$MODDABLE/build/bin/lin/release ` + +### macOS + + cd ./xs/makefiles/mac + make + +The debug version is built in `$MODDABLE/build/bin/mac/debug` +The release version is built in `$MODDABLE/build/bin/mac/release ` + +### Windows + + cd .\xs\makefiles\win + build + +The debug version is built in `$MODDABLE/build/bin/win/debug` +The release version is built in `$MODDABLE/build/bin/win/release ` + +## Usage + + xsnap [-f] [-h] [-v] + [-r ] [-w ] + [-i ] [-l ] [-p] + [-e] [-m] [-s] strings... + +- `-f`: freeze the XS machine +- `-h`: print this help message +- `-v`: print XS version +- `-r `: read snapshot to create the XS machine +- `-w `: write snapshot of the XS machine at exit +- `-i `: metering interval (defaults to 1) +- `-l `: metering limit (defaults to none) +- `-p`: prefix `print` output with metering index +- `-e`: eval `strings` +- `-m`: `strings` are paths to modules +- `-s`: `strings` are paths to scripts + +Without `-e`, `-m`, `-s`, if the extension is `.mjs`, strings are paths to modules, else strings are paths to scripts. The `-f` and `-w` options are incompatible. + +## Examples + +Add the debug or release directory here above to your path. + +### helloworld + + cd ./examples/helloworld + xsnap before.js -w snapshot.xsm + xsnap -r snapshot.xsm after.js + +The simplest example to see if the build was successful... + +### values + + cd ./examples/values + xsnap before.js -w snapshot.xsm + xsnap -r snapshot.xsm after.js + +Just to test how various JavaScript values survive the snapshot. + +### generators + + cd ./examples/generators + xsnap before.js -w snapshot.xsm + xsnap -r snapshot.xsm after.js + +Generators can be iterated before writing and after reading the snapshot. + +### promises + + cd ./examples/promises + xsnap before.js -w snapshot.xsm + xsnap -r snapshot.xsm after.js + +Promises can be part of a snapshot. And `then` can be used before writing and after reading the snapshot + +### proxy + + cd ./examples/proxy + xsnap before.js -w snapshot.xsm + xsnap -r snapshot.xsm after.js + +Just to test how a `Proxy` instance, its target and its handler survive the snapshot. + +### weaks + + cd ./examples/weaks + xsnap before.js -w snapshot.xsm + xsnap -r snapshot.xsm after.js + +`WeakMap`, `WeakSet`, `WeakRef` and `FinalizationRegistry` instances can be defined before writing the snapshot and used after reading the snapshot. + +### modules + +Use the `-m` option for modules + + cd ./examples/modules + xsnap -m before.js -w snapshot.xsm + xsnap -r snapshot.xsm -m after.js + +Modules imported before writing the snapshot are available after reading the snapshot. + +### compartments + +Use the `-f` option to freeze the machine in order to use compartments. + + cd ./examples/compartments + xsnap before.js -w snapshot.xsm + xsnap -r snapshot.xsm -f after.js + +`xsnap` warns about mutable closures and objects. + + ### warning() l: no const + 0 0 0 0 + undefined undefined undefined 1 + +### metering + +Use the `-l` option to limit the number of byte codes that can be executed. + + cd ./examples/metering + xsnap test.js -l 10000 + +The test prints numbers and exits when too many byte codes have been executed. + + ... + 524 + 525 + too much computation + +Use the `-i` option to change how often XS asks the runtime if the limit has been reached. + + xsnap test.js -l 10000 -i 1000 + +There is a performance gain but a precision lost. + + ... + 527 + 528 + too much computation + +### metering-built-ins + +Use the `-p` option to prefix `print` output with the metering index. + + cd ./examples/metering-built-ins + xsnap test.js -p + +The tests builds, sorts and reverses an array of 100 random numbers. Observe the metering index around `sort` and `reverse`. + + ... + [3516] 99 0.4153946155753893 + [3536] sort + [3651] reverse + [3782] 0 0.000007826369259425611 + ... + +By default built-ins functions do not change the metering index. But xsnap patches `Array.prototype.sort` and `Array.prototype.reverse` to increase the metering index by the length of the array. + diff --git a/packages/xsnap/example/README.md b/packages/xsnap/example/README.md new file mode 100644 index 000000000000..47edebd708ce --- /dev/null +++ b/packages/xsnap/example/README.md @@ -0,0 +1,2 @@ +These examples illustrate the preservation of state before and after a +snapshot. diff --git a/packages/xsnap/example/compartments/after.js b/packages/xsnap/example/compartments/after.js new file mode 100644 index 000000000000..8d7478931a06 --- /dev/null +++ b/packages/xsnap/example/compartments/after.js @@ -0,0 +1,12 @@ +const compartment = new Compartment(); +const globals = compartment.globalThis; + +compartment.evaluate(` + const c = 1; + let l = 1; + var v = 1; + globalThis.g = 1; +`); + +trace(c + " " + l + " " + v + " " + g + "\n"); // 0 0 0 0 +trace(globals.c + " " + globals.l + " " + globals.v + " " + globals.g + "\n"); // undefined undefined undefined 1 diff --git a/packages/xsnap/example/compartments/before.js b/packages/xsnap/example/compartments/before.js new file mode 100644 index 000000000000..81ec8f1e8ffd --- /dev/null +++ b/packages/xsnap/example/compartments/before.js @@ -0,0 +1,4 @@ +const c = 0; +let l = 0; +var v = 0; +globalThis.g = 0; diff --git a/packages/xsnap/example/generators/after.js b/packages/xsnap/example/generators/after.js new file mode 100644 index 000000000000..74d6ae83f47f --- /dev/null +++ b/packages/xsnap/example/generators/after.js @@ -0,0 +1,6 @@ +print(g0.next().value); // 0 +print(g0.next().value); // 1 +print(g0.next().value); // undefined +print(g1.next().value); // 1 +print(g1.next().value); // 2 +print(g1.next().value); // 3 diff --git a/packages/xsnap/example/generators/before.js b/packages/xsnap/example/generators/before.js new file mode 100644 index 000000000000..c99abd85024d --- /dev/null +++ b/packages/xsnap/example/generators/before.js @@ -0,0 +1,14 @@ +function* twice() { + yield 0; + yield 1; +} +const g0 = twice(); +function* infinite() { + let index = 0; + while (true) { + yield index++; + } +} +const g1 = infinite(); +g1.next(); + diff --git a/packages/xsnap/example/helloworld/after.js b/packages/xsnap/example/helloworld/after.js new file mode 100644 index 000000000000..438ed64ba3ba --- /dev/null +++ b/packages/xsnap/example/helloworld/after.js @@ -0,0 +1 @@ +print(helloworld); \ No newline at end of file diff --git a/packages/xsnap/example/helloworld/before.js b/packages/xsnap/example/helloworld/before.js new file mode 100644 index 000000000000..e430981eae79 --- /dev/null +++ b/packages/xsnap/example/helloworld/before.js @@ -0,0 +1 @@ +var helloworld = "Hello, world - xsnap"; \ No newline at end of file diff --git a/packages/xsnap/example/metering-built-ins/test.js b/packages/xsnap/example/metering-built-ins/test.js new file mode 100644 index 000000000000..41cee16cb56e --- /dev/null +++ b/packages/xsnap/example/metering-built-ins/test.js @@ -0,0 +1,12 @@ +const array = new Array(100); +for (let i = 0; i < 100; i++) { + array[i] = Math.random(); + print(i, array[i]); +} +print("sort"); +array.sort(); +print("reverse"); +array.sort(); +for (let i = 0; i < 100; i++) { + print(i, array[i]); +} diff --git a/packages/xsnap/example/metering/test.js b/packages/xsnap/example/metering/test.js new file mode 100644 index 000000000000..4f940a7fc1a1 --- /dev/null +++ b/packages/xsnap/example/metering/test.js @@ -0,0 +1,10 @@ +try { + for (let i = 0; i < 10000; i++) { + print(i); + } +} +catch(e) { + // never happens + // too much computation is no exception + print(e); +} diff --git a/packages/xsnap/example/modules/after.js b/packages/xsnap/example/modules/after.js new file mode 100644 index 000000000000..a8e90ca15292 --- /dev/null +++ b/packages/xsnap/example/modules/after.js @@ -0,0 +1,2 @@ +import increment from "./increment.js"; +print(increment()); // 1 \ No newline at end of file diff --git a/packages/xsnap/example/modules/before.js b/packages/xsnap/example/modules/before.js new file mode 100644 index 000000000000..a19d2488547c --- /dev/null +++ b/packages/xsnap/example/modules/before.js @@ -0,0 +1,2 @@ +import increment from "./increment.js"; +print(increment()); // 0 \ No newline at end of file diff --git a/packages/xsnap/example/modules/increment.js b/packages/xsnap/example/modules/increment.js new file mode 100644 index 000000000000..a649a635f8fc --- /dev/null +++ b/packages/xsnap/example/modules/increment.js @@ -0,0 +1,4 @@ +let x = 0; +export default function() { + return x++; +} diff --git a/packages/xsnap/example/promises/after.js b/packages/xsnap/example/promises/after.js new file mode 100644 index 000000000000..df8eddbea36e --- /dev/null +++ b/packages/xsnap/example/promises/after.js @@ -0,0 +1,6 @@ +p0.then(value => print(value)); // wow +p1.catch(value => print(value)); // oops +p2.then(value => print(value)); // wow +p3.catch(value => print(value)); // oops + +kit.resolve("wow"); // resolved wow \ No newline at end of file diff --git a/packages/xsnap/example/promises/before.js b/packages/xsnap/example/promises/before.js new file mode 100644 index 000000000000..d826df8f87ca --- /dev/null +++ b/packages/xsnap/example/promises/before.js @@ -0,0 +1,26 @@ + +const p0 = new Promise((resolve, reject) => { + resolve("wow"); +}); +const p1 = new Promise((resolve, reject) => { + reject("oops"); +}); + +async function f2() { + return "wow"; +} +const p2 = f2(); + +async function f3() { + throw "oops"; +} +const p3 = f3(); + +const kit = {} +kit.promise = new Promise((resolve, reject) => { + kit.resolve = resolve; + kit.reject = reject; +}); +kit.promise.then( + result => { print("resolved", result); }, + reason => { print("rejected", reason); }); diff --git a/packages/xsnap/example/proxy/after.js b/packages/xsnap/example/proxy/after.js new file mode 100644 index 000000000000..fdd618f58b96 --- /dev/null +++ b/packages/xsnap/example/proxy/after.js @@ -0,0 +1 @@ +print(proxy.key0, proxy.key1); diff --git a/packages/xsnap/example/proxy/before.js b/packages/xsnap/example/proxy/before.js new file mode 100644 index 000000000000..e3fffa50722d --- /dev/null +++ b/packages/xsnap/example/proxy/before.js @@ -0,0 +1,15 @@ +const target = { + key0: "hello", + key1: "everyone" +}; + +const handler = { + get: function (target, key, receiver) { + if (key === "key1") { + return "world"; + } + return Reflect.get(target, key, receiver); + }, +}; + +const proxy = new Proxy(target, handler); diff --git a/packages/xsnap/example/values/after.js b/packages/xsnap/example/values/after.js new file mode 100644 index 000000000000..f4d17091ed7f --- /dev/null +++ b/packages/xsnap/example/values/after.js @@ -0,0 +1 @@ +values.map((value, index) => print(`${Object.prototype.toString.call(value)}: ${strings[index]} === ${value.toString()}`)); diff --git a/packages/xsnap/example/values/before.js b/packages/xsnap/example/values/before.js new file mode 100644 index 000000000000..835491d50eb1 --- /dev/null +++ b/packages/xsnap/example/values/before.js @@ -0,0 +1,42 @@ +Map.prototype.toString = function() { + return Array.from(this).toString(); +} +Set.prototype.toString = function() { + return Array.from(this).toString(); +} + +const values = [ + false, + true, + Symbol(), + Symbol("description"), + new Error("message"), + new EvalError("message"), + new RangeError("message"), + new ReferenceError("message"), + new SyntaxError("message"), + new TypeError("message"), + new URIError("message"), + new AggregateError([new Error("message")], "message"), + 1, + 1n, + new Date(), + "string", + /regexp/g, + [0, 1, , 3], + new Int8Array([0, 1, 2, 3]), + new Uint8Array([0, 1, 2, 3]), + new Uint8ClampedArray([0, 1, 2, 3]), + new Int16Array([0, 1, 2, 3]), + new Uint16Array([0, 1, 2, 3]), + new Int32Array([0, 1, 2, 3]), + new Uint32Array([0, 1, 2, 3]), + new BigInt64Array([0n, 1n, 2n, 3n]), + new BigUint64Array([0n, 1n, 2n, 3n]), + new Float32Array([0, 1, 2, 3]), + new Float64Array([0, 1, 2, 3]), + new Map([["a", 0],["b", 1],["c", 2],["d", 2]]), + new Set([0, 1, 2, 3]), +]; + +const strings = values.map(value => value.toString()); diff --git a/packages/xsnap/example/weaks/after.js b/packages/xsnap/example/weaks/after.js new file mode 100644 index 000000000000..c4df313ab984 --- /dev/null +++ b/packages/xsnap/example/weaks/after.js @@ -0,0 +1,16 @@ +weak = null; +try { + strong = null; +} +catch { +} + +gc(); + +print(wm.has(strong)); // true +print(ws.has(strong)); // true + +print(wr0.deref()); // undefined +print(wr1.deref()); // [object Object] + +// FinalizationRegistry: weak \ No newline at end of file diff --git a/packages/xsnap/example/weaks/before.js b/packages/xsnap/example/weaks/before.js new file mode 100644 index 000000000000..f290b93fcf1f --- /dev/null +++ b/packages/xsnap/example/weaks/before.js @@ -0,0 +1,22 @@ +let weak = {}; +const strong = {}; + +const wm = new WeakMap; +wm.set(weak, "weak"); +wm.set(strong, "strong"); + +const ws = new WeakSet; +ws.add(weak); +ws.add(strong); + +const wr0 = new WeakRef(weak); +const wr1 = new WeakRef(strong); + +const fr = new FinalizationRegistry(value => { + print("FinalizationRegistry:", value) +}); +fr.register(weak, "weak"); +fr.register(strong, "strong"); + + + diff --git a/packages/xsnap/makefiles/lin/makefile b/packages/xsnap/makefiles/lin/makefile new file mode 100644 index 000000000000..145ec051f599 --- /dev/null +++ b/packages/xsnap/makefiles/lin/makefile @@ -0,0 +1,9 @@ +.NOTPARALLEL: + +all: debug release + +debug: + make -f xs-vat-worker.mk + +release: + make GOAL=release -f xs-vat-worker.mk diff --git a/packages/xsnap/makefiles/lin/xsnap.mk b/packages/xsnap/makefiles/lin/xsnap.mk new file mode 100644 index 000000000000..c148493255d9 --- /dev/null +++ b/packages/xsnap/makefiles/lin/xsnap.mk @@ -0,0 +1,129 @@ + : %.c +%.o : %.c + +GOAL ?= debug +NAME = xsnap +ifneq ($(VERBOSE),1) +MAKEFLAGS += --silent +endif + +HERE = $(dir $(realpath $(firstword $(MAKEFILE_LIST)))) +MODDABLE = $(HERE)/../../moddable +XS_DIR = $(MODDABLE)/xs +BUILD_DIR = $(HERE)/../../build + +BIN_DIR = $(BUILD_DIR)/bin/lin/$(GOAL) +INC_DIR = $(XS_DIR)/includes +PLT_DIR = $(XS_DIR)/platforms +SRC_DIR = $(XS_DIR)/sources +TLS_DIR = ../../src +TMP_DIR = $(BUILD_DIR)/tmp/lin/$(GOAL)/$(NAME) + +MACOS_ARCH ?= -arch i386 +MACOS_VERSION_MIN ?= -mmacosx-version-min=10.7 + +C_OPTIONS = \ + -fno-common \ + -DINCLUDE_XSPLATFORM \ + -DXSPLATFORM=\"xsnap.h\" \ + -DmxDebug=1 \ + -DmxMetering=1 \ + -DmxParse=1 \ + -DmxRun=1 \ + -DmxSloppy=1 \ + -DmxSnapshot=1 \ + -DmxRegExpUnicodePropertyEscapes=1 \ + -I$(INC_DIR) \ + -I$(PLT_DIR) \ + -I$(SRC_DIR) \ + -I$(TLS_DIR) \ + -I$(TMP_DIR) +C_OPTIONS += \ + -Wno-misleading-indentation \ + -Wno-implicit-fallthrough +ifeq ($(GOAL),debug) + C_OPTIONS += -g -O0 -Wall -Wextra -Wno-missing-field-initializers -Wno-unused-parameter +else + C_OPTIONS += -O3 +endif + +LIBRARIES = -ldl -lm -lpthread + +LINK_OPTIONS = -rdynamic + +OBJECTS = \ + $(TMP_DIR)/xsAll.o \ + $(TMP_DIR)/xsAPI.o \ + $(TMP_DIR)/xsArguments.o \ + $(TMP_DIR)/xsArray.o \ + $(TMP_DIR)/xsAtomics.o \ + $(TMP_DIR)/xsBigInt.o \ + $(TMP_DIR)/xsBoolean.o \ + $(TMP_DIR)/xsCode.o \ + $(TMP_DIR)/xsCommon.o \ + $(TMP_DIR)/xsDataView.o \ + $(TMP_DIR)/xsDate.o \ + $(TMP_DIR)/xsDebug.o \ + $(TMP_DIR)/xsDefaults.o \ + $(TMP_DIR)/xsError.o \ + $(TMP_DIR)/xsFunction.o \ + $(TMP_DIR)/xsGenerator.o \ + $(TMP_DIR)/xsGlobal.o \ + $(TMP_DIR)/xsJSON.o \ + $(TMP_DIR)/xsLexical.o \ + $(TMP_DIR)/xsMapSet.o \ + $(TMP_DIR)/xsMarshall.o \ + $(TMP_DIR)/xsMath.o \ + $(TMP_DIR)/xsMemory.o \ + $(TMP_DIR)/xsModule.o \ + $(TMP_DIR)/xsNumber.o \ + $(TMP_DIR)/xsObject.o \ + $(TMP_DIR)/xsPlatforms.o \ + $(TMP_DIR)/xsProfile.o \ + $(TMP_DIR)/xsPromise.o \ + $(TMP_DIR)/xsProperty.o \ + $(TMP_DIR)/xsProxy.o \ + $(TMP_DIR)/xsRegExp.o \ + $(TMP_DIR)/xsRun.o \ + $(TMP_DIR)/xsScope.o \ + $(TMP_DIR)/xsScript.o \ + $(TMP_DIR)/xsSnapshot.o \ + $(TMP_DIR)/xsSourceMap.o \ + $(TMP_DIR)/xsString.o \ + $(TMP_DIR)/xsSymbol.o \ + $(TMP_DIR)/xsSyntaxical.o \ + $(TMP_DIR)/xsTree.o \ + $(TMP_DIR)/xsType.o \ + $(TMP_DIR)/xsdtoa.o \ + $(TMP_DIR)/xsre.o \ + $(TMP_DIR)/$(NAME).o + +VPATH += $(SRC_DIR) $(TLS_DIR) + +build: $(TMP_DIR) $(BIN_DIR) $(BIN_DIR)/$(NAME) + +$(TMP_DIR): + mkdir -p $(TMP_DIR) + +$(BIN_DIR): + mkdir -p $(BIN_DIR) + +$(BIN_DIR)/$(NAME): $(OBJECTS) + @echo "#" $(NAME) $(GOAL) ": cc" $(@F) + $(CC) $(LINK_OPTIONS) $(OBJECTS) $(LIBRARIES) -o $@ + +$(OBJECTS): $(TLS_DIR)/xsnap.h +$(OBJECTS): $(PLT_DIR)/xsPlatform.h +$(OBJECTS): $(SRC_DIR)/xsCommon.h +$(OBJECTS): $(SRC_DIR)/xsAll.h +$(OBJECTS): $(SRC_DIR)/xsScript.h +$(OBJECTS): $(SRC_DIR)/xsSnapshot.h +$(TMP_DIR)/%.o: %.c + @echo "#" $(NAME) $(GOAL) ": cc" $(last; \ + if (list->last) \ + list->last->next = &name; \ + else \ + list->first = &name; \ + list->last = &name + +#define mxPopLink(name) \ + if (name.previous) \ + name.previous->next = C_NULL; \ + else \ + list->first = C_NULL; \ + list->last = name.previous + +static int fxSnapshotRead(void* stream, void* address, size_t size) +{ + return (fread(address, size, 1, stream) == 1) ? 0 : errno; +} + +static int fxSnapshotWrite(void* stream, void* address, size_t size) +{ + return (fwrite(address, size, 1, stream) == 1) ? 0 : errno; +} + +#define xsBeginMetering(_THE, _CALLBACK, _STEP) \ + do { \ + xsJump __HOST_JUMP__; \ + __HOST_JUMP__.nextJump = (_THE)->firstJump; \ + __HOST_JUMP__.stack = (_THE)->stack; \ + __HOST_JUMP__.scope = (_THE)->scope; \ + __HOST_JUMP__.frame = (_THE)->frame; \ + __HOST_JUMP__.environment = NULL; \ + __HOST_JUMP__.code = (_THE)->code; \ + __HOST_JUMP__.flag = 0; \ + (_THE)->firstJump = &__HOST_JUMP__; \ + if (setjmp(__HOST_JUMP__.buffer) == 0) { \ + fxBeginMetering(_THE, _CALLBACK, _STEP) + +#define xsEndMetering(_THE) \ + fxEndMetering(_THE); \ + } \ + (_THE)->stack = __HOST_JUMP__.stack, \ + (_THE)->scope = __HOST_JUMP__.scope, \ + (_THE)->frame = __HOST_JUMP__.frame, \ + (_THE)->code = __HOST_JUMP__.code, \ + (_THE)->firstJump = __HOST_JUMP__.nextJump; \ + break; \ + } while(1) + +#define xsPatchHostFunction(_FUNCTION,_PATCH) \ + (xsOverflow(-1), \ + fxPush(_FUNCTION), \ + fxPatchHostFunction(the, _PATCH), \ + fxPop()) +#define xsMeterHostFunction(_COUNT) \ + fxMeterHostFunction(the, _COUNT) + +static xsUnsignedValue gxMeteringLimit = 0; +static xsBooleanValue fxMeteringCallback(xsMachine* the, xsUnsignedValue index) +{ + if (index > gxMeteringLimit) { + fprintf(stderr, "too much computation\n"); + return 0; + } +// fprintf(stderr, "%d\n", index); + return 1; +} +static xsBooleanValue gxMeteringPrint = 0; + +static FILE *fromParent; +static FILE *toParent; + +int main(int argc, char* argv[]) +{ + int argi; + int argr = 0; + int error = 0; + int interval = 0; + int freeze = 0; + xsCreation _creation = { + 16 * 1024 * 1024, /* initialChunkSize */ + 16 * 1024 * 1024, /* incrementalChunkSize */ + 1 * 1024 * 1024, /* initialHeapCount */ + 1 * 1024 * 1024, /* incrementalHeapCount */ + 4096, /* stackCount */ + 32000, /* keyCount */ + 1993, /* nameModulo */ + 127, /* symbolModulo */ + 8192 * 1024, /* parserBufferSize */ + 1993, /* parserTableModulo */ + }; + xsCreation* creation = &_creation; + + txSnapshot snapshot = { + "xsnap 0.1.0", + 11, + gxSnapshotCallbacks, + mxSnapshotCallbackCount, + fxSnapshotRead, + fxSnapshotWrite, + NULL, + 0, + NULL, + NULL, + NULL, + }; + + xsMachine* machine; + char *path; + char* dot; + + for (argi = 1; argi < argc; argi++) { + if (argv[argi][0] != '-') + continue; + if (!strcmp(argv[argi], "-f")) { + freeze = 1; + } + else if (!strcmp(argv[argi], "-h")) { + fxPrintUsage(); + return 0; + } else if (!strcmp(argv[argi], "-i")) { + argi++; + if (argi < argc) + interval = atoi(argv[argi]); + else { + fxPrintUsage(); + return 1; + } + } + else if (!strcmp(argv[argi], "-l")) { + argi++; + if (argi < argc) + gxMeteringLimit = atoi(argv[argi]); + else { + fxPrintUsage(); + return 1; + } + } + else if (!strcmp(argv[argi], "-p")) + gxMeteringPrint = 1; + else if (!strcmp(argv[argi], "-r")) { + argi++; + if (argi < argc) + argr = argi; + else { + fxPrintUsage(); + return 1; + } + } + else if (!strcmp(argv[argi], "-v")) { + printf("XS %d.%d.%d\n", XS_MAJOR_VERSION, XS_MINOR_VERSION, XS_PATCH_VERSION); + return 0; + } else { + fxPrintUsage(); + return 1; + } + } + if (gxMeteringLimit) { + if (interval == 0) + interval = 1; + } + fxInitializeSharedCluster(); + if (argr) { + snapshot.stream = fopen(argv[argr], "rb"); + if (snapshot.stream) { + machine = fxReadSnapshot(&snapshot, "xsnap", NULL); + fclose(snapshot.stream); + } + else + snapshot.error = errno; + if (snapshot.error) { + fprintf(stderr, "cannot read snapshot %s: %s\n", argv[argr], strerror(snapshot.error)); + return 1; + } + } + else { + machine = xsCreateMachine(creation, "xsnap", NULL); + fxBuildAgent(machine); + fxPatchBuiltIns(machine); + } + if (freeze) { + fxFreezeBuiltIns(machine); + fxShareMachine(machine); + fxCheckAliases(machine); + machine = xsCloneMachine(creation, machine, "xsnap", NULL); + } + if (!(fromParent = fdopen(3, "rb"))) { + fprintf(stderr, "fdopen(3) from parent failed\n"); + c_exit(1); + } + if (!(toParent = fdopen(4, "wb"))) { + fprintf(stderr, "fdopen(4) to parent failed\n"); + c_exit(1); + } + xsBeginMetering(machine, fxMeteringCallback, interval); + { + char done = 0; + while (!done) { + char* nsbuf; + size_t nslen; + int readError = fxReadNetString(fromParent, &nsbuf, &nslen); + if (readError != 0) { + if (feof(fromParent)) { + break; + } else { + fprintf(stderr, "%s\n", fxReadNetStringError(readError)); + c_exit(1); + } + } + char command = *nsbuf; + // fprintf(stderr, "command: len %d %c arg: %s\n", nslen, command, nsbuf + 1); + switch(command) { + case '?': + case 'e': + error = 0; + char* response = NULL; + size_t responseLength = 0; + xsBeginHost(machine); + { + xsVars(3); + xsVar(1) = xsArrayBuffer(nsbuf + 1, nslen - 1); + xsTry { + if (command == '?') { + xsVar(2) = xsCall1(xsGlobal, xsID("answerSysCall"), xsVar(1)); + if (xsTypeOf(xsVar(2)) != xsUndefinedType) { + responseLength = fxGetArrayBufferLength(machine, &xsVar(2)); + response = malloc(responseLength); + fxGetArrayBufferData(machine, &xsVar(2), 0, response, responseLength); + } + } else { + xsVar(0) = xsGet(xsGlobal, xsID("String")); + xsVar(2) = xsCall1(xsVar(0), xsID("fromArrayBuffer"), xsVar(1)); + xsCall1(xsGlobal, xsID("eval"), xsVar(2)); + } + } + xsCatch { + if (xsTypeOf(xsException) != xsUndefinedType) { + fprintf(stderr, "%s\n", xsToString(xsException)); + error = 1; + xsException = xsUndefined; + } + } + } + xsEndHost(machine); + fxRunLoop(machine); + if (error == 0) { + int writeError = fxWriteNetString(toParent, '.', response, responseLength); + if (writeError != 0) { + fprintf(stderr, "%s\n", fxWriteNetStringError(writeError)); + c_exit(1); + } + } else { + // TODO: dynamically build error message including Exception message. + int writeError = fxWriteNetString(toParent, '!', "", 0); + if (writeError != 0) { + fprintf(stderr, "%s\n", fxWriteNetStringError(writeError)); + c_exit(1); + } + } + if (response != NULL) { + free(response); + response = NULL; + } + break; + case 's': + case 'm': + path = nsbuf + 1; + xsBeginHost(machine); + { + xsVars(1); + xsTry { + // ISSUE: realpath necessary? realpath(x, x) doesn't seem to work. + dot = strrchr(path, '.'); + if (command == 'm') + fxRunModuleFile(the, path); + else + fxRunProgramFile(the, path, mxProgramFlag | mxDebugFlag); + } + xsCatch { + if (xsTypeOf(xsException) != xsUndefinedType) { + fprintf(stderr, "%s\n", xsToString(xsException)); + error = 1; + xsException = xsUndefined; + } + } + } + xsEndHost(machine); + fxRunLoop(machine); + if (error == 0) { + int writeError = fxWriteNetString(toParent, '.', "", 0); + if (writeError != 0) { + fprintf(stderr, "%s\n", fxWriteNetStringError(writeError)); + c_exit(1); + } + } else { + // TODO: dynamically build error message including Exception message. + int writeError = fxWriteNetString(toParent, '!', "", 0); + if (writeError != 0) { + fprintf(stderr, "%s\n", fxWriteNetStringError(writeError)); + c_exit(1); + } + } + break; + + case 'w': + path = nsbuf + 1; + snapshot.stream = fopen(path, "wb"); + if (snapshot.stream) { + fxWriteSnapshot(machine, &snapshot); + fclose(snapshot.stream); + } + else + snapshot.error = errno; + if (snapshot.error) { + fprintf(stderr, "cannot write snapshot %s: %s\n", + path, strerror(snapshot.error)); + } + if (snapshot.error == 0) { + int writeError = fxWriteNetString(toParent, '.', "", 0); + if (writeError != 0) { + fprintf(stderr, "%s\n", fxWriteNetStringError(writeError)); + c_exit(1); + } + } else { + // TODO: dynamically build error message including Exception message. + int writeError = fxWriteNetString(toParent, '!', "", 0); + if (writeError != 0) { + fprintf(stderr, "%s\n", fxWriteNetStringError(writeError)); + c_exit(1); + } + } + break; + case -1: + default: + done = 1; + break; + } + free(nsbuf); + } + xsBeginHost(machine); + { + if (xsTypeOf(xsException) != xsUndefinedType) { + fprintf(stderr, "%s\n", xsToString(xsException)); + error = 1; + } + } + xsEndHost(machine); + } + xsEndMetering(machine); + xsDeleteMachine(machine); + fxTerminateSharedCluster(); + return error; +} + +void fxBuildAgent(xsMachine* the) +{ + txSlot* slot; + mxPush(mxGlobal); + slot = fxLastProperty(the, fxToInstance(the, the->stack)); + slot = fxNextHostFunctionProperty(the, slot, fx_clearTimer, 1, xsID("clearImmediate"), XS_DONT_ENUM_FLAG); + slot = fxNextHostFunctionProperty(the, slot, fx_clearTimer, 1, xsID("clearInterval"), XS_DONT_ENUM_FLAG); + slot = fxNextHostFunctionProperty(the, slot, fx_clearTimer, 1, xsID("clearTimeout"), XS_DONT_ENUM_FLAG); + slot = fxNextHostFunctionProperty(the, slot, fx_evalScript, 1, xsID("evalScript"), XS_DONT_ENUM_FLAG); + slot = fxNextHostFunctionProperty(the, slot, fx_gc, 1, xsID("gc"), XS_DONT_ENUM_FLAG); + slot = fxNextHostFunctionProperty(the, slot, fx_isPromiseJobQueueEmpty, 1, xsID("isPromiseJobQueueEmpty"), XS_DONT_ENUM_FLAG); + slot = fxNextHostFunctionProperty(the, slot, fx_print, 1, xsID("print"), XS_DONT_ENUM_FLAG); + slot = fxNextHostFunctionProperty(the, slot, fx_sysCall, 1, xsID("sysCall"), XS_DONT_ENUM_FLAG); + slot = fxNextHostFunctionProperty(the, slot, fx_setImmediate, 1, xsID("setImmediate"), XS_DONT_ENUM_FLAG); + slot = fxNextHostFunctionProperty(the, slot, fx_setInterval, 1, xsID("setInterval"), XS_DONT_ENUM_FLAG); + slot = fxNextHostFunctionProperty(the, slot, fx_setTimeout, 1, xsID("setTimeout"), XS_DONT_ENUM_FLAG); + + mxPush(mxObjectPrototype); + fxNextHostFunctionProperty(the, fxLastProperty(the, fxNewObjectInstance(the)), fx_print, 1, xsID("log"), XS_DONT_ENUM_FLAG); + slot = fxNextSlotProperty(the, slot, the->stack, xsID("console"), XS_DONT_ENUM_FLAG); + mxPop(); + + mxPop(); +} + +txInteger fxCheckAliases(txMachine* the) +{ + txAliasIDList _list = { C_NULL, C_NULL }, *list = &_list; + txSlot* module = mxProgram.value.reference->next; //@@ + list->aliases = c_calloc(the->aliasCount, sizeof(txFlag)); + while (module) { + txSlot* export = mxModuleExports(module)->value.reference->next; + if (export) { + mxPushLink(moduleLink, module->ID, XSL_MODULE_FLAG); + while (export) { + txSlot* closure = export->value.export.closure; + if (closure) { + mxPushLink(exportLink, export->ID, XSL_EXPORT_FLAG); + if (closure->ID != XS_NO_ID) { + if (list->aliases[closure->ID] == 0) { + list->aliases[closure->ID] = 1; + fxCheckAliasesError(the, list, 0); + } + } + if (closure->kind == XS_REFERENCE_KIND) { + fxCheckInstanceAliases(the, closure->value.reference, list); + } + mxPopLink(exportLink); + } + export = export->next; + } + mxPopLink(moduleLink); + } + module = module->next; + } + { + txSlot* global = mxGlobal.value.reference->next->next; + while (global) { + if ((global->ID != mxID(_global)) && (global->ID != mxID(_globalThis))) { + mxPushLink(globalLink, global->ID, XSL_GLOBAL_FLAG); + if (global->kind == XS_REFERENCE_KIND) { + fxCheckInstanceAliases(the, global->value.reference, list); + } + mxPopLink(globalLink); + } + global = global->next; + } + } + { + fxCheckEnvironmentAliases(the, mxException.value.reference, list); + } + return list->errorCount; +} + +void fxCheckAliasesError(txMachine* the, txAliasIDList* list, txFlag flag) +{ + txAliasIDLink* link = list->first; + if (flag > 1) + fprintf(stderr, "### error"); + else + fprintf(stderr, "### warning"); + while (link) { + switch (link->flag) { + case XSL_PROPERTY_FLAG: fprintf(stderr, "."); break; + case XSL_ITEM_FLAG: fprintf(stderr, "["); break; + case XSL_GETTER_FLAG: fprintf(stderr, ".get "); break; + case XSL_SETTER_FLAG: fprintf(stderr, ".set "); break; + case XSL_ENVIRONMENT_FLAG: fprintf(stderr, "() "); break; + case XSL_PROXY_HANDLER_FLAG: fprintf(stderr, ".(handler)"); break; + case XSL_PROXY_TARGET_FLAG: fprintf(stderr, ".(target)"); break; + default: fprintf(stderr, ": "); break; + } + if (link->id < 0) { + if (link->id != XS_NO_ID) { + char* string = fxGetKeyName(the, link->id); + if (string) { + if (link->flag == XSL_MODULE_FLAG) { + char* dot = c_strrchr(string, '.'); + if (dot) { + *dot = 0; + fprintf(stderr, "\"%s\"", string); + *dot = '.'; + } + else + fprintf(stderr, "%s", string); + } + else if (link->flag == XSL_GLOBAL_FLAG) { + fprintf(stderr, "globalThis."); + fprintf(stderr, "%s", string); + } + else + fprintf(stderr, "%s", string); + } + else + fprintf(stderr, "%d", link->id); + } + } + else + fprintf(stderr, "%d", link->id); + if (link->flag == XSL_ITEM_FLAG) + fprintf(stderr, "]"); + link = link->next; + } + if (flag == 3) { + fprintf(stderr, ": generator\n"); + list->errorCount++; + } + else if (flag == 2) { + fprintf(stderr, ": regexp\n"); + list->errorCount++; + } + else if (flag) + fprintf(stderr, ": not frozen\n"); + else + fprintf(stderr, ": no const\n"); +} + +void fxCheckEnvironmentAliases(txMachine* the, txSlot* environment, txAliasIDList* list) +{ + txSlot* closure = environment->next; + if (environment->flag & XS_LEVEL_FLAG) + return; + environment->flag |= XS_LEVEL_FLAG; + if (environment->value.instance.prototype) + fxCheckEnvironmentAliases(the, environment->value.instance.prototype, list); + while (closure) { + if (closure->kind == XS_CLOSURE_KIND) { + txSlot* slot = closure->value.closure; + mxPushLink(closureLink, closure->ID, XSL_ENVIRONMENT_FLAG); + if (slot->ID != XS_NO_ID) { + if (list->aliases[slot->ID] == 0) { + list->aliases[slot->ID] = 1; + fxCheckAliasesError(the, list, 0); + } + } + if (slot->kind == XS_REFERENCE_KIND) { + fxCheckInstanceAliases(the, slot->value.reference, list); + } + mxPopLink(closureLink); + } + closure = closure->next; + } + //environment->flag &= ~XS_LEVEL_FLAG; +} + +void fxCheckInstanceAliases(txMachine* the, txSlot* instance, txAliasIDList* list) +{ + txSlot* property = instance->next; + if (instance->flag & XS_LEVEL_FLAG) + return; + instance->flag |= XS_LEVEL_FLAG; + if (instance->value.instance.prototype) { + mxPushLink(propertyLink, mxID(___proto__), XSL_PROPERTY_FLAG); + fxCheckInstanceAliases(the, instance->value.instance.prototype, list); + mxPopLink(propertyLink); + } + if (instance->ID != XS_NO_ID) { + if (list->aliases[instance->ID] == 0) { + list->aliases[instance->ID] = 1; + fxCheckAliasesError(the, list, 1); + } + } + while (property) { + if (property->kind == XS_ACCESSOR_KIND) { + if (property->value.accessor.getter) { + mxPushLink(propertyLink, property->ID, XSL_GETTER_FLAG); + fxCheckInstanceAliases(the, property->value.accessor.getter, list); + mxPopLink(propertyLink); + } + if (property->value.accessor.setter) { + mxPushLink(propertyLink, property->ID, XSL_SETTER_FLAG); + fxCheckInstanceAliases(the, property->value.accessor.setter, list); + mxPopLink(propertyLink); + } + } + else if (property->kind == XS_ARRAY_KIND) { + txSlot* item = property->value.array.address; + txInteger length = (txInteger)fxGetIndexSize(the, property); + while (length > 0) { + if (item->kind == XS_REFERENCE_KIND) { + mxPushLink(propertyLink, (txInteger)(item->next), XSL_ITEM_FLAG); + fxCheckInstanceAliases(the, item->value.reference, list); + mxPopLink(propertyLink); + } + item++; + length--; + } + } + else if ((property->kind == XS_CODE_KIND) || (property->kind == XS_CODE_X_KIND)) { + if (property->value.code.closures) + fxCheckEnvironmentAliases(the, property->value.code.closures, list); + } + else if (property->kind == XS_PROXY_KIND) { + if (property->value.proxy.handler) { + mxPushLink(propertyLink, XS_NO_ID, XSL_PROXY_HANDLER_FLAG); + fxCheckInstanceAliases(the, property->value.proxy.handler, list); + mxPopLink(propertyLink); + } + if (property->value.proxy.target) { + mxPushLink(propertyLink, XS_NO_ID, XSL_PROXY_TARGET_FLAG); + fxCheckInstanceAliases(the, property->value.proxy.target, list); + mxPopLink(propertyLink); + } + } + else if (property->kind == XS_REFERENCE_KIND) { + mxPushLink(propertyLink, property->ID, XSL_PROPERTY_FLAG); + fxCheckInstanceAliases(the, property->value.reference, list); + mxPopLink(propertyLink); + } + property = property->next; + } +// instance->flag &= ~XS_LEVEL_FLAG; +} + +void fxFreezeBuiltIns(txMachine* the) +{ +#define mxFreezeBuiltInCall \ + mxPush(mxObjectConstructor); \ + mxPushSlot(freeze); \ + mxCall() +#define mxFreezeBuiltInRun \ + mxPushBoolean(1); \ + mxRunCount(2); \ + mxPop() + + txSlot* freeze; + txInteger id; + + mxTemporary(freeze); + mxPush(mxObjectConstructor); + fxGetID(the, mxID(_freeze)); + mxPullSlot(freeze); + + for (id = XS_SYMBOL_ID_COUNT; id < _Infinity; id++) { + mxFreezeBuiltInCall; mxPush(the->stackPrototypes[-1 - id]); mxFreezeBuiltInRun; + } + for (id = _Compartment; id < XS_INTRINSICS_COUNT; id++) { + mxFreezeBuiltInCall; mxPush(the->stackPrototypes[-1 - id]); mxFreezeBuiltInRun; + } + mxFreezeBuiltInCall; mxPush(mxGlobal); fxGetID(the, xsID("gc")); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxGlobal); fxGetID(the, xsID("evalScript")); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxGlobal); fxGetID(the, xsID("print")); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxGlobal); fxGetID(the, xsID("clearInterval")); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxGlobal); fxGetID(the, xsID("clearTimeout")); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxGlobal); fxGetID(the, xsID("setInterval")); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxGlobal); fxGetID(the, xsID("setTimeout")); mxFreezeBuiltInRun; + + mxFreezeBuiltInCall; mxPush(mxArgumentsSloppyPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxArgumentsStrictPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxArrayIteratorPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxAsyncFromSyncIteratorPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxAsyncFunctionPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxAsyncGeneratorFunctionPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxAsyncGeneratorPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxAsyncIteratorPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxGeneratorFunctionPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxGeneratorPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxHostPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxIteratorPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxMapEntriesIteratorPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxMapKeysIteratorPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxMapValuesIteratorPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxModulePrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxRegExpStringIteratorPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxSetEntriesIteratorPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxSetKeysIteratorPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxSetValuesIteratorPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxStringIteratorPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxTransferPrototype); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxTypedArrayPrototype); mxFreezeBuiltInRun; + + mxFreezeBuiltInCall; mxPush(mxAssignObjectFunction); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxCopyObjectFunction); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxEnumeratorFunction); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxInitializeRegExpFunction); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxOnRejectedPromiseFunction); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxOnResolvedPromiseFunction); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPush(mxOnThenableFunction); mxFreezeBuiltInRun; + + mxFreezeBuiltInCall; mxPushReference(mxArrayLengthAccessor.value.accessor.getter); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPushReference(mxArrayLengthAccessor.value.accessor.setter); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPushReference(mxStringAccessor.value.accessor.getter); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPushReference(mxStringAccessor.value.accessor.setter); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPushReference(mxProxyAccessor.value.accessor.getter); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPushReference(mxProxyAccessor.value.accessor.setter); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPushReference(mxTypedArrayAccessor.value.accessor.getter); mxFreezeBuiltInRun; + mxFreezeBuiltInCall; mxPushReference(mxTypedArrayAccessor.value.accessor.setter); mxFreezeBuiltInRun; + + mxFreezeBuiltInCall; mxPush(mxArrayPrototype); fxGetID(the, mxID(_Symbol_unscopables)); mxFreezeBuiltInRun; + + mxFreezeBuiltInCall; mxPush(mxProgram); mxFreezeBuiltInRun; //@@ + mxFreezeBuiltInCall; mxPush(mxHosts); mxFreezeBuiltInRun; //@@ + + mxPop(); +} + +void fx_Array_prototype_meter(xsMachine* the) +{ + xsIntegerValue length = xsToInteger(xsGet(xsThis, xsID("length"))); + xsMeterHostFunction(length); +} + +void fxPatchBuiltIns(txMachine* machine) +{ + xsBeginHost(machine); + xsVars(2); + xsVar(0) = xsGet(xsGlobal, xsID("Array")); + xsVar(0) = xsGet(xsVar(0), xsID("prototype")); + xsVar(1) = xsGet(xsVar(0), xsID("reverse")); + xsPatchHostFunction(xsVar(1), fx_Array_prototype_meter); + xsVar(1) = xsGet(xsVar(0), xsID("sort")); + xsPatchHostFunction(xsVar(1), fx_Array_prototype_meter); + xsEndHost(machine); +} + +void fxPrintUsage() +{ + printf("xsnap [-h] [-f] [i ] [l ] [-s] [-v]\n"); + printf("\t-f: freeze the XS machine\n"); + printf("\t-h: print this help message\n"); + printf("\t-i : metering interval (default to 1)\n"); + printf("\t-l : metering limit (default to none)\n"); + printf("\t-r : read snapshot to create the XS machine\n"); + printf("\t-v: print XS version\n"); +} + +void fx_evalScript(xsMachine* the) +{ + txSlot* realm = mxProgram.value.reference->next->value.module.realm; + txStringStream aStream; + aStream.slot = mxArgv(0); + aStream.offset = 0; + aStream.size = c_strlen(fxToString(the, mxArgv(0))); + fxRunScript(the, fxParseScript(the, &aStream, fxStringGetter, mxProgramFlag | mxDebugFlag), mxRealmGlobal(realm), C_NULL, mxRealmClosures(realm)->value.reference, C_NULL, mxProgram.value.reference); + mxPullSlot(mxResult); +} + +void fx_gc(xsMachine* the) +{ + xsCollectGarbage(); +} + +void fx_isPromiseJobQueueEmpty(txMachine* the) +{ + xsResult = (the->promiseJobs) ? xsFalse : xsTrue; +} + +void fx_print(xsMachine* the) +{ + xsIntegerValue c = xsToInteger(xsArgc), i; + if (gxMeteringPrint) + fprintf(stdout, "[%u] ", the->meterIndex); + for (i = 0; i < c; i++) { + if (i) + fprintf(stdout, " "); + fprintf(stdout, "%s", xsToString(xsArg(i))); + } + fprintf(stdout, "\n"); +} + +void fx_setImmediate(txMachine* the) +{ + fx_setTimer(the, 0, 0); +} + +void fx_setInterval(txMachine* the) +{ + fx_setTimer(the, fxToNumber(the, mxArgv(1)), 1); +} + +void fx_setTimeout(txMachine* the) +{ + fx_setTimer(the, fxToNumber(the, mxArgv(1)), 0); +} + +static txHostHooks gxTimerHooks = { + fx_destroyTimer, + fx_markTimer +}; + +void fx_clearTimer(txMachine* the) +{ + txJob* data = fxGetHostData(the, mxThis); + if (data) { + txJob* job; + txJob** address; + address = (txJob**)&(the->timerJobs); + while ((job = *address)) { + if (job == data) { + *address = job->next; + c_free(job); + break; + } + address = &(job->next); + } + fxSetHostData(the, mxThis, NULL); + } +} + +void fx_destroyTimer(void* data) +{ +} + +void fx_markTimer(txMachine* the, void* it, txMarkRoot markRoot) +{ + txJob* job = it; + if (job) { + (*markRoot)(the, &job->function); + (*markRoot)(the, &job->argument); + } +} + +void fx_setTimer(txMachine* the, txNumber interval, txBoolean repeat) +{ + c_timeval tv; + txJob* job; + txJob** address = (txJob**)&(the->timerJobs); + while ((job = *address)) + address = &(job->next); + job = *address = malloc(sizeof(txJob)); + c_memset(job, 0, sizeof(txJob)); + job->the = the; + job->callback = fx_setTimerCallback; + c_gettimeofday(&tv, NULL); + if (repeat) + job->interval = interval; + job->when = ((txNumber)(tv.tv_sec) * 1000.0) + ((txNumber)(tv.tv_usec) / 1000.0) + interval; + job->function.kind = mxArgv(0)->kind; + job->function.value = mxArgv(0)->value; + if (mxArgc > 2) { + job->argument.kind = mxArgv(2)->kind; + job->argument.value = mxArgv(2)->value; + } + fxNewHostObject(the, C_NULL); + fxSetHostData(the, the->stack, job); + fxSetHostHooks(the, the->stack, &gxTimerHooks); + mxPullSlot(mxResult); +} + +void fx_setTimerCallback(txJob* job) +{ + txMachine* the = job->the; + fxBeginHost(the); + { + mxTry(the) { + /* THIS */ + mxPushUndefined(); + /* FUNCTION */ + mxPush(job->function); + mxCall(); + mxPush(job->argument); + /* ARGC */ + mxRunCount(1); + mxPop(); + } + mxCatch(the) { + } + } + fxEndHost(the); +} + +/* PLATFORM */ + +void fxAbort(txMachine* the, int status) +{ + if (status == XS_NOT_ENOUGH_MEMORY_EXIT) + mxUnknownError("not enough memory"); + else if (status == XS_STACK_OVERFLOW_EXIT) + mxUnknownError("stack overflow"); + else if (status == XS_TOO_MUCH_COMPUTATION_EXIT) + fxExitToHost(the); + else + c_exit(status); +} + +void fxCreateMachinePlatform(txMachine* the) +{ +#ifdef mxDebug + the->connection = mxNoSocket; +#endif + the->promiseJobs = 0; + the->timerJobs = NULL; +} + +void fxDeleteMachinePlatform(txMachine* the) +{ +} + +void fxMarkHost(txMachine* the, txMarkRoot markRoot) +{ + txJob* job = the->timerJobs; + while (job) { + fx_markTimer(the, job, markRoot); + job = job->next; + } +} + +void fxQueuePromiseJobs(txMachine* the) +{ + the->promiseJobs = 1; +} + +void fxRunLoop(txMachine* the) +{ + c_timeval tv; + txNumber when; + txJob* job; + txJob** address; + for (;;) { + while (the->promiseJobs) { + the->promiseJobs = 0; + fxRunPromiseJobs(the); + } + c_gettimeofday(&tv, NULL); + when = ((txNumber)(tv.tv_sec) * 1000.0) + ((txNumber)(tv.tv_usec) / 1000.0); + address = (txJob**)&(the->timerJobs); + if (!*address) + break; + while ((job = *address)) { + if (job->when <= when) { + (*job->callback)(job); + if (job->interval) { + job->when += job->interval; + } + else { + *address = job->next; + c_free(job); + } + break; + } + address = &(job->next); + } + } +} + +void fxSweepHost(txMachine* the) +{ +} + +void fxFulfillModuleFile(txMachine* the) +{ + xsException = xsUndefined; +} + +void fxRejectModuleFile(txMachine* the) +{ + xsException = xsArg(0); +} + +void fxRunModuleFile(txMachine* the, txString path) +{ + txSlot* realm = mxProgram.value.reference->next->value.module.realm; + mxPushStringC(path); + fxRunImport(the, realm, XS_NO_ID); + mxDub(); + fxGetID(the, mxID(_then)); + mxCall(); + fxNewHostFunction(the, fxFulfillModuleFile, 1, XS_NO_ID); + fxNewHostFunction(the, fxRejectModuleFile, 1, XS_NO_ID); + mxRunCount(2); + mxPop(); +} + +void fxRunProgramFile(txMachine* the, txString path, txUnsigned flags) +{ + txSlot* realm = mxProgram.value.reference->next->value.module.realm; + txScript* script = fxLoadScript(the, path, flags); + if (!script) { + xsUnknownError("cannot load script; check filename"); + } + mxModuleInstanceInternal(mxProgram.value.reference)->value.module.id = fxID(the, path); + fxRunScript(the, script, mxRealmGlobal(realm), C_NULL, mxRealmClosures(realm)->value.reference, C_NULL, mxProgram.value.reference); + mxPullSlot(mxResult); +} + +/* DEBUG */ + +#ifdef mxDebug + +void fxConnect(txMachine* the) +{ + char name[256]; + char* colon; + int port; + struct sockaddr_in address; +#if mxWindows + if (GetEnvironmentVariable("XSBUG_HOST", name, sizeof(name))) { +#else + colon = getenv("XSBUG_HOST"); + if ((colon) && (c_strlen(colon) + 1 < sizeof(name))) { + c_strcpy(name, colon); +#endif + colon = strchr(name, ':'); + if (colon == NULL) + port = 5002; + else { + *colon = 0; + colon++; + port = strtol(colon, NULL, 10); + } + } + else { + strcpy(name, "localhost"); + port = 5002; + } + memset(&address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = inet_addr(name); + if (address.sin_addr.s_addr == INADDR_NONE) { + struct hostent *host = gethostbyname(name); + if (!host) + return; + memcpy(&(address.sin_addr), host->h_addr, host->h_length); + } + address.sin_port = htons(port); +#if mxWindows +{ + WSADATA wsaData; + unsigned long flag; + if (WSAStartup(0x202, &wsaData) == SOCKET_ERROR) + return; + the->connection = socket(AF_INET, SOCK_STREAM, 0); + if (the->connection == INVALID_SOCKET) + return; + flag = 1; + ioctlsocket(the->connection, FIONBIO, &flag); + if (connect(the->connection, (struct sockaddr*)&address, sizeof(address)) == SOCKET_ERROR) { + if (WSAEWOULDBLOCK == WSAGetLastError()) { + fd_set fds; + struct timeval timeout = { 2, 0 }; // 2 seconds, 0 micro-seconds + FD_ZERO(&fds); + FD_SET(the->connection, &fds); + if (select(0, NULL, &fds, NULL, &timeout) == 0) + goto bail; + if (!FD_ISSET(the->connection, &fds)) + goto bail; + } + else + goto bail; + } + flag = 0; + ioctlsocket(the->connection, FIONBIO, &flag); +} +#else +{ + int flag; + the->connection = socket(AF_INET, SOCK_STREAM, 0); + if (the->connection <= 0) + goto bail; + c_signal(SIGPIPE, SIG_IGN); +#if mxMacOSX + { + int set = 1; + setsockopt(the->connection, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); + } +#endif + flag = fcntl(the->connection, F_GETFL, 0); + fcntl(the->connection, F_SETFL, flag | O_NONBLOCK); + if (connect(the->connection, (struct sockaddr*)&address, sizeof(address)) < 0) { + if (errno == EINPROGRESS) { + fd_set fds; + struct timeval timeout = { 2, 0 }; // 2 seconds, 0 micro-seconds + int error = 0; + unsigned int length = sizeof(error); + FD_ZERO(&fds); + FD_SET(the->connection, &fds); + if (select(the->connection + 1, NULL, &fds, NULL, &timeout) == 0) + goto bail; + if (!FD_ISSET(the->connection, &fds)) + goto bail; + if (getsockopt(the->connection, SOL_SOCKET, SO_ERROR, &error, &length) < 0) + goto bail; + if (error) + goto bail; + } + else + goto bail; + } + fcntl(the->connection, F_SETFL, flag); + c_signal(SIGPIPE, SIG_DFL); +} +#endif + return; +bail: + fxDisconnect(the); +} + +void fxDisconnect(txMachine* the) +{ +#if mxWindows + if (the->connection != INVALID_SOCKET) { + closesocket(the->connection); + the->connection = INVALID_SOCKET; + } + WSACleanup(); +#else + if (the->connection >= 0) { + close(the->connection); + the->connection = -1; + } +#endif +} + +txBoolean fxIsConnected(txMachine* the) +{ + return (the->connection != mxNoSocket) ? 1 : 0; +} + +txBoolean fxIsReadable(txMachine* the) +{ + return 0; +} + +void fxReceive(txMachine* the) +{ + int count; + if (the->connection != mxNoSocket) { +#if mxWindows + count = recv(the->connection, the->debugBuffer, sizeof(the->debugBuffer) - 1, 0); + if (count < 0) + fxDisconnect(the); + else + the->debugOffset = count; +#else + again: + count = read(the->connection, the->debugBuffer, sizeof(the->debugBuffer) - 1); + if (count < 0) { + if (errno == EINTR) + goto again; + else + fxDisconnect(the); + } + else + the->debugOffset = count; +#endif + } + the->debugBuffer[the->debugOffset] = 0; +} + +void fxSend(txMachine* the, txBoolean more) +{ + if (the->connection != mxNoSocket) { +#if mxWindows + if (send(the->connection, the->echoBuffer, the->echoOffset, 0) <= 0) + fxDisconnect(the); +#else + again: + if (write(the->connection, the->echoBuffer, the->echoOffset) <= 0) { + if (errno == EINTR) + goto again; + else + fxDisconnect(the); + } +#endif + } +} + +#endif /* mxDebug */ + + +/** + * @param dest: where to store address of newly allocated buffer, + * which is null-terminated for compatibility with C strings + * @returns count of bytes read or -1 for error; + * caller takes ownership of *dest unless an error occurs + */ +static int fxReadNetString(FILE *inStream, char** dest, size_t* len) +{ + int code = 0; + char* buf = NULL; + + if (fscanf(inStream, "%9lu", len) < 1) { + /* >999999999 bytes is bad */ + code = 1; + } else if (fgetc(inStream) != ':') { + code = 2; + } else { + buf = malloc(*len + 1); /* malloc(0) is not portable */ + if (!buf) { + code = 3; + } else if (fread(buf, 1, *len, inStream) < *len) { + code = 4; + } else if (fgetc(inStream) != ',') { + code = 5; + } else { + *(buf + *len) = 0; + } + if (code != 0) { + free(buf); + } + *dest = buf; + } + return code; +} + +static char* fxReadNetStringError(int code) +{ + switch (code) { + case 0: return "OK"; + case 1: return "Cannot read netstring, reading length prefix, fscanf"; + case 2: return "Cannot read netstring, invalid delimiter or end of file, fgetc"; + case 3: return "Cannot read netstring, cannot allocate message buffer, malloc"; + case 4: return "Cannot read netstring, cannot read message body, fread"; + case 5: return "Cannot read netstring, cannot read trailer, fgetc"; + default: return "Cannot read netstring"; + } +} + +static int fxWriteNetString(FILE* outStream, char prefix, char* buf, size_t length) +{ + if (fprintf(outStream, "%lu:%c", length + 1, prefix) < 1) { + return 1; + } else if (fwrite(buf, 1, length, outStream) < length) { + return 2; + } else if (fputc(',', outStream) == EOF) { + return 3; + } else if (fflush(outStream) < 0) { + return 4; + } + + return 0; +} + +static char* fxWriteNetStringError(int code) +{ + switch (code) { + case 0: return "OK"; + case 1: return "Cannot write netstring, error writing length prefix"; + case 2: return "Cannot write netstring, error writing message body"; + case 3: return "Cannot write netstring, error writing terminator"; + case 4: return "Cannot write netstring, error flushing stream, fflush"; + default: return "Cannot write netstring"; + } +} + +/** + * sysCall: write netstring; read netstring reply + */ +static void fx_sysCall(xsMachine *the) +{ + int argc = xsToInteger(xsArgc); + if (argc < 1) { + mxTypeError("expected ArrayBuffer"); + } + + size_t length; + char* buf = NULL; + txSlot* arrayBuffer = &xsArg(0); + length = fxGetArrayBufferLength(the, arrayBuffer); + + buf = malloc(length); + + fxGetArrayBufferData(the, arrayBuffer, 0, buf, length); + int writeError = fxWriteNetString(toParent, '?', buf, length); + + free(buf); + + if (writeError != 0) { + xsUnknownError(fxWriteNetStringError(writeError)); + } + + // read netstring + size_t len; + int readError = fxReadNetString(fromParent, &buf, &len); + if (readError != 0) { + xsUnknownError(fxReadNetStringError(readError)); + } + + xsResult = xsArrayBuffer(buf, len); +} + +// vim: noet ts=4 sw=4 diff --git a/packages/xsnap/src/xsnap.h b/packages/xsnap/src/xsnap.h new file mode 100644 index 000000000000..be390037d805 --- /dev/null +++ b/packages/xsnap/src/xsnap.h @@ -0,0 +1,86 @@ +#ifndef __XSNAP__ +#define __XSNAP__ + +#if defined(_MSC_VER) + #if defined(_M_IX86) || defined(_M_X64) + #undef mxLittleEndian + #define mxLittleEndian 1 + #undef mxWindows + #define mxWindows 1 + #define mxExport extern + #define mxImport extern + #define XS_FUNCTION_NORETURN + #else + #error unknown Microsoft compiler + #endif +#elif defined(__GNUC__) + #if defined(__i386__) || defined(i386) || defined(intel) || defined(arm) || defined(__arm__) || defined(__k8__) || defined(__x86_64__) || defined(__aarch64__) + #undef mxLittleEndian + #define mxLittleEndian 1 + #if defined(__linux__) || defined(linux) + #undef mxLinux + #define mxLinux 1 + #else + #undef mxMacOSX + #define mxMacOSX 1 + #endif + #define mxExport extern + #define mxImport extern + #define XS_FUNCTION_NORETURN __attribute__((noreturn)) + #else + #error unknown GNU compiler + #endif +#else + #error unknown compiler +#endif + +#if mxWindows + #define _USE_MATH_DEFINES + #define WIN32_LEAN_AND_MEAN + #define _WINSOCK_DEPRECATED_NO_WARNINGS +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if mxWindows + #include + typedef SOCKET txSocket; + #define mxNoSocket INVALID_SOCKET +#else + #include + #include + #include + #include + #include + #include + typedef int txSocket; + #define mxNoSocket -1 + #define mxUseGCCAtomics 1 + #define mxUsePOSIXThreads 1 +#endif +#define mxMachinePlatform \ + txSocket connection; \ + int promiseJobs; \ + void* timerJobs; \ + void* waiterCondition; \ + void* waiterData; \ + txMachine* waiterLink; + +#define mxUseDefaultBuildKeys 1 +#define mxUseDefaultChunkAllocation 1 +#define mxUseDefaultSlotAllocation 1 +#define mxUseDefaultFindModule 1 +#define mxUseDefaultLoadModule 1 +#define mxUseDefaultParseScript 1 +#define mxUseDefaultSharedChunks 1 + +#endif /* __XSNAP__ */ + +// vim: noet ts=4 sw=4