├── .gitignore ├── .npmignore ├── README.md ├── lib ├── node │ ├── bin.js │ └── index.js ├── orchestrator │ ├── index.js │ ├── bin.js │ ├── forked.js │ ├── common.js │ └── inline.js ├── reset-ca.js ├── virus.js ├── trap-hints.js └── main.js ├── test ├── result.txt ├── base │ ├── index.html │ ├── generate-main.js │ └── main.js ├── run.sh └── meta │ ├── empty-async.js │ ├── empty-sync.js │ ├── forward-async.js │ ├── forward-sync.js │ └── linvail.js ├── package.json └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /node_modules/ 3 | /ca/ -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /node_modules/ 3 | /test/ 4 | /ca/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AranRemote 2 | 3 | Deploy Aran's analyses on distributed programs, for free. 4 | -------------------------------------------------------------------------------- /lib/node/bin.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const Minimist = require("minimist"); 3 | const Index = require("./index.js"); 4 | Index(Minimist(process.argv.slice(2))); -------------------------------------------------------------------------------- /test/result.txt: -------------------------------------------------------------------------------- 1 | 2 | browser forward-sync devtools 273,560731358 3 | browser forward-async devtools 237,413035132 4 | browser linvail devtools 198,68847787 5 | -------------------------------------------------------------------------------- /test/base/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Base 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /lib/node/index.js: -------------------------------------------------------------------------------- 1 | const Path = require("path"); 2 | const OtilukeNode = require("otiluke/node"); 3 | const Virus = require(Path.join(__dirname, "..", "virus.js")); 4 | module.exports = (options) => OtilukeNode(Virus, options); -------------------------------------------------------------------------------- /lib/orchestrator/index.js: -------------------------------------------------------------------------------- 1 | 2 | const Inline = require("./inline.js"); 3 | const Forked = require("./forked.js"); 4 | 5 | module.exports = (options, callback) => { 6 | if (options["inline"]) Inline(options, callback); 7 | else Forked(options, callback); 8 | }; 9 | -------------------------------------------------------------------------------- /lib/reset-ca.js: -------------------------------------------------------------------------------- 1 | const Path = require("path"); 2 | const cahome = process.argv.length > 2 ? Path.resolve(process.argv[2]) : Path.join(__dirname, "..", "ca"); 3 | require("otiluke/browser/ca/index.js")({ 4 | subj: "/CN=aran-remote/O=AranRemote", 5 | "ca-home": cahome 6 | }); 7 | console.log("New self-signed certificate generated at: "+Path.join(cahome, "cert.pem")); -------------------------------------------------------------------------------- /test/base/generate-main.js: -------------------------------------------------------------------------------- 1 | const Fs = require("fs"); 2 | const Path = require("path"); 3 | const dirname = Path.join(__dirname, "..", "..", "..", "aran", "test", "target", "atom"); 4 | const content = Fs.readdirSync(dirname).filter((filname) => filname.endsWith(".js")).sort().map((filname) => { 5 | return "// "+filname+"\nconsole.log("+JSON.stringify(filname)+");\n(function () {\n"+Fs.readFileSync(Path.join(dirname, filname), "utf8")+"\n} ());\n\n"; 6 | }).join(""); 7 | Fs.writeFileSync(Path.join(__dirname, "main.js"), content, "utf8"); -------------------------------------------------------------------------------- /lib/orchestrator/bin.js: -------------------------------------------------------------------------------- 1 | const Common = require("./common.js"); 2 | Common(JSON.parse(process.argv[2]), (location, hostname, message) => { 3 | console.error(location+" >> "+hostname+" >> "+message); 4 | // process.send({location, hostname, message}); 5 | }, (error, result) => { 6 | if (error) { 7 | process.send(error.message); 8 | process.exit(0); 9 | } else { 10 | process.on("SIGTERM", () => { 11 | result.server.close(); 12 | if (result.proxy) result.proxy.closeAll(); 13 | }); 14 | process.send(null); 15 | } 16 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aran-remote", 3 | "description": "Deploy local Aran advices on remote processes", 4 | "version": "0.3.9", 5 | "author": { 6 | "name": "Laurent Christophe", 7 | "email": "lachrist@vub.ac.be" 8 | }, 9 | "devDependencies": { 10 | "linvail": "6.0.10" 11 | }, 12 | "dependencies": { 13 | "acorn": "^6.1.0", 14 | "aran": "3.0.8", 15 | "astring": "^1.3.1", 16 | "melf": "2.2.14", 17 | "melf-share": "0.1.12", 18 | "otiluke": "5.4.3" 19 | }, 20 | "bin": "lib/node/bin.js", 21 | "main": "lib/main.js", 22 | "repository": "lachrist/aran-remote", 23 | "homepage": "http://github.com/lachrist/aran-remote", 24 | "license": "MIT", 25 | "keywords": [ 26 | "Remote", 27 | "Aran", 28 | "Instrumentation", 29 | "Distributed" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /test/run.sh: -------------------------------------------------------------------------------- 1 | 2 | # killall node ; clear && printf '\e[3J' ; node meta/linvail.js --node-port=9000 --alias=meta --log 3 | # clear && printf '\e[3J' ; node ../lib/node/bin.js --host=9000 --alias=base --meta-alias=meta -- base/main.js 4 | 5 | node $1 --node-port=9000 --alias=meta & 6 | sleep 2 7 | node ../lib/node/bin.js --host=9000 --alias=base --meta-alias=meta -- base/main.js 8 | wait $! 9 | 10 | # http-server base/ -p 8000 & 11 | # pid1=$! 12 | # node $1 --browser-port=8080 --alias=meta --argmpfx=META- --splitter=_META_ & 13 | # pid2=$! 14 | # sleep 2 15 | # /Applications/Firefox.app/Contents/MacOS/firefox-bin -private -devtools "http://localhost:8000?META-splitter=_META_&META-alias=base&META-meta-alias=meta" & 16 | # printf "\n\nPRESS ENTER WHEN DONE\n\n" 17 | # read -p "..." 18 | # kill $! 19 | # wait $pid2 20 | # kill $pid1 21 | 22 | # /bin/bash 1> /dev/null 2> /dev/null -c "/Applications/Firefox.app/Contents/MacOS/firefox-bin -private -devtools \"http://localhost:8000?META-splitter=_META_&META-alias=base&META-meta-alias=meta\"" & 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Laurent Christophe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/meta/empty-async.js: -------------------------------------------------------------------------------- 1 | 2 | const Minimist = require("minimist"); 3 | const Acorn = require("acorn"); 4 | const AranRemote = require("../../lib/main.js"); 5 | const Astring = require("astring"); 6 | 7 | const options = Object.assign(Minimist(process.argv.slice(2)), { 8 | inline: true, 9 | synchronous: false, 10 | }); 11 | 12 | AranRemote(options, (error, aran) => { 13 | if (error) 14 | throw error; 15 | const pointcut = (name, node) => name in advice; 16 | const transform = (script, source) => { 17 | const serial = typeof source === "number" ? source : null; 18 | const estree1 = Acorn.parse(script); 19 | const estree2 = aran.weave(estree1, pointcut, serial); 20 | return Astring.generate(estree2); 21 | }; 22 | aran.then(() => { aran.orchestrator.terminate() }, (error) => { throw error }); 23 | aran.orchestrator.then(() => { console.log("Success") }, (error) => { throw error }); 24 | aran.onterminate = (alias) => { if (alias !== aran.alias) aran.terminate(aran.alias) }; 25 | const advice = { 26 | __proto__: null, 27 | program: async function (serial) { aran.terminate(this.alias) }, 28 | eval: async (script, serial) => transform(await aran.reflect.binary("+", "", script), serial) 29 | }; 30 | return ({global, alias, argm}) => ({transform, advice: {__proto__:advice, alias}}); 31 | }); 32 | -------------------------------------------------------------------------------- /test/meta/empty-sync.js: -------------------------------------------------------------------------------- 1 | 2 | const Minimist = require("minimist"); 3 | const Acorn = require("acorn"); 4 | const AranRemote = require("../../lib/main.js"); 5 | const Astring = require("astring"); 6 | 7 | const options = Object.assign(Minimist(process.argv.slice(2)), { 8 | inline: false, 9 | synchronous: true, 10 | }); 11 | 12 | AranRemote(options, (error, aran) => { 13 | if (error) 14 | throw error; 15 | const pointcut = (name, node) => name in advice; 16 | const transform = (script, source) => { 17 | const serial = typeof source === "number" ? source : null; 18 | const estree1 = Acorn.parse(script); 19 | const estree2 = aran.weave(estree1, pointcut, serial); 20 | return Astring.generate(estree2); 21 | }; 22 | aran.then(() => { aran.orchestrator.terminate() }, (error) => { throw error }); 23 | aran.orchestrator.then(() => { console.log("Success") }, (error) => { throw error }); 24 | aran.onterminate = (alias) => { if (alias !== aran.alias) aran.terminate(aran.alias) }; 25 | const advice = { 26 | __proto__: null, 27 | program: function (serial) { aran.terminate(this.alias) }, 28 | eval: (script, serial) => transform( aran.reflect.binary("+", "", script), serial) 29 | }; 30 | return ({global, alias, argm}) => ({transform, advice: {__proto__:advice, alias}}); 31 | }); 32 | -------------------------------------------------------------------------------- /lib/orchestrator/forked.js: -------------------------------------------------------------------------------- 1 | 2 | const ChildProcess = require("child_process"); 3 | const Path = require("path"); 4 | 5 | // const signal = (location, hostname, message) => { 6 | // console.error(location+" >> "+hostname+" >> "+message); 7 | // }; 8 | 9 | module.exports = (options, callback) => { 10 | // , {execArgv:['--inspect-brk=9230']} 11 | const child = ChildProcess.fork(Path.join(__dirname, "bin.js"), [JSON.stringify(options)]); 12 | child.on("exit", (code) => { throw new Error("Orchestrator process exit ("+code+") before result message") }); 13 | child.once("message", (message) => { 14 | if (message) return callback(new Error(message)); 15 | child.removeAllListeners("exit"); 16 | let resolve, reject; 17 | const promise = new Promise((closure1, closure2) => { 18 | resolve = closure1; 19 | reject = closure2; 20 | }); 21 | // child.on("message", ({location, hostname, message}) => { 22 | // promise.onfailure(location, hostname, message); 23 | // }); 24 | child.on("exit", (code) => { 25 | if (code !== 0 && code !== null) throw new Error("Orchestrator process exit ("+code+")"); 26 | resolve(null); 27 | }); 28 | promise.address = options["node-port"]; 29 | promise.terminate = () => child.kill("SIGTERM"); 30 | promise.destroy = () => { 31 | child.kill("SIGINT"); 32 | reject(new Error("Destroyed by the user")); 33 | }; 34 | // promise.onfailure = signal; 35 | callback(null, promise); 36 | }); 37 | }; 38 | -------------------------------------------------------------------------------- /lib/orchestrator/common.js: -------------------------------------------------------------------------------- 1 | 2 | const Net = require("net"); 3 | const Path = require("path"); 4 | const Http = require("http"); 5 | const Distributor = require("melf/lib/distributor"); 6 | const OtilukeBrowser = require("otiluke/browser"); 7 | 8 | module.exports = (options, onfailure, callback) => { 9 | const server = Net.createServer(); 10 | server.on("error", callback); 11 | server.listen(options["node-port"], () => { 12 | server.removeListener("error", callback); 13 | const distributor = Distributor(options["log"]); 14 | server.on("connection", distributor.ConnectionListener()); 15 | if (options.otiluke.port === undefined || options.otiluke.port === null) { 16 | callback(null, {distributor, server, proxy:null}); 17 | } else { 18 | const proxy = OtilukeBrowser(Path.join(__dirname, "..", "virus.js"), Object.assign(options.otiluke, { 19 | onfailure, 20 | intercept: { 21 | request: distributor.RequestMiddleware(options.splitter), 22 | upgrade: distributor.UpgradeMiddleware(options.splitter) 23 | } 24 | })); 25 | const cleanup = (error) => { 26 | server.close(); 27 | callback(error instanceof Error ? error : new Error("Early connection")); 28 | } 29 | server.on("connection", cleanup); 30 | proxy.on("error", cleanup); 31 | proxy.listen(options.otiluke.port, () => { 32 | server.removeListener("connection", cleanup); 33 | proxy.removeListener("error", cleanup); 34 | callback(null, {distributor, server, proxy}); 35 | }); 36 | } 37 | }); 38 | }; 39 | -------------------------------------------------------------------------------- /lib/orchestrator/inline.js: -------------------------------------------------------------------------------- 1 | 2 | const Common = require("./common.js"); 3 | 4 | // const signal = (location, hostname, message) => { 5 | // console.error(location+" >> "+hostname+" >> "+message); 6 | // }; 7 | 8 | module.exports = (options, callback) => { 9 | const promise = new Promise((resolve, reject) => { 10 | Common(options, (location, hostname, message) => { 11 | console.error(location+" >> "+hostname+" >> "+message); 12 | // promise.onfailure(location, hostname, message); 13 | }, (error, {distributor, server, proxy}) => { 14 | if (error) return callback(error); 15 | const sockets = new Set(); 16 | function onsocketclose () { 17 | sockets.delete(this); 18 | } 19 | server.on("connection", (socket) => { 20 | sockets.add(socket); 21 | socket.on("close", onsocketclose); 22 | }); 23 | const failure = (error) => { 24 | server.close(); 25 | for (let socket of sockets) socket.destroy(); 26 | if (proxy) proxy.destroy(); 27 | reject(error); 28 | }; 29 | server.on("close", () => { 30 | if (!proxy || !proxy.listening) resolve(null) 31 | }); 32 | if (proxy) proxy.on("close", () => { 33 | // Unlike for the forked orchestrator, it is not 34 | // guaranteed that all the servers/sockets closed 35 | // gracefully 36 | proxy.closeAll(); 37 | proxy.destroyAll(); 38 | if (!server.listening) resolve(null); 39 | }); 40 | // promise.onfailure = signal; 41 | promise.address = distributor; 42 | promise.destroy = () => failure(new Error("Destroyed by the user")); 43 | promise.terminate = () => { 44 | server.close(); 45 | if (proxy) proxy.closeAll(); 46 | }; 47 | callback(null, promise); 48 | }); 49 | }); 50 | }; 51 | -------------------------------------------------------------------------------- /lib/virus.js: -------------------------------------------------------------------------------- 1 | const Melf = require("melf"); 2 | const MelfShare = require("melf-share"); 3 | const TrapHints = require("./trap-hints.js"); 4 | 5 | const process = (function () { return this } ()).process; 6 | 7 | // Inspired from: https://github.com/iliakan/detect-node 8 | 9 | const success = () => { 10 | if (Object.prototype.toString.call(process) === "[object process]") { 11 | process.exit(0); 12 | } else { 13 | alert("Success (this page will emit network errors)"); 14 | } 15 | }; 16 | 17 | const failure = (error) => { 18 | console.error(error.stack); 19 | if (Object.prototype.toString.call(process) === "[object process]") { 20 | process.exit(1); 21 | } else { 22 | alert("Failure (this page will emit network errors): "+error.message); 23 | } 24 | }; 25 | 26 | module.exports = (argm, callback) => { 27 | Melf(argm.host || argm.splitter, argm.alias, (error, melf) => { 28 | if (error) 29 | return failure(error); 30 | const alias = argm["meta-alias"] || "aran"; 31 | const share = MelfShare(melf, {synchronous:true}); 32 | melf.then(success, failure); 33 | melf.onterminate = () => { 34 | melf.rpcall(alias, "onterminate", null); 35 | }; 36 | melf.rprocedures.terminate = (alias, data, callback) => { 37 | callback(null, null); 38 | melf.terminate(); 39 | }; 40 | melf.rprocedures.destroy = (alias, data, callback) => { 41 | callback(null, null); 42 | melf.destroy(); 43 | }; 44 | const {namespace, setup} = melf.rpcall(alias, "initialize", { 45 | global: share.serialize(global), 46 | argm: argm 47 | }); 48 | global[namespace] = {__proto__:null}; 49 | Object.keys(TrapHints).forEach((name) => { 50 | const hints = TrapHints[name]; 51 | global[namespace][name] = (...array) => { 52 | for (let index = 0, length = array.length - 1; index < length; index++) 53 | array[index] = share.serialize(array[index], hints[index]); 54 | return share.instantiate(melf.rpcall(alias, name, array)); 55 | }; 56 | }); 57 | global.eval(setup); 58 | callback(null, (script, source) => melf.rpcall(alias, "transform", {script, source})); 59 | }); 60 | }; 61 | -------------------------------------------------------------------------------- /test/meta/forward-async.js: -------------------------------------------------------------------------------- 1 | 2 | const Minimist = require("minimist"); 3 | const Acorn = require("acorn"); 4 | const AranRemote = require("../../lib/main.js"); 5 | const Astring = require("astring"); 6 | 7 | const options = Object.assign(Minimist(process.argv.slice(2)), { 8 | inline: true, 9 | synchronous: false, 10 | }); 11 | 12 | AranRemote(options, (error, aran) => { 13 | if (error) 14 | throw error; 15 | const pointcut = (name, node) => true; 16 | const transform = (script, source) => { 17 | const serial = typeof source === "number" ? source : null; 18 | const estree1 = Acorn.parse(script); 19 | const estree2 = aran.weave(estree1, pointcut, serial); 20 | return Astring.generate(estree2); 21 | }; 22 | let time = null; 23 | aran.then(() => { aran.orchestrator.terminate() }, (error) => { throw error }); 24 | aran.orchestrator.then(() => { console.log("Success "+process.hrtime(time)) }, (error) => { throw error }); 25 | aran.onterminate = (alias) => { if (alias !== aran.alias) aran.terminate(aran.alias) }; 26 | const noop = async () => {}; 27 | const identity = async (arg0) => arg0; 28 | const advice = {__proto__:null}; 29 | // Informers // 30 | [ 31 | "arrival", 32 | "enter", 33 | "leave", 34 | "continue", 35 | "break", 36 | "debugger" 37 | ].forEach((key) => { advice[key] = noop }); 38 | advice.program = async function (serial) { 39 | time = process.hrtime(); 40 | aran.terminate(this.alias); 41 | }; 42 | // Transformers // 43 | [ 44 | "error", 45 | "abrupt", 46 | "throw", 47 | "return", 48 | "closure", 49 | "builtin", 50 | "primitive", 51 | "read", 52 | "argument", 53 | "drop", 54 | "test", 55 | "write", 56 | "success", 57 | "failure" 58 | ].forEach((key) => { advice[key] = identity }); 59 | advice.eval = async (script, serial) => transform(await aran.reflect.binary("+", "", script), serial); 60 | // Combiners // 61 | advice.construct = async (c, xs, s) => await aran.reflect.construct(c, xs); 62 | advice.apply = async (f, t, xs, s) => await aran.reflect.apply(f, t, xs); 63 | advice.unary = async (o, x) => await aran.reflect.unary(o, x); 64 | advice.binary = async (o, x1, x2) => await aran.reflect.binary(o, x1, x2); 65 | return ({global, alias, argm}) => ({transform, advice:{__proto__:advice, alias}}); 66 | }); 67 | -------------------------------------------------------------------------------- /test/meta/forward-sync.js: -------------------------------------------------------------------------------- 1 | 2 | const Minimist = require("minimist"); 3 | const Acorn = require("acorn"); 4 | const AranRemote = require("../../lib/main.js"); 5 | const Astring = require("astring"); 6 | 7 | const options = Object.assign(Minimist(process.argv.slice(2)), { 8 | inline: false, 9 | synchronous: true, 10 | }); 11 | 12 | AranRemote(options, (error, aran) => { 13 | if (error) 14 | throw error; 15 | const pointcut = (name, node) => true; 16 | const transform = (script, source) => { 17 | const serial = typeof source === "number" ? source : null; 18 | const estree1 = Acorn.parse(script); 19 | const estree2 = aran.weave(estree1, pointcut, serial); 20 | return Astring.generate(estree2); 21 | }; 22 | let time = null; 23 | aran.then(() => { aran.orchestrator.terminate() }, (error) => { throw error }); 24 | aran.orchestrator.then(() => { console.log("Success "+process.hrtime(time)) }, (error) => { throw error }); 25 | aran.onterminate = (alias) => { if (alias !== aran.alias) aran.terminate(aran.alias) }; 26 | const noop = () => {}; 27 | const identity = (arg0) => arg0; 28 | const advice = {__proto__:null}; 29 | // Informers // 30 | [ 31 | "arrival", 32 | "enter", 33 | "leave", 34 | "continue", 35 | "break", 36 | "debugger" 37 | ].forEach((key) => { advice[key] = noop }); 38 | advice.program = function (serial) { 39 | time = process.hrtime(); 40 | aran.terminate(this.alias); 41 | }; 42 | // Transformers // 43 | [ 44 | "error", 45 | "abrupt", 46 | "throw", 47 | "return", 48 | "closure", 49 | "builtin", 50 | "primitive", 51 | "read", 52 | "argument", 53 | "drop", 54 | "test", 55 | "write", 56 | "success", 57 | "failure" 58 | ].forEach((key) => { advice[key] = identity }); 59 | advice.eval = (script, serial) => transform( aran.reflect.binary("+", "", script), serial); 60 | // Combiners // 61 | advice.construct = (c, xs, s) => aran.reflect.construct(c, xs); 62 | advice.apply = (f, t, xs, s) => aran.reflect.apply(f, t, xs); 63 | advice.unary = (o, x) => aran.reflect.unary(o, x); 64 | advice.binary = (o, x1, x2) => aran.reflect.binary(o, x1, x2); 65 | return ({global, alias, argm}) => ({transform, advice:{__proto__:advice, alias}}); 66 | }); 67 | -------------------------------------------------------------------------------- /lib/trap-hints.js: -------------------------------------------------------------------------------- 1 | 2 | /////////////// 3 | // Informers // 4 | /////////////// 5 | exports.arrival = ["function", "constructor|undefined", "value", "arguments"]; 6 | exports.break = ["label|null"]; 7 | exports.continue = ["label|null"]; 8 | exports.debugger = []; 9 | exports.enter = ["tag", ["label"], ["indentifier"]]; 10 | exports.leave = []; 11 | exports.program = ["object"]; 12 | 13 | /////////////// 14 | // Modifiers // 15 | /////////////// 16 | exports.abrupt = ["value"]; 17 | exports.argument = ["value", "argument-index"]; 18 | exports.builtin = ["value", "builtin-name"]; 19 | exports.closure = ["function"]; 20 | exports.drop = ["value"]; 21 | exports.error = ["value"]; 22 | exports.failure = ["value"]; 23 | exports.primitive = ["primitive"]; 24 | exports.read = ["value", "identifier"]; 25 | exports.return = ["value"]; 26 | exports.success = ["value"]; 27 | exports.test = ["value"]; 28 | exports.throw = ["value"]; 29 | exports.write = ["value", "identifier"]; 30 | exports.eval = ["value"]; 31 | 32 | /////////////// 33 | // Combiners // 34 | /////////////// 35 | exports.apply = ["value", "value", ["value"]]; 36 | exports.binary = ["binary-operator", "value", "value"]; 37 | exports.construct = ["value", ["value"]]; 38 | exports.unary = ["unary-operator", "value"]; 39 | 40 | // 41 | // /////////////// 42 | // // Producers // 43 | // /////////////// 44 | // exports.arrival = ["strict", {}]; 45 | // exports.begin = ["strict", {}]; 46 | // exports.catch = ["error"]; 47 | // exports.closure = ["function"]; 48 | // exports.discard = ["identifier", "success"]; 49 | // exports.load = ["name", "builtin"]; 50 | // exports.primitive = ["primitive"]; 51 | // exports.read = ["identifier", "value"]; 52 | // exports.regexp = ["regexp"]; 53 | // 54 | // /////////////// 55 | // // Consumers // 56 | // /////////////// 57 | // exports.completion = ["value"]; 58 | // exports.declare = ["kind", "identifier", "value"]; 59 | // exports.eval = ["script"]; 60 | // exports.failure = [{}, "error"]; 61 | // exports.return = [{}, "result"]; 62 | // exports.save = ["string", "any"]; 63 | // exports.success = [{}, "value"]; 64 | // exports.test = ["value"]; 65 | // exports.throw = ["error"]; 66 | // exports.with = ["environment"]; 67 | // exports.write = ["identifier", "value"]; 68 | // 69 | // /////////////// 70 | // // Informers // 71 | // /////////////// 72 | // exports.block = []; 73 | // exports.break = ["continue", "label"]; 74 | // exports.copy = ["position"]; 75 | // exports.drop = []; 76 | // exports.end = [{}]; 77 | // exports.finally = []; 78 | // exports.label = ["continue", "label"]; 79 | // exports.leave = ["type"]; 80 | // exports.swap = ["position1", "position2"]; 81 | // exports.try = []; 82 | -------------------------------------------------------------------------------- /test/meta/linvail.js: -------------------------------------------------------------------------------- 1 | 2 | const Minimist = require("minimist"); 3 | const Acorn = require("acorn"); 4 | const AranRemote = require("../../lib/main.js"); 5 | const Astring = require("astring"); 6 | const Linvail = require("linvail"); 7 | 8 | const options = Object.assign(Minimist(process.argv.slice(2)), { 9 | inline: false, 10 | synchronous: true, 11 | }); 12 | 13 | AranRemote(options, (error, aran) => { 14 | if (error) 15 | throw error; 16 | const advice = {}; 17 | const pointcut = (name, node) => name in advice; 18 | const transform = (script, source) => { 19 | const serial = typeof source === "number" ? source : null; 20 | const estree1 = Acorn.parse(script); 21 | const estree2 = aran.weave(estree1, pointcut, serial); 22 | return Astring.generate(estree2); 23 | }; 24 | let time = null; 25 | aran.then(() => { aran.orchestrator.terminate() }, (error) => { throw error }); 26 | aran.orchestrator.then(() => { console.log("Success "+process.hrtime(time)) }, (error) => { throw error }); 27 | aran.onterminate = (alias) => { if (alias !== aran.alias) aran.terminate(aran.alias) }; 28 | let counter = 0; 29 | const membrane = { 30 | taint: (value) => ({meta:"@"+(++counter), base:value}), 31 | clean: ($$value) => $$value.base 32 | }; 33 | const {capture, release} = Linvail(membrane); 34 | // Informers // 35 | advice.program = function (serial) { 36 | time = process.hrtime(); 37 | aran.terminate(this.alias); 38 | }; 39 | // Consumers // 40 | advice.throw = ($$value, serial) => release(membrane.clean($$value)); 41 | advice.test = ($$value, serial) => { 42 | console.log($$value.meta+" TEST"); 43 | return membrane.clean($$value); 44 | }; 45 | advice.success = ($$value, serial) => release(membrane.clean($$value)); 46 | advice.eval = ($$value, serial) => { 47 | const script = release(membrane.clean($$value)); 48 | return transform(script, serial); 49 | }; 50 | // Producers // 51 | advice.error = (value, serial) => membrane.taint(capture(value)); 52 | advice.primitive = (primitive, serial) => { 53 | const $$primitive = membrane.taint(primitive); 54 | console.log($$primitive.meta+" := "+JSON.stringify(primitive)); 55 | return $$primitive; 56 | }; 57 | advice.builtin = (value, name, serial) => membrane.taint(capture(value)); 58 | advice.closure = ($closure, serial) => { 59 | Reflect.setPrototypeOf($closure, capture(Function.prototype)); 60 | return membrane.taint($closure); 61 | }; 62 | advice.argument = (_value, name) => { 63 | if (name === "length" || name === "new.target") 64 | return membrane.taint(_value); 65 | return _value; 66 | }; 67 | // Combiners // 68 | advice.apply = ($$value1, $$value2, $$values, serial) => { 69 | return Reflect.apply(membrane.clean($$value1), $$value2, $$values); 70 | }; 71 | advice.construct = ($$value, $$values, serial) => { 72 | return Reflect.construct(membrane.clean($$value), $$values); 73 | }; 74 | advice.unary = (operator, $$value, serial) => { 75 | const value = release(membrane.clean($$value)); 76 | const primitive = aran.reflect.unary(operator, value); 77 | const $$primitive = membrane.taint(primitive); 78 | console.log($$primitive.meta+" := "+operator+" "+$$value.meta); 79 | return $$primitive; 80 | }; 81 | advice.binary = (operator, $$value1, $$value2, serial) => { 82 | const value1 = release(membrane.clean($$value1)); 83 | const value2 = release(membrane.clean($$value2)); 84 | const primitive = aran.reflect.binary(operator, value1, value2); 85 | const $$primitive = membrane.taint(primitive); 86 | console.log($$primitive.meta+" := "+$$value1.meta+" "+operator+" "+$$value2.meta); 87 | return $$primitive; 88 | }; 89 | // Return 90 | return ({global, alias, argm}) => ({transform, advice:{__proto__:advice, alias}}); 91 | }); 92 | -------------------------------------------------------------------------------- /lib/main.js: -------------------------------------------------------------------------------- 1 | 2 | const Os = require("os"); 3 | const Fs = require("fs"); 4 | const Path = require("path"); 5 | const Aran = require("aran"); 6 | const Melf = require("melf"); 7 | const MelfShare = require("melf-share"); 8 | const Astring = require("astring"); 9 | const TrapHints = require("./trap-hints.js"); 10 | const Orchestrator = require("./orchestrator"); 11 | 12 | const identity = (argument) => argument; 13 | 14 | const noop = () => {}; 15 | 16 | module.exports = (options={}, callback) => { 17 | options = { 18 | "node-port": options["node-port"] || Path.join(Os.tmpdir(), "aran-"+Date.now().toString(36)+"-"+Math.random().toString(36).substring(2)+".sock"), 19 | "inline": options["inline"] || false, 20 | "alias": options["alias"] || "aran", 21 | "log": options["log"] || false, 22 | "splitter": options["splitter"] || "ARAN", 23 | "melf-share": { 24 | "synchronous": options["synchronous"] || false, 25 | }, 26 | aran: { 27 | "namespace": options["namespace"] || "_", 28 | "format": options["format"] || "estree", 29 | "root": options["roots"] || [] 30 | }, 31 | otiluke: { 32 | "port": options["browser-port"] || null, 33 | "argmpfx": options["argmpfx"] || "ARAN-", 34 | "cahome": options["cahome"] || Path.join(__dirname, "..", "ca"), 35 | "gvar": options["gvar"] || "__ARAN_TRANSFORM__", 36 | "sockdir": options["sockdir"] || null 37 | } 38 | }; 39 | if (options["melf-share"]["synchronous"] && options["inline"]) { 40 | return callback(new Error("Cannot use the synchronous API and inline the orchestrator")); 41 | } 42 | Orchestrator(options, (error, orchestrator) => { 43 | if (error) return callback(error); 44 | Melf(orchestrator.address, options["alias"], (error, melf) => { 45 | if (error) { 46 | orchestrator.interrupt(); 47 | callback(error); 48 | } else { 49 | const share = MelfShare(melf, options["melf-share"]); 50 | const analyses = {__proto__:null}; 51 | const generate = options["aran"]["format"] === "estree" ? Astring.generate : identity; 52 | const aran = Aran(options["aran"]); 53 | const promise = new Promise((resolve, reject) => { melf.then(resolve, reject) }); 54 | promise.orchestrator = orchestrator; 55 | promise._aran = aran; 56 | promise.weave = weave; 57 | promise.nodes = aran.nodes; 58 | promise.roots = aran.roots; 59 | promise.namespace = aran.namespace; 60 | promise.format = aran.format; 61 | promise._melf = melf; 62 | promise.alias = melf.alias; 63 | promise.onterminate = noop; 64 | promise.terminate = terminate; 65 | promise.destroy = destroy; 66 | promise._share = share; 67 | promise.ownerof = share.ownerof; 68 | promise.reflect = share.reflect; 69 | const Analysis = callback(null, promise); 70 | melf.rprocedures["onterminate"] = (origin, data, callback) => { 71 | callback(null, null); 72 | promise.onterminate(origin); 73 | }; 74 | melf.onterminate = () => { promise.onterminate(melf.alias) }; 75 | melf.rprocedures["initialize"] = (origin, data, callback) => { 76 | try { 77 | analyses[origin] = Analysis({ 78 | alias: origin, 79 | argm: data.argm, 80 | global: share.instantiate(data.global) 81 | }); 82 | callback(null, { 83 | namespace: aran.namespace, 84 | setup: generate(aran.setup()), 85 | }); 86 | } catch (error) { 87 | callback(error); 88 | } 89 | }; 90 | melf.rprocedures["transform"] = (origin, {script, source}, callback) => { 91 | try { 92 | callback(null, analyses[origin].transform(script, source)); 93 | } catch (error) { 94 | callback(error); 95 | } 96 | }; 97 | if (options["melf-share"]["synchronous"]) { 98 | Object.keys(TrapHints).forEach((name) => { 99 | const hints = TrapHints[name]; 100 | const length = hints.length; 101 | melf.rprocedures[name] = (origin, data, callback) => { 102 | try { 103 | for (let index = 0; index < length; index++) 104 | data[index] = share.instantiate(data[index], hints[index]); 105 | callback(null, share.serialize(analyses[origin].advice[name](...data))); 106 | } catch (error) { 107 | callback(error); 108 | } 109 | }; 110 | }); 111 | } else { 112 | Object.keys(TrapHints).forEach((name) => { 113 | const hints = TrapHints[name]; 114 | const length = hints.length; 115 | melf.rprocedures[name] = (origin, data, callback) => { 116 | for (let index = 0; index < length; index++) 117 | data[index] = share.instantiate(data[index], hints[index]); 118 | analyses[origin].advice[name](...data).then((result) => { 119 | callback(null, share.serialize(result), "value"); 120 | }, callback); 121 | }; 122 | }); 123 | } 124 | } 125 | }); 126 | }); 127 | }; 128 | 129 | function weave (root, pointcut, scope) { 130 | return this._aran.weave(root, pointcut, scope); 131 | } 132 | 133 | function terminate (alias, callback) { 134 | if (alias && alias !== this.alias) { 135 | this._melf.rpcall(alias, "terminate", null, (error, data) => { if (error) throw error }); 136 | } else { 137 | this._melf.terminate(); 138 | } 139 | } 140 | 141 | function destroy (alias) { 142 | if (alias && alias !== this.alias) { 143 | this._melf.rpcall(alias, "destroy", null, (error, data) => { if (error) throw error }); 144 | } else { 145 | this._melf.destroy(); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /test/base/main.js: -------------------------------------------------------------------------------- 1 | // Arguments.js 2 | console.log("Arguments.js"); 3 | (function () { 4 | let f = function () { 5 | if (arguments[0] !== "foo") 6 | throw new Error("Arguments1"); 7 | if (arguments[1] !== "bar") 8 | throw new Error("Arguments2"); 9 | if (arguments.length !== 2) 10 | throw new Error("Arguments3"); 11 | }; 12 | f("foo", "bar"); 13 | } ()); 14 | 15 | // Array.js 16 | console.log("Array.js"); 17 | (function () { 18 | if ([1,2,3][0] !== 1) 19 | throw new Error("Array"); 20 | } ()); 21 | 22 | // Arrow.js 23 | console.log("Arrow.js"); 24 | (function () { 25 | let a1 = () => "foo"; 26 | let a2 = () => { return "bar" }; 27 | if (a1() !== "foo") 28 | throw new Error("Arrow1"); 29 | if (a2() !== "bar") 30 | throw new Error("Arrow2"); 31 | let check = false; 32 | try { 33 | new a1(); 34 | } catch (error) { 35 | check = true; 36 | } 37 | if (!check) 38 | throw new Error("Arrow3"); 39 | } ()); 40 | 41 | // Binary.js 42 | console.log("Binary.js"); 43 | (function () { 44 | if (1+2 !== 3) 45 | throw new Error("Binary"); 46 | } ()); 47 | 48 | // Block.js 49 | console.log("Block.js"); 50 | (function () { 51 | (function () { 52 | {let x = 1} 53 | if (typeof x !== "undefined") 54 | throw new Error("Block1"); 55 | {var y = 1} 56 | if (y !== 1) 57 | throw new Error("Block2"); 58 | } ()); 59 | } ()); 60 | 61 | // Break.js 62 | console.log("Break.js"); 63 | (function () { 64 | a:{ 65 | break a; 66 | throw new Error("Break1"); 67 | } 68 | while (true) { 69 | break; 70 | throw new Error("Break2"); 71 | } 72 | } ()); 73 | 74 | // Call.js 75 | console.log("Call.js"); 76 | (function () { 77 | if ((() => 1)() !== 1) 78 | throw new Error("Call"); 79 | } ()); 80 | 81 | // Conditional.js 82 | console.log("Conditional.js"); 83 | (function () { 84 | if ((true?1:2) !== 1) 85 | throw new Error("Conditional1"); 86 | if ((false?1:2) !== 2) 87 | throw new Error("Conditional2"); 88 | } ()); 89 | 90 | // Const.js 91 | console.log("Const.js"); 92 | (function () { 93 | { 94 | const c = "foo"; 95 | try { 96 | c = "bar"; 97 | } catch (_) { 98 | } 99 | if (c !== "foo") 100 | throw new Error("Const1"); 101 | } 102 | if (typeof c !== "undefined") 103 | throw new Error("Const2"); 104 | } ()); 105 | 106 | // Continue.js 107 | console.log("Continue.js"); 108 | (function () { 109 | let x = true; 110 | while (x) { 111 | x = false; 112 | continue; 113 | throw new Error("Continue1"); 114 | } 115 | let y = true; 116 | a : while (y) { 117 | y = false; 118 | continue a; 119 | throw new Error("Continue2"); 120 | } 121 | } ()); 122 | 123 | // Declaration.js 124 | console.log("Declaration.js"); 125 | (function () { 126 | let x, y = 1; 127 | if (x !== undefined) 128 | throw new Error("Declaration1"); 129 | if (y !== 1) 130 | throw new Error("Declaration2"); 131 | } ()); 132 | 133 | // DeclarationFor.js 134 | console.log("DeclarationFor.js"); 135 | (function () { 136 | let sum = 0; 137 | for (let i=0; i<4; i++) 138 | sum = sum + i; 139 | if (sum !== 6) 140 | throw new Error("DeclarationFor"); 141 | } ()); 142 | 143 | // DeclarationForIn.js 144 | console.log("DeclarationForIn.js"); 145 | (function () { 146 | let sum = ""; 147 | for (let k in {a:1,b:2,c:3}) 148 | sum = sum + k; 149 | if (sum !== "abc") 150 | throw new Error("DeclarationForIn"); 151 | } ()); 152 | 153 | // DeclarationForOf.js 154 | console.log("DeclarationForOf.js"); 155 | (function () { 156 | let sum = ""; 157 | for (let x of ["a", "b", "c"]) 158 | sum = sum + x; 159 | if (sum !== "abc") 160 | throw new Error("ForOf"); 161 | } ()); 162 | 163 | // Definition.js 164 | console.log("Definition.js"); 165 | (function () { 166 | (function () { 167 | f(); 168 | function f () {} 169 | } ()); 170 | } ()); 171 | 172 | // DoWhile.js 173 | console.log("DoWhile.js"); 174 | (function () { 175 | let i = 0; 176 | do { 177 | i++; 178 | } while (i < 3) 179 | if (i !== 3) 180 | throw new Error("DoWhile"); 181 | } ()); 182 | 183 | // Empty.js 184 | console.log("Empty.js"); 185 | (function () { 186 | ; 187 | } ()); 188 | 189 | // EvalCall.js 190 | console.log("EvalCall.js"); 191 | (function () { 192 | let x = 1; 193 | if (eval("x") !== 1) 194 | throw new Error("EvalCall"); 195 | } ()); 196 | 197 | // Expression.js 198 | console.log("Expression.js"); 199 | (function () { 200 | "foo"; 201 | } ()); 202 | 203 | // For.js 204 | console.log("For.js"); 205 | (function () { 206 | let i; 207 | for (i=0; i<3; i++) {} 208 | if (i !== 3) 209 | throw new Error("For"); 210 | } ()); 211 | 212 | // ForIn.js 213 | console.log("ForIn.js"); 214 | (function () { 215 | let k; 216 | for (k in {a:1}) {} 217 | if (k !== "a") 218 | throw new Error("DeclarationForIn"); 219 | } ()); 220 | 221 | // ForOf.js 222 | console.log("ForOf.js"); 223 | (function () { 224 | let x; 225 | for (x of ["foo"]) {} 226 | if (x !== "foo") 227 | throw new Error("ForOf"); 228 | } ()); 229 | 230 | // Function.js 231 | console.log("Function.js"); 232 | (function () { 233 | let f = function () { return 1 } 234 | if (f() !== 1) 235 | throw new Error("Function"); 236 | } ()); 237 | 238 | // Identifier.js 239 | console.log("Identifier.js"); 240 | (function () { 241 | let x = 1; 242 | if (x !== 1) 243 | throw new Error("Identifier"); 244 | } ()); 245 | 246 | // IdentifierAssignment.js 247 | console.log("IdentifierAssignment.js"); 248 | (function () { 249 | let x; 250 | x = 1; 251 | if (x !== 1) 252 | throw new Error("IdentifierAssignment1"); 253 | x += 2; 254 | if (x !== 3) 255 | throw new Error("IdentifierAssignment2"); 256 | } ()); 257 | 258 | // IdentifierDelete.js 259 | console.log("IdentifierDelete.js"); 260 | (function () { 261 | let o = {a:1}; 262 | let x; 263 | with (o) { 264 | if (delete x) 265 | throw new Error("IdentifierDelete1"); 266 | if (! delete a) 267 | throw new Error("IdentifierDelete2"); 268 | if (! delete a$strange$id) 269 | throw new Error("IdentifierDelete3"); 270 | } 271 | if ("a" in o) 272 | throw new Error("IdentifierDelete4"); 273 | } ()); 274 | 275 | // IdentifierForIn.js 276 | console.log("IdentifierForIn.js"); 277 | (function () { 278 | let k; 279 | for (k in {a:1}) {} 280 | if (k !== "a") 281 | throw new Error("IdentifierForIn"); 282 | } ()); 283 | 284 | // IdentifierTypeof.js 285 | console.log("IdentifierTypeof.js"); 286 | (function () { 287 | if (typeof 1 !== "number") 288 | throw new Error("Typeof2"); 289 | if (typeof UndefinedVariable !== "undefined") 290 | throw new Error("Typeof1"); 291 | } ()); 292 | 293 | // IdentifierUpdate.js 294 | console.log("IdentifierUpdate.js"); 295 | (function () { 296 | let x = 1; 297 | if (x++ !== 1) 298 | throw new Error("IdentifierUpdate1"); 299 | if (++x !== 3) 300 | throw new Error("IdentifierUpdate2"); 301 | } ()); 302 | 303 | // If.js 304 | console.log("If.js"); 305 | (function () { 306 | if (true) {} else { 307 | throw new Error("If1"); 308 | } 309 | if (false) { 310 | throw new Error("If2"); 311 | } else {} 312 | } ()); 313 | 314 | // Label.js 315 | console.log("Label.js"); 316 | (function () { 317 | a:{ 318 | break a; 319 | throw new Error("Label"); 320 | } 321 | } ()); 322 | 323 | // Let.js 324 | console.log("Let.js"); 325 | (function () { 326 | { 327 | let l = "foo"; 328 | l = "bar" 329 | if (l !== "bar") 330 | throw new Error("Let1"); 331 | } 332 | if (typeof l !== "undefined") 333 | throw new Error("Let2"); 334 | } ()); 335 | 336 | // Literal.js 337 | console.log("Literal.js"); 338 | (function () { 339 | true; 340 | false; 341 | 1; 342 | -1; 343 | "a"; 344 | /abc/g; 345 | } ()); 346 | 347 | // Logical.js 348 | console.log("Logical.js"); 349 | (function () { 350 | if ((false||1) !== 1) 351 | throw new Error("Logical1"); 352 | if ((true&&1) !== 1) 353 | throw new Error("Logical2"); 354 | } ()); 355 | 356 | // LoopLabel.js 357 | console.log("LoopLabel.js"); 358 | (function () { 359 | let i = 0; 360 | a: for (; i<10; i++) { 361 | if (i === 5) 362 | break a; 363 | } 364 | if (i !== 5) 365 | throw new Error("LoopLabel"); 366 | } ()); 367 | 368 | // Member.js 369 | console.log("Member.js"); 370 | (function () { 371 | if ({a:1}.a !== 1) 372 | throw new Error("Member"); 373 | 374 | } ()); 375 | 376 | // MemberAssignment.js 377 | console.log("MemberAssignment.js"); 378 | (function () { 379 | let o = {}; 380 | o.a = 1; 381 | if (o.a !== 1) 382 | throw new Error("MemberAssignment1"); 383 | o.a += 2; 384 | if (o.a !== 3) 385 | throw new Error("MemberAssignment2"); 386 | } ()); 387 | 388 | // MemberCall.js 389 | console.log("MemberCall.js"); 390 | (function () { 391 | let o = { 392 | f: function () { 393 | if (this !== o) 394 | throw new Error("MemberCall"); 395 | } 396 | } 397 | o.f(); 398 | } ()); 399 | 400 | // MemberDelete.js 401 | console.log("MemberDelete.js"); 402 | (function () { 403 | let o = {a:1}; 404 | delete o.a; 405 | if ("a" in o) 406 | throw new Error("MemberDelete"); 407 | } ()); 408 | 409 | // MemberForIn.js 410 | console.log("MemberForIn.js"); 411 | (function () { 412 | let o = {}; 413 | for (o.a in {a:1}) {} 414 | if (o.a !== "a") 415 | throw new Error("MemberForIn"); 416 | } ()); 417 | 418 | // MemberForOf.js 419 | console.log("MemberForOf.js"); 420 | (function () { 421 | let o = {}; 422 | for (o.a of ["foo"]) {} 423 | if (o.a !== "foo") 424 | throw new Error("ForOf"); 425 | } ()); 426 | 427 | // MemberUpdate.js 428 | console.log("MemberUpdate.js"); 429 | (function () { 430 | let o = {a:1}; 431 | if (o.a++ !== 1) 432 | throw new Error("MemberUpdate1"); 433 | if (++o.a !== 3) 434 | throw new Error("MemberUpdate2"); 435 | } ()); 436 | 437 | // New.js 438 | console.log("New.js"); 439 | (function () { 440 | let o = {}; 441 | let F = function () { return o } 442 | if (new F() !== o) 443 | throw new Error("New"); 444 | 445 | } ()); 446 | 447 | // NewTarget.js 448 | console.log("NewTarget.js"); 449 | (function () { 450 | let f1 = function () { 451 | if (new.target !== void 0) 452 | throw new Error("NewTarget1"); 453 | } 454 | f1(); 455 | let f2 = function () { 456 | if (new.target === void 0) 457 | throw new Error("NewTarget2"); 458 | }; 459 | new f2(); 460 | } ()); 461 | 462 | // Object.js 463 | console.log("Object.js"); 464 | (function () { 465 | let b = "b"; 466 | let o = { 467 | a: 1, 468 | [b]: 2, 469 | get c () { return 3 }, 470 | set c (v) {} 471 | }; 472 | if (o.a !== 1) 473 | throw new Error("Object1"); 474 | if (o.b !== 2) 475 | throw new Error("Object2"); 476 | o.c = 666; 477 | if (o.c !== 3) 478 | throw new Error("Object3"); 479 | } ()); 480 | 481 | // PatternArray.js 482 | console.log("PatternArray.js"); 483 | (function () { 484 | let [x1,x2] = ["foo1", "bar1"]; 485 | if (x1 !== "foo1") 486 | throw new Error("PatternArray1"); 487 | if (x2 !== "bar1") 488 | throw new Error("PatternArray2"); 489 | let [y1, y2, ... ys] = ["foo2", "bar2", "buz2", "qux2"]; 490 | if (y1 !== "foo2") 491 | throw new Error("PatternArray3"); 492 | if (y2 !== "bar2") 493 | throw new Error("PatternArray4"); 494 | if (ys[0] !== "buz2") 495 | throw new Error("PatternArray5"); 496 | if (ys[1] !== "qux2") 497 | throw new Error("PatternArray6"); 498 | } ()); 499 | 500 | // PatternDefault.js 501 | console.log("PatternDefault.js"); 502 | (function () { 503 | let [x="foo", y="bar"] = [undefined, null]; 504 | if (x !== "foo") 505 | throw new Error("PatternDefault1"); 506 | if (y !== null) 507 | throw new Error("PatternDefault2"); 508 | } ()); 509 | 510 | // PatternObject.js 511 | console.log("PatternObject.js"); 512 | (function () { 513 | let {x, y:z} = {x:"foo", y:"bar"}; 514 | if (x !== "foo") 515 | throw new Error("PatternObject1"); 516 | if (z !== "bar") 517 | throw new Error("PatternObject2"); 518 | } ()); 519 | 520 | // Rest.js 521 | console.log("Rest.js"); 522 | (function () { 523 | let f = function (x, ...xs) { 524 | if (x !== "foo") 525 | throw new Error("Rest1"); 526 | if (xs[0] !== "bar") 527 | throw new Error("Rest2"); 528 | if (xs[1] !== "qux") 529 | throw new Error("Rest3"); 530 | }; 531 | f("foo", "bar", "qux"); 532 | } ()); 533 | 534 | // Return.js 535 | console.log("Return.js"); 536 | (function () { 537 | let f = function () { 538 | return; 539 | throw new Error("Return1"); 540 | } 541 | if (f() !== undefined) 542 | throw new Error("Return2"); 543 | } ()); 544 | 545 | // Sequence.js 546 | console.log("Sequence.js"); 547 | (function () { 548 | if ((1,2) !== 2) 549 | throw new Error("Sequence"); 550 | } ()); 551 | 552 | // Spread.js 553 | console.log("Spread.js"); 554 | (function () { 555 | let f = function (x, y, z, t) { 556 | if (x !== 1) 557 | throw new Error("Spread1"); 558 | if (y !== 2) 559 | throw new Error("Spread2"); 560 | if (z !== 3) 561 | throw new Error("Spread3"); 562 | if (t !== 4) 563 | throw new Error("Spread4") 564 | if (arguments.length !== 4) 565 | throw new Error("Spread5"); 566 | } 567 | f (1, ...[2,3], 4); 568 | } ()); 569 | 570 | // StrictFunction.js 571 | console.log("StrictFunction.js"); 572 | (function () { 573 | (function () { 574 | "use strict"; 575 | let f = function () { 576 | if (this !== undefined) 577 | throw new Error("Strict"); 578 | } 579 | f(); 580 | } ()); 581 | } ()); 582 | 583 | // StrictProgram.js 584 | console.log("StrictProgram.js"); 585 | (function () { 586 | "use strict"; 587 | let f = function () { 588 | if (this !== undefined) 589 | throw new Error("Strict"); 590 | } 591 | f(); 592 | } ()); 593 | 594 | // Switch.js 595 | console.log("Switch.js"); 596 | (function () { 597 | let i=0; 598 | switch (2) { 599 | case 1: throw new Error("Switch1"); 600 | case 2: i++; 601 | default: 602 | i++; 603 | break; 604 | case 2: throw new Error("Switch2"); 605 | } 606 | if(i !== 2) 607 | throw new Error("Switch3"); 608 | } ()); 609 | 610 | // TaggedTemplateExpression.js 611 | console.log("TaggedTemplateExpression.js"); 612 | (function () { 613 | 614 | function f () { 615 | "use strict"; 616 | if (this !== void 0) 617 | throw new Error("TaggedTemplateExpression1"); 618 | if (arguments.length !== 2) 619 | throw new Error("TaggedTemplateExpression2"); 620 | if (!Array.isArray(arguments[0])) 621 | throw new Error("TaggedTemplateExpression3"); 622 | if (Reflect.isExtensible(arguments[0])) 623 | throw new Error("TaggedTemplateExpression4"); 624 | if (arguments[0].length !== 2) 625 | throw new Error("TaggedTemplateExpression5"); 626 | if (arguments[0][0] !== "foo") 627 | throw new Error("TaggedTemplateExpression6"); 628 | if (arguments[0][1] !== "bar") 629 | throw new Error("TaggedTemplateExpression7"); 630 | if (!Array.isArray(arguments[0].raw)) 631 | throw new Error("TaggedTemplateExpression8"); 632 | if (Reflect.isExtensible(arguments[0].raw)) 633 | throw new Error("TaggedTemplateExpression9"); 634 | if (arguments[0].raw.length !== 2) 635 | throw new Error("TaggedTemplateExpression10"); 636 | if (arguments[0].raw[0] !== "foo") 637 | throw new Error("TaggedTemplateExpression11"); 638 | if (arguments[0].raw[1] !== "bar") 639 | throw new Error("TaggedTemplateExpression12"); 640 | if (arguments[1] !== 123) 641 | throw new Error("TaggedTemplateExpression13"); 642 | return "abc"; 643 | }; 644 | 645 | if (f `foo${123}bar` !== "abc") 646 | throw new Error("TaggedTemplateExpression14"); 647 | 648 | } ()); 649 | 650 | // TemplateLiteral.js 651 | console.log("TemplateLiteral.js"); 652 | (function () { 653 | if (`foo${"bar"}qux` !== "foobarqux") 654 | throw new Error("Template1"); 655 | 656 | } ()); 657 | 658 | // This.js 659 | console.log("This.js"); 660 | (function () { 661 | let o = { 662 | f:function () { 663 | if (this !== o) 664 | throw new Error("This"); 665 | } 666 | } 667 | o.f(); 668 | } ()); 669 | 670 | // Throw.js 671 | console.log("Throw.js"); 672 | (function () { 673 | let c = false; 674 | let f = false; 675 | try { 676 | throw "ok"; 677 | throw "BOUM"; 678 | } catch (e) { 679 | c = e; 680 | } finally { 681 | f = true; 682 | } 683 | if (c !== "ok") 684 | throw new Error("Throw1"); 685 | if (!f) 686 | throw new Error("Throw2"); 687 | } ()); 688 | 689 | // Unary.js 690 | console.log("Unary.js"); 691 | (function () { 692 | if (!true) 693 | throw new Error("Unary1"); 694 | let o = {a:1,b:2}; 695 | delete o.a; 696 | if ("a" in o) 697 | throw new Error("Unary2"); 698 | delete o["b"] 699 | if ("b" in o) 700 | throw new Error("Unary3"); 701 | } ()); 702 | 703 | // While.js 704 | console.log("While.js"); 705 | (function () { 706 | let i = 0; 707 | while (i < 3) 708 | i++; 709 | if (i !== 3) 710 | throw new Error("While"); 711 | } ()); 712 | 713 | // With.js 714 | console.log("With.js"); 715 | (function () { 716 | let o = {a:1}; 717 | with (o) { 718 | if (a !== 1) 719 | throw new Error("With1"); 720 | a = 2; 721 | } 722 | if (o.a !== 2) 723 | throw new Error("With2"); 724 | } ()); 725 | 726 | --------------------------------------------------------------------------------