├── .gitignore ├── .mocharc.json ├── .prettierrc.js ├── .vscode ├── launch.json └── settings.json ├── bin └── shift-interpret ├── package-lock.json ├── package.json ├── readme.md ├── src ├── context.ts ├── errors.ts ├── index.ts ├── instruction-buffer.ts ├── instruction.ts ├── interpreter.ts ├── node-handler.ts ├── operators.ts ├── types.ts ├── util.ts └── waterfall.ts ├── test ├── evaluation │ ├── binary-expressions.test.ts │ ├── classes.test.ts │ ├── compound-assignments.test.ts │ ├── conditional-expression.test.ts │ ├── context.test.ts │ ├── errors.test.ts │ ├── exotic-scenarios.test.ts │ ├── functions.test.ts │ ├── host.test.ts │ ├── if-then-else.test.ts │ ├── literal-expressions.test.ts │ ├── loops.test.ts │ ├── new-expression.test.ts │ ├── objects-and-arrays.test.ts │ ├── this.test.ts │ ├── try.statements.test.ts │ ├── unary-expressions.test.ts │ ├── update-expressions.test.ts │ └── variables.test.ts ├── interpreter.test.ts ├── nostrict-eval.js ├── script.test.ts ├── util.ts └── waterfall.test.ts ├── tsconfig.json └── types ├── shift-codegen └── index.d.ts ├── shift-parser └── index.d.ts ├── shift-scope └── index.d.ts ├── shift-traverser └── index.d.ts └── shift-validator └── index.d.ts /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": ["ts"], 3 | "spec": "test/**/*.test.ts", 4 | "require": "ts-node/register" 5 | } 6 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | trailingComma: 'all', 3 | tabWidth: 2, 4 | semi: true, 5 | singleQuote: true, 6 | printWidth: 120, 7 | bracketSpacing: true, 8 | arrowParens: 'avoid', 9 | }; 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Debug Mocha Tests", 9 | "type": "node", 10 | "request": "attach", 11 | "port": 9229, 12 | "protocol": "inspector", 13 | "timeout": 5000, 14 | "skipFiles": [ 15 | "/**" 16 | ], 17 | "stopOnEntry": false 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "mochaExplorer.debuggerConfig": "Debug Mocha Tests", 3 | "mochaExplorer.env": { 4 | "DEBUG": "shift:interpreter:*" 5 | }, 6 | "workbench.colorCustomizations": { 7 | "activityBar.activeBackground": "#0c284c", 8 | "activityBar.activeBorder": "#a51a57", 9 | "activityBar.background": "#0c284c", 10 | "activityBar.foreground": "#e7e7e7", 11 | "activityBar.inactiveForeground": "#e7e7e799", 12 | "activityBarBadge.background": "#a51a57", 13 | "activityBarBadge.foreground": "#e7e7e7", 14 | "statusBar.background": "#051120", 15 | "statusBar.border": "#051120", 16 | "statusBar.foreground": "#e7e7e7", 17 | "statusBarItem.hoverBackground": "#0c284c", 18 | "titleBar.activeBackground": "#051120", 19 | "titleBar.activeForeground": "#e7e7e7", 20 | "titleBar.border": "#051120", 21 | "titleBar.inactiveBackground": "#05112099", 22 | "titleBar.inactiveForeground": "#e7e7e799" 23 | }, 24 | "peacock.color": "#051120", 25 | "debug.javascript.usePreview": false 26 | } 27 | -------------------------------------------------------------------------------- /bin/shift-interpret: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const path = require('path'); 3 | const { interpret } = require('..'); 4 | const { readFileSync } = require('fs'); 5 | 6 | const commandLineArgs = require("command-line-args"); 7 | 8 | const optionDefinitions = [ 9 | { name: "help", alias: "h", type: Boolean }, 10 | { name: "execute", alias: "e", type: String, multiple: true }, 11 | ]; 12 | 13 | options = commandLineArgs(optionDefinitions, {stopAtFirstUnknown:false, partial:true}); 14 | 15 | console.log(options); 16 | 17 | // get rid of node so this can run itself 18 | process.argv.shift(); 19 | 20 | const file = process.argv[1]; 21 | 22 | const contents = readFileSync(file, 'utf8').replace(/^#!.*$/m,''); 23 | 24 | const context = Object.assign({ 25 | require, 26 | module, 27 | process, 28 | }, global); 29 | Object.setPrototypeOf(context, global); 30 | 31 | module.path = path.dirname(file); 32 | module.filename = file; 33 | 34 | console.log(interpret(contents, context)); 35 | 36 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shift-interpreter", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@babel/code-frame": { 8 | "version": "7.10.4", 9 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", 10 | "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", 11 | "dev": true, 12 | "requires": { 13 | "@babel/highlight": "^7.10.4" 14 | } 15 | }, 16 | "@babel/helper-validator-identifier": { 17 | "version": "7.10.4", 18 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", 19 | "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", 20 | "dev": true 21 | }, 22 | "@babel/highlight": { 23 | "version": "7.10.4", 24 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", 25 | "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", 26 | "dev": true, 27 | "requires": { 28 | "@babel/helper-validator-identifier": "^7.10.4", 29 | "chalk": "^2.0.0", 30 | "js-tokens": "^4.0.0" 31 | }, 32 | "dependencies": { 33 | "ansi-styles": { 34 | "version": "3.2.1", 35 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 36 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 37 | "dev": true, 38 | "requires": { 39 | "color-convert": "^1.9.0" 40 | } 41 | }, 42 | "chalk": { 43 | "version": "2.4.2", 44 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 45 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 46 | "dev": true, 47 | "requires": { 48 | "ansi-styles": "^3.2.1", 49 | "escape-string-regexp": "^1.0.5", 50 | "supports-color": "^5.3.0" 51 | } 52 | }, 53 | "color-convert": { 54 | "version": "1.9.3", 55 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 56 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 57 | "dev": true, 58 | "requires": { 59 | "color-name": "1.1.3" 60 | } 61 | }, 62 | "color-name": { 63 | "version": "1.1.3", 64 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 65 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 66 | "dev": true 67 | }, 68 | "has-flag": { 69 | "version": "3.0.0", 70 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 71 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 72 | "dev": true 73 | }, 74 | "supports-color": { 75 | "version": "5.5.0", 76 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 77 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 78 | "dev": true, 79 | "requires": { 80 | "has-flag": "^3.0.0" 81 | } 82 | } 83 | } 84 | }, 85 | "@jsoverson/shift-codegen": { 86 | "version": "7.0.0", 87 | "resolved": "https://registry.npmjs.org/@jsoverson/shift-codegen/-/shift-codegen-7.0.0.tgz", 88 | "integrity": "sha512-fBklFErIYirpd3IufjLYxoijJTjIYI9zqN3irxrNE6XvEPuU4hkPBqFsPwFWJvepSsGvqV4k5Cd8fky8Axya4w==", 89 | "requires": { 90 | "esutils": "^2.0.2", 91 | "object-assign": "^4.1.0", 92 | "shift-reducer": "6.0.0" 93 | } 94 | }, 95 | "@types/chai": { 96 | "version": "4.2.11", 97 | "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.11.tgz", 98 | "integrity": "sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw==", 99 | "dev": true 100 | }, 101 | "@types/chai-spies": { 102 | "version": "1.0.1", 103 | "resolved": "https://registry.npmjs.org/@types/chai-spies/-/chai-spies-1.0.1.tgz", 104 | "integrity": "sha512-vvlTzisMpzmPZaKDd0pFybCJB5bzx398JEdPsVD9Rwq4a7dcGSUDlNG2PAVhsanRBPl4hezqnlaFtL1/YwBGgw==", 105 | "dev": true, 106 | "requires": { 107 | "@types/chai": "*" 108 | } 109 | }, 110 | "@types/color-name": { 111 | "version": "1.1.1", 112 | "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", 113 | "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" 114 | }, 115 | "@types/debug": { 116 | "version": "4.1.5", 117 | "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", 118 | "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==", 119 | "dev": true 120 | }, 121 | "@types/deep-equal": { 122 | "version": "1.0.1", 123 | "resolved": "https://registry.npmjs.org/@types/deep-equal/-/deep-equal-1.0.1.tgz", 124 | "integrity": "sha512-mMUu4nWHLBlHtxXY17Fg6+ucS/MnndyOWyOe7MmwkoMYxvfQU2ajtRaEvqSUv+aVkMqH/C0NCI8UoVfRNQ10yg==", 125 | "dev": true 126 | }, 127 | "@types/mocha": { 128 | "version": "5.2.7", 129 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", 130 | "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", 131 | "dev": true 132 | }, 133 | "@types/multimap": { 134 | "version": "1.1.1", 135 | "resolved": "https://registry.npmjs.org/@types/multimap/-/multimap-1.1.1.tgz", 136 | "integrity": "sha512-vS9RPOp1aG6AIWXJp0grLpDH3FwB0/ROdw8/M85m/Uv9EAAH2626JPZB3i8bpPGgGPPdteK6eW0Ziw7NIWnTUQ==", 137 | "dev": true 138 | }, 139 | "@types/node": { 140 | "version": "13.13.14", 141 | "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.14.tgz", 142 | "integrity": "sha512-Az3QsOt1U/K1pbCQ0TXGELTuTkPLOiFIQf3ILzbOyo0FqgV9SxRnxbxM5QlAveERZMHpZY+7u3Jz2tKyl+yg6g==", 143 | "dev": true 144 | }, 145 | "ansi-colors": { 146 | "version": "3.2.3", 147 | "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", 148 | "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", 149 | "dev": true 150 | }, 151 | "ansi-regex": { 152 | "version": "3.0.0", 153 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", 154 | "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", 155 | "dev": true 156 | }, 157 | "ansi-styles": { 158 | "version": "4.2.1", 159 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", 160 | "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", 161 | "requires": { 162 | "@types/color-name": "^1.1.1", 163 | "color-convert": "^2.0.1" 164 | } 165 | }, 166 | "arg": { 167 | "version": "4.1.3", 168 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 169 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 170 | "dev": true 171 | }, 172 | "argparse": { 173 | "version": "1.0.10", 174 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 175 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 176 | "dev": true, 177 | "requires": { 178 | "sprintf-js": "~1.0.2" 179 | } 180 | }, 181 | "array-back": { 182 | "version": "3.1.0", 183 | "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", 184 | "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==" 185 | }, 186 | "array-filter": { 187 | "version": "1.0.0", 188 | "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", 189 | "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=", 190 | "dev": true 191 | }, 192 | "assertion-error": { 193 | "version": "1.1.0", 194 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 195 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 196 | "dev": true 197 | }, 198 | "available-typed-arrays": { 199 | "version": "1.0.2", 200 | "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", 201 | "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", 202 | "dev": true, 203 | "requires": { 204 | "array-filter": "^1.0.0" 205 | } 206 | }, 207 | "balanced-match": { 208 | "version": "1.0.0", 209 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 210 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 211 | "dev": true 212 | }, 213 | "brace-expansion": { 214 | "version": "1.1.11", 215 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 216 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 217 | "dev": true, 218 | "requires": { 219 | "balanced-match": "^1.0.0", 220 | "concat-map": "0.0.1" 221 | } 222 | }, 223 | "browser-stdout": { 224 | "version": "1.3.1", 225 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 226 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 227 | "dev": true 228 | }, 229 | "buffer-from": { 230 | "version": "1.1.1", 231 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 232 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 233 | "dev": true 234 | }, 235 | "builtin-modules": { 236 | "version": "1.1.1", 237 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", 238 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", 239 | "dev": true 240 | }, 241 | "camelcase": { 242 | "version": "5.3.1", 243 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 244 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", 245 | "dev": true 246 | }, 247 | "chai": { 248 | "version": "4.2.0", 249 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", 250 | "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", 251 | "dev": true, 252 | "requires": { 253 | "assertion-error": "^1.1.0", 254 | "check-error": "^1.0.2", 255 | "deep-eql": "^3.0.1", 256 | "get-func-name": "^2.0.0", 257 | "pathval": "^1.1.0", 258 | "type-detect": "^4.0.5" 259 | } 260 | }, 261 | "chai-spies": { 262 | "version": "1.0.0", 263 | "resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-1.0.0.tgz", 264 | "integrity": "sha512-elF2ZUczBsFoP07qCfMO/zeggs8pqCf3fZGyK5+2X4AndS8jycZYID91ztD9oQ7d/0tnS963dPkd0frQEThDsg==", 265 | "dev": true 266 | }, 267 | "chalk": { 268 | "version": "4.1.0", 269 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", 270 | "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", 271 | "requires": { 272 | "ansi-styles": "^4.1.0", 273 | "supports-color": "^7.1.0" 274 | } 275 | }, 276 | "check-error": { 277 | "version": "1.0.2", 278 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 279 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 280 | "dev": true 281 | }, 282 | "cliui": { 283 | "version": "5.0.0", 284 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", 285 | "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", 286 | "dev": true, 287 | "requires": { 288 | "string-width": "^3.1.0", 289 | "strip-ansi": "^5.2.0", 290 | "wrap-ansi": "^5.1.0" 291 | }, 292 | "dependencies": { 293 | "ansi-regex": { 294 | "version": "4.1.0", 295 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 296 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 297 | "dev": true 298 | }, 299 | "string-width": { 300 | "version": "3.1.0", 301 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 302 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 303 | "dev": true, 304 | "requires": { 305 | "emoji-regex": "^7.0.1", 306 | "is-fullwidth-code-point": "^2.0.0", 307 | "strip-ansi": "^5.1.0" 308 | } 309 | }, 310 | "strip-ansi": { 311 | "version": "5.2.0", 312 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 313 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 314 | "dev": true, 315 | "requires": { 316 | "ansi-regex": "^4.1.0" 317 | } 318 | } 319 | } 320 | }, 321 | "color-convert": { 322 | "version": "2.0.1", 323 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 324 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 325 | "requires": { 326 | "color-name": "~1.1.4" 327 | } 328 | }, 329 | "color-name": { 330 | "version": "1.1.4", 331 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 332 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 333 | }, 334 | "command-line-args": { 335 | "version": "5.1.1", 336 | "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.1.1.tgz", 337 | "integrity": "sha512-hL/eG8lrll1Qy1ezvkant+trihbGnaKaeEjj6Scyr3DN+RC7iQ5Rz84IeLERfAWDGo0HBSNAakczwgCilDXnWg==", 338 | "requires": { 339 | "array-back": "^3.0.1", 340 | "find-replace": "^3.0.0", 341 | "lodash.camelcase": "^4.3.0", 342 | "typical": "^4.0.0" 343 | } 344 | }, 345 | "commander": { 346 | "version": "2.20.3", 347 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 348 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", 349 | "dev": true 350 | }, 351 | "concat-map": { 352 | "version": "0.0.1", 353 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 354 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 355 | "dev": true 356 | }, 357 | "debug": { 358 | "version": "4.1.1", 359 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 360 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 361 | "requires": { 362 | "ms": "^2.1.1" 363 | } 364 | }, 365 | "decamelize": { 366 | "version": "1.2.0", 367 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 368 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 369 | "dev": true 370 | }, 371 | "deep-eql": { 372 | "version": "3.0.1", 373 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 374 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 375 | "dev": true, 376 | "requires": { 377 | "type-detect": "^4.0.0" 378 | } 379 | }, 380 | "deep-equal": { 381 | "version": "2.0.3", 382 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.3.tgz", 383 | "integrity": "sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA==", 384 | "dev": true, 385 | "requires": { 386 | "es-abstract": "^1.17.5", 387 | "es-get-iterator": "^1.1.0", 388 | "is-arguments": "^1.0.4", 389 | "is-date-object": "^1.0.2", 390 | "is-regex": "^1.0.5", 391 | "isarray": "^2.0.5", 392 | "object-is": "^1.1.2", 393 | "object-keys": "^1.1.1", 394 | "object.assign": "^4.1.0", 395 | "regexp.prototype.flags": "^1.3.0", 396 | "side-channel": "^1.0.2", 397 | "which-boxed-primitive": "^1.0.1", 398 | "which-collection": "^1.0.1", 399 | "which-typed-array": "^1.1.2" 400 | } 401 | }, 402 | "define-properties": { 403 | "version": "1.1.3", 404 | "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", 405 | "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", 406 | "dev": true, 407 | "requires": { 408 | "object-keys": "^1.0.12" 409 | } 410 | }, 411 | "diff": { 412 | "version": "3.5.0", 413 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 414 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", 415 | "dev": true 416 | }, 417 | "emoji-regex": { 418 | "version": "7.0.3", 419 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 420 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", 421 | "dev": true 422 | }, 423 | "es-abstract": { 424 | "version": "1.17.6", 425 | "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", 426 | "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", 427 | "dev": true, 428 | "requires": { 429 | "es-to-primitive": "^1.2.1", 430 | "function-bind": "^1.1.1", 431 | "has": "^1.0.3", 432 | "has-symbols": "^1.0.1", 433 | "is-callable": "^1.2.0", 434 | "is-regex": "^1.1.0", 435 | "object-inspect": "^1.7.0", 436 | "object-keys": "^1.1.1", 437 | "object.assign": "^4.1.0", 438 | "string.prototype.trimend": "^1.0.1", 439 | "string.prototype.trimstart": "^1.0.1" 440 | } 441 | }, 442 | "es-get-iterator": { 443 | "version": "1.1.0", 444 | "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", 445 | "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", 446 | "dev": true, 447 | "requires": { 448 | "es-abstract": "^1.17.4", 449 | "has-symbols": "^1.0.1", 450 | "is-arguments": "^1.0.4", 451 | "is-map": "^2.0.1", 452 | "is-set": "^2.0.1", 453 | "is-string": "^1.0.5", 454 | "isarray": "^2.0.5" 455 | } 456 | }, 457 | "es-to-primitive": { 458 | "version": "1.2.1", 459 | "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", 460 | "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", 461 | "dev": true, 462 | "requires": { 463 | "is-callable": "^1.1.4", 464 | "is-date-object": "^1.0.1", 465 | "is-symbol": "^1.0.2" 466 | } 467 | }, 468 | "escape-string-regexp": { 469 | "version": "1.0.5", 470 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 471 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 472 | "dev": true 473 | }, 474 | "esprima": { 475 | "version": "4.0.1", 476 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 477 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 478 | "dev": true 479 | }, 480 | "esutils": { 481 | "version": "2.0.3", 482 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 483 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" 484 | }, 485 | "find-replace": { 486 | "version": "3.0.0", 487 | "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", 488 | "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", 489 | "requires": { 490 | "array-back": "^3.0.1" 491 | } 492 | }, 493 | "find-up": { 494 | "version": "3.0.0", 495 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", 496 | "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", 497 | "dev": true, 498 | "requires": { 499 | "locate-path": "^3.0.0" 500 | } 501 | }, 502 | "flat": { 503 | "version": "4.1.0", 504 | "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", 505 | "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", 506 | "dev": true, 507 | "requires": { 508 | "is-buffer": "~2.0.3" 509 | } 510 | }, 511 | "foreach": { 512 | "version": "2.0.5", 513 | "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", 514 | "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", 515 | "dev": true 516 | }, 517 | "fs.realpath": { 518 | "version": "1.0.0", 519 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 520 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 521 | "dev": true 522 | }, 523 | "function-bind": { 524 | "version": "1.1.1", 525 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 526 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", 527 | "dev": true 528 | }, 529 | "get-caller-file": { 530 | "version": "2.0.5", 531 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 532 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 533 | "dev": true 534 | }, 535 | "get-func-name": { 536 | "version": "2.0.0", 537 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 538 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 539 | "dev": true 540 | }, 541 | "glob": { 542 | "version": "7.1.3", 543 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", 544 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", 545 | "dev": true, 546 | "requires": { 547 | "fs.realpath": "^1.0.0", 548 | "inflight": "^1.0.4", 549 | "inherits": "2", 550 | "minimatch": "^3.0.4", 551 | "once": "^1.3.0", 552 | "path-is-absolute": "^1.0.0" 553 | } 554 | }, 555 | "growl": { 556 | "version": "1.10.5", 557 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 558 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 559 | "dev": true 560 | }, 561 | "has": { 562 | "version": "1.0.3", 563 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 564 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 565 | "dev": true, 566 | "requires": { 567 | "function-bind": "^1.1.1" 568 | } 569 | }, 570 | "has-flag": { 571 | "version": "4.0.0", 572 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 573 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" 574 | }, 575 | "has-symbols": { 576 | "version": "1.0.1", 577 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", 578 | "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", 579 | "dev": true 580 | }, 581 | "he": { 582 | "version": "1.2.0", 583 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", 584 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", 585 | "dev": true 586 | }, 587 | "inflight": { 588 | "version": "1.0.6", 589 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 590 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 591 | "dev": true, 592 | "requires": { 593 | "once": "^1.3.0", 594 | "wrappy": "1" 595 | } 596 | }, 597 | "inherits": { 598 | "version": "2.0.4", 599 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 600 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 601 | "dev": true 602 | }, 603 | "is-arguments": { 604 | "version": "1.0.4", 605 | "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", 606 | "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", 607 | "dev": true 608 | }, 609 | "is-bigint": { 610 | "version": "1.0.0", 611 | "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.0.tgz", 612 | "integrity": "sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==", 613 | "dev": true 614 | }, 615 | "is-boolean-object": { 616 | "version": "1.0.1", 617 | "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz", 618 | "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==", 619 | "dev": true 620 | }, 621 | "is-buffer": { 622 | "version": "2.0.4", 623 | "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", 624 | "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", 625 | "dev": true 626 | }, 627 | "is-callable": { 628 | "version": "1.2.0", 629 | "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", 630 | "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", 631 | "dev": true 632 | }, 633 | "is-date-object": { 634 | "version": "1.0.2", 635 | "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", 636 | "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", 637 | "dev": true 638 | }, 639 | "is-fullwidth-code-point": { 640 | "version": "2.0.0", 641 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 642 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", 643 | "dev": true 644 | }, 645 | "is-map": { 646 | "version": "2.0.1", 647 | "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", 648 | "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==", 649 | "dev": true 650 | }, 651 | "is-number-object": { 652 | "version": "1.0.4", 653 | "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", 654 | "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", 655 | "dev": true 656 | }, 657 | "is-regex": { 658 | "version": "1.1.0", 659 | "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", 660 | "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", 661 | "dev": true, 662 | "requires": { 663 | "has-symbols": "^1.0.1" 664 | } 665 | }, 666 | "is-set": { 667 | "version": "2.0.1", 668 | "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", 669 | "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==", 670 | "dev": true 671 | }, 672 | "is-string": { 673 | "version": "1.0.5", 674 | "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", 675 | "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", 676 | "dev": true 677 | }, 678 | "is-symbol": { 679 | "version": "1.0.3", 680 | "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", 681 | "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", 682 | "dev": true, 683 | "requires": { 684 | "has-symbols": "^1.0.1" 685 | } 686 | }, 687 | "is-typed-array": { 688 | "version": "1.1.3", 689 | "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz", 690 | "integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==", 691 | "dev": true, 692 | "requires": { 693 | "available-typed-arrays": "^1.0.0", 694 | "es-abstract": "^1.17.4", 695 | "foreach": "^2.0.5", 696 | "has-symbols": "^1.0.1" 697 | } 698 | }, 699 | "is-weakmap": { 700 | "version": "2.0.1", 701 | "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", 702 | "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", 703 | "dev": true 704 | }, 705 | "is-weakset": { 706 | "version": "2.0.1", 707 | "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", 708 | "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==", 709 | "dev": true 710 | }, 711 | "isarray": { 712 | "version": "2.0.5", 713 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", 714 | "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", 715 | "dev": true 716 | }, 717 | "isexe": { 718 | "version": "2.0.0", 719 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 720 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 721 | "dev": true 722 | }, 723 | "js-tokens": { 724 | "version": "4.0.0", 725 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 726 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 727 | "dev": true 728 | }, 729 | "js-yaml": { 730 | "version": "3.13.1", 731 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", 732 | "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", 733 | "dev": true, 734 | "requires": { 735 | "argparse": "^1.0.7", 736 | "esprima": "^4.0.0" 737 | } 738 | }, 739 | "locate-path": { 740 | "version": "3.0.0", 741 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", 742 | "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", 743 | "dev": true, 744 | "requires": { 745 | "p-locate": "^3.0.0", 746 | "path-exists": "^3.0.0" 747 | } 748 | }, 749 | "lodash": { 750 | "version": "4.17.19", 751 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", 752 | "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", 753 | "dev": true 754 | }, 755 | "lodash.camelcase": { 756 | "version": "4.3.0", 757 | "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", 758 | "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" 759 | }, 760 | "log-symbols": { 761 | "version": "2.2.0", 762 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", 763 | "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", 764 | "dev": true, 765 | "requires": { 766 | "chalk": "^2.0.1" 767 | }, 768 | "dependencies": { 769 | "ansi-styles": { 770 | "version": "3.2.1", 771 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 772 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 773 | "dev": true, 774 | "requires": { 775 | "color-convert": "^1.9.0" 776 | } 777 | }, 778 | "chalk": { 779 | "version": "2.4.2", 780 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 781 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 782 | "dev": true, 783 | "requires": { 784 | "ansi-styles": "^3.2.1", 785 | "escape-string-regexp": "^1.0.5", 786 | "supports-color": "^5.3.0" 787 | } 788 | }, 789 | "color-convert": { 790 | "version": "1.9.3", 791 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 792 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 793 | "dev": true, 794 | "requires": { 795 | "color-name": "1.1.3" 796 | } 797 | }, 798 | "color-name": { 799 | "version": "1.1.3", 800 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 801 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 802 | "dev": true 803 | }, 804 | "has-flag": { 805 | "version": "3.0.0", 806 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 807 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 808 | "dev": true 809 | }, 810 | "supports-color": { 811 | "version": "5.5.0", 812 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 813 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 814 | "dev": true, 815 | "requires": { 816 | "has-flag": "^3.0.0" 817 | } 818 | } 819 | } 820 | }, 821 | "make-error": { 822 | "version": "1.3.6", 823 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 824 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 825 | "dev": true 826 | }, 827 | "minimatch": { 828 | "version": "3.0.4", 829 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 830 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 831 | "dev": true, 832 | "requires": { 833 | "brace-expansion": "^1.1.7" 834 | } 835 | }, 836 | "minimist": { 837 | "version": "1.2.5", 838 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 839 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", 840 | "dev": true 841 | }, 842 | "mkdirp": { 843 | "version": "0.5.4", 844 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz", 845 | "integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==", 846 | "dev": true, 847 | "requires": { 848 | "minimist": "^1.2.5" 849 | } 850 | }, 851 | "mocha": { 852 | "version": "6.2.3", 853 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.3.tgz", 854 | "integrity": "sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg==", 855 | "dev": true, 856 | "requires": { 857 | "ansi-colors": "3.2.3", 858 | "browser-stdout": "1.3.1", 859 | "debug": "3.2.6", 860 | "diff": "3.5.0", 861 | "escape-string-regexp": "1.0.5", 862 | "find-up": "3.0.0", 863 | "glob": "7.1.3", 864 | "growl": "1.10.5", 865 | "he": "1.2.0", 866 | "js-yaml": "3.13.1", 867 | "log-symbols": "2.2.0", 868 | "minimatch": "3.0.4", 869 | "mkdirp": "0.5.4", 870 | "ms": "2.1.1", 871 | "node-environment-flags": "1.0.5", 872 | "object.assign": "4.1.0", 873 | "strip-json-comments": "2.0.1", 874 | "supports-color": "6.0.0", 875 | "which": "1.3.1", 876 | "wide-align": "1.1.3", 877 | "yargs": "13.3.2", 878 | "yargs-parser": "13.1.2", 879 | "yargs-unparser": "1.6.0" 880 | }, 881 | "dependencies": { 882 | "debug": { 883 | "version": "3.2.6", 884 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 885 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 886 | "dev": true, 887 | "requires": { 888 | "ms": "^2.1.1" 889 | } 890 | }, 891 | "has-flag": { 892 | "version": "3.0.0", 893 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 894 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 895 | "dev": true 896 | }, 897 | "ms": { 898 | "version": "2.1.1", 899 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 900 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", 901 | "dev": true 902 | }, 903 | "supports-color": { 904 | "version": "6.0.0", 905 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", 906 | "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", 907 | "dev": true, 908 | "requires": { 909 | "has-flag": "^3.0.0" 910 | } 911 | } 912 | } 913 | }, 914 | "ms": { 915 | "version": "2.1.2", 916 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 917 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 918 | }, 919 | "multimap": { 920 | "version": "1.1.0", 921 | "resolved": "https://registry.npmjs.org/multimap/-/multimap-1.1.0.tgz", 922 | "integrity": "sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw==" 923 | }, 924 | "node-environment-flags": { 925 | "version": "1.0.5", 926 | "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", 927 | "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", 928 | "dev": true, 929 | "requires": { 930 | "object.getownpropertydescriptors": "^2.0.3", 931 | "semver": "^5.7.0" 932 | } 933 | }, 934 | "object-assign": { 935 | "version": "4.1.1", 936 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 937 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 938 | }, 939 | "object-inspect": { 940 | "version": "1.8.0", 941 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", 942 | "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", 943 | "dev": true 944 | }, 945 | "object-is": { 946 | "version": "1.1.2", 947 | "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz", 948 | "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==", 949 | "dev": true, 950 | "requires": { 951 | "define-properties": "^1.1.3", 952 | "es-abstract": "^1.17.5" 953 | } 954 | }, 955 | "object-keys": { 956 | "version": "1.1.1", 957 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 958 | "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", 959 | "dev": true 960 | }, 961 | "object.assign": { 962 | "version": "4.1.0", 963 | "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", 964 | "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", 965 | "dev": true, 966 | "requires": { 967 | "define-properties": "^1.1.2", 968 | "function-bind": "^1.1.1", 969 | "has-symbols": "^1.0.0", 970 | "object-keys": "^1.0.11" 971 | } 972 | }, 973 | "object.getownpropertydescriptors": { 974 | "version": "2.1.0", 975 | "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", 976 | "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", 977 | "dev": true, 978 | "requires": { 979 | "define-properties": "^1.1.3", 980 | "es-abstract": "^1.17.0-next.1" 981 | } 982 | }, 983 | "once": { 984 | "version": "1.4.0", 985 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 986 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 987 | "dev": true, 988 | "requires": { 989 | "wrappy": "1" 990 | } 991 | }, 992 | "p-limit": { 993 | "version": "2.3.0", 994 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", 995 | "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", 996 | "dev": true, 997 | "requires": { 998 | "p-try": "^2.0.0" 999 | } 1000 | }, 1001 | "p-locate": { 1002 | "version": "3.0.0", 1003 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", 1004 | "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", 1005 | "dev": true, 1006 | "requires": { 1007 | "p-limit": "^2.0.0" 1008 | } 1009 | }, 1010 | "p-try": { 1011 | "version": "2.2.0", 1012 | "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", 1013 | "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", 1014 | "dev": true 1015 | }, 1016 | "path-exists": { 1017 | "version": "3.0.0", 1018 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", 1019 | "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", 1020 | "dev": true 1021 | }, 1022 | "path-is-absolute": { 1023 | "version": "1.0.1", 1024 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1025 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1026 | "dev": true 1027 | }, 1028 | "path-parse": { 1029 | "version": "1.0.6", 1030 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", 1031 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", 1032 | "dev": true 1033 | }, 1034 | "pathval": { 1035 | "version": "1.1.0", 1036 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", 1037 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", 1038 | "dev": true 1039 | }, 1040 | "prettier": { 1041 | "version": "1.19.1", 1042 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", 1043 | "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", 1044 | "dev": true 1045 | }, 1046 | "regexp.prototype.flags": { 1047 | "version": "1.3.0", 1048 | "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", 1049 | "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", 1050 | "dev": true, 1051 | "requires": { 1052 | "define-properties": "^1.1.3", 1053 | "es-abstract": "^1.17.0-next.1" 1054 | } 1055 | }, 1056 | "require-directory": { 1057 | "version": "2.1.1", 1058 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1059 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 1060 | "dev": true 1061 | }, 1062 | "require-main-filename": { 1063 | "version": "2.0.0", 1064 | "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", 1065 | "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", 1066 | "dev": true 1067 | }, 1068 | "resolve": { 1069 | "version": "1.17.0", 1070 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", 1071 | "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", 1072 | "dev": true, 1073 | "requires": { 1074 | "path-parse": "^1.0.6" 1075 | } 1076 | }, 1077 | "semver": { 1078 | "version": "5.7.1", 1079 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 1080 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", 1081 | "dev": true 1082 | }, 1083 | "set-blocking": { 1084 | "version": "2.0.0", 1085 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 1086 | "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", 1087 | "dev": true 1088 | }, 1089 | "shift-ast": { 1090 | "version": "6.1.0", 1091 | "resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.1.0.tgz", 1092 | "integrity": "sha512-Vj4XUIJIFPIh6VcBGJ1hjH/kM88XGer94Pr7Rvxa+idEylDsrwtLw268HoxGo5xReL6T3DdRl/9/Pr1XihZ/8Q==" 1093 | }, 1094 | "shift-parser": { 1095 | "version": "7.0.3", 1096 | "resolved": "https://registry.npmjs.org/shift-parser/-/shift-parser-7.0.3.tgz", 1097 | "integrity": "sha512-uYX2ORyZfKZrUc4iKKkO9KOhzUSxCrSBk7QK6ZmShId+BOo1gh1IwecVy97ynyOTpmhPWUttjC8BzsnQl65Zew==", 1098 | "requires": { 1099 | "multimap": "^1.0.2", 1100 | "shift-ast": "6.0.0", 1101 | "shift-reducer": "6.0.0", 1102 | "shift-regexp-acceptor": "2.0.3" 1103 | }, 1104 | "dependencies": { 1105 | "shift-ast": { 1106 | "version": "6.0.0", 1107 | "resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.0.0.tgz", 1108 | "integrity": "sha512-XXxDcEBWVBzqWXfNYJlLyJ1/9kMvOXVRXiqPjkOrTCC5qRsBvEMJMRLLFhU3tn8ue56Y7IZyBE6bexFum5QLUw==" 1109 | } 1110 | } 1111 | }, 1112 | "shift-printer": { 1113 | "version": "1.0.1", 1114 | "resolved": "https://registry.npmjs.org/shift-printer/-/shift-printer-1.0.1.tgz", 1115 | "integrity": "sha512-kv59gA/8dZuz175dqbnBEJQH9ctusX3IDaA50oWcSGq5LOxLxZ4Rl6+7rMsiQywxA7K4FBJY/XJ+OEz0f5Ux0w==", 1116 | "requires": { 1117 | "@jsoverson/shift-codegen": "^7.0.0" 1118 | } 1119 | }, 1120 | "shift-reducer": { 1121 | "version": "6.0.0", 1122 | "resolved": "https://registry.npmjs.org/shift-reducer/-/shift-reducer-6.0.0.tgz", 1123 | "integrity": "sha512-2rJraRP8drIOjvaE/sALa+0tGJmMVUzlmS3wIJerJbaYuCjpFAiF0WjkTOFVtz1144Nm/ECmqeG+7yRhuMVsMg==", 1124 | "requires": { 1125 | "shift-ast": "6.0.0" 1126 | }, 1127 | "dependencies": { 1128 | "shift-ast": { 1129 | "version": "6.0.0", 1130 | "resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.0.0.tgz", 1131 | "integrity": "sha512-XXxDcEBWVBzqWXfNYJlLyJ1/9kMvOXVRXiqPjkOrTCC5qRsBvEMJMRLLFhU3tn8ue56Y7IZyBE6bexFum5QLUw==" 1132 | } 1133 | } 1134 | }, 1135 | "shift-regexp-acceptor": { 1136 | "version": "2.0.3", 1137 | "resolved": "https://registry.npmjs.org/shift-regexp-acceptor/-/shift-regexp-acceptor-2.0.3.tgz", 1138 | "integrity": "sha512-sxL7e5JNUFxm+gutFRXktX2D6KVgDAHNuDsk5XHB9Z+N5yXooZG6pdZ1GEbo3Jz6lF7ETYLBC4WAjIFm2RKTmA==", 1139 | "requires": { 1140 | "unicode-match-property-ecmascript": "1.0.4", 1141 | "unicode-match-property-value-ecmascript": "1.0.2", 1142 | "unicode-property-aliases-ecmascript": "1.0.4" 1143 | } 1144 | }, 1145 | "shift-scope": { 1146 | "version": "4.0.0", 1147 | "resolved": "https://registry.npmjs.org/shift-scope/-/shift-scope-4.0.0.tgz", 1148 | "integrity": "sha512-b5Ub5m6eP3obI1h6MVmt3Rthh1akLKKjresLtYYqV+V4aNFL1XtdvB+yK6mTROakriKFu4ehwbs6mFMUCenyKA==", 1149 | "requires": { 1150 | "multimap": "1.0.2", 1151 | "shift-reducer": "5.0.0", 1152 | "shift-spec": "2017.0.0" 1153 | }, 1154 | "dependencies": { 1155 | "multimap": { 1156 | "version": "1.0.2", 1157 | "resolved": "https://registry.npmjs.org/multimap/-/multimap-1.0.2.tgz", 1158 | "integrity": "sha1-aqdvwyM5BbqUi75MdNwsOgNW6zY=" 1159 | }, 1160 | "shift-ast": { 1161 | "version": "5.0.0", 1162 | "resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-5.0.0.tgz", 1163 | "integrity": "sha512-kMhr/GwgrQ1U2kaa50sD5YxNDQEZHAZigVwrf/NNeezb6oiYnpPMV8v1WVRhCW8sjI7xUdzuPujSQ3gA2IuUAQ==" 1164 | }, 1165 | "shift-reducer": { 1166 | "version": "5.0.0", 1167 | "resolved": "https://registry.npmjs.org/shift-reducer/-/shift-reducer-5.0.0.tgz", 1168 | "integrity": "sha512-Jgr6kPMZuzsQ63NdeLJT6BZvtJ6IDbYFBVqiid1bZlwxeJYq81Cj2Wc4UUERjO4Q9tz5U4KpWaZhitjcBvfiYA==", 1169 | "requires": { 1170 | "shift-ast": "5.0.0" 1171 | } 1172 | } 1173 | } 1174 | }, 1175 | "shift-spec": { 1176 | "version": "2017.0.0", 1177 | "resolved": "https://registry.npmjs.org/shift-spec/-/shift-spec-2017.0.0.tgz", 1178 | "integrity": "sha512-bJvpDhV0BPI3Qtr0Dztrv9nKjQmeSlRRjMOPe5ojsnzauhwu3fls+iMT6qDUXS+kXloZo71dn15j9D2qWuuQvw==" 1179 | }, 1180 | "side-channel": { 1181 | "version": "1.0.2", 1182 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.2.tgz", 1183 | "integrity": "sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA==", 1184 | "dev": true, 1185 | "requires": { 1186 | "es-abstract": "^1.17.0-next.1", 1187 | "object-inspect": "^1.7.0" 1188 | } 1189 | }, 1190 | "source-map": { 1191 | "version": "0.6.1", 1192 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1193 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1194 | "dev": true 1195 | }, 1196 | "source-map-support": { 1197 | "version": "0.5.19", 1198 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", 1199 | "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", 1200 | "dev": true, 1201 | "requires": { 1202 | "buffer-from": "^1.0.0", 1203 | "source-map": "^0.6.0" 1204 | } 1205 | }, 1206 | "sprintf-js": { 1207 | "version": "1.0.3", 1208 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1209 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1210 | "dev": true 1211 | }, 1212 | "string-width": { 1213 | "version": "2.1.1", 1214 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", 1215 | "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", 1216 | "dev": true, 1217 | "requires": { 1218 | "is-fullwidth-code-point": "^2.0.0", 1219 | "strip-ansi": "^4.0.0" 1220 | } 1221 | }, 1222 | "string.prototype.trimend": { 1223 | "version": "1.0.1", 1224 | "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", 1225 | "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", 1226 | "dev": true, 1227 | "requires": { 1228 | "define-properties": "^1.1.3", 1229 | "es-abstract": "^1.17.5" 1230 | } 1231 | }, 1232 | "string.prototype.trimstart": { 1233 | "version": "1.0.1", 1234 | "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", 1235 | "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", 1236 | "dev": true, 1237 | "requires": { 1238 | "define-properties": "^1.1.3", 1239 | "es-abstract": "^1.17.5" 1240 | } 1241 | }, 1242 | "strip-ansi": { 1243 | "version": "4.0.0", 1244 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", 1245 | "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", 1246 | "dev": true, 1247 | "requires": { 1248 | "ansi-regex": "^3.0.0" 1249 | } 1250 | }, 1251 | "strip-json-comments": { 1252 | "version": "2.0.1", 1253 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1254 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 1255 | "dev": true 1256 | }, 1257 | "supports-color": { 1258 | "version": "7.1.0", 1259 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", 1260 | "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", 1261 | "requires": { 1262 | "has-flag": "^4.0.0" 1263 | } 1264 | }, 1265 | "ts-node": { 1266 | "version": "8.10.2", 1267 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", 1268 | "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", 1269 | "dev": true, 1270 | "requires": { 1271 | "arg": "^4.1.0", 1272 | "diff": "^4.0.1", 1273 | "make-error": "^1.1.1", 1274 | "source-map-support": "^0.5.17", 1275 | "yn": "3.1.1" 1276 | }, 1277 | "dependencies": { 1278 | "diff": { 1279 | "version": "4.0.2", 1280 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 1281 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 1282 | "dev": true 1283 | } 1284 | } 1285 | }, 1286 | "tslib": { 1287 | "version": "1.13.0", 1288 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", 1289 | "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", 1290 | "dev": true 1291 | }, 1292 | "tslint": { 1293 | "version": "5.20.1", 1294 | "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", 1295 | "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", 1296 | "dev": true, 1297 | "requires": { 1298 | "@babel/code-frame": "^7.0.0", 1299 | "builtin-modules": "^1.1.1", 1300 | "chalk": "^2.3.0", 1301 | "commander": "^2.12.1", 1302 | "diff": "^4.0.1", 1303 | "glob": "^7.1.1", 1304 | "js-yaml": "^3.13.1", 1305 | "minimatch": "^3.0.4", 1306 | "mkdirp": "^0.5.1", 1307 | "resolve": "^1.3.2", 1308 | "semver": "^5.3.0", 1309 | "tslib": "^1.8.0", 1310 | "tsutils": "^2.29.0" 1311 | }, 1312 | "dependencies": { 1313 | "ansi-styles": { 1314 | "version": "3.2.1", 1315 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 1316 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 1317 | "dev": true, 1318 | "requires": { 1319 | "color-convert": "^1.9.0" 1320 | } 1321 | }, 1322 | "chalk": { 1323 | "version": "2.4.2", 1324 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 1325 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 1326 | "dev": true, 1327 | "requires": { 1328 | "ansi-styles": "^3.2.1", 1329 | "escape-string-regexp": "^1.0.5", 1330 | "supports-color": "^5.3.0" 1331 | } 1332 | }, 1333 | "color-convert": { 1334 | "version": "1.9.3", 1335 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 1336 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 1337 | "dev": true, 1338 | "requires": { 1339 | "color-name": "1.1.3" 1340 | } 1341 | }, 1342 | "color-name": { 1343 | "version": "1.1.3", 1344 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 1345 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 1346 | "dev": true 1347 | }, 1348 | "diff": { 1349 | "version": "4.0.2", 1350 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 1351 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 1352 | "dev": true 1353 | }, 1354 | "has-flag": { 1355 | "version": "3.0.0", 1356 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 1357 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 1358 | "dev": true 1359 | }, 1360 | "supports-color": { 1361 | "version": "5.5.0", 1362 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1363 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1364 | "dev": true, 1365 | "requires": { 1366 | "has-flag": "^3.0.0" 1367 | } 1368 | } 1369 | } 1370 | }, 1371 | "tsutils": { 1372 | "version": "2.29.0", 1373 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", 1374 | "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", 1375 | "dev": true, 1376 | "requires": { 1377 | "tslib": "^1.8.1" 1378 | } 1379 | }, 1380 | "type-detect": { 1381 | "version": "4.0.8", 1382 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", 1383 | "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", 1384 | "dev": true 1385 | }, 1386 | "typescript": { 1387 | "version": "3.9.7", 1388 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", 1389 | "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", 1390 | "dev": true 1391 | }, 1392 | "typical": { 1393 | "version": "4.0.0", 1394 | "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", 1395 | "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==" 1396 | }, 1397 | "unicode-canonical-property-names-ecmascript": { 1398 | "version": "1.0.4", 1399 | "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", 1400 | "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==" 1401 | }, 1402 | "unicode-match-property-ecmascript": { 1403 | "version": "1.0.4", 1404 | "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", 1405 | "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", 1406 | "requires": { 1407 | "unicode-canonical-property-names-ecmascript": "^1.0.4", 1408 | "unicode-property-aliases-ecmascript": "^1.0.4" 1409 | } 1410 | }, 1411 | "unicode-match-property-value-ecmascript": { 1412 | "version": "1.0.2", 1413 | "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz", 1414 | "integrity": "sha512-Rx7yODZC1L/T8XKo/2kNzVAQaRE88AaMvI1EF/Xnj3GW2wzN6fop9DDWuFAKUVFH7vozkz26DzP0qyWLKLIVPQ==" 1415 | }, 1416 | "unicode-property-aliases-ecmascript": { 1417 | "version": "1.0.4", 1418 | "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz", 1419 | "integrity": "sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg==" 1420 | }, 1421 | "which": { 1422 | "version": "1.3.1", 1423 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 1424 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 1425 | "dev": true, 1426 | "requires": { 1427 | "isexe": "^2.0.0" 1428 | } 1429 | }, 1430 | "which-boxed-primitive": { 1431 | "version": "1.0.1", 1432 | "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz", 1433 | "integrity": "sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==", 1434 | "dev": true, 1435 | "requires": { 1436 | "is-bigint": "^1.0.0", 1437 | "is-boolean-object": "^1.0.0", 1438 | "is-number-object": "^1.0.3", 1439 | "is-string": "^1.0.4", 1440 | "is-symbol": "^1.0.2" 1441 | } 1442 | }, 1443 | "which-collection": { 1444 | "version": "1.0.1", 1445 | "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", 1446 | "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", 1447 | "dev": true, 1448 | "requires": { 1449 | "is-map": "^2.0.1", 1450 | "is-set": "^2.0.1", 1451 | "is-weakmap": "^2.0.1", 1452 | "is-weakset": "^2.0.1" 1453 | } 1454 | }, 1455 | "which-module": { 1456 | "version": "2.0.0", 1457 | "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", 1458 | "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", 1459 | "dev": true 1460 | }, 1461 | "which-typed-array": { 1462 | "version": "1.1.2", 1463 | "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.2.tgz", 1464 | "integrity": "sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==", 1465 | "dev": true, 1466 | "requires": { 1467 | "available-typed-arrays": "^1.0.2", 1468 | "es-abstract": "^1.17.5", 1469 | "foreach": "^2.0.5", 1470 | "function-bind": "^1.1.1", 1471 | "has-symbols": "^1.0.1", 1472 | "is-typed-array": "^1.1.3" 1473 | } 1474 | }, 1475 | "wide-align": { 1476 | "version": "1.1.3", 1477 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", 1478 | "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", 1479 | "dev": true, 1480 | "requires": { 1481 | "string-width": "^1.0.2 || 2" 1482 | } 1483 | }, 1484 | "wrap-ansi": { 1485 | "version": "5.1.0", 1486 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", 1487 | "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", 1488 | "dev": true, 1489 | "requires": { 1490 | "ansi-styles": "^3.2.0", 1491 | "string-width": "^3.0.0", 1492 | "strip-ansi": "^5.0.0" 1493 | }, 1494 | "dependencies": { 1495 | "ansi-regex": { 1496 | "version": "4.1.0", 1497 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 1498 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 1499 | "dev": true 1500 | }, 1501 | "ansi-styles": { 1502 | "version": "3.2.1", 1503 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 1504 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 1505 | "dev": true, 1506 | "requires": { 1507 | "color-convert": "^1.9.0" 1508 | } 1509 | }, 1510 | "color-convert": { 1511 | "version": "1.9.3", 1512 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 1513 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 1514 | "dev": true, 1515 | "requires": { 1516 | "color-name": "1.1.3" 1517 | } 1518 | }, 1519 | "color-name": { 1520 | "version": "1.1.3", 1521 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 1522 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", 1523 | "dev": true 1524 | }, 1525 | "string-width": { 1526 | "version": "3.1.0", 1527 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 1528 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 1529 | "dev": true, 1530 | "requires": { 1531 | "emoji-regex": "^7.0.1", 1532 | "is-fullwidth-code-point": "^2.0.0", 1533 | "strip-ansi": "^5.1.0" 1534 | } 1535 | }, 1536 | "strip-ansi": { 1537 | "version": "5.2.0", 1538 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1539 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1540 | "dev": true, 1541 | "requires": { 1542 | "ansi-regex": "^4.1.0" 1543 | } 1544 | } 1545 | } 1546 | }, 1547 | "wrappy": { 1548 | "version": "1.0.2", 1549 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1550 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1551 | "dev": true 1552 | }, 1553 | "y18n": { 1554 | "version": "4.0.0", 1555 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", 1556 | "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", 1557 | "dev": true 1558 | }, 1559 | "yargs": { 1560 | "version": "13.3.2", 1561 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", 1562 | "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", 1563 | "dev": true, 1564 | "requires": { 1565 | "cliui": "^5.0.0", 1566 | "find-up": "^3.0.0", 1567 | "get-caller-file": "^2.0.1", 1568 | "require-directory": "^2.1.1", 1569 | "require-main-filename": "^2.0.0", 1570 | "set-blocking": "^2.0.0", 1571 | "string-width": "^3.0.0", 1572 | "which-module": "^2.0.0", 1573 | "y18n": "^4.0.0", 1574 | "yargs-parser": "^13.1.2" 1575 | }, 1576 | "dependencies": { 1577 | "ansi-regex": { 1578 | "version": "4.1.0", 1579 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 1580 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", 1581 | "dev": true 1582 | }, 1583 | "string-width": { 1584 | "version": "3.1.0", 1585 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 1586 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 1587 | "dev": true, 1588 | "requires": { 1589 | "emoji-regex": "^7.0.1", 1590 | "is-fullwidth-code-point": "^2.0.0", 1591 | "strip-ansi": "^5.1.0" 1592 | } 1593 | }, 1594 | "strip-ansi": { 1595 | "version": "5.2.0", 1596 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1597 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1598 | "dev": true, 1599 | "requires": { 1600 | "ansi-regex": "^4.1.0" 1601 | } 1602 | } 1603 | } 1604 | }, 1605 | "yargs-parser": { 1606 | "version": "13.1.2", 1607 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", 1608 | "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", 1609 | "dev": true, 1610 | "requires": { 1611 | "camelcase": "^5.0.0", 1612 | "decamelize": "^1.2.0" 1613 | } 1614 | }, 1615 | "yargs-unparser": { 1616 | "version": "1.6.0", 1617 | "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", 1618 | "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", 1619 | "dev": true, 1620 | "requires": { 1621 | "flat": "^4.1.0", 1622 | "lodash": "^4.17.15", 1623 | "yargs": "^13.3.0" 1624 | } 1625 | }, 1626 | "yn": { 1627 | "version": "3.1.1", 1628 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 1629 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 1630 | "dev": true 1631 | } 1632 | } 1633 | } 1634 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shift-interpreter", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/src/index.js", 6 | "types": "dist/src/index.d.ts", 7 | "scripts": { 8 | "build": "tsc", 9 | "compile": "npm run clean && tsc --declaration", 10 | "clean": "rm -rf dist", 11 | "prepublishOnly": "npm run compile", 12 | "format": "prettier --write 'src/**/*.ts' 'test/**/*.ts'", 13 | "watch": "tsc -w", 14 | "test": "mocha" 15 | }, 16 | "keywords": [ 17 | "interpreter", 18 | "sandbox", 19 | "vm", 20 | "evaluate", 21 | "javascript", 22 | "ecmascript", 23 | "debug", 24 | "deobfuscate" 25 | ], 26 | "author": "", 27 | "license": "ISC", 28 | "devDependencies": { 29 | "@types/chai": "^4.2.11", 30 | "@types/chai-spies": "^1.0.1", 31 | "@types/debug": "^4.1.5", 32 | "@types/deep-equal": "^1.0.1", 33 | "@types/mocha": "^5.2.7", 34 | "@types/multimap": "^1.1.1", 35 | "@types/node": "^13.13.4", 36 | "chai": "^4.2.0", 37 | "chai-spies": "^1.0.0", 38 | "deep-equal": "^2.0.3", 39 | "mocha": "^6.2.3", 40 | "prettier": "^1.19.1", 41 | "ts-node": "^8.9.1", 42 | "tslint": "^5.19.0", 43 | "typescript": "^3.8.3" 44 | }, 45 | "dependencies": { 46 | "chalk": "^4.0.0", 47 | "command-line-args": "^5.1.1", 48 | "debug": "^4.1.1", 49 | "shift-ast": "^6.1.0", 50 | "shift-parser": "^7.0.0", 51 | "shift-printer": "^1.0.1", 52 | "shift-scope": "^4.0.0" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Shift Interpreter 2 | 3 | Shift-interpreter is an experimental JavaScript meta-interpreter useful for reverse engineering and analysis. One notable difference from other projects is that shift-interpreter retains state over an entire script but can be fed expressions and statements piecemeal, regardless of their original location. This allows a user to execute only the portions of JavaScript necessary for analysis. 4 | 5 | ## Who is this for 6 | 7 | JavaScript analyzers. 8 | 9 | This was created to aid "static" analysis, reverse engineering, and deobfuscation of JavaScript code. 10 | 11 | ## Status 12 | 13 | [Experimental](http://nodejs.org/api/documentation.html#documentation_stability_index). 14 | 15 | This library is frequently used in ad-hoc analysis but has never been used in any high performance or mission critical applications. 16 | 17 | ## Warning! 18 | 19 | This should not be considered a secure JavaScript sandbox. This is _not_ a secure JavaScript sandbox. 20 | 21 | ## Installation 22 | 23 | ```sh 24 | $ npm install shift-interpreter 25 | ``` 26 | 27 | ## Usage 28 | 29 | ### Basic usage 30 | 31 | ```js 32 | const { interpret } = require('shift-interpreter'); 33 | 34 | const source = ` 35 | const a = 40; 36 | a + a + 20; 37 | `; 38 | 39 | const result = interpret(source); 40 | console.log(result); // 100 41 | ``` 42 | 43 | The above is equivalent to the following: 44 | 45 | ```js 46 | const { parseScript } = require('shift-parser'); 47 | const { Interpreter } = require('shift-interpreter'); 48 | 49 | const source = ` 50 | const a = 40; 51 | a + a + 20; 52 | `; 53 | 54 | const tree = parseScript(source); 55 | 56 | const interpreter = new Interpreter(); 57 | interpreter.load(tree); 58 | 59 | const result = interpreter.run(); 60 | console.log(result); // 100 61 | ``` 62 | 63 | ### Passing contexts 64 | 65 | By default, a script has access to nothing, its global context is empty. Pass a JavaScript object as the second parameter to `.load()` to use as the global context. 66 | 67 | ```js 68 | const { parseScript } = require('shift-parser'); 69 | const { Interpreter } = require('shift-interpreter'); 70 | 71 | const source = ` 72 | console.log("hello world!"); 73 | `; 74 | 75 | const tree = parseScript(source); 76 | 77 | const interpreter = new Interpreter(); 78 | interpreter.load(tree); 79 | const result = interpreter.run(); // ReferenceError: console is not defined 80 | 81 | interpreter.load(tree, { console }); 82 | const result = interpreter.run(); // "hello world!" 83 | ``` 84 | 85 | ### Selective Execution 86 | 87 | The following is an example of selective execution. This program decodes an array of strings while only actually executing one statement in the target source. 88 | 89 | ```js 90 | const { parseScript } = require('shift-parser'); 91 | const { Interpreter } = require('shift-interpreter'); 92 | 93 | const source = ` 94 | const strings = [ "ZG9jdW1lbnQ=", "YWRkRXZlbnRMaXN0ZW5lcg==", "bG9hZA==" ]; 95 | function decode(str) { 96 | return atob(str) 97 | } 98 | window[decode(strings[0])][decode(strings[1])](decode(strings[2]), () => { 99 | somework(); 100 | }) 101 | `; 102 | 103 | const tree = parseScript(source); 104 | 105 | const interpreter = new Interpreter(); 106 | 107 | // load the tree with a context that has an "atob" function 108 | interpreter.load(tree, { 109 | atob: str => Buffer.from(str, 'base64').toString('ascii'), 110 | }); 111 | 112 | // run the second statement in the script (the "decode" function declaration) 113 | interpreter.run(tree.statements[1]); 114 | 115 | // retrieve the array expression node from the parsed source. 116 | const stringArrayExpression = tree.statements[0].declaration.declarators[0].init; 117 | 118 | // get the runtime value of the function declaration above. This is the interpreter's 119 | // value for the passed identifier, which in this case is an executable function. 120 | const decodeFunction = interpreter.getRuntimeValue(tree.statements[1].name); 121 | 122 | // map over the elements of the array expression, decoding each value with the function from the interpreter. 123 | const decodedStrings = stringArrayExpression.elements.map(node => decodeFunction(node.value)); 124 | 125 | console.log(decodedStrings); // [ 'document', 'addEventListener', 'load' ] 126 | ``` 127 | 128 | ## API 129 | 130 | ### interpret/intrepretSource(src, context) 131 | 132 | ### interpretTree(ast, context) 133 | 134 | These methods run the source (or AST), with the optional context, and return the result of execution. These are convenience methods exposed for rapid testing and are not the main use of the library. If you find your use case covered with these, you probably want another tool (or `eval`) 135 | 136 | ### Interpreter(options) 137 | 138 | Constructor for the interpreter, takes an options object. 139 | 140 | #### options 141 | 142 | **options.skipUnsupported: boolean** 143 | 144 | Skip unsupported nodes, false by default. There are few nodes unsupported outright but there are numerous combinations of nodes that are not supported (e.g. array assignments in for..of). These throw an error by default but you can skip them if their execution isn't important to you by passing `{skipUnsupported: true}` 145 | 146 | Note: Submit issues for unsupported nodes along with use cases and minimal reproducible source. Most cases are not supported only due to time and lack of a pressing need, not due to complexity. 147 | 148 | **options.handler: NodeHandler** 149 | 150 | Pass your own handler to override or augment behavior for specific nodes. 151 | 152 | #### .load(ast, context = {}) 153 | 154 | Load an ast as the script this interpreter will analyze for state and execution. Optionally pass a context object to use as the 155 | global context 156 | 157 | #### .run(node?) 158 | 159 | Run the script or the passed node. Returns the result of execution. 160 | 161 | #### .runToFirstError(node?) 162 | 163 | Like .run(), but swallows the error so you don't need to litter try/catches around implementing code. Useful when you find unsupported cases but enough of the program runs to cover your needs. 164 | 165 | #### .getRuntimeValue(identifierNode) 166 | 167 | Get the interpreter's runtime value for the passed identifier node. 168 | 169 | ## Known limitations 170 | 171 | Too many to list, but here are a few. 172 | 173 | - This is not a sandbox. Modifications of native APIs will persist in the host environment. 174 | - Edge cases around Symbols not explored. 175 | - Does not support Class constructors with `super()`. These could be supported but I haven't had a reason to work on it yet. 176 | 177 | The following support is deferred until necessary. The syntax is not often found in production code due to common practices or transpilation. 178 | 179 | - Does not support with statements. 180 | - Does not support label statements. 181 | - Does not support yield expressions. 182 | - Does not support tagged template strings. 183 | - Does not support await expressions. 184 | 185 | ## Contributing 186 | 187 | This is a "get stuff done" library. It has grown as limitations have been found in real world usage. Contributions need to reflect real world use cases. Improvements to interpreter accuracy that primarily address edge cases will only be considered if they make the codebase more maintainable. 188 | 189 | Are you an ECMAScript specification or interpreter guru and can point out the million ways this fails? Feel free to submit issues or PRs with (skipped) tests for minimal reproducible cases. They will be included to help future implementers. 190 | -------------------------------------------------------------------------------- /src/context.ts: -------------------------------------------------------------------------------- 1 | export type BasicContext = Record; 2 | -------------------------------------------------------------------------------- /src/errors.ts: -------------------------------------------------------------------------------- 1 | export class InterpreterRuntimeError extends Error {} 2 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Script } from 'shift-ast'; 2 | import { parseScript } from 'shift-parser'; 3 | import { Interpreter } from './interpreter'; 4 | 5 | export function interpretSource(source: string, context = {}) { 6 | return interpretTree(parseScript(source), context); 7 | } 8 | 9 | export function interpretTree(tree: Script, context = {}) { 10 | const interpreter = new Interpreter(); 11 | interpreter.pushContext(context); 12 | interpreter.load(tree); 13 | return interpreter.run(); 14 | } 15 | 16 | export default interpretSource; 17 | 18 | export const interpret = interpretSource; 19 | 20 | export { Interpreter } from './interpreter'; 21 | -------------------------------------------------------------------------------- /src/instruction-buffer.ts: -------------------------------------------------------------------------------- 1 | import { InstructionNode } from './types'; 2 | 3 | export enum InstructionBufferEventName { 4 | REQUEST_EXECUTION = 'requestExecution', 5 | HALT = 'halt', 6 | CONTINUE = 'continue', 7 | } 8 | 9 | export class Instruction { 10 | node: InstructionNode; 11 | id: number; 12 | result: any; 13 | constructor(node: InstructionNode, id: number) { 14 | this.node = node; 15 | this.id = id; 16 | } 17 | } 18 | 19 | export class InstructionBuffer { 20 | buffer: Instruction[] = []; 21 | numInstructions = 0; 22 | add(node: InstructionNode): Instruction { 23 | const instruction = new Instruction(node, this.numInstructions++); 24 | this.buffer.push(instruction); 25 | return instruction; 26 | } 27 | nextInstruction() { 28 | return this.buffer.shift(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/instruction.ts: -------------------------------------------------------------------------------- 1 | import { InstructionNode } from './types'; 2 | -------------------------------------------------------------------------------- /src/interpreter.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | import DEBUG from 'debug'; 3 | import { EventEmitter } from 'events'; 4 | import { 5 | ArrayBinding, 6 | BindingIdentifier, 7 | BindingWithDefault, 8 | EmptyStatement, 9 | Expression, 10 | Node, 11 | ObjectBinding, 12 | Script, 13 | Statement, 14 | VariableDeclaration, 15 | VariableDeclarationStatement, 16 | } from 'shift-ast'; 17 | import * as codegen from 'shift-printer'; 18 | import shiftScope, { Scope, ScopeLookup, Variable } from 'shift-scope'; 19 | import { BasicContext } from './context'; 20 | import { InterpreterRuntimeError } from './errors'; 21 | import { InstructionBuffer, Instruction } from './instruction-buffer'; 22 | import { NodeHandler } from './node-handler'; 23 | import { BlockType, FuncType, Identifier, InstructionNode } from './types'; 24 | import { isStatement } from './util'; 25 | 26 | const debug = DEBUG('shift-interpreter'); 27 | 28 | interface Options { 29 | skipUnsupported?: boolean; 30 | handler?: { new (interpreter: Interpreter): NodeHandler }; 31 | } 32 | 33 | export enum InterpreterEventName { 34 | COMPLETE = 'complete', 35 | } 36 | 37 | export abstract class InterpreterEvent { 38 | static type = InterpreterEventName; 39 | } 40 | 41 | export class InterpreterCompleteEvent extends InterpreterEvent { 42 | result: any; 43 | constructor(result: any) { 44 | super(); 45 | this.result = result; 46 | } 47 | } 48 | export class Interpreter { 49 | contexts: BasicContext[] = []; 50 | globalScope: Scope = shiftScope(new Script({ directives: [], statements: [] })); 51 | lookupTable: ScopeLookup = new ScopeLookup(this.globalScope); 52 | scopeMap: WeakMap = new WeakMap(); 53 | scopeOwnerMap: WeakMap = new WeakMap(); 54 | variableMap = new Map(); 55 | options: Options; 56 | loadedScript: Script = new Script({ directives: [], statements: [] }); 57 | handler: NodeHandler; 58 | contextProxies = new WeakMap(); 59 | pointer = new InstructionBuffer(); 60 | lastStatement: Statement = new EmptyStatement(); 61 | lastInstruction: Instruction = new Instruction(new EmptyStatement(), -1); 62 | _isReturning: boolean = false; 63 | _isBreaking: boolean = false; 64 | _isContinuing: boolean = false; 65 | errorLocation?: { lastInstruction: Instruction; lastStatement: Statement }; 66 | 67 | constructor(options: Options = {}) { 68 | this.options = options; 69 | if (this.options.handler) { 70 | this.handler = new this.options.handler(this); 71 | } else { 72 | this.handler = new NodeHandler(this); 73 | } 74 | } 75 | 76 | print(node?: Node) { 77 | return codegen.prettyPrint(node || this.lastInstruction.node); 78 | } 79 | 80 | logNode(node: Node) { 81 | codegen.log(node); 82 | } 83 | 84 | codegen(node: Node) { 85 | codegen.printTruncated(node); 86 | } 87 | 88 | skipOrThrow(type: string) { 89 | if (this.options.skipUnsupported) return; 90 | throw new InterpreterRuntimeError(`Unsupported node ${type}`); 91 | } 92 | 93 | load(script: Script, context: BasicContext = {}) { 94 | debug('loading script'); 95 | this.globalScope = shiftScope(script); 96 | this.lookupTable = new ScopeLookup(this.globalScope); 97 | this.buildScopeMap(); 98 | this.loadedScript = script; 99 | this.pushContext(context); 100 | } 101 | 102 | private buildScopeMap() { 103 | const lookupTable = this.lookupTable; 104 | this.scopeMap = new WeakMap(); 105 | const recurse = (scope: Scope) => { 106 | this.scopeOwnerMap.set(scope.astNode, scope); 107 | scope.variableList.forEach((variable: Variable) => { 108 | this.scopeMap.set(variable, scope); 109 | }); 110 | scope.children.forEach(recurse); 111 | }; 112 | recurse(lookupTable.scope); 113 | } 114 | 115 | pushContext(context: any) { 116 | this.contexts.push(context); 117 | } 118 | 119 | popContext() { 120 | return this.contexts.pop(); 121 | } 122 | 123 | getCurrentContext() { 124 | if (this.contexts.length === 0) { 125 | debug('interpreter created with no context, creating empty context.'); 126 | this.pushContext({}); 127 | } 128 | const context = this.contexts[this.contexts.length - 1]; 129 | if (context === undefined) return this.contexts[0]; 130 | return context; 131 | } 132 | 133 | getContexts() { 134 | return this.contexts; 135 | } 136 | 137 | getContext(index: number) { 138 | const context = this.contexts[index]; 139 | if (context === undefined) return this.contexts[0]; 140 | return context; 141 | } 142 | 143 | isReturning(state?: boolean) { 144 | if (state !== undefined) this._isReturning = state; 145 | return this._isReturning; 146 | } 147 | 148 | isBreaking(state?: boolean) { 149 | if (state !== undefined) this._isBreaking = state; 150 | return this._isBreaking; 151 | } 152 | 153 | isContinuing(state?: boolean) { 154 | if (state !== undefined) this._isContinuing = state; 155 | return this._isContinuing; 156 | } 157 | 158 | run(passedNode?: InstructionNode): Promise { 159 | let nodeToEvaluate: InstructionNode | undefined = undefined; 160 | 161 | if (passedNode) { 162 | if (passedNode.type === 'Script') { 163 | this.load(passedNode); 164 | } 165 | nodeToEvaluate = passedNode; 166 | } else if (this.loadedScript) { 167 | nodeToEvaluate = this.loadedScript; 168 | } 169 | 170 | if (!this.loadedScript) { 171 | // If we don't have a currentScript (haven't run load()) but were passed a node 172 | // the node must be a Statement or Expression (or bad object) and we shouldn't run 173 | // it without the user knowing what they are doing. 174 | if (passedNode) 175 | throw new InterpreterRuntimeError( 176 | `Can not evaluate ${passedNode.type} node without loading a program (Script node) first.`, 177 | ); 178 | else throw new InterpreterRuntimeError('No program to evaluate'); 179 | } 180 | 181 | if (!nodeToEvaluate) { 182 | throw new InterpreterRuntimeError('No program to evaluate'); 183 | } 184 | 185 | debug('starting execution'); 186 | let programResult: any = null; 187 | try { 188 | programResult = this.evaluate(nodeToEvaluate); 189 | debug(`completed execution with result: %o`, programResult); 190 | return programResult; 191 | } catch (e) { 192 | this.errorLocation = { 193 | lastStatement: this.lastStatement, 194 | lastInstruction: this.lastInstruction, 195 | }; 196 | this.handleError(e); 197 | throw e; 198 | } 199 | } 200 | private handleError(e: any) { 201 | debug.extend('error')(`Error during execution`); 202 | if (this.errorLocation) { 203 | const statementSrc = codegen.printSummary(this.errorLocation.lastStatement); 204 | const nodeSrc = codegen.printSummary(this.errorLocation.lastInstruction.node); 205 | console.log(statementSrc.replace(nodeSrc, `👉👉👉${chalk.red(nodeSrc)}`)); 206 | } else { 207 | console.log('No error location recorded.'); 208 | } 209 | } 210 | 211 | runToFirstError(passedNode?: Script | Statement | Expression) { 212 | try { 213 | return this.run(passedNode); 214 | } catch (e) {} 215 | } 216 | evaluateInstruction(instruction: Instruction) { 217 | this.lastInstruction = instruction; 218 | const node = instruction.node; 219 | if (isStatement(node)) this.lastStatement = node; 220 | let result = this.handler[node.type](node); 221 | return (instruction.result = result); 222 | } 223 | evaluate(node: InstructionNode | null): any { 224 | if (node === null) { 225 | return undefined; 226 | } else { 227 | // yeah this looks weird, it's a remnant of when this was all async. You used 228 | // to be able to stop, start, and break the program but the implementation with native JS promises 229 | // caused problems. I'm keeping the instruction buffer because hey, maybe it'll come back. 230 | this.pointer.add(node); 231 | const instruction = this.pointer.nextInstruction(); 232 | if (!instruction) { 233 | debug(`no instruction to evaluate, returning`); 234 | return; 235 | } 236 | debug(`evaluating instruction from %o -> %o`, this.lastInstruction.node.type, node.type); 237 | return this.evaluateInstruction(instruction); 238 | } 239 | } 240 | hoistFunctions(block: BlockType) { 241 | const functions = block.statements.filter(s => s.type === 'FunctionDeclaration'); 242 | if (functions.length) debug(`hoisting %o functions in %o`, functions.length, block.type); 243 | for (let fnDecl of functions) { 244 | this.evaluate(fnDecl); 245 | } 246 | } 247 | 248 | hoistVars(block: BlockType) { 249 | const vars = block.statements 250 | .filter( 251 | <(T: Statement) => T is VariableDeclarationStatement>(stmt => stmt.type === 'VariableDeclarationStatement'), 252 | ) 253 | .filter((decl: VariableDeclarationStatement) => decl.declaration.kind === 'var'); 254 | if (vars.length) debug(`hoisting %o var statements in %o`, vars.length, block.type); 255 | for (let varDecl of vars) { 256 | for (let declarator of varDecl.declaration.declarators) this.bindVariable(declarator.binding, undefined); 257 | } 258 | } 259 | 260 | declareVariables(decl: VariableDeclaration) { 261 | for (let declarator of decl.declarators) { 262 | this.evaluate(declarator); 263 | } 264 | } 265 | 266 | createFunction(node: FuncType) { 267 | const _debug = debug.extend('createFunction'); 268 | let name: string | undefined = undefined; 269 | if (node.name) { 270 | switch (node.name.type) { 271 | case 'BindingIdentifier': 272 | name = node.name.name; 273 | break; 274 | case 'ComputedPropertyName': 275 | name = this.evaluate(node.name.expression); 276 | break; 277 | case 'StaticPropertyName': 278 | name = node.name.value; 279 | } 280 | } 281 | 282 | _debug(`creating intermediary %o: %o`, node.type, name); 283 | 284 | const interpreter = this; 285 | 286 | const fnDebug = debug.extend('function'); 287 | let fn: (this: any, ...args: any) => any; 288 | 289 | // anonymous functions have an empty string as the name 290 | if (!name) name = ''; 291 | 292 | // creating a function like this, i.e. { someName: function(){} ) 293 | // allows us to create a named function by inferring the name from the property value. 294 | fn = { 295 | [name]: function(this: any, ...args: any): any { 296 | fnDebug(`calling intermediary %o: %o`, node.type, name); 297 | interpreter.pushContext(this); 298 | const scope = interpreter.scopeOwnerMap.get(node); 299 | if (scope) { 300 | const argsRef = scope.variables.get('arguments'); 301 | if (argsRef) interpreter.setRuntimeValue(argsRef, arguments); 302 | } 303 | 304 | if (node.type === 'Getter') { 305 | // nothing 306 | } else if (node.type === 'Setter') { 307 | fnDebug(`setter: binding passed parameter`); 308 | interpreter.bindVariable(node.param, args[0]); 309 | } else { 310 | node.params.items.forEach( 311 | (el: ArrayBinding | BindingIdentifier | BindingWithDefault | ObjectBinding, i: number) => { 312 | fnDebug(`binding function argument %o`, i + 1); 313 | return interpreter.bindVariable(el, args[i]); 314 | }, 315 | ); 316 | } 317 | fnDebug('evaluating function body'); 318 | const result = interpreter.evaluate(node.body); 319 | fnDebug('completed evaluating function body'); 320 | 321 | interpreter.popContext(); 322 | 323 | if (new.target) { 324 | if (interpreter.isReturning()) { 325 | interpreter.isReturning(false); 326 | if (typeof result === 'object') return result; 327 | } 328 | return this; 329 | } else { 330 | if (interpreter.isReturning()) { 331 | interpreter.isReturning(false); 332 | } 333 | return result; 334 | } 335 | }, 336 | }[name]; 337 | 338 | return Object.assign(fn, { _interp: true }); 339 | } 340 | 341 | bindVariable(binding: BindingIdentifier | ArrayBinding | ObjectBinding | BindingWithDefault, init: any) { 342 | const _debug = debug.extend('bindVariable'); 343 | switch (binding.type) { 344 | case 'BindingIdentifier': 345 | { 346 | const variables = this.lookupTable.variableMap.get(binding); 347 | 348 | if (variables.length > 1) throw new Error('reproduce this and handle it better'); 349 | const variable = variables[0]; 350 | _debug(`binding %o to %o`, binding.name, init); 351 | this.setRuntimeValue(variable, init); 352 | } 353 | break; 354 | case 'ArrayBinding': 355 | { 356 | for (let i = 0; i < binding.elements.length; i++) { 357 | const el = binding.elements[i]; 358 | const indexElement = init[i]; 359 | if (el) this.bindVariable(el, indexElement); 360 | } 361 | if (binding.rest) this.skipOrThrow('ArrayBinding->Rest/Spread'); 362 | } 363 | break; 364 | case 'ObjectBinding': 365 | { 366 | for (let i = 0; i < binding.properties.length; i++) { 367 | const prop = binding.properties[i]; 368 | if (prop.type === 'BindingPropertyIdentifier') { 369 | const name = prop.binding.name; 370 | if (init[name] === undefined && prop.init) { 371 | this.bindVariable(prop.binding, this.evaluate(prop.init)); 372 | } else { 373 | this.bindVariable(prop.binding, init[name]); 374 | } 375 | } else { 376 | const name = 377 | prop.name.type === 'ComputedPropertyName' ? this.evaluate(prop.name.expression) : prop.name.value; 378 | this.bindVariable(prop.binding, init[name]); 379 | } 380 | } 381 | if (binding.rest) this.skipOrThrow('ObjectBinding->Rest/Spread'); 382 | } 383 | break; 384 | case 'BindingWithDefault': 385 | if (init === undefined) { 386 | _debug(`evaluating default for undefined argument`); 387 | const defaults = this.evaluate(binding.init); 388 | _debug(`binding default`); 389 | this.bindVariable(binding.binding, defaults); 390 | } else { 391 | this.bindVariable(binding.binding, init); 392 | } 393 | break; 394 | } 395 | } 396 | updateVariableValue(node: Identifier, value: any) { 397 | const variables = this.lookupTable.variableMap.get(node); 398 | 399 | if (variables.length > 1) throw new Error('reproduce this and handle it better'); 400 | const variable = variables[0]; 401 | const decl = variable.declarations[0]; 402 | if (decl && decl.type.name === 'Const') throw new TypeError('Assignment to constant variable.'); 403 | this.setRuntimeValue(variable, value); 404 | return value; 405 | } 406 | setRuntimeValue(variable: Variable, value: any) { 407 | this.variableMap.set(variable, value); 408 | } 409 | getRuntimeValue(node: Identifier): any { 410 | const _debug = debug.extend('getVariableValue'); 411 | _debug(`retrieving value for ${node.name}`); 412 | const variables = this.lookupTable.variableMap.get(node); 413 | 414 | if (!variables) { 415 | throw new Error(`${node.type} variable not found. Make sure you are passing a valid Identifier node.`); 416 | } 417 | 418 | if (variables.length > 1) { 419 | _debug(`>1 variable returned, ${variables}`); 420 | throw new Error('reproduce this and handle it better'); 421 | } 422 | const variable = variables[0]; 423 | 424 | if (this.variableMap.has(variable)) { 425 | const value = this.variableMap.get(variable); 426 | return value; 427 | } else { 428 | const contexts = this.getContexts(); 429 | for (let i = contexts.length - 1; i > -1; i--) { 430 | const context = this.getContext(i); 431 | if (!context) { 432 | throw new Error('No context to evaluate in.'); 433 | } 434 | if (variable.name in context) { 435 | const value = context[variable.name]; 436 | return value; 437 | } 438 | } 439 | throw new ReferenceError(`${node.name} is not defined`); 440 | } 441 | } 442 | } 443 | -------------------------------------------------------------------------------- /src/node-handler.ts: -------------------------------------------------------------------------------- 1 | import DEBUG from 'debug'; 2 | import { 3 | ArrayExpression, 4 | ArrowExpression, 5 | AssignmentExpression, 6 | BinaryExpression, 7 | Block, 8 | BlockStatement, 9 | CallExpression, 10 | ClassDeclaration, 11 | CompoundAssignmentExpression, 12 | ComputedMemberExpression, 13 | ConditionalExpression, 14 | DoWhileStatement, 15 | ExpressionStatement, 16 | ForInStatement, 17 | ForOfStatement, 18 | ForStatement, 19 | FunctionBody, 20 | FunctionDeclaration, 21 | FunctionExpression, 22 | IdentifierExpression, 23 | IfStatement, 24 | LiteralBooleanExpression, 25 | LiteralInfinityExpression, 26 | LiteralNullExpression, 27 | LiteralNumericExpression, 28 | LiteralRegExpExpression, 29 | LiteralStringExpression, 30 | NewExpression, 31 | ObjectExpression, 32 | ReturnStatement, 33 | Script, 34 | StaticMemberExpression, 35 | TemplateExpression, 36 | ThisExpression, 37 | ThrowStatement, 38 | TryCatchStatement, 39 | TryFinallyStatement, 40 | UnaryExpression, 41 | UpdateExpression, 42 | VariableDeclarationStatement, 43 | VariableDeclarator, 44 | WhileStatement, 45 | } from 'shift-ast'; 46 | import { BasicContext } from './context'; 47 | import { InterpreterRuntimeError } from './errors'; 48 | import { Interpreter } from './interpreter'; 49 | import { binaryOperatorMap, compoundAssignmentOperatorMap, unaryOperatorMap } from './operators'; 50 | import { toString } from './util'; 51 | import { Variable } from 'shift-scope'; 52 | 53 | export interface DynamicClass { 54 | [key: string]: any; 55 | } 56 | 57 | const debug = DEBUG('shift:interpreter:node-handler'); 58 | 59 | export class NodeHandler { 60 | interpreter: Interpreter; 61 | 62 | constructor(interpreter: Interpreter) { 63 | this.interpreter = interpreter; 64 | } 65 | 66 | ReturnStatement(stmt: ReturnStatement) { 67 | const value = this.interpreter.evaluate(stmt.expression); 68 | this.interpreter.isReturning(true); 69 | return value; 70 | } 71 | 72 | ExpressionStatement(stmt: ExpressionStatement) { 73 | return this.interpreter.evaluate(stmt.expression); 74 | } 75 | VariableDeclarationStatement(stmt: VariableDeclarationStatement) { 76 | return this.interpreter.declareVariables(stmt.declaration); 77 | } 78 | 79 | VariableDeclarator(declarator: VariableDeclarator) { 80 | const value = this.interpreter.evaluate(declarator.init); 81 | return this.interpreter.bindVariable(declarator.binding, value); 82 | } 83 | 84 | FunctionDeclaration(decl: FunctionDeclaration) { 85 | const fn = this.interpreter.createFunction(decl); 86 | 87 | const variables = this.interpreter.lookupTable.variableMap.get(decl.name); 88 | variables.forEach((variable: Variable) => this.interpreter.variableMap.set(variable, fn)); 89 | } 90 | 91 | BlockStatement(stmt: BlockStatement) { 92 | return this.interpreter.evaluate(stmt.block); 93 | } 94 | 95 | ClassDeclaration(decl: ClassDeclaration) { 96 | const staticMethods: [string, Function][] = []; 97 | const methods: [string, Function][] = []; 98 | let constructor: null | Function = null; 99 | 100 | if (decl.elements.length > 0) { 101 | for (let el of decl.elements) { 102 | if (el.method.type === 'Method') { 103 | const intermediateFunction = this.interpreter.createFunction(el.method); 104 | if (el.isStatic) { 105 | staticMethods.push([intermediateFunction.name!, intermediateFunction]); 106 | } else { 107 | if (intermediateFunction.name === 'constructor') constructor = intermediateFunction; 108 | else methods.push([intermediateFunction.name!, intermediateFunction]); 109 | } 110 | } else { 111 | this.interpreter.skipOrThrow(`ClassElement type ${el.method.type}`); 112 | } 113 | } 114 | } 115 | 116 | let Class: DynamicClass = class {}; 117 | 118 | if (decl.super) { 119 | const xtends = this.interpreter.evaluate(decl.super); 120 | Class = ((SuperClass: any = xtends) => { 121 | if (constructor === null) { 122 | class InterpreterClassWithExtendsA extends SuperClass { 123 | constructor(...args: any) { 124 | super(...args); 125 | } 126 | } 127 | 128 | return InterpreterClassWithExtendsA; 129 | } else { 130 | class InterpreterClassWithExtendsB extends SuperClass { 131 | constructor(...args: any) { 132 | super(...args); 133 | constructor!(args, this); 134 | } 135 | } 136 | 137 | return InterpreterClassWithExtendsB; 138 | } 139 | })(); 140 | } else { 141 | Class = (() => { 142 | if (constructor === null) { 143 | class InterpreterClassA { 144 | constructor() {} 145 | } 146 | 147 | return InterpreterClassA; 148 | } else { 149 | class InterpreterClassB { 150 | constructor(...args: any) { 151 | constructor!(args, this); 152 | } 153 | } 154 | 155 | return InterpreterClassB; 156 | } 157 | })(); 158 | } 159 | 160 | methods.forEach(([name, intermediateFunction]) => { 161 | Class.prototype[name] = intermediateFunction; 162 | }); 163 | 164 | staticMethods.forEach(([name, intermediateFunction]) => { 165 | Class[name] = intermediateFunction; 166 | }); 167 | 168 | const variables = this.interpreter.lookupTable.variableMap.get(decl.name); 169 | 170 | variables.forEach((variable: any) => this.interpreter.variableMap.set(variable, Class)); 171 | 172 | return Class; 173 | } 174 | 175 | IfStatement(stmt: IfStatement) { 176 | const test = this.interpreter.evaluate(stmt.test); 177 | if (test) return this.interpreter.evaluate(stmt.consequent); 178 | else if (stmt.alternate) return this.interpreter.evaluate(stmt.alternate); 179 | } 180 | 181 | ConditionalExpression(stmt: ConditionalExpression) { 182 | const test = this.interpreter.evaluate(stmt.test); 183 | if (test) return this.interpreter.evaluate(stmt.consequent); 184 | else if (stmt.alternate) return this.interpreter.evaluate(stmt.alternate); 185 | } 186 | 187 | ThrowStatement(stmt: ThrowStatement) { 188 | const error = this.interpreter.evaluate(stmt.expression); 189 | throw error; 190 | } 191 | 192 | TryCatchStatement(stmt: TryCatchStatement) { 193 | let returnValue = undefined; 194 | try { 195 | returnValue = this.interpreter.evaluate(stmt.body); 196 | } catch (e) { 197 | this.interpreter.bindVariable(stmt.catchClause.binding, e); 198 | try { 199 | returnValue = this.interpreter.evaluate(stmt.catchClause.body); 200 | } catch (e) { 201 | throw e; 202 | } 203 | } 204 | return returnValue; 205 | } 206 | 207 | TryFinallyStatement(stmt: TryFinallyStatement) { 208 | let returnValue = undefined; 209 | if (stmt.catchClause) { 210 | try { 211 | returnValue = this.interpreter.evaluate(stmt.body); 212 | } catch (e) { 213 | this.interpreter.bindVariable(stmt.catchClause.binding, e); 214 | try { 215 | returnValue = this.interpreter.evaluate(stmt.catchClause.body); 216 | } catch (e) { 217 | throw e; 218 | } 219 | } finally { 220 | returnValue = this.interpreter.evaluate(stmt.finalizer); 221 | } 222 | } else { 223 | try { 224 | returnValue = this.interpreter.evaluate(stmt.body); 225 | } finally { 226 | returnValue = this.interpreter.evaluate(stmt.finalizer); 227 | } 228 | } 229 | return returnValue; 230 | } 231 | 232 | Block(block: Block) { 233 | let value; 234 | const _debug = debug.extend('Block'); 235 | 236 | this.interpreter.hoistFunctions(block); 237 | this.interpreter.hoistVars(block); 238 | const statements = block.statements.filter(stmt => stmt.type !== 'FunctionDeclaration'); 239 | 240 | for (let i = 0; i < statements.length; i++) { 241 | const statement = statements[i]; 242 | _debug(`Evaluating next ${statement.type} in ${block.type}`); 243 | value = this.interpreter.evaluate(statement); 244 | _debug(`${block.type} statement ${statement.type} completed`); 245 | } 246 | _debug(`completed ${block.type}, returning with: ${value}`); 247 | return value; 248 | } 249 | 250 | FunctionBody(body: FunctionBody) { 251 | let value; 252 | const _debug = debug.extend(body.type); 253 | 254 | this.interpreter.hoistFunctions(body); 255 | this.interpreter.hoistVars(body); 256 | const statements = body.statements.filter(stmt => stmt.type !== 'FunctionDeclaration'); 257 | 258 | for (let i = 0; i < statements.length; i++) { 259 | const statement = statements[i]; 260 | _debug(`Evaluating ${statement.type} in ${body.type}`); 261 | value = this.interpreter.evaluate(statement); 262 | _debug(`${body.type} statement ${statement.type} completed`); 263 | if (this.interpreter.isReturning()) { 264 | break; 265 | } 266 | } 267 | _debug(`completed ${body.type}, returning with: ${value}`); 268 | return value; 269 | } 270 | 271 | Script(body: Script) { 272 | let value; 273 | const _debug = debug.extend(body.type); 274 | 275 | this.interpreter.hoistFunctions(body); 276 | this.interpreter.hoistVars(body); 277 | const statements = body.statements.filter(stmt => stmt.type !== 'FunctionDeclaration'); 278 | 279 | for (let i = 0; i < statements.length; i++) { 280 | const statement = statements[i]; 281 | _debug(`Evaluating ${statement.type} in ${body.type}`); 282 | value = this.interpreter.evaluate(statement); 283 | _debug(`${body.type} statement ${statement.type} completed`); 284 | } 285 | _debug(`completed ${body.type}, returning with: ${value}`); 286 | return value; 287 | } 288 | loopBlock(stmt: ForOfStatement | ForInStatement | ForStatement | WhileStatement | DoWhileStatement) { 289 | const _debug = debug.extend(stmt.type); 290 | let statements = null; 291 | if (stmt.body.type === 'BlockStatement') { 292 | this.interpreter.hoistFunctions(stmt.body.block); 293 | this.interpreter.hoistVars(stmt.body.block); 294 | statements = stmt.body.block.statements.filter(stmt => stmt.type !== 'FunctionDeclaration'); 295 | } else { 296 | statements = [stmt.body]; 297 | } 298 | for (let i = 0; i < statements.length; i++) { 299 | const statement = statements[i]; 300 | _debug(`Evaluating ${statement.type} in ${stmt.type}`); 301 | this.interpreter.evaluate(statement); 302 | _debug(`${stmt.type} statement ${statement.type} completed`); 303 | if (this.interpreter.isBreaking()) { 304 | break; 305 | } 306 | if (this.interpreter.isContinuing()) { 307 | break; 308 | } 309 | } 310 | } 311 | ForOfStatement(stmt: ForOfStatement) { 312 | const iterationExpression = this.interpreter.evaluate(stmt.right); 313 | function* nextValue() { 314 | yield* iterationExpression; 315 | } 316 | let iterator = nextValue(); 317 | let result = null; 318 | 319 | while ((result = iterator.next())) { 320 | if (result.done) break; 321 | const { value } = result; 322 | switch (stmt.left.type) { 323 | case 'VariableDeclaration': { 324 | this.interpreter.declareVariables(stmt.left); 325 | const binding = stmt.left.declarators[0].binding; 326 | if (binding.type === 'BindingIdentifier') this.interpreter.updateVariableValue(binding, value); 327 | else this.interpreter.skipOrThrow(stmt.type + '.left->' + binding.type); 328 | break; 329 | } 330 | default: 331 | this.interpreter.skipOrThrow(stmt.type + '.left->' + stmt.left.type); 332 | } 333 | this.loopBlock(stmt); 334 | if (this.interpreter.isContinuing()) { 335 | this.interpreter.isContinuing(false); 336 | continue; 337 | } 338 | if (this.interpreter.isBreaking()) { 339 | this.interpreter.isBreaking(false); 340 | break; 341 | } 342 | } 343 | } 344 | 345 | ForInStatement(stmt: ForInStatement) { 346 | const iterationExpression = this.interpreter.evaluate(stmt.right); 347 | 348 | switch (stmt.left.type) { 349 | case 'VariableDeclaration': { 350 | this.interpreter.declareVariables(stmt.left); 351 | const binding = stmt.left.declarators[0].binding; 352 | for (let a in iterationExpression) { 353 | if (binding.type === 'BindingIdentifier') this.interpreter.updateVariableValue(binding, a); 354 | else this.interpreter.skipOrThrow(stmt.type + '.left->' + binding.type); 355 | this.loopBlock(stmt); 356 | if (this.interpreter.isContinuing()) { 357 | this.interpreter.isContinuing(false); 358 | continue; 359 | } 360 | if (this.interpreter.isBreaking()) { 361 | this.interpreter.isBreaking(false); 362 | break; 363 | } 364 | } 365 | break; 366 | } 367 | case 'AssignmentTargetIdentifier': { 368 | for (let a in iterationExpression) { 369 | this.interpreter.updateVariableValue(stmt.left, a); 370 | this.loopBlock(stmt); 371 | if (this.interpreter.isContinuing()) { 372 | this.interpreter.isContinuing(false); 373 | continue; 374 | } 375 | if (this.interpreter.isBreaking()) { 376 | this.interpreter.isBreaking(false); 377 | break; 378 | } 379 | } 380 | break; 381 | } 382 | default: 383 | this.interpreter.skipOrThrow(stmt.type + '.left->' + stmt.left.type); 384 | } 385 | } 386 | 387 | ForStatement(stmt: ForStatement) { 388 | if (stmt.init) { 389 | if (stmt.init.type === 'VariableDeclaration') this.interpreter.declareVariables(stmt.init); 390 | else this.interpreter.evaluate(stmt.init); 391 | } 392 | while (this.interpreter.evaluate(stmt.test)) { 393 | this.loopBlock(stmt); 394 | if (this.interpreter.isBreaking()) { 395 | this.interpreter.isBreaking(false); 396 | break; 397 | } 398 | if (stmt.update) this.interpreter.evaluate(stmt.update); 399 | if (this.interpreter.isContinuing()) { 400 | this.interpreter.isContinuing(false); 401 | continue; 402 | } 403 | } 404 | } 405 | 406 | WhileStatement(stmt: WhileStatement) { 407 | while (this.interpreter.evaluate(stmt.test)) { 408 | this.loopBlock(stmt); 409 | if (this.interpreter.isContinuing()) { 410 | this.interpreter.isContinuing(false); 411 | continue; 412 | } 413 | if (this.interpreter.isBreaking()) { 414 | this.interpreter.isBreaking(false); 415 | break; 416 | } 417 | } 418 | } 419 | 420 | DoWhileStatement(stmt: DoWhileStatement) { 421 | do { 422 | this.loopBlock(stmt); 423 | if (this.interpreter.isContinuing()) { 424 | this.interpreter.isContinuing(false); 425 | continue; 426 | } 427 | if (this.interpreter.isBreaking()) { 428 | this.interpreter.isBreaking(false); 429 | break; 430 | } 431 | } while (this.interpreter.evaluate(stmt.test)); 432 | } 433 | 434 | ThisExpression(expr: ThisExpression) { 435 | return this.interpreter.getCurrentContext(); 436 | } 437 | 438 | NewExpression(expr: NewExpression) { 439 | const newTarget = this.interpreter.evaluate(expr.callee); 440 | const args: any[] = []; 441 | for (let arg of expr.arguments) { 442 | if (arg.type === 'SpreadElement') { 443 | const value = this.interpreter.evaluate(arg.expression); 444 | args.push(...value); 445 | } else { 446 | args.push(this.interpreter.evaluate(arg)); 447 | } 448 | } 449 | let result = new newTarget(...args); 450 | 451 | return result; 452 | } 453 | 454 | ArrayExpression(expr: ArrayExpression) { 455 | const elements = []; 456 | for (let el of expr.elements) { 457 | if (el === null) { 458 | elements.push(null); 459 | } else if (el.type === 'SpreadElement') { 460 | const iterable = this.interpreter.evaluate(el.expression); 461 | elements.push(...Array.from(iterable)); 462 | } else { 463 | elements.push(this.interpreter.evaluate(el)); 464 | } 465 | } 466 | return elements; 467 | } 468 | 469 | ObjectExpression(expr: ObjectExpression) { 470 | const _debug = debug.extend('ObjectExpression'); 471 | const obj: { [key: string]: any } = {}; 472 | const batchOperations: Map any>> = new Map(); 473 | function getPropertyDescriptors(name: string) { 474 | if (batchOperations.has(name)) return batchOperations.get(name)!; 475 | const operations = new Map(); 476 | batchOperations.set(name, operations); 477 | return operations; 478 | } 479 | for (let prop of expr.properties) { 480 | switch (prop.type) { 481 | case 'DataProperty': { 482 | const name = 483 | prop.name.type === 'StaticPropertyName' ? prop.name.value : this.interpreter.evaluate(prop.name.expression); 484 | obj[name] = this.interpreter.evaluate(prop.expression); 485 | break; 486 | } 487 | case 'Method': { 488 | const name = 489 | prop.name.type === 'StaticPropertyName' ? prop.name.value : this.interpreter.evaluate(prop.name.expression); 490 | obj[name] = this.interpreter.createFunction(prop); 491 | break; 492 | } 493 | case 'ShorthandProperty': { 494 | const name = prop.name.name; 495 | obj[name] = this.interpreter.getRuntimeValue(prop.name); 496 | break; 497 | } 498 | case 'Getter': { 499 | const name = 500 | prop.name.type === 'StaticPropertyName' ? prop.name.value : this.interpreter.evaluate(prop.name.expression); 501 | const operations = getPropertyDescriptors(name); 502 | operations.set('get', this.interpreter.createFunction(prop)); 503 | break; 504 | } 505 | case 'Setter': { 506 | const name = 507 | prop.name.type === 'StaticPropertyName' ? prop.name.value : this.interpreter.evaluate(prop.name.expression); 508 | const operations = getPropertyDescriptors(name); 509 | operations.set('set', this.interpreter.createFunction(prop)); 510 | break; 511 | } 512 | default: 513 | this.interpreter.skipOrThrow(`${expr.type}[${prop.type}]`); 514 | } 515 | } 516 | 517 | Array.from(batchOperations.entries()).forEach(([prop, ops]) => { 518 | _debug(`setting object property ${prop} (setter:${ops.has('set')}, getter:${ops.has('get')})`); 519 | const descriptor: PropertyDescriptor = { 520 | get: ops.get('get'), 521 | set: ops.get('set'), 522 | configurable: true, 523 | }; 524 | Object.defineProperty(obj, prop, descriptor); 525 | }); 526 | 527 | return obj; 528 | } 529 | 530 | StaticMemberExpression(expr: StaticMemberExpression) { 531 | if (expr.object.type === 'Super') return this.interpreter.skipOrThrow(expr.object.type); 532 | const object = this.interpreter.evaluate(expr.object); 533 | let result = object[expr.property]; 534 | return result; 535 | } 536 | 537 | ComputedMemberExpression(expr: ComputedMemberExpression) { 538 | if (expr.object.type === 'Super') return this.interpreter.skipOrThrow(expr.object.type); 539 | const object = this.interpreter.evaluate(expr.object); 540 | const property = this.interpreter.evaluate(expr.expression); 541 | let result = object[property]; 542 | return result; 543 | } 544 | 545 | CallExpression(expr: CallExpression) { 546 | const _debug = debug.extend('CallExpression'); 547 | if (expr.callee.type === 'Super') return this.interpreter.skipOrThrow(expr.callee.type); 548 | 549 | const args: any[] = []; 550 | for (let arg of expr.arguments) { 551 | if (arg.type === 'SpreadElement') { 552 | const value = this.interpreter.evaluate(arg.expression); 553 | args.push(...value); 554 | } else { 555 | args.push(this.interpreter.evaluate(arg)); 556 | } 557 | } 558 | 559 | let context = this.interpreter.getCurrentContext(); 560 | let fn = null; 561 | if (expr.callee.type === 'StaticMemberExpression') { 562 | context = this.interpreter.evaluate(expr.callee.object); 563 | fn = context[expr.callee.property]; 564 | } else if (expr.callee.type === 'ComputedMemberExpression') { 565 | context = this.interpreter.evaluate(expr.callee.object); 566 | const computedProperty = this.interpreter.evaluate(expr.callee.expression); 567 | fn = context[computedProperty]; 568 | } else { 569 | fn = this.interpreter.evaluate(expr.callee); 570 | } 571 | 572 | if (typeof fn === 'function') { 573 | let returnValue: any; 574 | _debug(`calling function ${fn.name}`); 575 | returnValue = fn.apply(context, args); 576 | _debug(`function completed ${fn.name}`); 577 | return returnValue; 578 | } else { 579 | new TypeError(`${fn} is not a function (${this.interpreter.codegen(expr)})`); 580 | } 581 | } 582 | 583 | AssignmentExpression(expr: AssignmentExpression) { 584 | const _debug = debug.extend('AssignmentExpression'); 585 | switch (expr.binding.type) { 586 | case 'AssignmentTargetIdentifier': 587 | _debug(`assigning ${expr.binding.name} new value`); 588 | return this.interpreter.updateVariableValue(expr.binding, this.interpreter.evaluate(expr.expression)); 589 | case 'ComputedMemberAssignmentTarget': { 590 | const object = this.interpreter.evaluate(expr.binding.object); 591 | const property = this.interpreter.evaluate(expr.binding.expression); 592 | _debug(`evaluating expression ${expr.expression.type} to assign to ${toString(property)}`); 593 | const value = this.interpreter.evaluate(expr.expression); 594 | _debug(`assigning object property "${toString(property)}" new value`); 595 | let result = (object[property] = value); 596 | 597 | return result; 598 | } 599 | case 'StaticMemberAssignmentTarget': { 600 | const object = this.interpreter.evaluate(expr.binding.object); 601 | const property = expr.binding.property; 602 | _debug(`evaluating expression ${expr.expression.type} to assign to ${property}`); 603 | const value = this.interpreter.evaluate(expr.expression); 604 | _debug(`assigning object property "${property}" new value`); 605 | const descriptor = Object.getOwnPropertyDescriptor(object, property); 606 | 607 | let result = null; 608 | result = object[property] = value; 609 | return result; 610 | } 611 | case 'ArrayAssignmentTarget': 612 | case 'ObjectAssignmentTarget': 613 | default: 614 | return this.interpreter.skipOrThrow(expr.binding.type); 615 | } 616 | } 617 | 618 | UpdateExpression(expr: UpdateExpression) { 619 | switch (expr.operand.type) { 620 | case 'AssignmentTargetIdentifier': { 621 | const currentValue = this.interpreter.getRuntimeValue(expr.operand); 622 | const nextValue = expr.operator === '++' ? currentValue + 1 : currentValue - 1; 623 | this.interpreter.updateVariableValue(expr.operand, nextValue); 624 | return expr.isPrefix ? nextValue : currentValue; 625 | } 626 | case 'ComputedMemberAssignmentTarget': { 627 | const object = this.interpreter.evaluate(expr.operand.object); 628 | const property = this.interpreter.evaluate(expr.operand.expression); 629 | const currentValue = object[property]; 630 | const nextValue = expr.operator === '++' ? currentValue + 1 : currentValue - 1; 631 | object[property] = nextValue; 632 | return expr.isPrefix ? nextValue : currentValue; 633 | } 634 | case 'StaticMemberAssignmentTarget': { 635 | const object = this.interpreter.evaluate(expr.operand.object); 636 | const property = expr.operand.property; 637 | const currentValue = object[property]; 638 | const nextValue = expr.operator === '++' ? currentValue + 1 : currentValue - 1; 639 | object[property] = nextValue; 640 | return expr.isPrefix ? nextValue : currentValue; 641 | } 642 | default: 643 | return; 644 | } 645 | } 646 | 647 | CompoundAssignmentExpression(expr: CompoundAssignmentExpression) { 648 | const operation = compoundAssignmentOperatorMap.get(expr.operator); 649 | switch (expr.binding.type) { 650 | case 'AssignmentTargetIdentifier': { 651 | const currentValue = this.interpreter.getRuntimeValue(expr.binding); 652 | const newValue = this.interpreter.evaluate(expr.expression); 653 | return this.interpreter.updateVariableValue(expr.binding, operation(currentValue, newValue)); 654 | } 655 | case 'ComputedMemberAssignmentTarget': { 656 | const object = this.interpreter.evaluate(expr.binding.object); 657 | const property = this.interpreter.evaluate(expr.binding.expression); 658 | const currentValue = object[property]; 659 | const newValue = this.interpreter.evaluate(expr.expression); 660 | const result = (object[property] = operation(currentValue, newValue)); 661 | return result; 662 | } 663 | case 'StaticMemberAssignmentTarget': { 664 | const object = this.interpreter.evaluate(expr.binding.object); 665 | const property = expr.binding.property; 666 | const currentValue = object[property]; 667 | const newValue = this.interpreter.evaluate(expr.expression); 668 | const result = (object[property] = operation(currentValue, newValue)); 669 | return result; 670 | } 671 | default: 672 | return; 673 | } 674 | } 675 | 676 | LiteralRegExpExpression(expr: LiteralRegExpExpression) { 677 | const flags = [ 678 | expr.global ? 'g' : '', 679 | expr.ignoreCase ? 'i' : '', 680 | expr.dotAll ? 's' : '', 681 | expr.multiLine ? 'm' : '', 682 | expr.sticky ? 'y' : '', 683 | expr.unicode ? 'u' : '', 684 | ].filter(_ => !!_); 685 | return new RegExp(expr.pattern, ...flags); 686 | } 687 | 688 | TemplateExpression(expr: TemplateExpression) { 689 | const parts = []; 690 | for (let el of expr.elements) { 691 | if (el.type === 'TemplateElement') { 692 | parts.push(el.rawValue); 693 | } else { 694 | parts.push(this.interpreter.evaluate(el)); 695 | } 696 | } 697 | return parts.join(''); 698 | } 699 | 700 | ArrowExpression(expr: ArrowExpression) { 701 | const interpreter = this.interpreter; 702 | const currentContext = interpreter.getCurrentContext(); 703 | 704 | return function(this: BasicContext) { 705 | const arrowFn = (...args: any) => { 706 | interpreter.pushContext(this); 707 | for (let i = 0; i < expr.params.items.length; i++) { 708 | let param = expr.params.items[i]; 709 | interpreter.bindVariable(param, args[i]); 710 | } 711 | let returnValue = undefined; 712 | if (expr.body.type === 'FunctionBody') { 713 | const blockResult = interpreter.evaluate(expr.body); 714 | returnValue = blockResult; 715 | } else { 716 | returnValue = interpreter.evaluate(expr.body); 717 | } 718 | interpreter.popContext(); 719 | return returnValue; 720 | }; 721 | Object.assign(arrowFn); 722 | return arrowFn; 723 | }.bind(currentContext)(); 724 | } 725 | FunctionExpression(expr: FunctionExpression) { 726 | return this.interpreter.createFunction(expr); 727 | } 728 | IdentifierExpression(expr: IdentifierExpression) { 729 | return this.interpreter.getRuntimeValue(expr); 730 | } 731 | LiteralNumericExpression(expr: LiteralNumericExpression) { 732 | return expr.value; 733 | } 734 | LiteralStringExpression(expr: LiteralStringExpression) { 735 | return expr.value; 736 | } 737 | LiteralBooleanExpression(expr: LiteralBooleanExpression) { 738 | return expr.value; 739 | } 740 | LiteralInfinityExpression(expr?: LiteralInfinityExpression) { 741 | return 1 / 0; 742 | } 743 | LiteralNullExpression(expr?: LiteralNullExpression) { 744 | return null; 745 | } 746 | BinaryExpression(expr: BinaryExpression) { 747 | const operation = binaryOperatorMap.get(expr.operator); 748 | const left = this.interpreter.evaluate(expr.left); 749 | const deferredRight = () => { 750 | return this.interpreter.evaluate(expr.right); 751 | }; 752 | return operation(left, deferredRight); 753 | } 754 | UnaryExpression(expr: UnaryExpression) { 755 | const operation = unaryOperatorMap.get(expr.operator); 756 | if (!operation) return this.interpreter.skipOrThrow(`${expr.type} : ${expr.operator}`); 757 | try { 758 | const operand = this.interpreter.evaluate(expr.operand); 759 | return operation(operand); 760 | } catch (e) { 761 | if (e instanceof ReferenceError && expr.operator === 'typeof' && expr.operand.type === 'IdentifierExpression') { 762 | return 'undefined'; 763 | } 764 | throw e; 765 | } 766 | } 767 | 768 | BreakStatement(...args: any) { 769 | this.interpreter.isBreaking(true); 770 | } 771 | ContinueStatement(...args: any) { 772 | this.interpreter.isContinuing(true); 773 | } 774 | DebuggerStatement(...args: any) { 775 | debugger; 776 | } 777 | 778 | EmptyStatement(...args: any) {} 779 | 780 | // TODO support these nodes 781 | WithStatement(...args: any) { 782 | throw new InterpreterRuntimeError(`Unsupported node ${arguments[0].type}`); 783 | } 784 | SwitchStatementWithDefault(...args: any) { 785 | throw new InterpreterRuntimeError(`Unsupported node ${arguments[0].type}`); 786 | } 787 | SwitchStatement(...args: any) { 788 | throw new InterpreterRuntimeError(`Unsupported node ${arguments[0].type}`); 789 | } 790 | LabeledStatement(...args: any) { 791 | throw new InterpreterRuntimeError(`Unsupported node ${arguments[0].type}`); 792 | } 793 | ForAwaitStatement(...args: any) { 794 | throw new InterpreterRuntimeError(`Unsupported node ${arguments[0].type}`); 795 | } 796 | NewTargetExpression(...args: any) { 797 | throw new InterpreterRuntimeError(`Unsupported node ${arguments[0].type}`); 798 | } 799 | AwaitExpression(...args: any) { 800 | throw new InterpreterRuntimeError(`Unsupported node ${arguments[0].type}`); 801 | } 802 | Super(...args: any) { 803 | throw new InterpreterRuntimeError(`Unsupported node ${arguments[0].type}`); 804 | } 805 | ClassExpression(...args: any) { 806 | throw new InterpreterRuntimeError(`Unsupported node ${arguments[0].type}`); 807 | } 808 | YieldExpression(...args: any) { 809 | throw new InterpreterRuntimeError(`Unsupported node ${arguments[0].type}`); 810 | } 811 | YieldGeneratorExpression(...args: any) { 812 | throw new InterpreterRuntimeError(`Unsupported node ${arguments[0].type}`); 813 | } 814 | } 815 | -------------------------------------------------------------------------------- /src/operators.ts: -------------------------------------------------------------------------------- 1 | export const binaryOperatorMap = new Map([ 2 | ['+', (l: any, r: any) => l + r()], 3 | ['-', (l: any, r: any) => l - r()], 4 | ['/', (l: any, r: any) => l / r()], 5 | ['*', (l: any, r: any) => l * r()], 6 | ['**', (l: any, r: any) => l ** r()], 7 | ['==', (l: any, r: any) => l == r()], 8 | ['!=', (l: any, r: any) => l != r()], 9 | ['===', (l: any, r: any) => l === r()], 10 | ['!==', (l: any, r: any) => l !== r()], 11 | ['<', (l: any, r: any) => l < r()], 12 | ['<=', (l: any, r: any) => l <= r()], 13 | ['>', (l: any, r: any) => l > r()], 14 | ['>=', (l: any, r: any) => l >= r()], 15 | ['in', (l: any, r: any) => l in r()], 16 | ['instanceof', (l: any, r: any) => l instanceof r()], 17 | ['<<', (l: any, r: any) => l << r()], 18 | ['>>', (l: any, r: any) => l >> r()], 19 | ['>>>', (l: any, r: any) => l >>> r()], 20 | ['%', (l: any, r: any) => l % r()], 21 | [',', (l: any, r: any) => r()], 22 | ['||', (l: any, r: any) => l || r()], 23 | ['&&', (l: any, r: any) => l && r()], 24 | ['|', (l: any, r: any) => l | r()], 25 | ['&', (l: any, r: any) => l & r()], 26 | ['^', (l: any, r: any) => l ^ r()], 27 | ]); 28 | 29 | export const unaryOperatorMap = new Map([ 30 | ['+', (oper: any) => +oper], 31 | ['-', (oper: any) => -oper], 32 | ['!', (oper: any) => !oper], 33 | ['~', (oper: any) => ~oper], 34 | ['typeof', (oper: any) => typeof oper], 35 | ['void', (oper: any) => void oper], 36 | // ["delete", (l: any) => l * r], 37 | ]); 38 | 39 | export const compoundAssignmentOperatorMap = new Map([ 40 | ['+=', (l: any, r: any) => l + r], 41 | ['-=', (l: any, r: any) => l - r], 42 | ['/=', (l: any, r: any) => l / r], 43 | ['*=', (l: any, r: any) => l * r], 44 | ['**=', (l: any, r: any) => l ** r], 45 | ['<<=', (l: any, r: any) => l << r], 46 | ['>>=', (l: any, r: any) => l >> r], 47 | ['>>>=', (l: any, r: any) => l >>> r], 48 | ['%=', (l: any, r: any) => l % r], 49 | ['|=', (l: any, r: any) => l | r], 50 | ['&=', (l: any, r: any) => l & r], 51 | ['^=', (l: any, r: any) => l ^ r], 52 | ]); 53 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AssignmentTargetIdentifier, 3 | BindingIdentifier, 4 | Block, 5 | DoWhileStatement, 6 | Expression, 7 | ForInStatement, 8 | ForOfStatement, 9 | ForStatement, 10 | FunctionBody, 11 | FunctionDeclaration, 12 | FunctionExpression, 13 | Getter, 14 | IdentifierExpression, 15 | Method, 16 | Script, 17 | Setter, 18 | Statement, 19 | Super, 20 | VariableDeclarator, 21 | WhileStatement, 22 | } from 'shift-ast'; 23 | 24 | export type Identifier = BindingIdentifier | IdentifierExpression | AssignmentTargetIdentifier; 25 | 26 | export type Loop = ForStatement | WhileStatement | ForOfStatement | ForInStatement | DoWhileStatement; 27 | 28 | export type BlockType = Script | Block | FunctionBody; 29 | 30 | export type FuncType = FunctionDeclaration | FunctionExpression | Method | Getter | Setter; 31 | 32 | export type InstructionNode = Script | Statement | Expression | Super | BlockType | VariableDeclarator; 33 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | import { Node, Statement } from 'shift-ast'; 2 | import { BlockType } from './types'; 3 | import { default as nodeReadline } from 'readline'; 4 | 5 | export function isStatement(node: Node): node is Statement { 6 | return node.type.match(/Statement/) || node.type.match('Declaration') ? true : false; 7 | } 8 | 9 | export function isBlockType(node: Node): node is BlockType { 10 | switch (node.type) { 11 | case 'Script': 12 | case 'FunctionBody': 13 | case 'Block': 14 | return true; 15 | default: 16 | return false; 17 | } 18 | } 19 | 20 | export function createReadlineInterface() { 21 | const readline = nodeReadline.createInterface({ 22 | input: process.stdin, 23 | output: process.stdout, 24 | }); 25 | 26 | return (question: string) => { 27 | return new Promise(resolve => { 28 | readline.question(question, resolve); 29 | }); 30 | }; 31 | } 32 | 33 | export function toString(obj: any): String { 34 | return obj.toString ? obj.toString() : '' + obj; 35 | } 36 | -------------------------------------------------------------------------------- /src/waterfall.ts: -------------------------------------------------------------------------------- 1 | export function waterfallMap(array: Array, iterator: (el: J, i: number) => Promise): Promise> { 2 | const reducer = (accumulator: Promise, next: J, i: number): Promise => { 3 | const a = accumulator.then(result => iterator(next, i).then(newNode => result.concat(newNode))); 4 | return a; 5 | }; 6 | 7 | const waterfall: Promise> = array.reduce(reducer, Promise.resolve([])); 8 | 9 | return waterfall; 10 | } 11 | -------------------------------------------------------------------------------- /test/evaluation/binary-expressions.test.ts: -------------------------------------------------------------------------------- 1 | import { assertResult, compare } from '../util'; 2 | 3 | const operators = [ 4 | '==', 5 | '!=', 6 | '===', 7 | '!==', 8 | '<', 9 | '<=', 10 | '>', 11 | '>=', 12 | '<<', 13 | '>>', 14 | '>>>', 15 | '+', 16 | '-', 17 | '*', 18 | '/', 19 | '%', 20 | '**', 21 | ',', 22 | '&&', 23 | ',', 24 | '^', 25 | '&', 26 | ]; 27 | // const specialOps = ['in' , 'instanceof']; 28 | describe('BinaryExpressions', () => { 29 | it('should evaluate operators the same as the host environment', () => { 30 | const sample = [2, 120, 1981, '2', 'hi', NaN, true, false, 1 / 0]; 31 | const results = operators.flatMap(op => 32 | sample.flatMap(l => sample.map(r => compare(`${JSON.stringify(l)} ${op} ${JSON.stringify(r)}`))), 33 | ); 34 | results.forEach(result => assertResult(result)); 35 | }); 36 | it('should retain shortcircuit behavior', () => { 37 | assertResult(compare(`typeof x == 'string' && nonexistant()`)); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/evaluation/classes.test.ts: -------------------------------------------------------------------------------- 1 | import { assertResult, compare } from '../util'; 2 | 3 | describe('Classes', () => { 4 | it('should declare a class', () => { 5 | assertResult(compare('class A {}; A === A')); 6 | }); 7 | it('should allow static methods to be called', () => { 8 | assertResult(compare('class A {static b(){return 20}}; A.b();')); 9 | }); 10 | it('should allow class to be instantiated methods to be called', () => { 11 | assertResult(compare('class A {b(){return 2222}}; let a = new A(); a.b();')); 12 | }); 13 | it('should allow for inheritance', () => { 14 | assertResult( 15 | compare('class A {a(){return "AA"}}; class B extends A {b(){return "BB"}} let b = new B(); b.a() + b.b();'), 16 | ); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/evaluation/compound-assignments.test.ts: -------------------------------------------------------------------------------- 1 | import { compare, assertResult } from '../util'; 2 | 3 | const operators = ['+=', '-=', '*=', '/=', '%=', '**=', '<<=', '>>=', '>>>=', '^=', '&=']; 4 | // const specialOps = ['in' , 'instanceof']; 5 | describe('CompoundAssignment', () => { 6 | it('should assign to identifiers', () => { 7 | const sample = [2, 120, 1981, '2', 'hi', NaN, true, false, 1 / 0]; 8 | const results = operators.flatMap(op => 9 | sample.flatMap(l => sample.map(r => compare(`let b = ${JSON.stringify(l)}; b ${op} ${JSON.stringify(r)}`))), 10 | ); 11 | results.forEach(result => assertResult(result)); 12 | }); 13 | it('should assign to static members', () => { 14 | const sample = [2, 120, 1981, '2', 'hi', NaN, true, false, 1 / 0]; 15 | const results = operators.flatMap(op => 16 | sample.flatMap(l => sample.map(r => compare(`let b = {a:${JSON.stringify(l)}}; b.a ${op} ${JSON.stringify(r)}`))), 17 | ); 18 | results.forEach(result => assertResult(result)); 19 | }); 20 | it('should assign to computed members', () => { 21 | const sample = [2, 120, 1981, '2', 'hi', NaN, true, false, 1 / 0]; 22 | const results = operators.flatMap(op => 23 | sample.flatMap(l => 24 | sample.map(r => compare(`let b = {["a"]:${JSON.stringify(l)}}; b["a"] ${op} ${JSON.stringify(r)}`)), 25 | ), 26 | ); 27 | results.forEach(result => assertResult(result)); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /test/evaluation/conditional-expression.test.ts: -------------------------------------------------------------------------------- 1 | import { assertResult, compare } from '../util'; 2 | 3 | describe('conditional expressions', () => { 4 | it('should evaluate basic conditional expressions', () => { 5 | assertResult(compare('true ? "a" : "b"')); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /test/evaluation/context.test.ts: -------------------------------------------------------------------------------- 1 | import { assertResult, compare } from '../util'; 2 | import chai, { expect } from 'chai'; 3 | import spies from 'chai-spies'; 4 | 5 | chai.use(spies); 6 | 7 | describe('External context', () => { 8 | it('should call functions defined on the context', () => { 9 | const console = { 10 | log: function(msg: string) { 11 | return undefined; 12 | }, 13 | }; 14 | chai.spy.on(console, 'log'); 15 | 16 | assertResult( 17 | compare( 18 | ` 19 | console.log("Hello world"); 20 | `, 21 | { console }, 22 | ), 23 | ); 24 | 25 | expect(console.log).to.have.been.called.with('Hello world'); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/evaluation/errors.test.ts: -------------------------------------------------------------------------------- 1 | import { assertResult, compare } from '../util'; 2 | 3 | describe('Errors', () => { 4 | it('should throw', () => { 5 | assertResult(compare("throw new Error('hello world')", { Error })); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /test/evaluation/exotic-scenarios.test.ts: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import { parseScript } from 'shift-parser'; 3 | import { Interpreter } from '../../src/interpreter'; 4 | import { assertResult, compare } from '../util'; 5 | 6 | describe('ExoticScenarios', () => { 7 | it('should allow piecemeal execution', () => { 8 | const source = ` 9 | (function() { 10 | var c = { 11 | hsakO: "3|4|2|1|0", 12 | tPkAq: function(d, e) { 13 | return d === e; 14 | }, 15 | YhcXR: "reader", 16 | PFwHf: "world" 17 | }; 18 | class f { 19 | constructor(g) { 20 | this["target"] = g; 21 | } 22 | ["greet"]() { 23 | var h = c["hsakO"]["split"]("|"), 24 | i = 0x0; 25 | while (!![]) { 26 | switch (h[i++]) { 27 | case "0": 28 | console["log"]( 29 | "I\x20have\x20run\x20out\x20of\x20things\x20to\x20say." 30 | ); 31 | continue; 32 | case "1": 33 | console["log"]("I\x20hope\x20you\x20have\x20a\x20good\x20day!"); 34 | continue; 35 | case "2": 36 | if (c["tPkAq"](this["target"], c["YhcXR"])) { 37 | console["log"]("I\x20like\x20your\x20hair!"); 38 | } 39 | continue; 40 | case "3": 41 | console["log"]("Hello\x20" + this["target"]); 42 | continue; 43 | case "4": 44 | if (c["tPkAq"](this["target"], c["PFwHf"])) { 45 | console["log"]("It\x27s\x20beautiful\x20out,\x20isn\x27t\x20it?"); 46 | } 47 | continue; 48 | } 49 | break; 50 | } 51 | } 52 | ["setTarget"](j) { 53 | this["target"] = j; 54 | } 55 | } 56 | const k = new f(c["PFwHf"]); 57 | k["greet"](); 58 | k["setTarget"](c["YhcXR"]); 59 | k["greet"](); 60 | })(); 61 | `; 62 | const tree = parseScript(source); 63 | const interpreter = new Interpreter(); 64 | interpreter.load(tree); 65 | // @ts-ignore 66 | interpreter.evaluate(tree.statements[0].expression.callee.body.statements[0]); 67 | interpreter.evaluate( 68 | // @ts-ignore 69 | tree.statements[0].expression.callee.body.statements[1].elements[1].method.body.statements[0], 70 | ); 71 | const v = interpreter.getRuntimeValue( 72 | // @ts-ignore 73 | tree.statements[0].expression.callee.body.statements[1].elements[1].method.body.statements[0].declaration 74 | .declarators[0].binding, 75 | ); 76 | chai.expect(v).to.deep.equal(['3', '4', '2', '1', '0']); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /test/evaluation/functions.test.ts: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import { AssignmentExpression, FunctionDeclaration, Statement } from 'shift-ast'; 3 | import { parseScript } from 'shift-parser'; 4 | import { Interpreter } from '../../src'; 5 | import { assertResult, compare } from '../util'; 6 | 7 | describe('Functions', () => { 8 | it('should declare functions', () => { 9 | assertResult(compare('function a(){return 2}; a();')); 10 | }); 11 | it('should hoist functions for purposes of member access/assignment', () => { 12 | assertResult(compare("a.foo = 'bar'; function a(){}; a.foo;")); 13 | }); 14 | it('should use global context if called with undefined context', () => { 15 | assertResult(compare(`'();'.replace('()', function(){})`)); 16 | }); 17 | it('declarations in block scope should be defined across all scope Variable references', () => { 18 | // shift-scope defines multiple variables for block scopes, references should be the same all around. 19 | assertResult(compare(`let a = '';{function fn() {return 'decl'}a+=fn()}a+=fn();a;`)); 20 | assertResult(compare(`let inner;{function fn() {}inner=fn}let outer = fn; outer === inner`)); 21 | }); 22 | it('should call methods on primitive literals', () => { 23 | assertResult(compare("'a'.replace('a','b');")); 24 | }); 25 | it('should call methods on primitive return values', () => { 26 | assertResult(compare("fn = () => 'a';fn().replace('a','b');")); 27 | }); 28 | it('should assign arrow expressions', () => { 29 | assertResult(compare('let a = (a) => {return a}; a(4)')); 30 | }); 31 | it('arrow expressions should retain `this` binding', () => { 32 | assertResult(compare('let a = { a: () => {return this.b}, b: 44 }; const b = a.a; b();')); 33 | }); 34 | it('should evaluate shorthand arrow expressions', () => { 35 | assertResult(compare('let a = _ => _ + 10; a(2);')); 36 | }); 37 | it('should call functions with arguments that have defaults', () => { 38 | assertResult(compare('function fn(a = 22){return a + 10}; fn() + fn(33);')); 39 | }); 40 | it('should call functions with arguments', () => { 41 | assertResult(compare('function a(a,b){return a+b}; a(2,5) === 7;')); 42 | }); 43 | it('should allow reference to arguments special variable', () => { 44 | assertResult(compare('function a(b){return arguments[0] + 10}; a(33);')); 45 | }); 46 | it('should support .call()', () => { 47 | assertResult( 48 | compare(` 49 | var context = { 50 | expected: "hello", 51 | }; 52 | function dyno(prop) { 53 | return this[prop]; 54 | } 55 | dyno.call(context, "expected"); 56 | `), 57 | ); 58 | }); 59 | it('should support prototype modifications', () => { 60 | assertResult( 61 | compare(` 62 | function d() {} 63 | d.prototype.run = function () {return "expected"}; 64 | d.prototype.run(); 65 | `), 66 | ); 67 | }); 68 | it('should support .apply()', () => { 69 | assertResult( 70 | compare(` 71 | var context = { 72 | expected: "hello", 73 | }; 74 | function dyno(prop) { 75 | return this[prop]; 76 | } 77 | dyno.apply(context, ["expected"]); 78 | `), 79 | ); 80 | }); 81 | it('should access appropriate context', () => { 82 | assertResult( 83 | compare(` 84 | var c = { 85 | expected: "hello", 86 | test: function(actual) { 87 | return actual === c.expected; 88 | } 89 | }; 90 | c.test("hello") === true; 91 | `), 92 | ); 93 | assertResult( 94 | compare(` 95 | var c = { 96 | expected: "hello", 97 | test: function(actual) { 98 | return actual === c.expected; 99 | } 100 | }; 101 | var b = { 102 | expected: "on b" 103 | }; 104 | b.test = c.test; 105 | b.test('on b') === true; 106 | `), 107 | ); 108 | }); 109 | 110 | it('should hoist functions', () => { 111 | const src = 'a.b = 2; function a(){}'; 112 | const ast = parseScript(src); 113 | const interpreter = new Interpreter(); 114 | interpreter.load(ast); 115 | interpreter.run(); 116 | const fnDecl = ast.statements.find((st: Statement) => st.type === 'FunctionDeclaration') as FunctionDeclaration; 117 | 118 | const fn = () => { 119 | const value = interpreter.getRuntimeValue(fnDecl.name); 120 | chai.expect(value.b).to.equal(2); 121 | }; 122 | chai.expect(fn).to.not.throw(); 123 | }); 124 | 125 | it('should store and execute function expressions', () => { 126 | assertResult(compare('let a = function(){return 2}; a();')); 127 | }); 128 | it('should return from sub statements', () => { 129 | assertResult(compare("function a() { if (true) return 'in branch'; return 'should not get here'}; a();")); 130 | }); 131 | it('should return from sub blocks', () => { 132 | assertResult( 133 | compare(` 134 | function _isSameValue(a, b) { 135 | if (a === b) { 136 | return true; 137 | } 138 | return false; 139 | }; 140 | _isSameValue("1","1"); 141 | `), 142 | ); 143 | }); 144 | }); 145 | 146 | describe('Getters/Setters', () => { 147 | it('should define getters', () => { 148 | assertResult(compare('let a = { get b() {return 2} }; a.b;')); 149 | }); 150 | it('should define setters', () => { 151 | assertResult( 152 | compare( 153 | 'let holder = { set property(argument) {this._secretProp = argument} }; holder.property = 22; false /* dummy expression to catch promise-based setter regression */; holder._secretProp', 154 | ), 155 | ); 156 | }); 157 | it('should register setters properly on host objects', () => { 158 | const tree = parseScript(`holder = { set property(argument) {this._secretProp = argument} };`); 159 | //@ts-ignore 160 | const objectExpression = tree.statements[0].expression as AssignmentExpression; 161 | const interpreter = new Interpreter(); 162 | interpreter.load(tree); 163 | const obj = interpreter.evaluate(objectExpression); 164 | obj.property = 22; 165 | chai.expect(obj._secretProp).to.equal(22); 166 | }); 167 | it('should define both', () => { 168 | const src = ` 169 | let a = { 170 | _b: 0, 171 | set b(c) { 172 | this._b = c + 10; 173 | }, 174 | get b() { 175 | return this._b; 176 | }, 177 | }; 178 | a.b = 22; 179 | a.b; 180 | `; 181 | assertResult(compare(src)); 182 | }); 183 | }); 184 | -------------------------------------------------------------------------------- /test/evaluation/host.test.ts: -------------------------------------------------------------------------------- 1 | import { assertResult, compare } from '../util'; 2 | 3 | describe('host', () => { 4 | describe('ffi', () => { 5 | it('should have access to simple values in the context', () => { 6 | assertResult(compare('let b = a; b;', { a: '' })); 7 | }); 8 | it('should have access to complex values in the context', () => { 9 | class A { 10 | prop: number; 11 | constructor() { 12 | this.prop = 222; 13 | } 14 | } 15 | assertResult(compare('let b = new A(); b.prop;', { A })); 16 | }); 17 | }); 18 | describe('literal regexes', () => { 19 | // it('should declare and init variables and be able to retrieve the value', () => { 20 | // assertResult(compare('let a = 2; a;')); 21 | // }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/evaluation/if-then-else.test.ts: -------------------------------------------------------------------------------- 1 | import { assertResult, compare } from '../util'; 2 | 3 | describe('If Then Else', () => { 4 | it('should evaluate if statements', () => { 5 | assertResult(compare('let a = 2; if (a > 1) { a = 5 } a;')); 6 | }); 7 | it('should evaluate if else statements', () => { 8 | assertResult(compare('let a = 2; if (a > 100) { a = 5 } else a = 2; a;')); 9 | }); 10 | it('should evaluate if else if statements', () => { 11 | assertResult(compare('let a = 2; if (a > 100) a = 5; else if (true) a = "foo"; a;')); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /test/evaluation/literal-expressions.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { Expression, LiteralInfinityExpression } from 'shift-ast'; 3 | import { Interpreter } from '../../src/interpreter'; 4 | import { assertResult, compare } from '../util'; 5 | 6 | function evaluate(expr: Expression) { 7 | const interpreter = new Interpreter(); 8 | return interpreter.evaluate(expr); 9 | } 10 | 11 | describe('Literals', () => { 12 | it('should evaluate LiteralStringExpression', () => { 13 | assertResult(compare('1/* prevent directive */;"hello"')); 14 | }); 15 | it('should evaluate LiteralNumericExpression', () => { 16 | assertResult(compare('20')); 17 | }); 18 | it('should evaluate LiteralBooleanExpression', () => { 19 | assertResult(compare('true')); 20 | assertResult(compare('false')); 21 | }); 22 | it('should evaluate LiteralInfinityExpression', () => { 23 | expect(evaluate(new LiteralInfinityExpression())).to.equal(1 / 0); 24 | }); 25 | it('should evaluate LiteralNullExpression', () => { 26 | assertResult(compare('null')); 27 | }); 28 | describe('TemplateStrings', () => { 29 | it('should evaluate basic templates', () => { 30 | assertResult(compare('`hello world`')); 31 | }); 32 | it('should evaluate template strings with embedded expressions', () => { 33 | assertResult(compare('`hello ${"world"}`')); 34 | }); 35 | xit('should evaluate tagged template strings', () => { 36 | // should it though? Deferring support until I run across them in a script I care about. 37 | }); 38 | }); 39 | describe('regexes', () => { 40 | it('should be able to pass LiteralRegExpExpressions to string methods', () => { 41 | assertResult(compare(`"abcd".match(/abcd/)[0] === 'abcd';`)); 42 | }); 43 | it('LiteralRegExpExpressions should be executable as expected', () => { 44 | assertResult(compare(`/ab/.exec("abcd")`)); 45 | }); 46 | it('should be able to store LiteralRegExpExpressions', () => { 47 | assertResult(compare(`var a = /ab/; a.exec("abcd")`)); 48 | }); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /test/evaluation/loops.test.ts: -------------------------------------------------------------------------------- 1 | import { assertResult, compare } from '../util'; 2 | 3 | describe('Loops', () => { 4 | describe('For Loops', () => { 5 | it('should evaluate basic for loops', () => { 6 | assertResult(compare('let b = 0; for (let a = 1; a <= 2; a++) {b = b + a} b;')); 7 | }); 8 | it('should support break statements', () => { 9 | assertResult(compare('let b = 0; for (let a = 1; a <= 2; a++) {b = b + a; break} b;')); 10 | }); 11 | it('should support nested break statements', () => { 12 | assertResult( 13 | compare('let b = 0; for (let a = 1; a <= 2; a++) {for (let i = 1; i < 10; i++) {break; b++;}; b = b + a;} b;'), 14 | ); 15 | }); 16 | it('should support continue statements', () => { 17 | assertResult(compare('let b = 0; for (let a = 1; a <= 2; a++) {b = b + a; continue; b += 2} b;')); 18 | }); 19 | it('should support nested continue statements', () => { 20 | assertResult( 21 | compare( 22 | 'let b = 0; for (let a = 1; a <= 2; a++) {for (let i = 1; i < 10; i++) {b++; continue; b++;}; b = b + a;} b;', 23 | ), 24 | ); 25 | }); 26 | }); 27 | it('should support for...in statements', () => { 28 | assertResult(compare(`let a = {a:1,b:2},c = 0; for (let b in a) { c+=a[b]; } c;`)); 29 | assertResult(compare(`let a = {a:1,b:2},c = 0,b; for (b in a) { c+=a[b]; } c;`)); 30 | }); 31 | it('should support for...of statements', () => { 32 | assertResult(compare(`let a = [1,2],c = 0; for (let b of a) { c+=b; } c;`)); 33 | }); 34 | it('should support while statements', () => { 35 | assertResult(compare('let a = 0; while(a < 10) {a++} a;')); 36 | }); 37 | it('should support do-while statements', () => { 38 | assertResult(compare('let a = 0; do{a++}while(a < 10) a;')); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /test/evaluation/new-expression.test.ts: -------------------------------------------------------------------------------- 1 | import { describe } from 'mocha'; 2 | import { assertResult, compare } from '../util'; 3 | 4 | describe('new', () => { 5 | it('should instantiate functions and retain prototype chain', () => { 6 | assertResult( 7 | compare(` 8 | function d() {} 9 | d.prototype.run = function () {return "expected"}; 10 | d.prototype.run(); 11 | const a = new d(); 12 | a.run(); 13 | `), 14 | ); 15 | }); 16 | 17 | it('should return the return value for an instantiated function if the fn returns', () => { 18 | assertResult( 19 | compare(` 20 | function d() { return { run() {return 'this one'}}; } 21 | d.prototype.run = function () {return "not this one"}; 22 | d.prototype.run(); 23 | const a = new d(); 24 | a.run(); 25 | `), 26 | ); 27 | }); 28 | it('should still return the "this" if a primitive is returned', () => { 29 | assertResult( 30 | compare(` 31 | function d() { return 2; } 32 | d.prototype.run = function () {return "expected"}; 33 | d.prototype.run(); 34 | const a = new d(); 35 | a.run(); 36 | `), 37 | ); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/evaluation/objects-and-arrays.test.ts: -------------------------------------------------------------------------------- 1 | import { assertResult, compare } from '../util'; 2 | 3 | describe('Objects', () => { 4 | it('should not interfere with external promises', () => { 5 | //@ts-ignore 6 | const outer = (global.outer = new Promise(res => { 7 | res(22); 8 | })); 9 | assertResult(compare('let inner = this.outer; inner', { outer })); 10 | //@ts-ignore 11 | delete global.outer; 12 | }); 13 | it('should evaluate object expressions and allow static member access', () => { 14 | assertResult(compare('let a = {b:2}; a.b;')); 15 | }); 16 | it('should evaluate object expressions and allow computed member access', () => { 17 | assertResult(compare('let a = {b:2}; a["b"];')); 18 | }); 19 | it('should evaluate complex objects', () => { 20 | assertResult(compare('let a = {b:2,c:{ca:"hello"}}; a.c.ca;')); 21 | }); 22 | it('should evaluate methods on objects', () => { 23 | assertResult(compare('let a = {b(a){return a}}; a.b(2);')); 24 | }); 25 | it('should evaluate shorthand props', () => { 26 | assertResult(compare('let b = 2; let a = {b}; a.b;')); 27 | }); 28 | // ES2018, not implemented in shift parser yet 29 | xit('should evaluate objects with rest/spread element', () => { 30 | assertResult(compare('let b = {a:1,b:2,c:3}; let a = {...b}; a.a+a.b+a.c === b.a+b.b+b.c;')); 31 | }); 32 | it('should allow for object member assignment', () => { 33 | assertResult(compare('let a = {}; a.b = 2;')); 34 | assertResult(compare('let a = {}; a["b"] = 2;')); 35 | }); 36 | }); 37 | describe('Arrays', () => { 38 | it('should parse array expressions', () => { 39 | assertResult(compare('let a = [1,2,3,4]; a[3];')); 40 | }); 41 | it('should parse array expressions with spread', () => { 42 | assertResult(compare('let a = [1,2,3,4], b = [...a];b')); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/evaluation/this.test.ts: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import spies from 'chai-spies'; 3 | import { assertResult, compare } from '../util'; 4 | 5 | chai.use(spies); 6 | 7 | describe('this', () => { 8 | it('should refer to local context', () => { 9 | const context = { global: {}, console }; 10 | context.global = context; 11 | 12 | assertResult( 13 | compare(` 14 | const c = { 15 | a : function() {return this.b}, 16 | b : "Hello" 17 | } 18 | c.a(); 19 | `), 20 | ); 21 | }); 22 | it('should support dynamic context', () => { 23 | const context = { global: {}, console }; 24 | context.global = context; 25 | 26 | assertResult( 27 | compare(` 28 | const outer = { 29 | inner: { 30 | innerFn() { 31 | return this.b 32 | }, 33 | b: "innerValue" 34 | }, 35 | outerFn() { 36 | this.extracted = this.inner.innerFn; 37 | return this.extracted(); 38 | }, 39 | b : "outerValue" 40 | } 41 | outer.outerFn(); 42 | `), 43 | ); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/evaluation/try.statements.test.ts: -------------------------------------------------------------------------------- 1 | import { compare, assertResult } from '../util'; 2 | import assert from 'assert'; 3 | 4 | describe('Try/Catch', () => { 5 | it('should catch errors', () => { 6 | assertResult(compare(`let msg = ''; try{ throw new Error('err') } catch(e) {msg = e.message} msg`, { Error })); 7 | }); 8 | it('should allow rethrowing', () => { 9 | assertResult(compare(`try{ throw new Error('err') } catch(e) {throw e}`, { Error })); 10 | }); 11 | it('should return from try', () => { 12 | assertResult( 13 | compare( 14 | ` 15 | function returns() { 16 | try { 17 | return 11; 18 | } catch (thrown) { 19 | return 'not this 1'; 20 | } 21 | return 'not this 2'; 22 | }; 23 | returns(); 24 | `, 25 | ), 26 | ); 27 | }); 28 | it('should be able to return errors from catch', () => { 29 | const src = ` 30 | (function() { 31 | try { 32 | null[0]; 33 | } catch (t) { 34 | return t; 35 | } 36 | })();`; 37 | const result = compare(src); 38 | assert.equal(result.interpreter.errorLocation, undefined); 39 | assertResult(result); 40 | }); 41 | it('should return from catch', () => { 42 | assertResult( 43 | compare( 44 | ` 45 | let assert = {}; 46 | function assertThrows(expectedErrorConstructor, func) { 47 | try { 48 | func(); 49 | } catch (thrown) { 50 | if (thrown.constructor !== expectedErrorConstructor) { 51 | return 'Expected a ' + expectedErrorConstructor.name + ' but got a ' + thrown.constructor.name; 52 | } 53 | return 'OK'; 54 | } 55 | return 'Expected a ' + expectedErrorConstructor.name + ' to be thrown but no exception was thrown at all'; 56 | }; 57 | 58 | assertThrows(Error, () => { throw new Error() }) 59 | `, 60 | { Error, console }, 61 | ), 62 | ); 63 | }); 64 | }); 65 | describe('Try/Finally', () => { 66 | it('should catch errors', () => { 67 | assertResult( 68 | compare(`let msg = ''; try{ throw new Error('err') } catch(e) {msg = e.message} finally {msg+='finally'} msg`, { 69 | Error, 70 | }), 71 | ); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /test/evaluation/unary-expressions.test.ts: -------------------------------------------------------------------------------- 1 | import { compare, assertResult } from '../util'; 2 | 3 | const operators = [ 4 | '+', 5 | '-', 6 | '!', 7 | '~', 8 | 'typeof', 9 | 'void', //, 'delete' 10 | ]; 11 | // const specialOps = ['in' , 'instanceof']; 12 | describe('UnaryExpressions', () => { 13 | it('should evaluate operators the same as the host environment', () => { 14 | const operands = [2, 120, 1981, '2', 'hi', NaN, true, false, {}, 1 / 0]; 15 | const results = operators.flatMap(op => operands.map(oper => compare(`${op} ${JSON.stringify(oper)}`))); 16 | results.forEach(result => assertResult(result)); 17 | }); 18 | it('should evaluate typeof on an undeclared variable', () => { 19 | assertResult(compare(`typeof foo`)); 20 | }); 21 | it('should propagate thrown errors', () => { 22 | assertResult(compare(`try {!(() => n++)();'bad';} catch (e) {'good';}`)); 23 | }); 24 | it('should not swallow continues/breaks', () => { 25 | assertResult(compare(`do{if(!null)continue; nonexistant();}while(i = 0);i`)); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /test/evaluation/update-expressions.test.ts: -------------------------------------------------------------------------------- 1 | import { compare, assertResult } from '../util'; 2 | 3 | describe('UpdateExpression', () => { 4 | it('should evaluate operators the same as the host environment', () => { 5 | assertResult(compare(`let a = 0; let b = a++; b`)); 6 | assertResult(compare(`let a = 0; let b = ++a; b`)); 7 | assertResult(compare(`let a = 0; let b = a--; b`)); 8 | assertResult(compare(`let a = 0; let b = --a; b`)); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /test/evaluation/variables.test.ts: -------------------------------------------------------------------------------- 1 | import { assertResult, compare } from '../util'; 2 | 3 | describe('Variables', () => { 4 | it('should declare and init variables and be able to retrieve the value', () => { 5 | assertResult(compare('let a = 2; a;')); 6 | }); 7 | it('should update values', () => { 8 | assertResult(compare('let a = 2; a = 3; a;')); 9 | }); 10 | it('should update and retrieve in one statement', () => { 11 | assertResult(compare('let a = 2; a = a + 2; a;')); 12 | }); 13 | 14 | it('decltype-less assignments should assign a global', () => { 15 | const context = {}; 16 | assertResult(compare('function a() { b = 2; } a(); b', context)); 17 | // @ts-ignore 18 | delete global.b; 19 | }); 20 | 21 | it('var statements should be hoisted', () => { 22 | assertResult(compare('function a() { b = 2; var b; } a(); b')); 23 | }); 24 | 25 | it('should assign to hoisted variables', () => { 26 | assertResult(compare('function a() { return b; b = 3; var b = 2; } a()')); 27 | assertResult(compare('function a() { b = 3; return b; var b = 2; } a()')); 28 | assertResult(compare('function a() { b = 3; var b = 2; return b; } a()')); 29 | }); 30 | 31 | it('should support const', () => { 32 | assertResult(compare('const a = 2; a;')); 33 | }); 34 | it('should not allow reassignment to constants', () => { 35 | assertResult(compare('const a = 3; try { a = 4 } catch(e) {} a; ')); 36 | }); 37 | it('should allow array pattern assignments', () => { 38 | assertResult(compare('let [a] = [2]; a;')); 39 | }); 40 | it('should allow array pattern assignments with defaults', () => { 41 | assertResult(compare('let [a = 22] = []; a === 22;')); 42 | }); 43 | it('should allow object pattern assignments', () => { 44 | assertResult(compare('let {a} = {a:22}; a;')); 45 | }); 46 | it('should allow object pattern assignments with defaults', () => { 47 | assertResult(compare('let {a = 33} = {}; a;')); 48 | }); 49 | it('should allow object pattern assignments with binding property', () => { 50 | assertResult(compare('let {a : b} = {a:22}; b;')); 51 | }); 52 | it('should allow object pattern assignments with computed property names', () => { 53 | assertResult(compare('let {["a"] : b} = {a:22}; b;')); 54 | }); 55 | it('should allow nested pattern assignments with computed property names', () => { 56 | assertResult(compare('let {["a"] : [b]} = {a:[22]}; b === 22;')); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /test/interpreter.test.ts: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import { parseScript } from 'shift-parser'; 3 | import { Interpreter } from '../src'; 4 | 5 | describe('interpreter', () => { 6 | it('should lookup variable value', () => { 7 | const src = 'const a = 2, b = 4;'; 8 | const ast = parseScript(src); 9 | const interpreter = new Interpreter(); 10 | interpreter.run(ast); 11 | //@ts-ignore 12 | const value = interpreter.getRuntimeValue(ast.statements[0].declaration.declarators[0].binding); 13 | chai.expect(value).to.equal(2); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /test/nostrict-eval.js: -------------------------------------------------------------------------------- 1 | exports.eval = function(src, context) { 2 | let result; 3 | if (context) { 4 | with (context) { 5 | result = eval(src); 6 | } 7 | } else result = eval(src); 8 | return result; 9 | }; 10 | -------------------------------------------------------------------------------- /test/script.test.ts: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import { parseScript } from 'shift-parser'; 3 | import { Interpreter } from '../src'; 4 | import { FunctionDeclaration, BindingIdentifier } from 'shift-ast'; 5 | 6 | describe('Script', () => { 7 | it('should retain access to variables after script execution', () => { 8 | const src = '(function(){ const b = 22; }())'; 9 | const ast = parseScript(src); 10 | const interpreter = new Interpreter(); 11 | interpreter.load(ast); 12 | interpreter.run(); 13 | // @ts-ignore 14 | const id = ast.statements[0].expression.callee.body.statements[0].declaration.declarators[0] 15 | .binding as BindingIdentifier; 16 | const value = interpreter.getRuntimeValue(id); 17 | 18 | chai.expect(value).to.equal(22); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /test/util.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { parseScript } from 'shift-parser'; 3 | import { Interpreter } from '../src/interpreter'; 4 | import { BasicContext } from '../src/context'; 5 | import deepEqual from 'deep-equal'; 6 | 7 | import DEBUG from 'debug'; 8 | 9 | const debug = DEBUG('shift:interpreter:test'); 10 | 11 | const evaluate = require('./nostrict-eval.js').eval; 12 | 13 | export interface Result { 14 | actual: any; 15 | actualError: Error; 16 | expected: any; 17 | expectedError: Error; 18 | src: string; 19 | success: boolean; 20 | interpreter: Interpreter; 21 | } 22 | 23 | export function assertResult(result: Result) { 24 | const message = result.expectedError 25 | ? `${result.src}: Actual "${result.actualError.message}", Expected "${result.expectedError.message}"` 26 | : `${result.src}: Actual ${JSON.stringify(result.actual)}, Expected ${JSON.stringify(result.expected)}`; 27 | expect(result.success).to.equal(true, message); 28 | } 29 | 30 | export function assertError(src: string, error: string) { 31 | debug(`assertError(\`${src}\`)`); 32 | const interpreter = new Interpreter(); 33 | 34 | let expected = 'No error', 35 | actual = 'No error'; 36 | try { 37 | evaluate(src); 38 | } catch (e) { 39 | expected = e.message; 40 | } 41 | try { 42 | interpreter.run(parseScript(src)); 43 | } catch (e) { 44 | actual = e.message; 45 | } 46 | 47 | if (actual) debug(`Interpreter error: ${actual}`); 48 | if (expected) debug(`Native error: ${expected}`); 49 | 50 | expect(actual).to.equal(expected); 51 | } 52 | 53 | function funcify(fn: Function) { 54 | const src = fn.toString(); 55 | return `(${src})()`; 56 | } 57 | 58 | export function compare(src: string | Function, context?: BasicContext): Result { 59 | const interpreter = new Interpreter(); 60 | let nativeExpectedValue, nativeExpectedError; 61 | debug(`compare(\`${src}\`)`); 62 | try { 63 | nativeExpectedValue = evaluate(src, context); 64 | } catch (e) { 65 | nativeExpectedError = e; 66 | } 67 | let interpreterActualValue, interpreterActualError; 68 | let finalSrc = typeof src === 'string' ? src : funcify(src); 69 | try { 70 | interpreter.load(parseScript(finalSrc), context); 71 | interpreterActualValue = interpreter.run(); 72 | } catch (e) { 73 | interpreterActualError = e; 74 | } 75 | debug(`== Interpreter result: ${interpreterActualValue}`); 76 | debug(`== Native result : ${nativeExpectedValue}`); 77 | if (interpreterActualError) debug(`!! Interpreter error: ${interpreterActualError.message}`); 78 | else debug(`!! Interpreter error: `); 79 | if (nativeExpectedError) debug(`!! Native error : ${nativeExpectedError.message}`); 80 | else debug(`!! Native error : `); 81 | let success = false; 82 | if (Number.isNaN(nativeExpectedValue)) { 83 | success = Number.isNaN(interpreterActualValue); 84 | debug(`Interpreter produced NaN, Native produced ${interpreterActualValue}`); 85 | } else if (nativeExpectedError) { 86 | if (!interpreterActualError) { 87 | debug(`Failure: Native produced error, Interpreter did not`); 88 | interpreterActualError = { message: '<>' }; 89 | success = false; 90 | } else { 91 | success = interpreterActualError.message === nativeExpectedError.message; 92 | debug(`Both produced errors (same===${success})`); 93 | } 94 | } else { 95 | if (interpreterActualError) { 96 | debug(`Failure: Interpreter produced error, Native did not`); 97 | console.log(interpreterActualError); 98 | success = false; 99 | } else { 100 | success = deepEqual(nativeExpectedValue, interpreterActualValue); 101 | } 102 | } 103 | 104 | return { 105 | actual: interpreterActualValue, 106 | actualError: interpreterActualError, 107 | expected: nativeExpectedValue, 108 | expectedError: nativeExpectedError, 109 | src: finalSrc, 110 | success, 111 | interpreter, 112 | }; 113 | } 114 | -------------------------------------------------------------------------------- /test/waterfall.test.ts: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import { waterfallMap } from '../src/waterfall'; 3 | 4 | describe('waterfall', () => { 5 | it('should run a series of promises in order', async function() { 6 | const array = ['something', 1, { other: 'this' }]; 7 | 8 | const arrayIndex: any[] = []; 9 | 10 | function promiseGenerator(el: any, i: number) { 11 | return new Promise((res, rej) => { 12 | setTimeout(() => res(el), Math.random() * 200); 13 | }); 14 | } 15 | function promiseGeneratorIndex(el: any, i: number) { 16 | return new Promise((res, rej) => { 17 | setTimeout(() => { 18 | arrayIndex[i] = el; 19 | res(); 20 | }, Math.random() * 200); 21 | }); 22 | } 23 | const newArray = await waterfallMap(array, promiseGenerator); 24 | chai.expect(newArray).to.deep.equal(array); 25 | await waterfallMap(array, promiseGeneratorIndex); 26 | chai.expect(arrayIndex).to.deep.equal(array); 27 | chai.expect(arrayIndex).to.deep.equal(newArray); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src/**/*", "test/**/*", "types/**/*"], 3 | "exclude": ["node_modules"], 4 | "compilerOptions": { 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ 8 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ 9 | "lib": [ 10 | "es2020" 11 | ], 12 | // "allowJs": true, /* Allow javascript files to be compiled. */ 13 | // "checkJs": true, /* Report errors in .js files. */ 14 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 15 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 16 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 17 | "sourceMap": true, /* Generates corresponding '.map' file. */ 18 | // "outFile": "./", /* Concatenate and emit output to single file. */ 19 | "outDir": "./dist", /* Redirect output structure to the directory. */ 20 | "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 21 | // "composite": true, /* Enable project compilation */ 22 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 23 | // "removeComments": true, /* Do not emit comments to output. */ 24 | // "noEmit": true, /* Do not emit outputs. */ 25 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 26 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 27 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 28 | 29 | /* Strict Type-Checking Options */ 30 | "strict": true, /* Enable all strict type-checking options. */ 31 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 32 | // "strictNullChecks": true, /* Enable strict null checks. */ 33 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 34 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 35 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 36 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 37 | // "alwaysStrict": false, /* Parse in strict mode and emit "use strict" for each source file. */ 38 | // "noImplicitUseStrict":true, 39 | 40 | /* Additional Checks */ 41 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 42 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 43 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 44 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 45 | 46 | /* Module Resolution Options */ 47 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 48 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 49 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 50 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 51 | // "typeRoots": [], /* List of folders to include type definitions from. */ 52 | "typeRoots": [ "./types", "./node_modules/@types"], 53 | // "types": ["mocha", "chai", "chai-spies", "node", "types/shift-parser/index.d.ts"], /* Type declaration files to be included in compilation. */ 54 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 55 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 56 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 57 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 58 | 59 | /* Source Map Options */ 60 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 61 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 62 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 63 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 64 | 65 | /* Experimental Options */ 66 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 67 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 68 | 69 | /* Advanced Options */ 70 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /types/shift-codegen/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "@jsoverson/shift-codegen"; -------------------------------------------------------------------------------- /types/shift-parser/index.d.ts: -------------------------------------------------------------------------------- 1 | 2 | declare module 'shift-parser' { 3 | import { Script, Module } from 'shift-ast'; 4 | export function parseScript(src: string): Script; 5 | export function parseModule(src: string): Module; 6 | } 7 | 8 | -------------------------------------------------------------------------------- /types/shift-scope/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'shift-scope' { 2 | export class ScopeLookup { 3 | scope: GlobalScope; 4 | variableMap: import('multimap'); 5 | constructor(globalScope: GlobalScope); 6 | 7 | lookup(node: import('shift-ast').Node): Variable; 8 | 9 | isGlobal(node: Scope): node is GlobalScope; 10 | } 11 | 12 | export class DeclarationType { 13 | name: string; 14 | isBlockScoped: boolean; 15 | isFunctionScoped: boolean; 16 | 17 | constructor(name: string, isBlockScoped: boolean); 18 | static VAR: FunctionScopedDeclaration; 19 | static CONST: BlockScopedDeclaration; 20 | static LET: BlockScopedDeclaration; 21 | static FUNCTION_DECLARATION: BlockScopedDeclaration; 22 | static FUNCTION_VAR_DECLARATION: FunctionScopedDeclaration; 23 | static FUNCTION_NAME: BlockScopedDeclaration; 24 | static CLASS_DECLARATION: BlockScopedDeclaration; 25 | static CLASS_NAME: BlockScopedDeclaration; 26 | static PARAMETER: FunctionScopedDeclaration; 27 | static CATCH_PARAMETER: BlockScopedDeclaration; 28 | static IMPORT: BlockScopedDeclaration; 29 | static fromVarDeclKind: (variableDeclarationKind: string) => FunctionScopedDeclaration | BlockScopedDeclaration; 30 | } 31 | 32 | export class BlockScopedDeclaration extends DeclarationType { 33 | constructor(name: string); 34 | } 35 | 36 | export class FunctionScopedDeclaration extends DeclarationType { 37 | constructor(name: string); 38 | } 39 | 40 | export class Declaration { 41 | node: import('shift-ast').BindingIdentifier | import('shift-ast').AssignmentTargetIdentifier; 42 | type: DeclarationType; 43 | 44 | constructor(node: import('shift-ast').Node, type: DeclarationType); 45 | } 46 | 47 | export default function analyze(script: import('shift-ast').Node): Scope; 48 | 49 | export class Accessibility { 50 | isRead: boolean; 51 | isWrite: boolean; 52 | isDelete: boolean; 53 | isReadWrite: boolean; 54 | 55 | constructor(isRead: boolean, isWrite: boolean, isDelete: boolean); 56 | 57 | static READ: Accessibility; 58 | static WRITE: Accessibility; 59 | static READWRITE: Accessibility; 60 | static DELETE: Accessibility; 61 | } 62 | 63 | export class Reference { 64 | node: 65 | | import('shift-ast').IdentifierExpression 66 | | import('shift-ast').AssignmentTargetIdentifier 67 | | import('shift-ast').BindingIdentifier; 68 | accessibility: Accessibility; 69 | constructor(node: import('shift-ast').Node, accessibility: Accessibility); 70 | } 71 | 72 | export class ScopeType { 73 | name: ScopeDefinition; 74 | constructor(name: ScopeDefinition); 75 | static GLOBAL: ScopeType; 76 | static MODULE: ScopeType; 77 | static SCRIPT: ScopeType; 78 | static ARROW_FUNCTION: ScopeType; 79 | static FUNCTION: ScopeType; 80 | static FUNCTION_NAME: ScopeType; 81 | static CLASS_NAME: ScopeType; 82 | static PARAMETERS: ScopeType; 83 | static PARAMETER_EXPRESSION: ScopeType; 84 | static WITH: ScopeType; 85 | static CATCH: ScopeType; 86 | static BLOCK: ScopeType; 87 | } 88 | 89 | export enum ScopeDefinition { 90 | GLOBAL = 'Global', 91 | MODULE = 'Module', 92 | SCRIPT = 'Script', 93 | ARROW_FUNCTION = 'ArrowFunction', 94 | FUNCTION = 'Function', 95 | FUNCTION_NAME = 'FunctionName', 96 | CLASS_NAME = 'ClassName', 97 | PARAMETERS = 'Parameters', 98 | PARAMETER_EXPRESSION = 'ParameterExpression', 99 | WITH = 'With', 100 | CATCH = 'Catch', 101 | BLOCK = 'Block', 102 | } 103 | 104 | export class Scope { 105 | children: Scope[]; 106 | through: import('multimap'); 107 | type: ScopeType; 108 | astNode: import('shift-ast').Node; 109 | variables: Map; 110 | variableList: Variable[]; 111 | dynamic: boolean; 112 | constructor(scope: Scope); 113 | } 114 | export class GlobalScope extends Scope {} 115 | export class Variable { 116 | name: string; 117 | references: Reference[]; 118 | declarations: Declaration[]; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /types/shift-traverser/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "shift-traverser"; -------------------------------------------------------------------------------- /types/shift-validator/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "shift-validator"; --------------------------------------------------------------------------------