├── .gitattributes ├── .gitignore ├── .npmrc ├── .nvmrc ├── .prettierrc ├── LICENSE ├── README-en_US.md ├── README.md ├── babel.config.esm.js ├── babel.config.js ├── docs ├── common.css ├── examples.html ├── examples │ ├── angularjs.html │ ├── angularjs.js │ ├── echarts.html │ ├── echarts.js │ ├── jquery.html │ ├── jquery.js │ ├── knockout.html │ ├── knockout.js │ ├── react-demo-code.js │ ├── react.html │ ├── react.js │ ├── sandbox.html │ ├── sandbox.js │ ├── self-interpreter-demo-code.js │ ├── self-interpreter.html │ ├── self-interpreter.js │ ├── timeout.html │ ├── timeout.js │ ├── vue-demo-code.js │ ├── vue.html │ └── vue.js ├── github.png ├── index.html ├── index.js └── umd │ ├── eval5.js │ └── eval5.min.js ├── jest.config.js ├── package.json ├── rollup.config.min.mjs ├── rollup.config.mjs ├── scripts ├── banner.js └── version.js ├── src ├── Function.ts ├── evaluate.ts ├── index.ts ├── interpreter │ ├── main.ts │ ├── messages.ts │ └── nodes.ts ├── types.ts ├── umd.ts └── vm.ts ├── test ├── array │ └── ArrayExpression.test.ts ├── binary-expression │ └── BinaryExpression.test.ts ├── conditional │ └── ConditionalExpression.test.ts ├── do-while │ ├── DoWhileStatement.test.ts │ └── label.test.ts ├── ecmaVersion │ └── ecmaVersion.test.ts ├── evaluate │ └── evaluate.test.ts ├── for-in │ ├── ForInStatement.test.ts │ └── label.test.ts ├── for │ ├── ForStatement.test.ts │ └── label.test.ts ├── function │ └── FunctionExpression.test.ts ├── if-else │ └── IfStatement.test.ts ├── literal │ └── Literal.test.ts ├── logic-operator │ └── LogicalExpression.test.ts ├── new │ └── NewExpression.test.ts ├── object │ └── ObjectExpression.test.ts ├── regular-expression │ └── RegExpLiteral.test.ts ├── root-context │ └── RootContext.test.ts ├── sequence │ └── SequenceExpression.test.ts ├── super-global │ └── SuperGlobal.test.ts ├── switch │ └── SwitchStatement.test.ts ├── this │ └── ThisExpression.test.ts ├── timeout │ └── timeout.test.ts ├── try-catch │ └── TryStatement.test.ts ├── unary-expression │ └── UnaryExpression.test.ts ├── var │ └── VariableDeclaration.test.ts ├── while │ ├── WhileStatement.test.ts │ └── label.test.ts └── with │ └── withStatement.test.ts ├── tsconfig.json ├── umd ├── eval5.js └── eval5.min.js └── yarn.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=TypeScript -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | /node_modules 3 | /lib 4 | /esm 5 | /dest 6 | /dist 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry="https://registry.npmjs.org" -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v18 -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "bracketSpacing": true, 4 | "endOfLine": "auto", 5 | "htmlWhitespaceSensitivity": "css", 6 | "insertPragma": false, 7 | "jsxBracketSameLine": true, 8 | "jsxSingleQuote": false, 9 | "printWidth": 100, 10 | "proseWrap": "preserve", 11 | "quoteProps": "as-needed", 12 | "requirePragma": false, 13 | "semi": true, 14 | "singleQuote": false, 15 | "tabWidth": 2, 16 | "trailingComma": "es5", 17 | "useTabs": true 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2020 eval5 nobo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README-en_US.md: -------------------------------------------------------------------------------- 1 | # eval5 2 | 3 | [中文](./README.md) | English 4 | 5 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/bplok20010/eval5/blob/master/LICENSE) 6 | [![npm](https://img.shields.io/npm/v/eval5)](https://www.npmjs.com/package/eval5) 7 | [![npm bundle size](https://img.shields.io/bundlephobia/min/eval5)](https://raw.githubusercontent.com/bplok20010/eval5/master/umd/eval5.min.js) 8 | 9 | A JavaScript interpreter written in TypeScript, Supports full ES5 syntax. 10 | 11 | > Support JavaScript running environment such as browser, node.js, WeChat Mini Program, etc 12 | 13 | [Try it out](https://bplok20010.github.io/eval5/) 14 | 15 | [Examples](https://bplok20010.github.io/eval5/examples.html) 16 | 17 | ## You may not need it unless 18 | 19 | - Need to execute code in the browser with a sandbox environment 20 | - Controlling execution time 21 | - JavaScript runtime environment that does not support `eval` and `Function`. for example: WeChat Mini Program [demo](https://github.com/bplok20010/eval5-wx-demo) 22 | - Be interested or Be curious 23 | 24 | ## ECMAScript version supported 25 | 26 | ES5 27 | 28 | ## Install 29 | 30 | ``` 31 | npm install --save eval5 32 | ``` 33 | 34 | ## Usage 35 | 36 | [![Edit eval5](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/eval5-zxndl?fontsize=14&hidenavigation=1&theme=dark) 37 | 38 | ```javascript 39 | import { Interpreter } from "eval5"; 40 | 41 | const interpreter = new Interpreter(window, { 42 | timeout: 1000, 43 | }); 44 | 45 | let result; 46 | 47 | try { 48 | result = interpreter.evaluate("1+1"); 49 | console.log(result); 50 | 51 | interpreter.evaluate("var a=100"); 52 | interpreter.evaluate("var b=200"); 53 | result = interpreter.evaluate("a+b"); 54 | 55 | console.log(result); 56 | } catch (e) { 57 | console.log(e); 58 | } 59 | ``` 60 | 61 | ## Options 62 | 63 | ```ts 64 | interface Options { 65 | // default: 0 not limited 66 | timeout?: number; 67 | // readonly 68 | rootContext?: {} | null; 69 | globalContextInFunction?: any; 70 | } 71 | ``` 72 | 73 | Example 74 | 75 | ``` 76 | import { Interpreter } from "eval5"; 77 | 78 | const ctx = {}; 79 | const interpreter = new Interpreter(ctx, { 80 | rootContext: window, 81 | timeout: 1000, 82 | }); 83 | 84 | interpreter.evaluate(` 85 | a = 100; 86 | console.log(a); // 100 87 | `); 88 | 89 | window.a;//undefined 90 | 91 | ``` 92 | 93 | ## Interpreter 94 | 95 | **`version`** 96 | 97 | current version 98 | 99 | **`global`** 100 | 101 | default: `{}` 102 | 103 | global context 104 | 105 | ```js 106 | Interpreter.global = window; 107 | const interpreter = new Interpreter(); 108 | interpreter.evaluate('alert("hello eval5")'); 109 | ``` 110 | 111 | **`globalContextInFunction`** 112 | 113 | default: `undefined` 114 | 115 | `eval5` does not support `use strict` mode, but the default value of `this` in function calls is `undefined`, you can set this property as the default. 116 | 117 | ```js 118 | import { Interpreter } from "Interpreter"; 119 | 120 | const ctx = {}; 121 | const interpreter = new Interpreter(ctx); 122 | interpreter.evaluate(` 123 | this; // ctx 124 | function func(){ 125 | return this; // undefined 126 | } 127 | func(); 128 | `); 129 | ``` 130 | 131 | ```js 132 | import { Interpreter } from "Interpreter"; 133 | 134 | Interpreter.globalContextInFunction = window; 135 | const ctx = {}; 136 | const interpreter = new Interpreter(ctx); 137 | interpreter.evaluate(` 138 | this; // ctx 139 | function func(){ 140 | return this; // window 141 | } 142 | func(); 143 | `); 144 | ``` 145 | 146 | Let's take a look at the following example 147 | 148 | > **Note: Illegal invocation** 149 | > 150 | > that's why `globalContextInFunction` is set to `undefined` 151 | 152 | ``` 153 | import { Interpreter } from "Interpreter"; 154 | 155 | Interpreter.globalContextInFunction = {}; 156 | 157 | const ctx = {alert: alert}; 158 | 159 | const interpreter = new Interpreter(ctx); 160 | 161 | interpreter.evaluate(` 162 | // alert.call({}, 'Hello eval5') 163 | // Illegal invocation 164 | alert('Hello eval5'); 165 | `); 166 | ``` 167 | 168 | **`constructor(context = Interpreter.global, options?: Options )`** 169 | 170 | --- 171 | 172 | ## Instance methods 173 | 174 | **`evaluate(code: string): any`** 175 | 176 | executes string code and returns the value of the last expression 177 | 178 | ```js 179 | import { Interpreter } from "Interpreter"; 180 | 181 | const interpreter = new Interpreter(window); 182 | 183 | const result = interpreter.evaluate(` 184 | var a = 100; 185 | var b = 200; 186 | 187 | a+b; 188 | 189 | `); 190 | 191 | console.log(result); // 300 192 | ``` 193 | 194 | **`appendCode(code: string): any`** 195 | 196 | alias of `evaluate` 197 | 198 | **`getExecutionTime(): number`** 199 | 200 | get the last execution time 201 | 202 | **`setExecTimeout(timeout: number = 0): void`** 203 | 204 | set the timeout for each execution 205 | 206 | **`getOptions(): Readonly`** 207 | 208 | get interpreter options 209 | 210 | ## evaluate(code: string, ctx?: {}, options?: Options) 211 | 212 | executes string code and returns the value of the last expression 213 | 214 | > note: a new interpreter is created with every execution 215 | 216 | ```js 217 | import { evaluate } from "eval5"; 218 | 219 | evaluate( 220 | ` 221 | var a = 100; 222 | var b = 100; 223 | console.log(a+b); 224 | `, 225 | { console: console } 226 | ); // 200 227 | 228 | evaluate(` 229 | a; 230 | `); // a is not defined 231 | ``` 232 | 233 | ## Function 234 | 235 | use `Interpreter.global` as the default context, `Interpreter.globalContextInFunction` also 236 | 237 | ```js 238 | import { Function } from "eval5"; 239 | 240 | const func = new Function("a", "b", "return a+b;"); 241 | console.log(func(100, 200)); // 300 242 | ``` 243 | 244 | ## vm 245 | 246 | see [vm](https://nodejs.org/dist/latest-v13.x/docs/api/vm.html) 247 | 248 | - vm.createContext 249 | - vm.compileFunction 250 | - vm.runInContext 251 | - vm.runInNewContext 252 | - vm.Script 253 | 254 | ## License 255 | 256 | MIT 257 | 258 | ## Related 259 | 260 | - [evaljs](https://github.com/marten-de-vries/evaljs) 261 | - [closure-interpreter](https://github.com/int3/closure-interpreter) 262 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eval5 2 | 3 | 中文 | [English](./README-en_US.md) 4 | 5 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/bplok20010/eval5/blob/master/LICENSE) 6 | [![npm](https://img.shields.io/npm/v/eval5)](https://www.npmjs.com/package/eval5) 7 | [![npm bundle size](https://img.shields.io/bundlephobia/min/eval5)](https://raw.githubusercontent.com/bplok20010/eval5/master/umd/eval5.min.js) 8 | 9 | 基于 TypeScript 编写的 JavaScript 解释器,支持完整 ES5 语法 10 | 11 | **支持浏览器、node.js、小程序等 JavaScript 运行环境** 12 | 13 | [在线体验](https://bplok20010.github.io/eval5/) 14 | 15 | [更多示例](https://bplok20010.github.io/eval5/examples.html) 16 | 17 | ## 使用场景 18 | 19 | - 浏览器环境中需要使用沙盒环境执行 JavaScript 脚本 20 | - 控制执行时长 21 | - 不支持`eval` `Function`的 JavaScript 运行环境:如 微信小程序 [demo](https://github.com/bplok20010/eval5-wx-demo) [we-script](https://github.com/bplok20010/we-script) [taro-script](https://github.com/bplok20010/taro-script) 22 | - 研究/学习用 23 | 24 | ## 支持 ECMAScript 版本 25 | 26 | ES5 27 | 28 | ## 安装 29 | 30 | ``` 31 | npm install --save eval5 32 | ``` 33 | 34 | ## 使用 35 | 36 | [![Edit eval5](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/eval5-zxndl?fontsize=14&hidenavigation=1&theme=dark) 37 | 38 | ```javascript 39 | import { Interpreter } from "eval5"; 40 | 41 | const interpreter = new Interpreter(window, { 42 | timeout: 1000, 43 | }); 44 | 45 | let result; 46 | 47 | try { 48 | result = interpreter.evaluate("1+1"); 49 | console.log(result); 50 | 51 | interpreter.evaluate("var a=100"); 52 | interpreter.evaluate("var b=200"); 53 | result = interpreter.evaluate("a+b"); 54 | 55 | console.log(result); 56 | } catch (e) { 57 | console.log(e); 58 | } 59 | ``` 60 | 61 | ## 参数 62 | 63 | ```ts 64 | interface Options { 65 | // 默认为:0,不限制 66 | timeout?: number; 67 | // 根作用域,只读 68 | rootContext?: {} | null; 69 | globalContextInFunction?: any; 70 | } 71 | ``` 72 | 73 | 示例 74 | 75 | ``` 76 | import { Interpreter } from "eval5"; 77 | 78 | const ctx = {}; 79 | const interpreter = new Interpreter(ctx, { 80 | rootContext: window, 81 | timeout: 1000, 82 | }); 83 | 84 | interpreter.evaluate(` 85 | a = 100; 86 | console.log(a); // 100 87 | `); 88 | 89 | window.a;//undefined 90 | 91 | ``` 92 | 93 | ## Interpreter 94 | 95 | **`version`** 96 | 97 | 当前版本 98 | 99 | **`global`** 100 | 101 | 默认值: `{}` 102 | 103 | 设置默认的全局作用域 104 | 105 | ```js 106 | Interpreter.global = window; 107 | const interpreter = new Interpreter(); 108 | interpreter.evaluate('alert("hello eval5")'); 109 | ``` 110 | 111 | **`globalContextInFunction`** 112 | 113 | 默认值: `undefined` 114 | 115 | `eval5` 不支持 `use strict` 严格模式, 在非严格下的函数中`this`默认指向的是全局作用域,但在`eval5`中是`undefined`, 可通过`globalContextInFunction`来设置默认指向。 116 | 117 | ```js 118 | import { Interpreter } from "Interpreter"; 119 | 120 | const ctx = {}; 121 | const interpreter = new Interpreter(ctx); 122 | interpreter.evaluate(` 123 | this; // ctx 124 | function func(){ 125 | return this; // undefined 126 | } 127 | func(); 128 | `); 129 | ``` 130 | 131 | ```js 132 | import { Interpreter } from "Interpreter"; 133 | 134 | Interpreter.globalContextInFunction = window; 135 | const ctx = {}; 136 | const interpreter = new Interpreter({}); 137 | interpreter.evaluate(` 138 | this; // ctx 139 | function func(){ 140 | return this; // window 141 | } 142 | func(); 143 | `); 144 | ``` 145 | 146 | 原因,示例代码: 147 | 148 | > **注意: alert异常** 149 | 150 | ``` 151 | import { Interpreter } from "Interpreter"; 152 | 153 | Interpreter.globalContextInFunction = {}; 154 | 155 | const ctx = {alert: alert}; 156 | 157 | const interpreter = new Interpreter(ctx); 158 | 159 | interpreter.evaluate(` 160 | // throw Illegal invocation 161 | alert('Hello eval5'); // 同 alert.call({}, 'Hello eval5') 162 | `); 163 | ``` 164 | 165 | **`constructor(context = Interpreter.global, options?: Options )`** 166 | 167 | 构造函数 168 | 169 | ## Interpreter 的实例方法 170 | 171 | **`evaluate(code: string): any`** 172 | 173 | 执行给定的字符串代码,并返回最后一个表达式的值 174 | 175 | ```js 176 | import { Interpreter } from "Interpreter"; 177 | 178 | const interpreter = new Interpreter(window); 179 | 180 | const result = interpreter.evaluate(` 181 | var a = 100; 182 | var b = 200; 183 | 184 | a+b; 185 | 186 | `); 187 | 188 | console.log(result); // 300 189 | ``` 190 | 191 | **`appendCode(code: string): any`** 192 | 193 | `evaluate`的别名 194 | 195 | **`getExecutionTime(): number`** 196 | 197 | 获取上一次调用`evaluate`的执行时长 198 | 199 | **`setExecTimeout(timeout: number = 0): void`** 200 | 201 | 设置执行时长 202 | 203 | **`getOptions(): Readonly`** 204 | 205 | 获取解释器参数 206 | 207 | --- 208 | 209 | ## evaluate(code: string, ctx?: {}, options?: Options) 210 | 211 | 执行给定的字符串代码,并返回最后一个表达式的值 212 | 213 | > 注: 该函数每次执行都会创建一个新的解释器 214 | 215 | ```js 216 | import { evaluate } from "eval5"; 217 | 218 | evaluate( 219 | ` 220 | var a = 100; 221 | var b = 100; 222 | console.log(a+b); 223 | `, 224 | { console: console } 225 | ); // 200 226 | 227 | evaluate(` 228 | a; 229 | `); // a is not defined 230 | ``` 231 | 232 | ## Function 233 | 234 | 该函数会将`Interpreter.global` `Interpreter.globalContextInFunction`当作默认值并创建新的解释器 235 | 236 | ```js 237 | import { Function } from "eval5"; 238 | 239 | const func = new Function("a", "b", "return a+b;"); 240 | console.log(func(100, 200)); // 300 241 | ``` 242 | 243 | ## vm 244 | 245 | 查看 [vm](https://nodejs.org/dist/latest-v13.x/docs/api/vm.html) 246 | 247 | - vm.createContext 248 | - vm.compileFunction 249 | - vm.runInContext 250 | - vm.runInNewContext 251 | - vm.Script 252 | 253 | ## License 254 | 255 | MIT 256 | 257 | ## 相关 258 | 259 | - [evaljs](https://github.com/marten-de-vries/evaljs) 260 | - [closure-interpreter](https://github.com/int3/closure-interpreter) 261 | -------------------------------------------------------------------------------- /babel.config.esm.js: -------------------------------------------------------------------------------- 1 | const pkg = require("./package.json"); 2 | 3 | module.exports = api => { 4 | const isTest = api.env("test"); //jest 5 | 6 | return { 7 | presets: [ 8 | [ 9 | "babel-preset-packez", 10 | { 11 | modules: false, 12 | loose: true, 13 | runtimeOptions: { 14 | helpers: false, 15 | }, 16 | }, 17 | ], 18 | ], 19 | plugins: [ 20 | [ 21 | "search-and-replace", 22 | { 23 | rules: [ 24 | { 25 | search: "%VERSION%", 26 | replace: pkg.version, 27 | }, 28 | ], 29 | }, 30 | ], 31 | ], 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const pkg = require("./package.json"); 2 | 3 | module.exports = api => { 4 | const isTest = api.env("test"); //jest 5 | 6 | return { 7 | presets: [ 8 | [ 9 | "babel-preset-packez", 10 | { 11 | modules: "cjs", 12 | loose: true, 13 | runtimeOptions: { 14 | helpers: false, 15 | }, 16 | }, 17 | ], 18 | ], 19 | plugins: [ 20 | [ 21 | "search-and-replace", 22 | { 23 | rules: [ 24 | { 25 | search: "%VERSION%", 26 | replace: pkg.version, 27 | }, 28 | ], 29 | }, 30 | ], 31 | ], 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /docs/common.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: "Segoe UI", Arial, "Microsoft Yahei", sans-serif; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | a { 8 | color: #0366d6; 9 | text-decoration: none; 10 | } 11 | 12 | a:hover { 13 | text-decoration: underline; 14 | } 15 | 16 | .repo-link { 17 | margin-left: 8px; 18 | font-size: 0; 19 | padding: 5px; 20 | } 21 | 22 | .repo-link:hover { 23 | background: #E9EBEC; 24 | border-radius: 3px; 25 | } 26 | 27 | .repo-link img { 28 | width: 20px; 29 | height: 20px; 30 | } 31 | 32 | .container { 33 | width: 60%; 34 | margin: 0 auto; 35 | padding: 15px 0; 36 | } 37 | 38 | @media screen and (max-width: 760px) { 39 | .container { 40 | width: 90%; 41 | } 42 | 43 | .github { 44 | display: none; 45 | } 46 | } 47 | 48 | .tools { 49 | margin-bottom: 15px; 50 | display: flex; 51 | align-items: center; 52 | } 53 | 54 | .btn-run { 55 | background-color: #007acc; 56 | cursor: pointer; 57 | border: none; 58 | padding: 8px 25px; 59 | border-radius: 3px; 60 | color: #fff; 61 | } 62 | 63 | .github { 64 | position: absolute; 65 | right: 0; 66 | top: 0; 67 | } 68 | 69 | .star { 70 | position: absolute; 71 | right: 0; 72 | top: 8px; 73 | } 74 | 75 | .error { 76 | color: red; 77 | } 78 | 79 | textarea { 80 | display: block; 81 | border: 1px solid #dfe2e5; 82 | width: 100%; 83 | box-sizing: border-box; 84 | padding: 5px; 85 | } 86 | 87 | .code { 88 | height: 400px; 89 | } 90 | 91 | .code-lib { 92 | height: 100px; 93 | } 94 | 95 | .version { 96 | margin-left: 10px; 97 | font-size: 14px; 98 | } 99 | 100 | .results-container { 101 | position: relative; 102 | padding: 5px; 103 | border: 1px solid #dfe2e5; 104 | margin-top: -1px; 105 | } 106 | 107 | .results-label { 108 | font-size: 14px; 109 | margin-bottom: 5px; 110 | } 111 | 112 | .results { 113 | font-size: 14px; 114 | min-height: 18px; 115 | } 116 | 117 | .example-item { 118 | padding: 10px 0; 119 | } -------------------------------------------------------------------------------- /docs/examples.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | eval5 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 15 | eval5 16 | 17 | 18 | Try it out 19 | 41 |
42 |
43 |
44 |
45 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /docs/examples/angularjs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | eval5 5 | 6 | 7 | 8 | 14 | 15 | 16 | 17 |
18 | 40 |
41 | 42 | 43 | eval5 44 | 45 | 46 | Examples 47 |
48 | 49 | 50 | 51 | 52 |
53 |
Console:
54 |
55 |
56 |
57 |

Todo

58 |
59 | {{todoList.remaining()}} of {{todoList.todos.length}} remaining 60 | [ archive ] 61 |
    62 |
  • 63 | 67 |
  • 68 |
69 |
70 | 76 | 77 |
78 |
79 |
80 |
81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /docs/examples/angularjs.js: -------------------------------------------------------------------------------- 1 | var libUrl = "https://cdn.jsdelivr.net/npm/angular@latest/angular.min.js"; 2 | version.innerHTML = "version: " + eval5.Interpreter.version; 3 | var interpreter = new eval5.Interpreter(window); 4 | var _init = false; 5 | function run() { 6 | try { 7 | !_init && interpreter.evaluate(lib.value); 8 | _init = true; 9 | 10 | var result = interpreter.evaluate(code.value); 11 | results.innerHTML = "complete"; 12 | console.log(result); 13 | } catch (e) { 14 | console.log(e); 15 | results.innerHTML = '
' + e.message + "
"; 16 | } 17 | } 18 | 19 | function startRun() { 20 | results.innerHTML = "parsing..."; 21 | setTimeout(run, 10); 22 | } 23 | main(); 24 | function main() { 25 | results.innerHTML = "loading..."; 26 | runBtn.disabled = true; 27 | 28 | fetch(libUrl) 29 | .then(res => res.text()) 30 | .then(s => { 31 | runBtn.disabled = false; 32 | 33 | lib.value = s; 34 | code.value = ` 35 | angular.module('todoApp', []) 36 | .controller('TodoListController', function() { 37 | var todoList = this; 38 | todoList.todos = [ 39 | {text:'learn AngularJS', done:true}, 40 | {text:'build an AngularJS app', done:false}]; 41 | 42 | todoList.addTodo = function() { 43 | todoList.todos.push({text:todoList.todoText, done:false}); 44 | todoList.todoText = ''; 45 | }; 46 | 47 | todoList.remaining = function() { 48 | var count = 0; 49 | angular.forEach(todoList.todos, function(todo) { 50 | count += todo.done ? 0 : 1; 51 | }); 52 | return count; 53 | }; 54 | 55 | todoList.archive = function() { 56 | var oldTodos = todoList.todos; 57 | todoList.todos = []; 58 | angular.forEach(oldTodos, function(todo) { 59 | if (!todo.done) todoList.todos.push(todo); 60 | }); 61 | }; 62 | }); 63 | 64 | `; 65 | 66 | startRun(); 67 | }); 68 | } 69 | -------------------------------------------------------------------------------- /docs/examples/echarts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | eval5 5 | 6 | 7 | 8 | 13 | 14 | 15 | 16 |
17 | 39 |
40 | 41 | 42 | eval5 43 | 44 | 45 | Examples 46 |
47 | 48 | 49 | 50 | 51 |
52 |
Console:
53 |
54 |
55 |
56 |
57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /docs/examples/echarts.js: -------------------------------------------------------------------------------- 1 | var libUrl = "https://cdn.jsdelivr.net/npm/echarts@4.6.0/dist/echarts.min.js"; 2 | version.innerHTML = "version: " + eval5.Interpreter.version; 3 | var interpreter = new eval5.Interpreter(window); 4 | var _init = false; 5 | function run() { 6 | try { 7 | !_init && interpreter.evaluate(lib.value); 8 | _init = true; 9 | 10 | var result = interpreter.evaluate(code.value); 11 | results.innerHTML = "complete"; 12 | console.log(result); 13 | } catch (e) { 14 | console.log(e); 15 | results.innerHTML = '
' + e.message + "
"; 16 | } 17 | } 18 | 19 | function startRun() { 20 | results.innerHTML = "parsing..."; 21 | setTimeout(run, 10); 22 | } 23 | main(); 24 | function main() { 25 | results.innerHTML = "loading..."; 26 | runBtn.disabled = true; 27 | 28 | fetch(libUrl) 29 | .then(res => res.text()) 30 | .then(s => { 31 | runBtn.disabled = false; 32 | 33 | lib.value = s; 34 | code.value = ` 35 | var hours = ['12a', '1a', '2a', '3a', '4a', '5a', '6a', 36 | '7a', '8a', '9a','10a','11a', 37 | '12p', '1p', '2p', '3p', '4p', '5p', 38 | '6p', '7p', '8p', '9p', '10p', '11p']; 39 | var days = ['Saturday', 'Friday', 'Thursday', 40 | 'Wednesday', 'Tuesday', 'Monday', 'Sunday']; 41 | 42 | var data = [[0,0,5],[0,1,1],[0,2,0],[0,3,0],[0,4,0],[0,5,0],[0,6,0],[0,7,0],[0,8,0],[0,9,0],[0,10,0],[0,11,2],[0,12,4],[0,13,1],[0,14,1],[0,15,3],[0,16,4],[0,17,6],[0,18,4],[0,19,4],[0,20,3],[0,21,3],[0,22,2],[0,23,5],[1,0,7],[1,1,0],[1,2,0],[1,3,0],[1,4,0],[1,5,0],[1,6,0],[1,7,0],[1,8,0],[1,9,0],[1,10,5],[1,11,2],[1,12,2],[1,13,6],[1,14,9],[1,15,11],[1,16,6],[1,17,7],[1,18,8],[1,19,12],[1,20,5],[1,21,5],[1,22,7],[1,23,2],[2,0,1],[2,1,1],[2,2,0],[2,3,0],[2,4,0],[2,5,0],[2,6,0],[2,7,0],[2,8,0],[2,9,0],[2,10,3],[2,11,2],[2,12,1],[2,13,9],[2,14,8],[2,15,10],[2,16,6],[2,17,5],[2,18,5],[2,19,5],[2,20,7],[2,21,4],[2,22,2],[2,23,4],[3,0,7],[3,1,3],[3,2,0],[3,3,0],[3,4,0],[3,5,0],[3,6,0],[3,7,0],[3,8,1],[3,9,0],[3,10,5],[3,11,4],[3,12,7],[3,13,14],[3,14,13],[3,15,12],[3,16,9],[3,17,5],[3,18,5],[3,19,10],[3,20,6],[3,21,4],[3,22,4],[3,23,1],[4,0,1],[4,1,3],[4,2,0],[4,3,0],[4,4,0],[4,5,1],[4,6,0],[4,7,0],[4,8,0],[4,9,2],[4,10,4],[4,11,4],[4,12,2],[4,13,4],[4,14,4],[4,15,14],[4,16,12],[4,17,1],[4,18,8],[4,19,5],[4,20,3],[4,21,7],[4,22,3],[4,23,0],[5,0,2],[5,1,1],[5,2,0],[5,3,3],[5,4,0],[5,5,0],[5,6,0],[5,7,0],[5,8,2],[5,9,0],[5,10,4],[5,11,1],[5,12,5],[5,13,10],[5,14,5],[5,15,7],[5,16,11],[5,17,6],[5,18,0],[5,19,5],[5,20,3],[5,21,4],[5,22,2],[5,23,0],[6,0,1],[6,1,0],[6,2,0],[6,3,0],[6,4,0],[6,5,0],[6,6,0],[6,7,0],[6,8,0],[6,9,0],[6,10,1],[6,11,0],[6,12,2],[6,13,1],[6,14,3],[6,15,4],[6,16,0],[6,17,0],[6,18,0],[6,19,0],[6,20,1],[6,21,2],[6,22,2],[6,23,6]]; 43 | 44 | var option = { 45 | tooltip: { 46 | position: 'top' 47 | }, 48 | title: [], 49 | singleAxis: [], 50 | series: [] 51 | }; 52 | 53 | echarts.util.each(days, function (day, idx) { 54 | option.title.push({ 55 | textBaseline: 'middle', 56 | top: (idx + 0.5) * 100 / 7 + '%', 57 | text: day 58 | }); 59 | option.singleAxis.push({ 60 | left: 150, 61 | type: 'category', 62 | boundaryGap: false, 63 | data: hours, 64 | top: (idx * 100 / 7 + 5) + '%', 65 | height: (100 / 7 - 10) + '%', 66 | axisLabel: { 67 | interval: 2 68 | } 69 | }); 70 | option.series.push({ 71 | singleAxisIndex: idx, 72 | coordinateSystem: 'singleAxis', 73 | type: 'scatter', 74 | data: [], 75 | symbolSize: function (dataItem) { 76 | return dataItem[1] * 4; 77 | } 78 | }); 79 | }); 80 | 81 | echarts.util.each(data, function (dataItem) { 82 | option.series[dataItem[0]].data.push([dataItem[1], dataItem[2]]); 83 | }); 84 | 85 | 86 | var myChart = echarts.init(document.getElementById('example')); 87 | myChart.setOption(option); 88 | `; 89 | 90 | startRun(); 91 | }); 92 | } 93 | -------------------------------------------------------------------------------- /docs/examples/jquery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | eval5 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 35 |
36 | 37 | 38 | eval5 39 | 40 | 41 | Examples 42 |
43 | 44 | 45 | 46 | 47 |
48 |
Console:
49 |
50 |
51 |
52 |
53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /docs/examples/jquery.js: -------------------------------------------------------------------------------- 1 | var libUrl = "https://cdn.jsdelivr.net/npm/jquery@latest"; 2 | version.innerHTML = "version: " + eval5.Interpreter.version; 3 | var interpreter = new eval5.Interpreter(window); 4 | var _init = false; 5 | function run() { 6 | try { 7 | !_init && interpreter.evaluate(lib.value); 8 | _init = true; 9 | 10 | var result = interpreter.evaluate(code.value); 11 | results.innerHTML = "complete"; 12 | console.log(result); 13 | } catch (e) { 14 | console.log(e); 15 | results.innerHTML = '
' + e.message + "
"; 16 | } 17 | } 18 | 19 | function startRun() { 20 | results.innerHTML = "parsing..."; 21 | setTimeout(run, 10); 22 | } 23 | main(); 24 | function main() { 25 | results.innerHTML = "loading..."; 26 | runBtn.disabled = true; 27 | 28 | fetch(libUrl) 29 | .then(res => res.text()) 30 | .then(s => { 31 | runBtn.disabled = false; 32 | 33 | lib.value = s; 34 | code.value = ` 35 | $('#example') 36 | .css({ 37 | height: 50, 38 | padding: 10, 39 | border: '1px solid blue' 40 | }) 41 | .html('

Hello eval5

') 42 | `; 43 | 44 | startRun(); 45 | }); 46 | } 47 | -------------------------------------------------------------------------------- /docs/examples/knockout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | eval5 5 | 6 | 7 | 8 | 41 | 42 | 43 | 44 |
45 | 67 |
68 | 69 | 70 | eval5 71 | 72 | 73 | Examples 74 |
75 | 76 | 77 | 78 | 79 |
80 |
Console:
81 |
82 |
83 |
84 |

Contacts

85 |
86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 102 | 103 | 124 | 125 | 126 |
First nameLast namePhone numbers
95 | 96 |
97 | Delete 100 |
101 |
104 | 105 | 106 | 107 | 108 | 109 | 114 | 115 | 116 |
110 | Delete 113 |
117 | Add number 123 |
127 |
128 | 129 |

130 | 131 | 134 |

135 | 136 | 138 |
139 |
140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /docs/examples/knockout.js: -------------------------------------------------------------------------------- 1 | var libUrl = "https://cdn.jsdelivr.net/npm/knockout@3.5.1"; 2 | version.innerHTML = "version: " + eval5.Interpreter.version; 3 | var interpreter = new eval5.Interpreter(window); 4 | var _init = false; 5 | function run() { 6 | try { 7 | !_init && interpreter.evaluate(lib.value); 8 | _init = true; 9 | 10 | var result = interpreter.evaluate(code.value); 11 | results.innerHTML = "complete"; 12 | console.log(result); 13 | } catch (e) { 14 | console.log(e); 15 | results.innerHTML = '
' + e.message + "
"; 16 | } 17 | } 18 | 19 | function startRun() { 20 | results.innerHTML = "parsing..."; 21 | setTimeout(run, 10); 22 | } 23 | main(); 24 | function main() { 25 | results.innerHTML = "loading..."; 26 | runBtn.disabled = true; 27 | 28 | fetch(libUrl) 29 | .then(res => res.text()) 30 | .then(s => { 31 | runBtn.disabled = false; 32 | 33 | lib.value = s; 34 | code.value = ` 35 | var initialData = [ 36 | { firstName: "Danny", lastName: "LaRusso", phones: [ 37 | { type: "Mobile", number: "(555) 121-2121" }, 38 | { type: "Home", number: "(555) 123-4567"}] 39 | }, 40 | { firstName: "Sensei", lastName: "Miyagi", phones: [ 41 | { type: "Mobile", number: "(555) 444-2222" }, 42 | { type: "Home", number: "(555) 999-1212"}] 43 | } 44 | ]; 45 | 46 | var ContactsModel = function(contacts) { 47 | var self = this; 48 | self.contacts = ko.observableArray(ko.utils.arrayMap(contacts, function(contact) { 49 | return { firstName: contact.firstName, lastName: contact.lastName, phones: ko.observableArray(contact.phones) }; 50 | })); 51 | 52 | self.addContact = function() { 53 | self.contacts.push({ 54 | firstName: "", 55 | lastName: "", 56 | phones: ko.observableArray() 57 | }); 58 | }; 59 | 60 | self.removeContact = function(contact) { 61 | self.contacts.remove(contact); 62 | }; 63 | 64 | self.addPhone = function(contact) { 65 | contact.phones.push({ 66 | type: "", 67 | number: "" 68 | }); 69 | }; 70 | 71 | self.removePhone = function(phone) { 72 | self.contacts().forEach(function(item){ 73 | item.phones.remove(phone) 74 | }) 75 | }; 76 | 77 | self.save = function() { 78 | self.lastSavedJson(JSON.stringify(ko.toJS(self.contacts), null, 2)); 79 | }; 80 | 81 | self.lastSavedJson = ko.observable("") 82 | }; 83 | 84 | ko.applyBindings(new ContactsModel(initialData), document.getElementById('example')); 85 | 86 | `; 87 | 88 | startRun(); 89 | }); 90 | } 91 | -------------------------------------------------------------------------------- /docs/examples/react-demo-code.js: -------------------------------------------------------------------------------- 1 | function _typeof(obj) { 2 | "@babel/helpers - typeof"; 3 | if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { 4 | _typeof = function _typeof(obj) { 5 | return typeof obj; 6 | }; 7 | } else { 8 | _typeof = function _typeof(obj) { 9 | return obj && 10 | typeof Symbol === "function" && 11 | obj.constructor === Symbol && 12 | obj !== Symbol.prototype 13 | ? "symbol" 14 | : typeof obj; 15 | }; 16 | } 17 | return _typeof(obj); 18 | } 19 | 20 | function _classCallCheck(instance, Constructor) { 21 | if (!(instance instanceof Constructor)) { 22 | throw new TypeError("Cannot call a class as a function"); 23 | } 24 | } 25 | 26 | function _defineProperties(target, props) { 27 | for (var i = 0; i < props.length; i++) { 28 | var descriptor = props[i]; 29 | descriptor.enumerable = descriptor.enumerable || false; 30 | descriptor.configurable = true; 31 | if ("value" in descriptor) descriptor.writable = true; 32 | Object.defineProperty(target, descriptor.key, descriptor); 33 | } 34 | } 35 | 36 | function _createClass(Constructor, protoProps, staticProps) { 37 | if (protoProps) _defineProperties(Constructor.prototype, protoProps); 38 | if (staticProps) _defineProperties(Constructor, staticProps); 39 | return Constructor; 40 | } 41 | 42 | function _createSuper(Derived) { 43 | return function() { 44 | var Super = _getPrototypeOf(Derived), 45 | result; 46 | if (_isNativeReflectConstruct()) { 47 | var NewTarget = _getPrototypeOf(this).constructor; 48 | result = Reflect.construct(Super, arguments, NewTarget); 49 | } else { 50 | result = Super.apply(this, arguments); 51 | } 52 | return _possibleConstructorReturn(this, result); 53 | }; 54 | } 55 | 56 | function _possibleConstructorReturn(self, call) { 57 | if (call && (_typeof(call) === "object" || typeof call === "function")) { 58 | return call; 59 | } 60 | return _assertThisInitialized(self); 61 | } 62 | 63 | function _assertThisInitialized(self) { 64 | if (self === void 0) { 65 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); 66 | } 67 | return self; 68 | } 69 | 70 | function _isNativeReflectConstruct() { 71 | if (typeof Reflect === "undefined" || !Reflect.construct) return false; 72 | if (Reflect.construct.sham) return false; 73 | if (typeof Proxy === "function") return true; 74 | try { 75 | Date.prototype.toString.call(Reflect.construct(Date, [], function() {})); 76 | return true; 77 | } catch (e) { 78 | return false; 79 | } 80 | } 81 | 82 | function _getPrototypeOf(o) { 83 | _getPrototypeOf = Object.setPrototypeOf 84 | ? Object.getPrototypeOf 85 | : function _getPrototypeOf(o) { 86 | return o.__proto__ || Object.getPrototypeOf(o); 87 | }; 88 | return _getPrototypeOf(o); 89 | } 90 | 91 | function _inherits(subClass, superClass) { 92 | if (typeof superClass !== "function" && superClass !== null) { 93 | throw new TypeError("Super expression must either be null or a function"); 94 | } 95 | subClass.prototype = Object.create(superClass && superClass.prototype, { 96 | constructor: { value: subClass, writable: true, configurable: true }, 97 | }); 98 | if (superClass) _setPrototypeOf(subClass, superClass); 99 | } 100 | 101 | function _setPrototypeOf(o, p) { 102 | _setPrototypeOf = 103 | Object.setPrototypeOf || 104 | function _setPrototypeOf(o, p) { 105 | o.__proto__ = p; 106 | return o; 107 | }; 108 | return _setPrototypeOf(o, p); 109 | } 110 | 111 | var TodoApp = /*#__PURE__*/ (function(_React$Component) { 112 | _inherits(TodoApp, _React$Component); 113 | 114 | var _super = _createSuper(TodoApp); 115 | 116 | function TodoApp(props) { 117 | var _this; 118 | 119 | _classCallCheck(this, TodoApp); 120 | 121 | _this = _super.call(this, props); 122 | _this.state = { 123 | items: [], 124 | text: "", 125 | }; 126 | _this.handleChange = _this.handleChange.bind(_assertThisInitialized(_this)); 127 | _this.handleSubmit = _this.handleSubmit.bind(_assertThisInitialized(_this)); 128 | return _this; 129 | } 130 | 131 | _createClass(TodoApp, [ 132 | { 133 | key: "render", 134 | value: function render() { 135 | return /*#__PURE__*/ React.createElement( 136 | "div", 137 | null, 138 | /*#__PURE__*/ React.createElement("h3", null, "TODO"), 139 | /*#__PURE__*/ React.createElement(TodoList, { 140 | items: this.state.items, 141 | }), 142 | /*#__PURE__*/ React.createElement( 143 | "form", 144 | { 145 | onSubmit: this.handleSubmit, 146 | }, 147 | /*#__PURE__*/ React.createElement( 148 | "label", 149 | { 150 | htmlFor: "new-todo", 151 | }, 152 | "What needs to be done?" 153 | ), 154 | /*#__PURE__*/ React.createElement("input", { 155 | id: "new-todo", 156 | onChange: this.handleChange, 157 | value: this.state.text, 158 | }), 159 | /*#__PURE__*/ React.createElement( 160 | "button", 161 | null, 162 | "Add #", 163 | this.state.items.length + 1 164 | ) 165 | ) 166 | ); 167 | }, 168 | }, 169 | { 170 | key: "handleChange", 171 | value: function handleChange(e) { 172 | this.setState({ 173 | text: e.target.value, 174 | }); 175 | }, 176 | }, 177 | { 178 | key: "handleSubmit", 179 | value: function handleSubmit(e) { 180 | e.preventDefault(); 181 | 182 | if (this.state.text.length === 0) { 183 | return; 184 | } 185 | 186 | var newItem = { 187 | text: this.state.text, 188 | id: Date.now(), 189 | }; 190 | this.setState(function(state) { 191 | return { 192 | items: state.items.concat(newItem), 193 | text: "", 194 | }; 195 | }); 196 | }, 197 | }, 198 | ]); 199 | 200 | return TodoApp; 201 | })(React.Component); 202 | 203 | var TodoList = /*#__PURE__*/ (function(_React$Component2) { 204 | _inherits(TodoList, _React$Component2); 205 | 206 | var _super2 = _createSuper(TodoList); 207 | 208 | function TodoList() { 209 | _classCallCheck(this, TodoList); 210 | 211 | return _super2.apply(this, arguments); 212 | } 213 | 214 | _createClass(TodoList, [ 215 | { 216 | key: "render", 217 | value: function render() { 218 | return /*#__PURE__*/ React.createElement( 219 | "ul", 220 | null, 221 | this.props.items.map(function(item) { 222 | return /*#__PURE__*/ React.createElement( 223 | "li", 224 | { 225 | key: item.id, 226 | }, 227 | item.text 228 | ); 229 | }) 230 | ); 231 | }, 232 | }, 233 | ]); 234 | 235 | return TodoList; 236 | })(React.Component); 237 | 238 | ReactDOM.render( 239 | /*#__PURE__*/ React.createElement(TodoApp, null), 240 | document.getElementById("example") 241 | ); 242 | -------------------------------------------------------------------------------- /docs/examples/react.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | eval5 5 | 6 | 7 | 8 | 13 | 14 | 15 | 16 |
17 | 39 |
40 | 41 | 42 | eval5 43 | 44 | 45 | Examples 46 |
47 | 48 | 49 | 50 | 51 |
52 |
Console:
53 |
54 |
55 |
56 |
57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /docs/examples/react.js: -------------------------------------------------------------------------------- 1 | var reactLibUrl = "https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.production.min.js"; 2 | var reactDOMLibUrl = 3 | "https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.production.min.js"; 4 | var demoCode = "./react-demo-code.js"; 5 | version.innerHTML = "version: " + eval5.Interpreter.version; 6 | var interpreter = new eval5.Interpreter(window); 7 | var _init = false; 8 | function run() { 9 | try { 10 | !_init && interpreter.evaluate(lib.value); 11 | _init = true; 12 | 13 | var result = interpreter.evaluate(code.value); 14 | results.innerHTML = "complete"; 15 | console.log(result); 16 | } catch (e) { 17 | console.log(e); 18 | results.innerHTML = '
' + e.message + "
"; 19 | } 20 | } 21 | 22 | function startRun() { 23 | results.innerHTML = "parsing..."; 24 | setTimeout(run, 10); 25 | } 26 | main(); 27 | function main() { 28 | results.innerHTML = "loading..."; 29 | runBtn.disabled = true; 30 | 31 | var p1 = fetch(reactLibUrl).then(res => res.text()); 32 | var p2 = fetch(reactDOMLibUrl).then(res => res.text()); 33 | var p3 = fetch(demoCode).then(res => res.text()); 34 | 35 | Promise.all([p1, p2, p3]).then(([React, ReactDOM, demoCode]) => { 36 | var s = ` 37 | ${React} 38 | ${ReactDOM} 39 | `; 40 | 41 | runBtn.disabled = false; 42 | 43 | lib.value = s; 44 | code.value = demoCode; 45 | 46 | startRun(); 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /docs/examples/sandbox.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | eval5 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 35 |
36 | 37 | 38 | eval5 39 | 40 | 41 | Examples 42 |
43 | 44 |
45 |
Console:
46 |
47 |
48 |
49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /docs/examples/sandbox.js: -------------------------------------------------------------------------------- 1 | version.innerHTML = "version: " + eval5.Interpreter.version; 2 | var interpreter = new eval5.Interpreter({ 3 | hello: function() { 4 | return "hello eval5"; 5 | }, 6 | }); 7 | function run() { 8 | try { 9 | var result = interpreter.evaluate(code.value); 10 | results.innerHTML = result; 11 | console.log(result); 12 | } catch (e) { 13 | console.log(e); 14 | results.innerHTML = '
' + e.message + "
"; 15 | } 16 | runBtn.disabled = false; 17 | } 18 | 19 | function startRun() { 20 | runBtn.disabled = true; 21 | results.innerHTML = "parsing..."; 22 | setTimeout(run, 10); 23 | } 24 | main(); 25 | function main() { 26 | code.value = ` 27 | // eval without window 28 | // console is not defined 29 | // console.log('hello eval5'); 30 | hello();`; 31 | 32 | startRun(); 33 | } 34 | -------------------------------------------------------------------------------- /docs/examples/self-interpreter-demo-code.js: -------------------------------------------------------------------------------- 1 | var interpreter = new eval5.Interpreter({}); 2 | interpreter.evaluate("100+200"); 3 | -------------------------------------------------------------------------------- /docs/examples/self-interpreter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | eval5 5 | 6 | 7 | 8 | 67 | 68 | 69 | 70 |
71 | 93 |
94 | 95 | 96 | eval5 97 | 98 | 99 | Examples 100 |
101 | 102 | 103 | 104 | 105 |
106 |
Console:
107 |
108 |
109 |
110 | 111 | 113 |
114 |
115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /docs/examples/self-interpreter.js: -------------------------------------------------------------------------------- 1 | var eval5LibUrl = "../umd/eval5.min.js"; 2 | var demoCode = "./self-interpreter-demo-code.js"; 3 | version.innerHTML = "version: " + eval5.Interpreter.version; 4 | var interpreter = new eval5.Interpreter({}); 5 | var _init = false; 6 | function run() { 7 | try { 8 | !_init && interpreter.evaluate(lib.value); 9 | _init = true; 10 | 11 | var result = interpreter.evaluate(code.value); 12 | results.innerHTML = result; 13 | console.log(result); 14 | } catch (e) { 15 | console.log(e); 16 | results.innerHTML = '
' + e.message + "
"; 17 | } 18 | } 19 | 20 | function startRun() { 21 | results.innerHTML = "parsing..."; 22 | setTimeout(run, 10); 23 | } 24 | main(); 25 | function main() { 26 | results.innerHTML = "loading..."; 27 | runBtn.disabled = true; 28 | 29 | var p1 = fetch(eval5LibUrl).then(res => res.text()); 30 | var p2 = fetch(demoCode).then(res => res.text()); 31 | 32 | Promise.all([p1, p2]).then(([Vue, demoCode]) => { 33 | var s = ` 34 | ${Vue} 35 | `; 36 | 37 | runBtn.disabled = false; 38 | 39 | lib.value = s; 40 | code.value = demoCode; 41 | 42 | startRun(); 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /docs/examples/timeout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | eval5 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 35 |
36 | 37 | 38 | eval5 39 | 40 | 41 | Examples 42 |
43 | 44 |
45 |
Console:
46 |
47 |
48 |
49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /docs/examples/timeout.js: -------------------------------------------------------------------------------- 1 | version.innerHTML = "version: " + eval5.Interpreter.version; 2 | var interpreter = new eval5.Interpreter(window, { 3 | timeout: 1000, 4 | }); 5 | function run() { 6 | try { 7 | var result = interpreter.evaluate(code.value); 8 | results.innerHTML = result; 9 | console.log(result); 10 | } catch (e) { 11 | console.log(e); 12 | results.innerHTML = '
' + e.message + "
"; 13 | } 14 | runBtn.disabled = false; 15 | } 16 | 17 | function startRun() { 18 | runBtn.disabled = true; 19 | results.innerHTML = "parsing..."; 20 | setTimeout(run, 10); 21 | } 22 | main(); 23 | function main() { 24 | code.value = ` 25 | while(1){} 26 | `; 27 | 28 | startRun(); 29 | } 30 | -------------------------------------------------------------------------------- /docs/examples/vue-demo-code.js: -------------------------------------------------------------------------------- 1 | var strVar = ""; 2 | strVar += ""; 3 | strVar += " "; 4 | strVar += " "; 5 | strVar += 6 | ' "; 9 | strVar += " "; 10 | strVar += " "; 11 | strVar += " "; 12 | strVar += ' '; 13 | strVar += ' '; 14 | strVar += " "; 15 | strVar += " "; 16 | strVar += "
{{ key | capitalize }} '; 7 | strVar += " "; 8 | strVar += "
{{entry[key]}}
"; 17 | 18 | Vue.component("demo-grid", { 19 | template: strVar, 20 | props: { 21 | heroes: Array, 22 | columns: Array, 23 | filterKey: String, 24 | }, 25 | data: function() { 26 | var sortOrders = {}; 27 | this.columns.forEach(function(key) { 28 | sortOrders[key] = 1; 29 | }); 30 | return { 31 | sortKey: "", 32 | sortOrders: sortOrders, 33 | }; 34 | }, 35 | computed: { 36 | filteredHeroes: function() { 37 | var sortKey = this.sortKey; 38 | var filterKey = this.filterKey && this.filterKey.toLowerCase(); 39 | var order = this.sortOrders[sortKey] || 1; 40 | var heroes = this.heroes; 41 | if (filterKey) { 42 | heroes = heroes.filter(function(row) { 43 | return Object.keys(row).some(function(key) { 44 | return ( 45 | String(row[key]) 46 | .toLowerCase() 47 | .indexOf(filterKey) > -1 48 | ); 49 | }); 50 | }); 51 | } 52 | if (sortKey) { 53 | heroes = heroes.slice().sort(function(a, b) { 54 | a = a[sortKey]; 55 | b = b[sortKey]; 56 | return (a === b ? 0 : a > b ? 1 : -1) * order; 57 | }); 58 | } 59 | return heroes; 60 | }, 61 | }, 62 | filters: { 63 | capitalize: function(str) { 64 | return str.charAt(0).toUpperCase() + str.slice(1); 65 | }, 66 | }, 67 | methods: { 68 | sortBy: function(key) { 69 | this.sortKey = key; 70 | this.sortOrders[key] = this.sortOrders[key] * -1; 71 | }, 72 | }, 73 | }); 74 | 75 | // bootstrap the demo 76 | var demo = new Vue({ 77 | el: "#example", 78 | template: 79 | "
" + 80 | '' + 81 | '' + 82 | "
", 83 | data: { 84 | searchQuery: "", 85 | gridColumns: ["name", "power"], 86 | gridData: [ 87 | { name: "Chuck Norris", power: Infinity }, 88 | { name: "Bruce Lee", power: 9000 }, 89 | { name: "Jackie Chan", power: 7000 }, 90 | { name: "Jet Li", power: 8000 }, 91 | ], 92 | }, 93 | }); 94 | -------------------------------------------------------------------------------- /docs/examples/vue.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | eval5 5 | 6 | 7 | 8 | 67 | 68 | 69 | 70 |
71 | 93 |
94 | 95 | 96 | eval5 97 | 98 | 99 | Examples 100 |
101 | 102 | 103 | 104 | 105 |
106 |
Console:
107 |
108 |
109 |
110 | 111 | 113 |
114 |
115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /docs/examples/vue.js: -------------------------------------------------------------------------------- 1 | var vueLibUrl = "https://cdn.jsdelivr.net/npm/vue@2.6.11"; 2 | var demoCode = "./vue-demo-code.js"; 3 | version.innerHTML = "version: " + eval5.Interpreter.version; 4 | var interpreter = new eval5.Interpreter(window); 5 | var _init = false; 6 | function run() { 7 | try { 8 | !_init && interpreter.evaluate(lib.value); 9 | _init = true; 10 | 11 | var result = interpreter.evaluate(code.value); 12 | results.innerHTML = "complete"; 13 | console.log(result); 14 | } catch (e) { 15 | console.log(e); 16 | results.innerHTML = '
' + e.message + "
"; 17 | } 18 | } 19 | 20 | function startRun() { 21 | results.innerHTML = "parsing..."; 22 | setTimeout(run, 10); 23 | } 24 | main(); 25 | function main() { 26 | results.innerHTML = "loading..."; 27 | runBtn.disabled = true; 28 | 29 | var p1 = fetch(vueLibUrl).then(res => res.text()); 30 | var p2 = fetch(demoCode).then(res => res.text()); 31 | 32 | Promise.all([p1, p2]).then(([Vue, demoCode]) => { 33 | var s = ` 34 | ${Vue} 35 | `; 36 | 37 | runBtn.disabled = false; 38 | 39 | lib.value = s; 40 | code.value = demoCode; 41 | 42 | startRun(); 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /docs/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bplok20010/eval5/64f9f7ffc97b6eb0ada78078b13897d1d52aea12/docs/github.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | eval5 5 | 6 | 7 | 8 | 9 | 10 |
11 | 33 |
34 | 35 | 36 | eval5 37 | 38 | 39 | Examples 40 |
41 | 42 | 60 | 61 |
62 |
Console:
63 |
64 |
65 |
66 | 67 |
68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /docs/index.js: -------------------------------------------------------------------------------- 1 | version.innerHTML = "eval5: " + eval5.Interpreter.version; 2 | var interpreter = new eval5.Interpreter(window); 3 | function run() { 4 | var source = code.value; 5 | 6 | try { 7 | var result = interpreter.evaluate(source); 8 | results.innerHTML = result; 9 | console.log(result); 10 | } catch (e) { 11 | console.log(e); 12 | results.innerHTML = `
${e.message}
`; 13 | } 14 | } 15 | run(); 16 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // eval5 2 | module.exports = { 3 | transform: { 4 | "^.+\\.[t|j]sx?$": "babel-jest", 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eval5", 3 | "version": "1.4.8", 4 | "description": "A JavaScript interpreter written in JavaScript", 5 | "main": "./dist/cjs/index.js", 6 | "module": "./dist/esm/index.js", 7 | "types": "./lib/index.d.ts", 8 | "scripts": { 9 | "start": "tsc -w", 10 | "build": "run-s clear build:* bundle:dev bundle:version bundle:prod bundle:banner", 11 | "build:lib": "tsc", 12 | "build:cjs": "tsc --module commonjs --target ES5 --outDir dist/cjs --declaration false", 13 | "build:esm": "tsc --module esnext --target ES6 --outDir dist/esm --declaration false", 14 | "clear": "rimraf lib dist umd", 15 | "bundle:dev": "rollup -c", 16 | "bundle:prod": "rollup -c rollup.config.min.mjs", 17 | "bundle:banner": "node scripts/banner.js", 18 | "bundle:version": "node scripts/version.js", 19 | "prepublishOnly": "run-s test build", 20 | "test": "jest" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/bplok20010/eval5.git" 25 | }, 26 | "keywords": [ 27 | "interpreter", 28 | "js-interpreter", 29 | "eval", 30 | "Function", 31 | "vm", 32 | "eval5" 33 | ], 34 | "files": [ 35 | "umd", 36 | "lib", 37 | "dist", 38 | "*.md" 39 | ], 40 | "author": "nobo.zhou@foxmail.com", 41 | "license": "MIT", 42 | "bugs": { 43 | "url": "https://github.com/bplok20010/eval5/issues" 44 | }, 45 | "homepage": "https://github.com/bplok20010/eval5#readme", 46 | "devDependencies": { 47 | "@babel/cli": "^7.8.4", 48 | "@rollup/plugin-commonjs": "^26.0.1", 49 | "@rollup/plugin-node-resolve": "^15.2.3", 50 | "@rollup/plugin-typescript": "^11.1.6", 51 | "rollup-plugin-terser": "^7.0.2", 52 | "@types/fs-extra": "^8.1.0", 53 | "@types/jest": "^24.0.25", 54 | "babel-plugin-search-and-replace": "^1.0.1", 55 | "babel-preset-packez": "^1.0.0", 56 | "fs-extra": "^8.1.0", 57 | "jest": "^24.9.0", 58 | "npm-run-all": "^4.1.5", 59 | "rollup": "^4.20.0", 60 | "rimraf": "^3.0.0" 61 | }, 62 | "dependencies": { 63 | "@babel/runtime": "^7.8.4", 64 | "@types/acorn": "^4.0.5", 65 | "@types/estree": "0.0.41", 66 | "acorn": "^7.1.0" 67 | }, 68 | "browserslist": [ 69 | ">=0.25%", 70 | "not dead", 71 | "not op_mini all", 72 | "not Android 4.4.3-4.4.4", 73 | "not ios_saf < 10", 74 | "not Chrome < 50", 75 | "firefox ESR" 76 | ] 77 | } 78 | -------------------------------------------------------------------------------- /rollup.config.min.mjs: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve"; 2 | import commonjs from "@rollup/plugin-commonjs"; 3 | import { terser } from "rollup-plugin-terser"; 4 | 5 | export default { 6 | input: "dist/cjs/umd.js", 7 | output: { 8 | format: "umd", 9 | file: "./umd/eval5.min.js", 10 | name: "eval5", 11 | }, 12 | plugins: [commonjs(), resolve(), terser()], 13 | }; 14 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import resolve from "@rollup/plugin-node-resolve"; 2 | import commonjs from "@rollup/plugin-commonjs"; 3 | 4 | export default { 5 | input: "dist/cjs/umd.js", 6 | output: { 7 | format: "umd", 8 | file: "./umd/eval5.js", 9 | name: "eval5", 10 | }, 11 | plugins: [commonjs(), resolve()], 12 | }; 13 | -------------------------------------------------------------------------------- /scripts/banner.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs-extra"); 2 | const pkg = require("../package.json"); 3 | 4 | exports.getBannerTemplate = function () { 5 | return `/*! 6 | * @license ${pkg.name} v${pkg.version} 7 | * Copyright (c) 2019-2020 nobo (MIT Licensed) 8 | * https://github.com/bplok20010/eval5 9 | */`; 10 | }; 11 | 12 | function main() { 13 | const data = fs.readFileSync("./umd/eval5.min.js"); 14 | fs.writeFileSync("./umd/eval5.min.js", exports.getBannerTemplate() + "\n" + data); 15 | } 16 | 17 | if (require.main === module) { 18 | main(); 19 | } 20 | -------------------------------------------------------------------------------- /scripts/version.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs-extra"); 2 | const pkg = require("../package.json"); 3 | 4 | function main() { 5 | const cjsFile = "./dist/cjs/interpreter/main.js"; 6 | const esmFile = "./dist/esm/interpreter/main.js"; 7 | let code1 = fs.readFileSync(cjsFile).toString(); 8 | code1 = code1.replace(/%VERSION%/g, pkg.version); 9 | fs.writeFileSync(cjsFile, code1); 10 | 11 | let code2 = fs.readFileSync(esmFile).toString(); 12 | code2 = code2.replace(/%VERSION%/g, pkg.version); 13 | fs.writeFileSync(esmFile, code2); 14 | } 15 | 16 | if (require.main === module) { 17 | main(); 18 | } 19 | -------------------------------------------------------------------------------- /src/Function.ts: -------------------------------------------------------------------------------- 1 | import { compileFunction } from "./vm"; 2 | 3 | export default function(...args: string[]) { 4 | const code = args.pop(); 5 | 6 | return compileFunction(code || "", args); 7 | } 8 | -------------------------------------------------------------------------------- /src/evaluate.ts: -------------------------------------------------------------------------------- 1 | import { VMContext, ScriptOptions } from "./types"; 2 | import { runInContext } from "./vm"; 3 | 4 | export default (code: string, ctx?: VMContext, options?: ScriptOptions) => { 5 | return runInContext(code, ctx, options); 6 | }; 7 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Interpreter } from "./interpreter/main"; 2 | import * as vm from "./vm"; 3 | import evaluate from "./evaluate"; 4 | import Function from "./Function"; 5 | 6 | export { Interpreter, vm, evaluate, Function }; 7 | 8 | export default evaluate; 9 | -------------------------------------------------------------------------------- /src/interpreter/messages.ts: -------------------------------------------------------------------------------- 1 | export class ThrowError extends Error {} 2 | export class ThrowSyntaxError extends SyntaxError {} 3 | export class ThrowReferenceError extends ReferenceError {} 4 | export class ThrowTypeError extends TypeError {} 5 | export class InterruptThrowError extends ThrowError {} 6 | export class InterruptThrowSyntaxError extends ThrowSyntaxError {} 7 | export class InterruptThrowReferenceError extends ThrowReferenceError {} 8 | 9 | interface Messages { 10 | [type: string]: MessageItem; 11 | } 12 | 13 | export type MessageItem = [ 14 | number, 15 | string, 16 | new (message: string) => Error //typeof ThrowError | typeof InterruptThrowReferenceError | typeof ThrowReferenceError 17 | ]; 18 | 19 | export const Messages: Messages = { 20 | UnknownError: [3001, "%0", InterruptThrowError], 21 | ExecutionTimeOutError: [3002, "Script execution timed out after %0ms", InterruptThrowError], 22 | NodeTypeSyntaxError: [1001, "Unknown node type: %0", InterruptThrowReferenceError], 23 | BinaryOperatorSyntaxError: [1002, "Unknown binary operator: %0", InterruptThrowReferenceError], 24 | LogicalOperatorSyntaxError: [ 25 | 1003, 26 | "Unknown logical operator: %0", 27 | InterruptThrowReferenceError, 28 | ], 29 | UnaryOperatorSyntaxError: [1004, "Unknown unary operator: %0", InterruptThrowReferenceError], 30 | UpdateOperatorSyntaxError: [1005, "Unknown update operator: %0", InterruptThrowReferenceError], 31 | ObjectStructureSyntaxError: [ 32 | 1006, 33 | "Unknown object structure: %0", 34 | InterruptThrowReferenceError, 35 | ], 36 | AssignmentExpressionSyntaxError: [ 37 | 1007, 38 | "Unknown assignment expression: %0", 39 | InterruptThrowReferenceError, 40 | ], 41 | VariableTypeSyntaxError: [1008, "Unknown variable type: %0", InterruptThrowReferenceError], 42 | ParamTypeSyntaxError: [1009, "Unknown param type: %0", InterruptThrowReferenceError], 43 | AssignmentTypeSyntaxError: [1010, "Unknown assignment type: %0", InterruptThrowReferenceError], 44 | FunctionUndefinedReferenceError: [2001, "%0 is not a function", ThrowReferenceError], 45 | VariableUndefinedReferenceError: [2002, "%0 is not defined", ThrowReferenceError], 46 | IsNotConstructor: [2003, "%0 is not a constructor", ThrowTypeError], 47 | }; 48 | -------------------------------------------------------------------------------- /src/interpreter/nodes.ts: -------------------------------------------------------------------------------- 1 | import * as ESTree from "estree"; 2 | 3 | export { ESTree }; 4 | 5 | export type Node = 6 | | ESTree.Node 7 | | ESTree.BinaryExpression 8 | | ESTree.LogicalExpression 9 | | ESTree.UnaryExpression 10 | | ESTree.UpdateExpression 11 | | ESTree.ObjectExpression 12 | | ESTree.ArrayExpression 13 | | ESTree.CallExpression 14 | | ESTree.NewExpression 15 | | ESTree.MemberExpression 16 | | ESTree.ThisExpression 17 | | ESTree.SequenceExpression 18 | | ESTree.Literal 19 | | ESTree.Identifier 20 | | ESTree.AssignmentExpression 21 | | ESTree.FunctionDeclaration 22 | | ESTree.VariableDeclaration 23 | | ESTree.BlockStatement 24 | | ESTree.Program 25 | | ESTree.ExpressionStatement 26 | | ESTree.EmptyStatement 27 | | ESTree.ReturnStatement 28 | | ESTree.FunctionExpression 29 | | ESTree.IfStatement 30 | | ESTree.ConditionalExpression 31 | | ESTree.ForStatement 32 | | ESTree.WhileStatement 33 | | ESTree.DoWhileStatement 34 | | ESTree.ForInStatement 35 | | ESTree.WithStatement 36 | | ESTree.ThrowStatement 37 | | ESTree.TryStatement 38 | | ESTree.ContinueStatement 39 | | ESTree.BreakStatement 40 | | ESTree.SwitchStatement 41 | | ESTree.SwitchCase 42 | | ESTree.LabeledStatement 43 | | ESTree.DebuggerStatement; 44 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export type VMContext = { 2 | [x: string]: any; 3 | [x: number]: any; 4 | }; 5 | 6 | export interface CompileOptions { 7 | parsingContext?: VMContext; 8 | timeout?: number; 9 | ecmaVersion?: 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020; 10 | rootContext?: VMContext | null; 11 | globalContextInFunction?: any; 12 | } 13 | 14 | export interface ScriptOptions { 15 | ecmaVersion?: 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020; 16 | timeout?: number; 17 | rootContext?: VMContext | null; 18 | globalContextInFunction?: any; 19 | } 20 | -------------------------------------------------------------------------------- /src/umd.ts: -------------------------------------------------------------------------------- 1 | import { Interpreter } from "./interpreter/main"; 2 | import * as vm from "./vm"; 3 | import evaluate from "./evaluate"; 4 | import Function from "./Function"; 5 | 6 | export { Interpreter, vm, evaluate, Function }; -------------------------------------------------------------------------------- /src/vm.ts: -------------------------------------------------------------------------------- 1 | import { Interpreter } from "./interpreter/main"; 2 | import { VMContext, CompileOptions, ScriptOptions } from "./types"; 3 | 4 | // TODO: 5 | // add tests 6 | 7 | export function createContext(ctx: VMContext = Object.create(null)): VMContext { 8 | return ctx; 9 | } 10 | 11 | export function compileFunction( 12 | code: string, 13 | params: string[] = [], 14 | options: CompileOptions = {} 15 | ): (...args: string[]) => any { 16 | const ctx: any = options.parsingContext; 17 | const timeout = options.timeout === undefined ? 0 : options.timeout; 18 | 19 | const wrapCode = ` 20 | (function anonymous(${params.join(",")}){ 21 | ${code} 22 | }); 23 | `; 24 | 25 | const interpreter = new Interpreter(ctx, { 26 | ecmaVersion: options.ecmaVersion, 27 | timeout, 28 | rootContext: options.rootContext, 29 | globalContextInFunction: options.globalContextInFunction, 30 | }); 31 | 32 | return interpreter.evaluate(wrapCode); 33 | } 34 | 35 | export function runInContext(code: string, ctx?: VMContext, options?: ScriptOptions): any { 36 | const interpreter = new Interpreter(ctx, options); 37 | 38 | return interpreter.evaluate(code); 39 | } 40 | 41 | export const runInNewContext = runInContext; 42 | 43 | export class Script { 44 | _code: string; 45 | constructor(code: string) { 46 | this._code = code; 47 | } 48 | runInContext(ctx: VMContext): any { 49 | return runInContext(this._code, ctx); 50 | } 51 | runInNewContext(ctx?: VMContext): any { 52 | return runInContext(this._code, ctx); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/array/ArrayExpression.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | test("ArrayExpression-1", () => { 4 | const arr = evaluate( 5 | ` 6 | var arr = [1, 2, 3]; 7 | arr.push(4); 8 | arr; 9 | ` 10 | ); 11 | 12 | expect(Array.isArray(arr)).toBe(true); 13 | expect(arr.length).toBe(4); 14 | expect(arr).toEqual([1, 2, 3, 4]); 15 | }); 16 | 17 | test("ArrayExpression-2", () => { 18 | const arr = evaluate( 19 | ` 20 | var arr = [,,1,2,3,,,]; 21 | arr.push(4); 22 | arr; 23 | ` 24 | ); 25 | 26 | expect(Array.isArray(arr)).toBe(true); 27 | expect(arr.length).toBe(8); 28 | }); 29 | 30 | test("ArrayExpression-3", () => { 31 | const arr = evaluate( 32 | ` 33 | function _t(){ 34 | return 1 35 | } 36 | var a = 1; 37 | var arr = [a++, _t() + 2, 3 + 3, undefined]; 38 | arr.push(4); 39 | arr; 40 | ` 41 | ); 42 | 43 | expect(Array.isArray(arr)).toBe(true); 44 | expect(arr.length).toBe(5); 45 | expect(arr).toEqual([1, 3, 6, undefined, 4]); 46 | }); 47 | 48 | test("ArrayExpression-4", () => { 49 | const arr = evaluate( 50 | ` 51 | function _t(){ 52 | return 1 53 | } 54 | var a = 1; 55 | var arr = [1,2,null,undefined]; 56 | arr.push(4); 57 | arr; 58 | ` 59 | ); 60 | 61 | expect(Array.isArray(arr)).toBe(true); 62 | expect(arr.length).toBe(5); 63 | expect(arr).toEqual([1, 2, null, undefined, 4]); 64 | }); 65 | 66 | test("ArrayExpression-5", () => { 67 | const arr = evaluate( 68 | ` 69 | [1,2] 70 | ` 71 | ); 72 | 73 | expect(arr).toEqual([1, 2]); 74 | }); 75 | -------------------------------------------------------------------------------- /test/binary-expression/BinaryExpression.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate, Interpreter } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("+", () => { 8 | const num = evaluate( 9 | ` 10 | 1 + 2; 11 | ` 12 | ); 13 | deepEqual(num, 3); 14 | }); 15 | 16 | test("+=", () => { 17 | const num = evaluate( 18 | ` 19 | var a = 1; 20 | var b = 2; 21 | a += 2; 22 | 23 | ` 24 | ); 25 | deepEqual(num, 3); 26 | }); 27 | 28 | test("-", () => { 29 | const num = evaluate( 30 | ` 31 | 2 - 1; 32 | ` 33 | ); 34 | deepEqual(num, 1); 35 | }); 36 | 37 | test("-=", () => { 38 | const num = evaluate( 39 | ` 40 | var a = 1; 41 | var b = 2; 42 | a -= 2; 43 | 44 | ` 45 | ); 46 | deepEqual(num, -1); 47 | }); 48 | 49 | test("*", () => { 50 | const num = evaluate( 51 | ` 52 | 2 * 1; 53 | ` 54 | ); 55 | deepEqual(num, 2); 56 | }); 57 | 58 | test("**", () => { 59 | const inst = new Interpreter({}, { ecmaVersion: 10 }); 60 | const num = inst.evaluate( 61 | ` 62 | 2 ** 3; 63 | ` 64 | ); 65 | deepEqual(num, 8); 66 | }); 67 | 68 | test("*=", () => { 69 | const num = evaluate( 70 | ` 71 | var a = 1; 72 | var b = 2; 73 | a *= 2; 74 | 75 | ` 76 | ); 77 | deepEqual(num, 2); 78 | }); 79 | 80 | test("**=", () => { 81 | const inst = new Interpreter({}, { ecmaVersion: 10 }); 82 | const num = inst.evaluate( 83 | ` 84 | var a = 2; 85 | var b = 3; 86 | a **= b; 87 | 88 | ` 89 | ); 90 | deepEqual(num, 8); 91 | }); 92 | 93 | test("/", () => { 94 | const num = evaluate( 95 | ` 96 | 2 / 1; 97 | ` 98 | ); 99 | deepEqual(num, 2); 100 | }); 101 | 102 | test("/=", () => { 103 | const num = evaluate( 104 | ` 105 | var a = 1; 106 | var b = 2; 107 | a /= 2; 108 | 109 | ` 110 | ); 111 | deepEqual(num, 0.5); 112 | }); 113 | 114 | test("%", () => { 115 | const num = evaluate( 116 | ` 117 | 2 % 1; 118 | ` 119 | ); 120 | deepEqual(num, 0); 121 | }); 122 | 123 | test("%=", () => { 124 | const num = evaluate( 125 | ` 126 | var a = 1; 127 | var b = 2; 128 | a %= 2; 129 | 130 | ` 131 | ); 132 | deepEqual(num, 1); 133 | }); 134 | 135 | test(">", () => { 136 | const output = evaluate( 137 | ` 138 | 2 > 2; 139 | ` 140 | ); 141 | deepEqual(output, false); 142 | }); 143 | 144 | test(">=", () => { 145 | const output = evaluate( 146 | ` 147 | 2 >= 2; 148 | ` 149 | ); 150 | deepEqual(output, true); 151 | }); 152 | 153 | test("<", () => { 154 | const output = evaluate( 155 | ` 156 | 2 < 2; 157 | ` 158 | ); 159 | deepEqual(output, false); 160 | }); 161 | 162 | test("<=", () => { 163 | const output = evaluate( 164 | ` 165 | 2 <= 2; 166 | ` 167 | ); 168 | deepEqual(output, true); 169 | }); 170 | 171 | test(">>", () => { 172 | const output = evaluate( 173 | ` 174 | 2 >> 2; 175 | ` 176 | ); 177 | deepEqual(output, 0); 178 | }); 179 | 180 | test(">>>", () => { 181 | const output = evaluate( 182 | ` 183 | 2 >>> 2; 184 | ` 185 | ); 186 | deepEqual(output, 0); 187 | }); 188 | 189 | test("<<", () => { 190 | const output = evaluate( 191 | ` 192 | 2 << 2; 193 | ` 194 | ); 195 | deepEqual(output, 8); 196 | }); 197 | 198 | test("&", () => { 199 | const output = evaluate( 200 | ` 201 | 2 & 2; 202 | ` 203 | ); 204 | deepEqual(output, 2); 205 | }); 206 | 207 | test("|", () => { 208 | const output = evaluate( 209 | ` 210 | 2 | 2; 211 | ` 212 | ); 213 | deepEqual(output, 2); 214 | }); 215 | -------------------------------------------------------------------------------- /test/conditional/ConditionalExpression.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("ConditionalExpression-1", () => { 8 | const num = evaluate( 9 | ` 10 | true ? 1 : 2; 11 | ` 12 | ); 13 | 14 | deepEqual(num, 1); 15 | }); 16 | 17 | test("ConditionalExpression-or-2", () => { 18 | const num = evaluate( 19 | ` 20 | false ? 1 : 2; 21 | ` 22 | ); 23 | 24 | deepEqual(num, 2); 25 | }); 26 | 27 | test("ConditionalExpression with function call", () => { 28 | const num = evaluate( 29 | ` 30 | function isOnline(){ 31 | return true 32 | } 33 | isOnline() ? 1 : 2; 34 | ` 35 | ); 36 | 37 | deepEqual(num, 1); 38 | }); 39 | 40 | test("ConditionalExpression in function call", () => { 41 | const isAdult = evaluate( 42 | ` 43 | function isAdult(age){ 44 | return age >= 18 ? true : false 45 | } 46 | isAdult; 47 | ` 48 | ); 49 | 50 | deepEqual(isAdult(18), true); 51 | deepEqual(isAdult(17.999), false); 52 | }); 53 | -------------------------------------------------------------------------------- /test/do-while/DoWhileStatement.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("basic", () => { 8 | const obj = evaluate( 9 | ` 10 | var obj = { 11 | i: 0 12 | }; 13 | do { 14 | obj.i++; 15 | } while (obj.i < 3); 16 | 17 | obj; 18 | ` 19 | ); 20 | 21 | deepEqual(true, typeof obj.i === "number"); 22 | deepEqual(obj.i, 3); 23 | }); 24 | 25 | test("break in do block", () => { 26 | const obj = evaluate( 27 | ` 28 | var obj = { 29 | i: 0 30 | }; 31 | do { 32 | obj.i++; 33 | break; 34 | } while (obj.i < 3); 35 | 36 | obj; 37 | ` 38 | ); 39 | deepEqual(obj.i, 1); 40 | }); 41 | 42 | test("do-while in function with return, it should cross block scope", () => { 43 | const get = evaluate( 44 | ` 45 | function get() { 46 | var obj = { 47 | i: 0 48 | }; 49 | do { 50 | obj.i++; 51 | return obj; 52 | } while (obj.i < 3); 53 | } 54 | 55 | get; 56 | ` 57 | ); 58 | deepEqual(get(), { i: 1 }); 59 | }); 60 | -------------------------------------------------------------------------------- /test/do-while/label.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("break with label", () => { 8 | const a = evaluate( 9 | ` 10 | var a = 1; 11 | 12 | doLoop: 13 | do { 14 | a++; 15 | break doLoop; 16 | } while (true); 17 | 18 | a; 19 | `, 20 | 21 | ); 22 | deepEqual(a, 2); 23 | }); 24 | 25 | test("continue with label", () => { 26 | const a = evaluate( 27 | ` 28 | var a = 1; 29 | 30 | doLoop: 31 | do { 32 | a++; 33 | continue doLoop; 34 | } while (a<10); 35 | 36 | a; 37 | `, 38 | 39 | ); 40 | deepEqual(a, 10); 41 | }); 42 | -------------------------------------------------------------------------------- /test/ecmaVersion/ecmaVersion.test.ts: -------------------------------------------------------------------------------- 1 | import { Interpreter } from "../../src"; 2 | 3 | test("es3", () => { 4 | let hasError = false; 5 | const interpreter = new Interpreter( 6 | {}, 7 | { 8 | ecmaVersion: 3, 9 | } 10 | ); 11 | try { 12 | interpreter.evaluate( 13 | ` 14 | var data = { 15 | _value: 4, 16 | get value(){ 17 | return this._value; 18 | } 19 | } 20 | ` 21 | ); 22 | } catch (e) { 23 | hasError = true; 24 | } 25 | 26 | expect(hasError).toEqual(true); 27 | }); 28 | 29 | test("es5 -1", () => { 30 | let hasError = false; 31 | const interpreter = new Interpreter( 32 | {}, 33 | { 34 | ecmaVersion: 5, 35 | } 36 | ); 37 | try { 38 | interpreter.evaluate( 39 | ` 40 | var data = { 41 | _value: 4, 42 | get value(){ 43 | return this._value; 44 | } 45 | } 46 | ` 47 | ); 48 | } catch (e) { 49 | hasError = true; 50 | } 51 | 52 | expect(hasError).toEqual(false); 53 | }); 54 | 55 | test("es5 -2", () => { 56 | let hasError = false; 57 | const interpreter = new Interpreter( 58 | {}, 59 | { 60 | ecmaVersion: 5, 61 | } 62 | ); 63 | try { 64 | interpreter.evaluate( 65 | ` 66 | let i = 1; 67 | ` 68 | ); 69 | } catch (e) { 70 | hasError = true; 71 | } 72 | 73 | expect(hasError).toEqual(true); 74 | }); 75 | -------------------------------------------------------------------------------- /test/evaluate/evaluate.test.ts: -------------------------------------------------------------------------------- 1 | import { Interpreter } from "../../src"; 2 | 3 | test("evaluate result -1", () => { 4 | const interpreter = new Interpreter(); 5 | 6 | const value = interpreter.evaluate(` 7 | var a = 1; 8 | a; 9 | `); 10 | 11 | expect(value).toEqual(1); 12 | }); 13 | 14 | test("evaluate result -2", () => { 15 | const interpreter = new Interpreter(); 16 | 17 | const ret1 = interpreter.evaluate(` 18 | var a = 1; 19 | a; 20 | `); 21 | 22 | const ret2 = interpreter.evaluate(` 23 | var b = 1; 24 | `); 25 | 26 | expect(ret1).toEqual(1); 27 | expect(ret2).toEqual(undefined); 28 | }); 29 | 30 | test("evaluate result -3", () => { 31 | const interpreter = new Interpreter(); 32 | 33 | const ret1 = interpreter.evaluate(` 34 | function fn(){ return 1 }; 35 | fn(); 36 | `); 37 | 38 | const ret2 = interpreter.evaluate(` 39 | var b = 1; 40 | `); 41 | 42 | expect(ret1).toEqual(1); 43 | expect(ret2).toEqual(undefined); 44 | }); 45 | 46 | test("evaluate result -4", () => { 47 | const interpreter = new Interpreter({}); 48 | 49 | const ret1 = interpreter.evaluate(` 50 | var a = (function () { 51 | var b = 1; 52 | return 2; 53 | })(); 54 | `); 55 | 56 | expect(ret1).toEqual(undefined); 57 | }); 58 | -------------------------------------------------------------------------------- /test/for-in/ForInStatement.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("ForInStatement-1", () => { 8 | const obj = evaluate( 9 | ` 10 | var obj = { 11 | 1: false, 12 | 2: false 13 | }; 14 | 15 | for (var attr in obj) { 16 | obj[attr] = true; 17 | } 18 | 19 | obj; 20 | ` 21 | ); 22 | 23 | deepEqual(true, obj[1]); 24 | deepEqual(true, obj[2]); 25 | }); 26 | 27 | test("ForInStatement-2", () => { 28 | const obj = evaluate( 29 | ` 30 | var obj = { 31 | 1: false, 32 | 2: false 33 | }; 34 | 35 | for (var attr in obj) { 36 | if (obj.hasOwnProperty(attr)){ 37 | obj[attr] = true; 38 | } 39 | } 40 | 41 | obj; 42 | ` 43 | ); 44 | 45 | deepEqual(true, obj[1]); 46 | deepEqual(true, obj[2]); 47 | }); 48 | -------------------------------------------------------------------------------- /test/for-in/label.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("break with label", () => { 8 | const obj = evaluate( 9 | ` 10 | var obj = { 11 | 1: false, 12 | 2: false, 13 | 3: false 14 | }; 15 | 16 | loop1: 17 | for (var attr in obj) { 18 | obj[attr] = true; 19 | if (attr % 2 === 0){ 20 | break loop1; 21 | } 22 | } 23 | 24 | obj; 25 | ` 26 | ); 27 | 28 | deepEqual(obj, { 29 | 1: true, 30 | 2: true, 31 | 3: false, 32 | }); 33 | }); 34 | 35 | test("break with label", () => { 36 | const { attr, index } = evaluate( 37 | ` 38 | var obj = { 39 | 1: false, 40 | 2: false, 41 | 3: false 42 | }; 43 | 44 | loop1: 45 | for (var attr in obj) { 46 | obj[attr] = true; 47 | loop2: 48 | for (var index in [1,2,3,4]){ 49 | if ((index + 1)%3 === 0){ 50 | break loop1; 51 | } 52 | } 53 | } 54 | 55 | d = {attr: attr, index: index}; 56 | ` 57 | ); 58 | 59 | deepEqual(attr, "1"); 60 | deepEqual(index, "2"); 61 | }); 62 | 63 | test("continue with label", () => { 64 | const { attr, index, m } = evaluate( 65 | ` 66 | var obj = { 67 | 1: false, 68 | 2: false, 69 | 3: false 70 | }; 71 | 72 | loop1: 73 | for (var attr in obj) { 74 | obj[attr] = true; 75 | loop2: 76 | for (var index in [1,2,3,4]){ 77 | if ((index + 1)%3 === 0){ 78 | break loop1; 79 | } 80 | loop3: 81 | for (var m in [1, 2, 3, 4]){ 82 | if ((m + 1) % 2 === 0){ 83 | continue loop2; 84 | } 85 | } 86 | } 87 | } 88 | 89 | d = {attr: attr, index:index,m: m}; 90 | ` 91 | ); 92 | 93 | deepEqual(attr, "1"); 94 | deepEqual(index, "2"); 95 | deepEqual(m, "3"); 96 | }); 97 | -------------------------------------------------------------------------------- /test/for/ForStatement.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("ForStatement-1", () => { 8 | const obj = evaluate( 9 | ` 10 | var obj = {num: 0}; 11 | for (var i = 0; i < 3; i++) { 12 | obj.num++; 13 | } 14 | 15 | obj; 16 | ` 17 | ); 18 | 19 | deepEqual(true, typeof obj.num === "number"); 20 | deepEqual(obj.num, 3); 21 | }); 22 | 23 | test("ForStatement-2", () => { 24 | const obj = evaluate( 25 | ` 26 | var obj = {num: 0}; 27 | for (;;) { 28 | obj.num++; 29 | if (obj.num >= 3) { 30 | break; 31 | } 32 | } 33 | 34 | obj; 35 | ` 36 | ); 37 | 38 | deepEqual(true, typeof obj.num === "number"); 39 | deepEqual(obj.num, 3); 40 | }); 41 | 42 | test("ForStatement-3", () => { 43 | const a = evaluate( 44 | ` 45 | var c = 0; 46 | for ( var i = 0;++i<5;) { 47 | c++ 48 | } 49 | c 50 | ` 51 | ); 52 | 53 | deepEqual(a, 4); 54 | }); 55 | -------------------------------------------------------------------------------- /test/for/label.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("break with label", () => { 8 | const num = evaluate( 9 | ` 10 | var num; 11 | 12 | loop1: 13 | for (var i=0;i<10;i++) { 14 | if (i===6){ 15 | break loop1; 16 | } 17 | num = ++i; 18 | } 19 | 20 | num; 21 | ` 22 | ); 23 | deepEqual(true, typeof num === "number"); 24 | deepEqual(num, 5); 25 | }); 26 | 27 | test("nest for loop with label", () => { 28 | const { i, m } = evaluate( 29 | ` 30 | 31 | loop1: 32 | for (var i=0;i<3;i++) { 33 | loop2: 34 | for (var m=1;m<3;m++){ 35 | if (m%2===0){ 36 | break loop1; 37 | } 38 | } 39 | } 40 | 41 | d= {i: i, m: m}; 42 | ` 43 | ); 44 | deepEqual(i, 0); 45 | deepEqual(m, 2); 46 | }); 47 | 48 | test("endless for loop with label", () => { 49 | const { i, m, y } = evaluate( 50 | ` 51 | loop1: 52 | for (var i=0;i<3;i++) { 53 | loop2: 54 | for (var m=1;m<3;m++){ 55 | if (m%2===0){ 56 | break loop1; 57 | } 58 | loop3: 59 | for (var y = 1; y < 10; y++){ 60 | if (y%5===0){ 61 | break loop2; 62 | } 63 | } 64 | } 65 | } 66 | 67 | d= {i: i, m: m, y: y}; 68 | ` 69 | ); 70 | deepEqual(i, 3); 71 | deepEqual(m, 1); 72 | deepEqual(y, 5); 73 | }); 74 | 75 | test("continue with label", () => { 76 | const { i, m, y } = evaluate( 77 | ` 78 | loop1: 79 | for (var i=0;i<3;i++) { 80 | loop2: 81 | for (var m=1;m<3;m++){ 82 | if (m%2===0){ 83 | break loop1; 84 | } 85 | loop3: 86 | for (var y = 1; y < 10; y++){ 87 | if (y%5===0){ 88 | continue loop2; // skip loop2 89 | } 90 | } 91 | } 92 | } 93 | 94 | d= {i: i, m: m, y: y}; 95 | ` 96 | ); 97 | deepEqual(i, 0); 98 | deepEqual(m, 2); 99 | deepEqual(y, 5); 100 | }); 101 | -------------------------------------------------------------------------------- /test/function/FunctionExpression.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate, Interpreter } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | function throws(fn) { 8 | expect(fn).toThrow(); 9 | } 10 | 11 | test("declare function -1", () => { 12 | const a = evaluate( 13 | ` 14 | var ttx=1; 15 | function ttx(name){ 16 | return "hello " + name; 17 | } 18 | 19 | ttx 20 | 21 | ` 22 | ); 23 | 24 | deepEqual(typeof a, "number"); 25 | }); 26 | 27 | test("declare function -2", () => { 28 | const a = evaluate( 29 | ` 30 | function ttxr(name){ 31 | return 1; 32 | } 33 | function ttxr(name){ 34 | return 2; 35 | } 36 | 37 | ttxr 38 | 39 | ` 40 | ); 41 | 42 | deepEqual(a(), 2); 43 | }); 44 | 45 | test("FunctionExpression-1", () => { 46 | const testFunc = evaluate( 47 | ` 48 | function test_x1(name){ 49 | return "hello " + name; 50 | } 51 | test_x1; 52 | ` 53 | ); 54 | 55 | deepEqual(true, typeof testFunc === "function"); 56 | deepEqual(testFunc.length, 1); 57 | deepEqual(testFunc.name, "test_x1"); 58 | deepEqual(testFunc("world"), "hello world"); 59 | }); 60 | 61 | test("FunctionDeclaration-2", () => { 62 | const testFunc = evaluate( 63 | ` 64 | var func = function(name){ 65 | return "hello " + name; 66 | } 67 | 68 | func; 69 | ` 70 | ); 71 | 72 | deepEqual(true, typeof testFunc === "function"); 73 | deepEqual(testFunc.length, 1); 74 | deepEqual(testFunc.name, "func"); 75 | deepEqual(testFunc("world"), "hello world"); 76 | }); 77 | 78 | test("FunctionDeclaration-name", () => { 79 | const inst = new Interpreter({}, { ecmaVersion: 6 }); 80 | const person = inst.evaluate( 81 | ` 82 | var person = { 83 | sayName() { 84 | console.log('hello!'); 85 | }, 86 | }; 87 | 88 | person; 89 | ` 90 | ); 91 | 92 | deepEqual(person.sayName.name, "sayName"); 93 | }); 94 | 95 | test("invalid function call", () => { 96 | throws(() => { 97 | evaluate( 98 | ` 99 | const a = 123; 100 | 101 | a(); // a is not a function 102 | ` 103 | ); 104 | }); 105 | }); 106 | 107 | test("object-property function call name", () => { 108 | throws(() => { 109 | evaluate( 110 | ` 111 | var obj = {}; 112 | obj.a(); 113 | ` 114 | ); 115 | }); 116 | }); 117 | 118 | test("object-property function call name", () => { 119 | throws(() => { 120 | evaluate( 121 | ` 122 | var obj = {}; 123 | obj["a"](); 124 | ` 125 | ); 126 | }); 127 | }); 128 | 129 | test("function params should can be overwrite", () => { 130 | const test = evaluate( 131 | ` 132 | function test_1 (a) { 133 | a = a || 'hello' 134 | return a 135 | } 136 | 137 | test_1 138 | ` 139 | ); 140 | 141 | deepEqual(test(), "hello"); 142 | }); 143 | 144 | test("object function scope -1", () => { 145 | const a = evaluate( 146 | ` 147 | var dx = { 148 | fy: function fy1() { 149 | return typeof fy1 150 | } 151 | }; 152 | 153 | [typeof fy1, dx.fy, dx.fy()] 154 | ` 155 | ); 156 | 157 | deepEqual(a[0], "undefined"); 158 | deepEqual(a[1].name, "fy1"); 159 | deepEqual(a[2], "function"); 160 | }); 161 | 162 | test("object function scope -2", () => { 163 | const a = evaluate( 164 | ` 165 | var d = { 166 | fy: function() { 167 | return typeof fy 168 | } 169 | }; 170 | 171 | [d.fy.name, d.fy()] 172 | ` 173 | ); 174 | 175 | deepEqual(a[0], "fy"); 176 | deepEqual(a[1], "undefined"); 177 | }); 178 | 179 | test("object function scope -3", () => { 180 | const inst = new Interpreter({}, { ecmaVersion: 6 }); 181 | const a = inst.evaluate( 182 | ` 183 | var d = { 184 | fy() { 185 | return typeof fy 186 | } 187 | }; 188 | 189 | [d.fy.name, d.fy()] 190 | ` 191 | ); 192 | 193 | deepEqual(a[0], "fy"); 194 | deepEqual(a[1], "undefined"); 195 | }); 196 | 197 | test("function name scope -1", () => { 198 | const a = evaluate( 199 | ` 200 | var tuh = 1; 201 | var t1 = function(){ return typeof t1 }; 202 | tuh = function(){ return typeof tuh }; 203 | 204 | [t1.name,t1(), tuh()] 205 | ` 206 | ); 207 | 208 | deepEqual(a, ["t1", "function", "function"]); 209 | }); 210 | 211 | test("function name scope -2", () => { 212 | const a = evaluate( 213 | ` 214 | var u = 1 215 | 216 | var x = function u() { u = 2; return u }; 217 | 218 | [u, x()] 219 | ` 220 | ); 221 | 222 | deepEqual(a, [1, 2]); 223 | }); 224 | 225 | test("function call non context", () => { 226 | const a = evaluate( 227 | ` 228 | function call_1(){ 229 | return typeof this; 230 | } 231 | call_1(); 232 | ` 233 | ); 234 | 235 | deepEqual(a, "undefined"); 236 | }); 237 | 238 | test("function call default context", () => { 239 | Interpreter.globalContextInFunction = "eval5"; 240 | 241 | const a = evaluate( 242 | ` 243 | function call_2(){ 244 | return this; 245 | } 246 | call_2(); 247 | ` 248 | ); 249 | 250 | Interpreter.globalContextInFunction = void 0; 251 | 252 | deepEqual(a, "eval5"); 253 | }); 254 | 255 | test("function call context", () => { 256 | Interpreter.globalContextInFunction = "eval5"; 257 | 258 | const a = evaluate( 259 | ` 260 | function call_2(){ 261 | return this; 262 | } 263 | call_2(); 264 | `, 265 | undefined, 266 | { 267 | globalContextInFunction: "eval5 context", 268 | } 269 | ); 270 | 271 | Interpreter.globalContextInFunction = void 0; 272 | 273 | deepEqual(a, "eval5 context"); 274 | }); 275 | 276 | test("function overlap1", () => { 277 | const ctx: { [x: string]: any } = {}; 278 | ctx.overlap1 = function () { 279 | return 1; 280 | }; 281 | 282 | const a = evaluate( 283 | ` 284 | function overlap1(){ 285 | return 2; 286 | } 287 | overlap1(); 288 | `, 289 | ctx 290 | ); 291 | 292 | deepEqual(a, 2); 293 | }); 294 | 295 | test("function overlap2", () => { 296 | const a = evaluate( 297 | ` 298 | var overlap1 = 1; 299 | function overlap1(){ 300 | return 2; 301 | } 302 | typeof overlap1; 303 | `, 304 | {} 305 | ); 306 | 307 | deepEqual(a, "number"); 308 | }); 309 | 310 | test("function overlap3", () => { 311 | var ctx = {}; 312 | 313 | evaluate( 314 | ` 315 | var overlap1 = 1; 316 | `, 317 | ctx 318 | ); 319 | 320 | const a = evaluate( 321 | ` 322 | function overlap1(){ 323 | return 2; 324 | } 325 | typeof overlap1; 326 | `, 327 | ctx 328 | ); 329 | 330 | deepEqual(a, "function"); 331 | }); 332 | 333 | test("function overlap4", () => { 334 | var ctx = {}; 335 | 336 | const a = evaluate( 337 | ` 338 | var dat = undefined; 339 | function dat() { } 340 | 341 | typeof dat 342 | `, 343 | ctx 344 | ); 345 | 346 | deepEqual(a, "undefined"); 347 | }); 348 | 349 | test("function overlap5", () => { 350 | var ctx = {}; 351 | 352 | const a = evaluate( 353 | ` 354 | var dat; 355 | function dat() { } 356 | 357 | typeof dat 358 | `, 359 | ctx 360 | ); 361 | 362 | deepEqual(a, "function"); 363 | }); 364 | 365 | test("function overlap5", () => { 366 | var ctx = {}; 367 | 368 | const a = evaluate( 369 | ` 370 | function dat() { 371 | function d1(){} 372 | } 373 | 374 | typeof d1 375 | `, 376 | ctx 377 | ); 378 | 379 | deepEqual(a, "undefined"); 380 | }); 381 | 382 | test("function .call -1", () => { 383 | const a = evaluate( 384 | ` 385 | function test(){ 386 | return this; 387 | } 388 | test(); 389 | `, 390 | {} 391 | ); 392 | 393 | deepEqual(a, undefined); 394 | }); 395 | 396 | test("function .call -2", () => { 397 | const a = evaluate( 398 | ` 399 | function test(){ 400 | return this; 401 | } 402 | test.call(100); 403 | `, 404 | {} 405 | ); 406 | 407 | deepEqual(a, 100); 408 | }); 409 | 410 | test("function .call -3", () => { 411 | const a = evaluate( 412 | ` 413 | function test(){ 414 | return this; 415 | } 416 | test.bind(100)(); 417 | `, 418 | {} 419 | ); 420 | 421 | deepEqual(a, 100); 422 | }); 423 | 424 | test("function .call -4", () => { 425 | const a = evaluate( 426 | ` 427 | function test(){ 428 | return this.o; 429 | } 430 | var da = { 431 | o: true, 432 | func: test, 433 | } 434 | da.func(); 435 | `, 436 | {} 437 | ); 438 | 439 | deepEqual(a, true); 440 | }); 441 | 442 | test("function .call -5", () => { 443 | const a = evaluate( 444 | ` 445 | function test(){ 446 | return this; 447 | } 448 | var da = { 449 | o: true, 450 | func: test, 451 | }; 452 | (0, da.func)(); 453 | `, 454 | {} 455 | ); 456 | 457 | deepEqual(a, undefined); 458 | }); 459 | 460 | test("function toString -1", () => { 461 | const a = evaluate( 462 | ` 463 | function test(a,b,c,d){return this;} 464 | 465 | test 466 | `, 467 | {} 468 | ); 469 | 470 | expect(a.toString()).toEqual(`function test(a,b,c,d){return this;}`); 471 | }); 472 | 473 | test("function toString -2", () => { 474 | const interpreter = new Interpreter({}); 475 | 476 | const a = interpreter.evaluate( 477 | ` 478 | function test(a,b,c,d){return this;} 479 | 480 | test 481 | ` 482 | ); 483 | 484 | interpreter.evaluate( 485 | ` 486 | test 487 | ` 488 | ); 489 | 490 | expect(a.toString()).toEqual(`function test(a,b,c,d){return this;}`); 491 | }); 492 | 493 | test("function arguments", () => { 494 | const interpreter = new Interpreter({}); 495 | 496 | const a = interpreter.evaluate( 497 | ` 498 | function test(arguments){ 499 | return arguments 500 | } 501 | 502 | test(1); 503 | ` 504 | ); 505 | 506 | expect(a).toEqual(1); 507 | }); 508 | 509 | test("function arguments reset without use strict", () => { 510 | const interpreter = new Interpreter({}); 511 | 512 | const a = interpreter.evaluate( 513 | ` 514 | function test(a,b,c){ 515 | // Note: eval5 does not support the use strict mode, but the performance here is the same as the use strict mode 516 | a=2; 517 | return [arguments[0],arguments[1],arguments[2]]; 518 | } 519 | 520 | test(1,2,3); 521 | ` 522 | ); 523 | 524 | expect(a).toEqual([1, 2, 3]); 525 | }); 526 | -------------------------------------------------------------------------------- /test/if-else/IfStatement.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("if", () => { 8 | const obj = evaluate( 9 | ` 10 | var obj = { 11 | isTrue: false 12 | }; 13 | 14 | if (true){ 15 | obj.isTrue = true; 16 | } 17 | 18 | obj; 19 | ` 20 | ); 21 | 22 | deepEqual(true, typeof obj.isTrue === "boolean"); 23 | deepEqual(true, obj.isTrue); 24 | }); 25 | 26 | test("if-else", () => { 27 | const obj = evaluate( 28 | ` 29 | var obj = { 30 | isTrue: false 31 | }; 32 | 33 | if (false){ 34 | obj.isTrue = true; 35 | }else{ 36 | obj.isTrue = true; 37 | } 38 | 39 | obj; 40 | ` 41 | ); 42 | 43 | deepEqual(true, typeof obj.isTrue === "boolean"); 44 | deepEqual(true, obj.isTrue); 45 | }); 46 | 47 | test("if else-else if", () => { 48 | const obj = evaluate( 49 | ` 50 | var obj = { 51 | block: '' 52 | }; 53 | 54 | if (false){ 55 | obj.block = "if"; 56 | }else if(true){ 57 | obj.block = "else if"; 58 | } 59 | 60 | obj; 61 | ` 62 | ); 63 | 64 | deepEqual(obj.block, "else if"); 65 | }); 66 | 67 | test("if-else-else if-else", () => { 68 | const obj = evaluate( 69 | ` 70 | var obj = { 71 | block: '' 72 | }; 73 | 74 | if (false){ 75 | obj.block = "if"; 76 | }else if(false){ 77 | obj.block = "else if"; 78 | }else{ 79 | obj.block = "else"; 80 | } 81 | 82 | obj; 83 | ` 84 | ); 85 | 86 | deepEqual(obj.block, "else"); 87 | }); 88 | -------------------------------------------------------------------------------- /test/literal/Literal.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("Literal", () => { 8 | const output = evaluate( 9 | ` 10 | d = { 11 | a: null, 12 | b: undefined, 13 | c: 0, 14 | d: "1", 15 | e: true 16 | }; 17 | ` 18 | ); 19 | deepEqual(output, { 20 | a: null, 21 | b: undefined, 22 | c: 0, 23 | d: "1", 24 | e: true, 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/logic-operator/LogicalExpression.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("LogicalExpression-or-1", () => { 8 | const num = evaluate( 9 | ` 10 | 0 || 2; 11 | ` 12 | ); 13 | 14 | deepEqual(num, 2); 15 | }); 16 | 17 | test("LogicalExpression-or-2", () => { 18 | const num = evaluate( 19 | ` 20 | 1 || 2; 21 | ` 22 | ); 23 | 24 | deepEqual(num, 1); 25 | }); 26 | 27 | test("LogicalExpression-and-1", () => { 28 | const num = evaluate( 29 | ` 30 | 1 && 2; 31 | ` 32 | ); 33 | 34 | deepEqual(num, 2); 35 | }); 36 | 37 | test("LogicalExpression-and-2", () => { 38 | const num = evaluate( 39 | ` 40 | 0 && 2; 41 | ` 42 | ); 43 | 44 | deepEqual(num, 0); 45 | }); 46 | -------------------------------------------------------------------------------- /test/new/NewExpression.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("NewExpression", () => { 8 | const { people, People } = evaluate( 9 | ` 10 | function People(name, age){ 11 | this.name = name; 12 | } 13 | 14 | d = { 15 | people: new People("eval5", 12), 16 | People: People 17 | }; 18 | ` 19 | ); 20 | 21 | // constructor 22 | deepEqual(People.length, 2); 23 | deepEqual(People.name, "People"); 24 | 25 | // entity 26 | deepEqual(true, people instanceof People); 27 | deepEqual(people.name, "eval5"); 28 | deepEqual(true, people.constructor === People); 29 | }); 30 | 31 | test("NewExpression for built-in functions", () => { 32 | const { array, date, regexp } = evaluate( 33 | ` 34 | var array = new Array(1, 2, 3); 35 | var date = new Date(); 36 | var regexp = new RegExp('abc'); 37 | 38 | d = { 39 | array: array, 40 | date: date, 41 | regexp: regexp 42 | } 43 | ` 44 | ); 45 | 46 | deepEqual(array.length, 3); 47 | deepEqual(true, date <= new Date()); 48 | deepEqual(true, regexp instanceof RegExp); 49 | }); 50 | 51 | test("NewExpression for constructor function which return object", () => { 52 | const { o, p } = evaluate( 53 | ` 54 | var o = { 55 | a: 1 56 | } 57 | 58 | function P() { 59 | this.name = 1 60 | 61 | return o 62 | } 63 | 64 | var p = new P() 65 | 66 | d= { 67 | o: o, 68 | p: p 69 | } 70 | ` 71 | ); 72 | 73 | deepEqual(o, p); 74 | }); 75 | -------------------------------------------------------------------------------- /test/object/ObjectExpression.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate, Interpreter } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("basic", () => { 8 | const obj = evaluate( 9 | ` 10 | var obj = { 11 | i: 0 12 | }; 13 | 14 | obj; 15 | ` 16 | ); 17 | 18 | deepEqual(true, typeof obj.i === "number"); 19 | deepEqual(obj.i, 0); 20 | }); 21 | 22 | test("object with method -1", () => { 23 | const obj = evaluate( 24 | ` 25 | var obj = { 26 | i: 0, 27 | get: function(){ 28 | return this.i; 29 | } 30 | }; 31 | 32 | obj; 33 | ` 34 | ); 35 | deepEqual(obj.i, 0); 36 | deepEqual(obj.get(), obj.i); 37 | }); 38 | 39 | test("object with method -2", () => { 40 | const inst = new Interpreter({}, { ecmaVersion: 6 }); 41 | const obj = inst.evaluate( 42 | ` 43 | var obj = { 44 | i: 0, 45 | get(){ 46 | return this.i; 47 | } 48 | }; 49 | 50 | obj; 51 | ` 52 | ); 53 | deepEqual(obj.i, 0); 54 | deepEqual(obj.get(), obj.i); 55 | }); 56 | 57 | test("object with getter method", () => { 58 | const obj = evaluate( 59 | ` 60 | var obj = { 61 | i: 0, 62 | get value(){ 63 | return this.i; 64 | } 65 | }; 66 | 67 | obj; 68 | ` 69 | ); 70 | deepEqual(obj.i, 0); 71 | deepEqual(obj.value, obj.i); 72 | }); 73 | 74 | test("object with setter method -1", () => { 75 | const obj = evaluate( 76 | ` 77 | var obj = { 78 | getCounter: 0, 79 | i: 0, 80 | get value(){ 81 | this.getCounter++; 82 | return this.i 83 | }, 84 | set value(val){ 85 | this.i = val; 86 | } 87 | }; 88 | 89 | obj; 90 | ` 91 | ); 92 | deepEqual(obj.i, 0); 93 | obj.value = 123; 94 | deepEqual(obj.i, 123); 95 | expect(obj.getCounter).toBe(0); 96 | }); 97 | 98 | test("object with setter method -2", () => { 99 | const r = evaluate( 100 | ` 101 | var obj = { 102 | getCounter: 0, 103 | i: 0, 104 | get value(){ 105 | this.getCounter++; 106 | return this.i 107 | }, 108 | set value(val){ 109 | this.i = val; 110 | } 111 | }; 112 | 113 | obj; 114 | var result = [obj.i]; 115 | obj.value = 123; 116 | result.push(obj.i); 117 | result.push(obj.getCounter); 118 | result 119 | ` 120 | ); 121 | 122 | expect(r).toEqual([0, 123, 0]); 123 | }); 124 | 125 | test("object function name", () => { 126 | const inst = new Interpreter({}, { ecmaVersion: 6 }); 127 | const a = inst.evaluate( 128 | ` 129 | var obj = { 130 | v1(){ 131 | }, 132 | 133 | v2: function(){}, 134 | 135 | v: function v3(){} 136 | 137 | }; 138 | 139 | [obj.v1.name, obj.v2.name, obj.v.name] 140 | ` 141 | ); 142 | deepEqual(a, ["v1", "v2", "v3"]); 143 | }); 144 | -------------------------------------------------------------------------------- /test/regular-expression/RegExpLiteral.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("basic without flags", () => { 8 | const func = evaluate( 9 | ` 10 | var reg = /^hello/; 11 | 12 | function isSayHi(word) { 13 | return reg.test(word); 14 | } 15 | 16 | isSayHi; 17 | ` 18 | ); 19 | 20 | deepEqual(true, func("hello world")); 21 | deepEqual(false, func("abcd")); 22 | }); 23 | 24 | test("with flags", () => { 25 | const func = evaluate( 26 | ` 27 | var reg = /^hello/i; 28 | 29 | function isSayHi(word) { 30 | return reg.test(word); 31 | } 32 | 33 | isSayHi; 34 | ` 35 | ); 36 | 37 | deepEqual(true, func("hello world")); 38 | deepEqual(true, func("Hello woRld")); 39 | }); 40 | 41 | test("with multiple flags", () => { 42 | const func = evaluate( 43 | ` 44 | var reg = /^hello/im; 45 | 46 | function isSayHi(word) { 47 | return reg.test(word); 48 | } 49 | 50 | isSayHi; 51 | ` 52 | ); 53 | 54 | deepEqual(true, func("hello world")); 55 | deepEqual(true, func("Hello woRld")); 56 | deepEqual(true, func("Hello \nwoRld")); 57 | }); 58 | -------------------------------------------------------------------------------- /test/root-context/RootContext.test.ts: -------------------------------------------------------------------------------- 1 | import { Interpreter } from "../../src"; 2 | 3 | test("rootContext -1", () => { 4 | const rootContext = { a: 1, b: 1, c: 1 }; 5 | const ctx = { a: 2 }; 6 | const interpreter = new Interpreter(ctx, { 7 | rootContext: rootContext, 8 | }); 9 | const result = interpreter.evaluate( 10 | ` 11 | [a,b,c] 12 | ` 13 | ); 14 | expect(result).toEqual([2, 1, 1]); 15 | }); 16 | 17 | test("rootContext -2", () => { 18 | const rootContext: Record = { a: 1, b: 1, c: 1 }; 19 | const ctx: Record = { a: 2 }; 20 | const interpreter = new Interpreter(ctx, { 21 | rootContext: rootContext, 22 | }); 23 | const result = interpreter.evaluate( 24 | ` 25 | var t = 1; 26 | y = 2 27 | ` 28 | ); 29 | expect(ctx.t).toEqual(1); 30 | expect(ctx.y).toEqual(2); 31 | expect(rootContext.t).toEqual(undefined); 32 | expect(rootContext.y).toEqual(undefined); 33 | }); 34 | 35 | test("rootContext -3", () => { 36 | const rootContext: Record = { a: 1, b: 1, c: 1 }; 37 | const ctx: Record = { a: 2 }; 38 | const interpreter = new Interpreter(ctx, { 39 | rootContext: rootContext, 40 | }); 41 | const result = interpreter.evaluate( 42 | ` 43 | delete a; 44 | delete b; 45 | [a,b]; 46 | ` 47 | ); 48 | expect(result).toEqual([1, 1]); 49 | }); 50 | 51 | test("rootContext -4", () => { 52 | const rootContext: Record = { a: 1, b: 1, c: 1, data: { z: 1 } }; 53 | const ctx: Record = { a: 2 }; 54 | const interpreter = new Interpreter(ctx, { 55 | rootContext: rootContext, 56 | }); 57 | const result = interpreter.evaluate( 58 | ` 59 | delete eval; 60 | delete a; 61 | delete b; 62 | delete data.z; 63 | [a,b, data.z, typeof eval]; 64 | ` 65 | ); 66 | expect(result).toEqual([1, 1, undefined, "undefined"]); 67 | }); 68 | 69 | test("rootContext -5", () => { 70 | const rootContext: Record = { a: 1, b: 1, c: 1, data: { z: 1 } }; 71 | const ctx: Record = { a: 2 }; 72 | const interpreter = new Interpreter(ctx, { 73 | rootContext: rootContext, 74 | }); 75 | const result = interpreter.evaluate( 76 | ` 77 | c = 2; 78 | c; 79 | ` 80 | ); 81 | expect(result).toEqual(2); 82 | expect(rootContext.c).toEqual(1); 83 | }); 84 | -------------------------------------------------------------------------------- /test/sequence/SequenceExpression.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("basic", () => { 8 | const a = evaluate( 9 | ` 10 | var a = (1 , 2); 11 | 12 | a; 13 | ` 14 | ); 15 | deepEqual(a, 2); 16 | }); 17 | 18 | test("with call expression", () => { 19 | const { a, b } = evaluate( 20 | ` 21 | var a = (get() , 2); 22 | var b; 23 | 24 | function get(){ 25 | b = 3; 26 | } 27 | 28 | d = {a: a, b: b}; 29 | ` 30 | ); 31 | deepEqual(a, 2); 32 | deepEqual(b, 3); 33 | }); 34 | 35 | test("with call expression", () => { 36 | const { a, b } = evaluate( 37 | ` 38 | var b; 39 | var a = (get() , 2); 40 | 41 | function get(){ 42 | b = 3; 43 | } 44 | 45 | d = {a: a, b: b}; 46 | ` 47 | ); 48 | deepEqual(a, 2); 49 | deepEqual(b, 3); 50 | }); 51 | -------------------------------------------------------------------------------- /test/super-global/SuperGlobal.test.ts: -------------------------------------------------------------------------------- 1 | const { evaluate, Interpreter } = require("../../src"); 2 | 3 | test("eval basic-1", () => { 4 | const ctx = Object.create(null); 5 | const a = evaluate( 6 | ` 7 | var a = 100; 8 | var b = 200; 9 | eval('a+b'); 10 | `, 11 | ctx 12 | ); 13 | expect(a).toEqual(300); 14 | }); 15 | 16 | test("eval basic-2", () => { 17 | const ctx = Object.create(null); 18 | const a = evaluate( 19 | ` 20 | var x = 10, y = 20; 21 | var xeval = eval; 22 | function test() { 23 | var x = 2, 24 | y = 4; 25 | var geval = eval; 26 | return [xeval("x + y"), eval("x + y"), geval("x + y"), (0, eval)("x + y")]; 27 | } 28 | test(); 29 | `, 30 | ctx 31 | ); 32 | expect(a).toEqual([30, 6, 30, 30]); 33 | }); 34 | 35 | test("eval alias name", () => { 36 | const ctx = Object.create(null); 37 | const a = evaluate( 38 | ` 39 | function test() { 40 | var x = 2; 41 | var geval = eval; 42 | return [geval("typeof x"), (0, eval)("typeof x")]; 43 | } 44 | test(); 45 | `, 46 | ctx 47 | ); 48 | expect(a).toEqual(["undefined", "undefined"]); 49 | }); 50 | 51 | test("eval with global.eval", () => { 52 | const ctx = Object.create(global); 53 | const a = evaluate( 54 | ` 55 | function test() { 56 | var xct = 2; 57 | return [eval("typeof xct"), (0, eval)("typeof xct")]; 58 | } 59 | test(); 60 | `, 61 | ctx 62 | ); 63 | expect(a).toEqual(["undefined", "undefined"]); 64 | }); 65 | 66 | test("Interpreter.eval", () => { 67 | const ctx = Object.create(global); 68 | ctx.eval = Interpreter.eval; 69 | const a = evaluate( 70 | ` 71 | function test() { 72 | var xct = 2; 73 | return [eval("typeof xct"), (0, eval)("typeof xct")]; 74 | } 75 | test(); 76 | `, 77 | ctx 78 | ); 79 | expect(a).toEqual(["number", "undefined"]); 80 | }); 81 | test("use global.Function", () => { 82 | const ctx = Object.create(global); 83 | const a = evaluate( 84 | ` 85 | var func = new Function('a','b', 'return a+b'); 86 | [func(100,200),Function.__IS_FUNCTION_FUNC] 87 | `, 88 | ctx 89 | ); 90 | expect(a).toEqual([300, undefined]); 91 | }); 92 | test("Interpreter.Function", () => { 93 | const ctx = Object.create(global); 94 | ctx.Function = Interpreter.Function; 95 | const a = evaluate( 96 | ` 97 | var func = new Function('a','b', 'return a+b'); 98 | [func(100,200),Function.__IS_FUNCTION_FUNC] 99 | `, 100 | ctx 101 | ); 102 | expect(a).toEqual([300, true]); 103 | }); 104 | 105 | test("eval basic-2", () => { 106 | const ctx = Object.create({ 107 | console, 108 | }); 109 | const a = evaluate( 110 | ` 111 | var a = 100; 112 | function test(){ 113 | var b = 200; 114 | return eval('a+b+this'); 115 | } 116 | test.call(300); 117 | `, 118 | ctx 119 | ); 120 | expect(a).toEqual(600); 121 | }); 122 | 123 | test("eval basic-3", () => { 124 | let message = ""; 125 | try { 126 | evaluate( 127 | ` 128 | new eval('1+1') 129 | ` 130 | ); 131 | } catch (e) { 132 | message = e.message; 133 | } 134 | expect(message).toEqual("eval is not a constructor [2:8]"); 135 | }); 136 | 137 | test("Function basic -1", () => { 138 | const ctx = Object.create(null); 139 | const a = evaluate( 140 | ` 141 | var func = new Function('a', 'b', 'return a+b'); 142 | 143 | [func(100,200), func]; 144 | `, 145 | ctx 146 | ); 147 | expect(a[0]).toEqual(300); 148 | expect(a[1](200, 300)).toEqual(500); 149 | }); 150 | 151 | test("Function basic -2", () => { 152 | const ctx = Object.create(null); 153 | const a = evaluate( 154 | ` 155 | var func = Function('a', 'b', 'return a+b'); 156 | 157 | [func(100,200), func]; 158 | `, 159 | ctx 160 | ); 161 | expect(a[0]).toEqual(300); 162 | expect(a[1](200, 300)).toEqual(500); 163 | }); 164 | 165 | test("global object", () => { 166 | const ctx = Object.create(null); 167 | const a = evaluate( 168 | ` 169 | [ eval, Function, 170 | NaN, 171 | Infinity, 172 | undefined, 173 | Object, 174 | Array, 175 | String, 176 | Boolean, 177 | Number, 178 | Date, 179 | RegExp, 180 | Error, 181 | TypeError, 182 | Math, 183 | parseInt, 184 | parseFloat, 185 | isNaN, 186 | isFinite, 187 | decodeURI, 188 | decodeURIComponent, 189 | encodeURI, 190 | encodeURIComponent, 191 | escape, 192 | unescape, 193 | JSON 194 | ] 195 | `, 196 | ctx 197 | ); 198 | expect(a).toEqual([ 199 | Interpreter.eval, 200 | Interpreter.Function, 201 | NaN, 202 | Infinity, 203 | undefined, 204 | Object, 205 | Array, 206 | String, 207 | Boolean, 208 | Number, 209 | Date, 210 | RegExp, 211 | Error, 212 | TypeError, 213 | Math, 214 | parseInt, 215 | parseFloat, 216 | isNaN, 217 | isFinite, 218 | decodeURI, 219 | decodeURIComponent, 220 | encodeURI, 221 | encodeURIComponent, 222 | escape, 223 | unescape, 224 | JSON, 225 | ]); 226 | }); 227 | 228 | test("delete global prop -1", () => { 229 | const ctx = { 230 | JSON, 231 | }; 232 | const a = evaluate( 233 | ` 234 | delete JSON; 235 | typeof JSON; 236 | `, 237 | ctx 238 | ); 239 | expect(a).toEqual("undefined"); 240 | }); 241 | 242 | test("delete global prop -2", () => { 243 | const O_JSON = global.JSON; 244 | const JSON_ = {} as JSON; 245 | global.JSON = JSON_; 246 | const ctx = global; 247 | const a1 = evaluate(`delete JSON; typeof JSON;`, ctx); 248 | const a2 = evaluate( 249 | ` 250 | JSON; 251 | `, 252 | ctx 253 | ); 254 | expect([a1, a2]).toEqual(["undefined", O_JSON]); 255 | 256 | global.JSON = O_JSON; 257 | }); 258 | 259 | test("replace super scope prop", () => { 260 | const ctx = Object.create({ 261 | Map: 1, 262 | }); 263 | ctx.Set = 2; 264 | ctx.Promise = 3; 265 | const a = evaluate( 266 | ` 267 | delete Map; 268 | delete Promise; 269 | [Map, Set, typeof Promise] 270 | `, 271 | ctx 272 | ); 273 | expect(a).toEqual([1, 2, "undefined"]); 274 | }); 275 | 276 | test("replace super scope prop", () => { 277 | const ctx = Object.create(null); 278 | evaluate(`d = 1`, ctx); 279 | expect(ctx).toEqual({ d: 1 }); 280 | }); 281 | 282 | test("call Interpreter.eval", () => { 283 | let msg = ""; 284 | try { 285 | Interpreter.eval("1+1"); 286 | } catch (e) { 287 | msg = e.message; 288 | } 289 | 290 | expect(msg).toEqual("Illegal call"); 291 | }); 292 | 293 | test("call Interpreter.Function", () => { 294 | let msg = ""; 295 | try { 296 | Interpreter.Function("a", "b", "c"); 297 | } catch (e) { 298 | msg = e.message; 299 | } 300 | 301 | expect(msg).toEqual("Illegal call"); 302 | }); 303 | -------------------------------------------------------------------------------- /test/switch/SwitchStatement.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("SwitchStatement", () => { 8 | const func = evaluate( 9 | ` 10 | function t1(type) { 11 | switch (type) { 12 | case "world": 13 | return "hi world"; 14 | case "eval5": 15 | return "hi eval5"; 16 | default: 17 | return "hello world"; 18 | } 19 | } 20 | 21 | t1; 22 | ` 23 | ); 24 | 25 | deepEqual(true, typeof func === "function"); 26 | deepEqual(func("world"), "hi world"); 27 | deepEqual(func("eval5"), "hi eval5"); 28 | deepEqual(func("aa"), "hello world"); 29 | }); 30 | 31 | test("SwitchStatement-2", () => { 32 | const func = evaluate( 33 | ` 34 | function t2(type) { 35 | switch (true) { 36 | case type === "world": 37 | return "hi world"; 38 | case type === "eval5": 39 | return "hi eval5"; 40 | default: 41 | return "hello world"; 42 | } 43 | } 44 | 45 | t2; 46 | ` 47 | ); 48 | 49 | deepEqual(true, typeof func === "function"); 50 | deepEqual(func("world"), "hi world"); 51 | deepEqual(func("eval5"), "hi eval5"); 52 | deepEqual(func("aa"), "hello world"); 53 | }); 54 | 55 | test("SwitchStatement with continue", () => { 56 | const func = evaluate( 57 | ` 58 | function t3(type) { 59 | var result = []; 60 | var i = 0; 61 | while (i < 5) { 62 | i++; 63 | switch (type + "") { 64 | case "0": 65 | continue; 66 | } 67 | result.push(i); 68 | } 69 | return result; 70 | } 71 | 72 | t3; 73 | ` 74 | ); 75 | 76 | // deepEqual(func(1), [1, 2, 3, 4, 5]); 77 | // deepEqual(func(2), [1, 2, 3, 4, 5]); 78 | 79 | // the will loop will be continue 80 | deepEqual(func(0), []); 81 | }); 82 | 83 | test("SwitchStatement with default continue", () => { 84 | const func = evaluate( 85 | ` 86 | function t3(type) { 87 | var result = []; 88 | var i = 0; 89 | while (i < 5) { 90 | i++; 91 | switch (type + "") { 92 | default: 93 | continue; 94 | } 95 | result.push(i); 96 | } 97 | return result; 98 | } 99 | 100 | t3; 101 | ` 102 | ); 103 | 104 | deepEqual(func(0), []); 105 | }); 106 | 107 | test("SwitchStatement with default break", () => { 108 | const func = evaluate( 109 | ` 110 | function t3(type) { 111 | var result = []; 112 | var i = 0; 113 | while (i < 5) { 114 | i++; 115 | switch (type + "") { 116 | default: 117 | break; 118 | result.push(i); 119 | } 120 | } 121 | 122 | return { 123 | result:result , 124 | i:i, 125 | }; 126 | } 127 | 128 | t3; 129 | ` 130 | ); 131 | 132 | const { result, i } = func(0); 133 | deepEqual(result, []); 134 | deepEqual(i, 5); 135 | }); 136 | 137 | 138 | 139 | test("SwitchStatement with default-case 3", () => { 140 | const r = evaluate( 141 | ` 142 | var s = []; 143 | function aa(v) { 144 | switch (v) { 145 | case 1: 146 | s.push(1); 147 | case 3: 148 | s.push(3); 149 | default: 150 | s.push("default"); 151 | case 2: 152 | s.push(2); 153 | break; 154 | case 4: 155 | s.push(4); 156 | } 157 | } 158 | aa(3); 159 | s; 160 | 161 | ` 162 | ); 163 | 164 | deepEqual(r, [3, 'default', 2]); 165 | }); 166 | 167 | 168 | 169 | test("SwitchStatement with default-case 5", () => { 170 | const r = evaluate( 171 | ` 172 | var s = []; 173 | function aa(v) { 174 | switch (v) { 175 | case 1: 176 | s.push(1); 177 | case 3: 178 | s.push(3); 179 | default: 180 | s.push("default"); 181 | case 2: 182 | s.push(2); 183 | break; 184 | case 4: 185 | s.push(4); 186 | } 187 | } 188 | aa(5); 189 | s; 190 | 191 | ` 192 | ); 193 | 194 | deepEqual(r, ['default', 2]); 195 | }); 196 | 197 | 198 | test("SwitchStatement with default-case 1", () => { 199 | const r = evaluate( 200 | ` 201 | var s = []; 202 | function aa(v) { 203 | switch (v) { 204 | case 1: 205 | s.push(1); 206 | case 3: 207 | s.push(3); 208 | default: 209 | s.push("default"); 210 | case 2: 211 | s.push(2); 212 | break; 213 | case 4: 214 | s.push(4); 215 | } 216 | } 217 | aa(1); 218 | s; 219 | 220 | ` 221 | ); 222 | 223 | deepEqual(r, [1, 3, 'default', 2]); 224 | }); 225 | 226 | 227 | test("SwitchStatement with default-case 4", () => { 228 | const r = evaluate( 229 | ` 230 | var s = []; 231 | function aa(v) { 232 | switch (v) { 233 | case 1: 234 | s.push(1); 235 | case 3: 236 | s.push(3); 237 | default: 238 | s.push("default"); 239 | case 2: 240 | s.push(2); 241 | break; 242 | case 4: 243 | s.push(4); 244 | } 245 | } 246 | aa(4); 247 | s; 248 | 249 | ` 250 | ); 251 | 252 | deepEqual(r, [4]); 253 | }); 254 | 255 | 256 | -------------------------------------------------------------------------------- /test/this/ThisExpression.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("ThisExpression", () => { 8 | const func = evaluate( 9 | ` 10 | function t(){ 11 | this.name = "hello"; 12 | return this; 13 | } 14 | 15 | t; 16 | ` 17 | ); 18 | 19 | const ctx: { [x: string]: any } = {}; 20 | 21 | func.call(ctx); 22 | 23 | deepEqual(ctx.name, "hello"); 24 | }); 25 | 26 | test("global this", () => { 27 | const ctx = { 28 | test: 1, 29 | }; 30 | const a = evaluate( 31 | ` 32 | this 33 | `, 34 | ctx 35 | ); 36 | 37 | expect(a === ctx).toEqual(true); 38 | }); 39 | -------------------------------------------------------------------------------- /test/timeout/timeout.test.ts: -------------------------------------------------------------------------------- 1 | import { Interpreter } from "../../src"; 2 | 3 | test("timeout -1", () => { 4 | let hasError = false; 5 | let msg = ""; 6 | const interpreter = new Interpreter( 7 | {}, 8 | { 9 | timeout: 500, 10 | } 11 | ); 12 | try { 13 | interpreter.evaluate( 14 | ` 15 | function test(){ 16 | var t = 0; 17 | for(;;) { 18 | t++; 19 | } 20 | } 21 | 22 | test(); 23 | 24 | ` 25 | ); 26 | } catch (e) { 27 | msg = e.message; 28 | hasError = true; 29 | } 30 | 31 | expect(interpreter.getExecutionTime() < 600).toEqual(true); 32 | 33 | expect(msg).toEqual("Script execution timed out after 500ms"); 34 | expect(hasError).toEqual(true); 35 | 36 | //again 37 | var _s = Date.now(); 38 | var _e = Date.now(); 39 | try { 40 | interpreter.evaluate( 41 | ` 42 | function test(){ 43 | var t = 0; 44 | for(;;) { 45 | t++; 46 | } 47 | } 48 | 49 | test(); 50 | 51 | ` 52 | ); 53 | } catch (e) { 54 | _e = Date.now(); 55 | } 56 | 57 | expect(_e - _s > 400).toBe(true); 58 | }); 59 | 60 | test("timeout -2", () => { 61 | let hasError = false; 62 | let msg = ""; 63 | let result: boolean; 64 | 65 | const interpreter = new Interpreter( 66 | {}, 67 | { 68 | timeout: 500, 69 | } 70 | ); 71 | 72 | const fn = interpreter.evaluate( 73 | ` 74 | function test(num){ 75 | var start = Date.now(); 76 | 77 | for(;;) { 78 | var current = Date.now(); 79 | if( current - start > 1000 ) { 80 | return true; 81 | } 82 | } 83 | 84 | return false; 85 | } 86 | 87 | test; 88 | 89 | ` 90 | ); 91 | 92 | try { 93 | result = fn(); 94 | } catch (e) { 95 | msg = e.message; 96 | hasError = true; 97 | } 98 | 99 | expect(msg).toEqual(""); 100 | expect(result).toEqual(true); 101 | expect(hasError).toEqual(false); 102 | }); 103 | -------------------------------------------------------------------------------- /test/try-catch/TryStatement.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("TryStatement", () => { 8 | const obj = evaluate( 9 | ` 10 | var obj = { 11 | runTry: false, 12 | runError: false 13 | }; 14 | 15 | try { 16 | obj.runTry = true; 17 | } catch (err) { 18 | obj.runError = true; 19 | } 20 | 21 | obj; 22 | ` 23 | ); 24 | 25 | deepEqual(true, obj.runTry); 26 | deepEqual(false, obj.runError); 27 | }); 28 | 29 | test("TryStatement-with-throw", () => { 30 | const obj = evaluate( 31 | ` 32 | var obj = { 33 | runTry: false, 34 | runError: false 35 | }; 36 | 37 | try { 38 | obj.runTry = true; 39 | throw new Error("invalid ..."); 40 | } catch (err) { 41 | obj.runError = true; 42 | } 43 | 44 | obj; 45 | ` 46 | ); 47 | 48 | deepEqual(true, obj.runTry); 49 | deepEqual(true, obj.runError); 50 | }); 51 | 52 | test("TryStatement with finally", () => { 53 | const obj = evaluate( 54 | ` 55 | var obj = { 56 | runTry: false, 57 | runError: false, 58 | runFinally: false 59 | }; 60 | 61 | try { 62 | obj.runTry = true; 63 | } catch (err) { 64 | obj.runError = true; 65 | }finally{ 66 | obj.runFinally = true; 67 | } 68 | 69 | obj; 70 | ` 71 | ); 72 | 73 | deepEqual(true, obj.runTry); 74 | deepEqual(false, obj.runError); 75 | deepEqual(true, obj.runFinally); 76 | }); 77 | 78 | test("continue in try block nest loop", () => { 79 | const arr = evaluate( 80 | ` 81 | var result = []; 82 | var i = 0; 83 | 84 | while(i<5){ 85 | i++; 86 | try { 87 | if (i %2 === 0){ 88 | continue; // continue the loop 89 | } 90 | } catch (err) { 91 | // 92 | } 93 | result.push(i); 94 | } 95 | 96 | result; 97 | ` 98 | ); 99 | deepEqual(arr, [1, 3, 5]); 100 | }); 101 | 102 | test("continue in catch block nest loop", () => { 103 | const arr = evaluate( 104 | ` 105 | var result = []; 106 | var i = 0; 107 | 108 | while(i<5){ 109 | i++; 110 | try { 111 | if (i %2 === 0){ 112 | throw new Error(); 113 | } 114 | } catch (err) { 115 | // 116 | continue 117 | } 118 | result.push(i); 119 | } 120 | 121 | result; 122 | ` 123 | ); 124 | deepEqual(arr, [1, 3, 5]); 125 | }); 126 | 127 | test("continue in finally block nest loop", () => { 128 | const arr = evaluate( 129 | ` 130 | var result = []; 131 | var i = 0; 132 | 133 | while(i<5){ 134 | i++; 135 | try { 136 | // 137 | } catch (err) { 138 | // 139 | }finally{ 140 | if (i %2 === 0){ 141 | continue; 142 | } 143 | } 144 | result.push(i); 145 | } 146 | 147 | result; 148 | ` 149 | ); 150 | deepEqual(arr, [1, 3, 5]); 151 | }); 152 | 153 | test("try-catch reset scope", () => { 154 | const a = evaluate( 155 | ` 156 | function m1(){ 157 | var title = 'm1' 158 | 159 | throw 'error' 160 | } 161 | function m2(){ 162 | var title = 'm2'; 163 | m1(); 164 | 165 | } 166 | function m3(){ 167 | var title = 'm3'; 168 | try { 169 | m2(); 170 | } catch(e) { 171 | return title 172 | } 173 | } 174 | 175 | m3() 176 | ` 177 | ); 178 | deepEqual(a, "m3"); 179 | }); 180 | 181 | test("try-catch reset context", () => { 182 | const a = evaluate( 183 | ` 184 | function m1(){ 185 | throw 'error' 186 | } 187 | function m2(){ 188 | m1.call('m1'); 189 | 190 | } 191 | function m3(){ 192 | try { 193 | m2.call('m2'); 194 | } catch(e) { 195 | return this; 196 | } 197 | } 198 | 199 | m3.call('m3') 200 | ` 201 | ); 202 | deepEqual(a, "m3"); 203 | }); 204 | 205 | test("try-catch value returns -1", () => { 206 | const a = evaluate(` 207 | 45; 208 | try { 209 | throw 'error' 210 | } catch(e){} 211 | `); 212 | expect(a).toEqual(undefined); 213 | }); 214 | 215 | test("try-catch returns sequence -1", () => { 216 | const a = evaluate(` 217 | function test(){ 218 | try { 219 | throw 1 220 | } catch(e){ 221 | return 2 222 | } 223 | } 224 | test(); 225 | `); 226 | 227 | expect(a).toEqual(2); 228 | }); 229 | 230 | test("try-catch returns sequence -2", () => { 231 | const a = evaluate(` 232 | function test(){ 233 | try { 234 | throw 1 235 | } catch(e){ 236 | return 2 237 | } finally { 238 | return 3 239 | } 240 | } 241 | test(); 242 | `); 243 | 244 | expect(a).toEqual(3); 245 | }); 246 | 247 | test("try-catch returns sequence -3", () => { 248 | const a = evaluate(` 249 | function test(){ 250 | try { 251 | return 1 252 | } catch(e){ 253 | return 2 254 | } finally { 255 | return 3 256 | } 257 | } 258 | test(); 259 | `); 260 | 261 | expect(a).toEqual(3); 262 | }); 263 | 264 | test("try-catch returns sequence -4", () => { 265 | const a = evaluate(` 266 | function test(){ 267 | try { 268 | return 1 269 | } catch(e){ 270 | return 2 271 | } 272 | } 273 | test(); 274 | `); 275 | 276 | expect(a).toEqual(1); 277 | }); 278 | -------------------------------------------------------------------------------- /test/unary-expression/UnaryExpression.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("typeof", () => { 8 | const type = evaluate( 9 | ` 10 | typeof 123; 11 | ` 12 | ); 13 | 14 | deepEqual(type, "number"); 15 | }); 16 | 17 | test("typeof before defined", () => { 18 | const type = evaluate( 19 | ` 20 | 21 | typeof a; // a is not defined, it should equal 'undefined' 22 | ` 23 | ); 24 | 25 | deepEqual(type, "undefined"); 26 | }); 27 | 28 | test("typeof before var", () => { 29 | const type = evaluate( 30 | ` 31 | 32 | typeof a; 33 | var a; 34 | ` 35 | ); 36 | 37 | deepEqual(type, "undefined"); 38 | }); 39 | 40 | test("void", () => { 41 | const type = evaluate( 42 | ` 43 | void 123; 44 | ` 45 | ); 46 | 47 | deepEqual(type, undefined); 48 | }); 49 | 50 | test("delete", () => { 51 | const obj = evaluate( 52 | ` 53 | var obj = { 54 | a: 123 55 | }; 56 | 57 | delete obj.a; 58 | 59 | obj; 60 | ` 61 | ); 62 | 63 | deepEqual(obj.a, undefined); 64 | deepEqual(Object.keys(obj).length, 0); 65 | }); 66 | 67 | test("!", () => { 68 | const isTrue = evaluate( 69 | ` 70 | var isTrue = !false; 71 | 72 | isTrue; 73 | ` 74 | ); 75 | deepEqual(true, isTrue); 76 | }); 77 | 78 | test("+", () => { 79 | const num = evaluate( 80 | ` 81 | var num = +("123"); 82 | 83 | num; 84 | ` 85 | ); 86 | deepEqual(num, 123); 87 | }); 88 | 89 | test("-", () => { 90 | const num = evaluate( 91 | ` 92 | var num = -("123"); 93 | 94 | num; 95 | ` 96 | ); 97 | deepEqual(num, -123); 98 | }); 99 | 100 | test("~", () => { 101 | const num = evaluate( 102 | ` 103 | var num = ~("123"); 104 | 105 | num; 106 | ` 107 | ); 108 | deepEqual(num, -124); 109 | }); 110 | 111 | test("getter trigger", () => { 112 | const v = evaluate( 113 | ` 114 | var triggerCount = 0; 115 | var data = { 116 | get value(){ 117 | triggerCount++ 118 | return 1; 119 | } 120 | }; 121 | 122 | -data.value; 123 | 124 | +data.value; 125 | 126 | !data.value; 127 | 128 | ~data.value; 129 | 130 | void data.value; 131 | 132 | typeof data.value; 133 | 134 | delete data.value 135 | 136 | triggerCount 137 | ` 138 | ); 139 | expect(v).toBe(6); 140 | }); 141 | -------------------------------------------------------------------------------- /test/var/VariableDeclaration.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | test("VariableDeclaration-var1", () => { 4 | const a = evaluate( 5 | ` 6 | var a = 123; 7 | 8 | ` 9 | ); 10 | 11 | expect(a).toEqual(undefined); 12 | }); 13 | 14 | test("VariableDeclaration-var2", () => { 15 | const a = evaluate( 16 | ` 17 | var a = 123; 18 | a; 19 | ` 20 | ); 21 | 22 | expect(a).toEqual(123); 23 | }); 24 | 25 | test("VariableDeclaration-var3", () => { 26 | const a = evaluate( 27 | ` 28 | function _t1(){ 29 | var a = 123; 30 | return a; 31 | } 32 | _t1(); 33 | ` 34 | ); 35 | expect(a).toEqual(123); 36 | }); 37 | 38 | test("VariableDeclaration-var4", () => { 39 | const a = evaluate( 40 | ` 41 | name = "story" 42 | var name; 43 | ` 44 | ); 45 | expect(a).toEqual("story"); 46 | }); 47 | 48 | test("VariableDeclaration-var5", () => { 49 | const a = evaluate( 50 | ` 51 | var name = "hello" 52 | name = "world" 53 | ` 54 | ); 55 | expect(a).toEqual("world"); 56 | }); 57 | 58 | test("VariableDeclaration-var6", () => { 59 | const func = evaluate( 60 | ` 61 | function run(){ 62 | var name = "world"; 63 | return name; 64 | } 65 | 66 | run 67 | 68 | ` 69 | ); 70 | 71 | expect(func()).toEqual("world"); 72 | }); 73 | 74 | test("VariableDeclaration-var7", () => { 75 | const a = evaluate( 76 | ` 77 | name = "world"; 78 | name = "hello"; 79 | 80 | ` 81 | ); 82 | 83 | expect(a).toEqual("hello"); 84 | }); 85 | 86 | test("VariableDeclaration-var8", () => { 87 | const a = evaluate( 88 | ` 89 | function r(){ 90 | return 'a' 91 | } 92 | 93 | var a = {n: 2, a: 1+1, c: r()+1,d: [1,2,r()]}; 94 | 95 | a 96 | ` 97 | ); 98 | 99 | expect(a).toEqual({ 100 | n: 2, 101 | a: 2, 102 | c: "a1", 103 | d: [1, 2, "a"], 104 | }); 105 | }); 106 | 107 | test("VariableDeclaration-var8", () => { 108 | const a = evaluate( 109 | ` 110 | var a = { 111 | b: 1, 112 | c: 1, 113 | xy: '1', 114 | z: { 115 | p:1, 116 | kk: 1 117 | } 118 | } 119 | 120 | a.b = 2; 121 | a['c'] = 2 122 | a['x'+'y'] = 2 123 | a.z['k'+'k'] = 2 124 | a.z.p = 2 125 | 126 | a 127 | ` 128 | ); 129 | 130 | expect(a).toEqual({ 131 | b: 2, 132 | c: 2, 133 | xy: 2, 134 | z: { 135 | p: 2, 136 | kk: 2, 137 | }, 138 | }); 139 | }); 140 | 141 | test("VariableDeclaration-var9", () => { 142 | const a = evaluate( 143 | ` 144 | var a = undefined; 145 | a; 146 | 147 | `, 148 | Object.create(null) 149 | ); 150 | 151 | expect(a).toEqual(undefined); 152 | }); 153 | 154 | test("VariableDeclaration-var10", () => { 155 | const a = evaluate( 156 | ` 157 | var a = {v:1}; 158 | a.p = a = {v:2}; 159 | a.p; 160 | 161 | `, 162 | Object.create(null) 163 | ); 164 | 165 | expect(a).toEqual(undefined); 166 | }); 167 | -------------------------------------------------------------------------------- /test/while/WhileStatement.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("WhileStatement-1", () => { 8 | const obj = evaluate( 9 | ` 10 | var obj = { 11 | i: 0 12 | }; 13 | 14 | while (obj.i < 3) { 15 | obj.i++; 16 | } 17 | 18 | obj; 19 | ` 20 | ); 21 | 22 | deepEqual(true, typeof obj.i === "number"); 23 | deepEqual(obj.i, 3); 24 | }); 25 | 26 | test("WhileStatement-2", () => { 27 | const obj = evaluate( 28 | ` 29 | var obj = { 30 | i: 0 31 | }; 32 | 33 | while (true) { 34 | obj.i++; 35 | if (obj.i >= 3) { 36 | break; 37 | } 38 | } 39 | 40 | obj; 41 | ` 42 | ); 43 | 44 | deepEqual(true, typeof obj.i === "number"); 45 | deepEqual(obj.i, 3); 46 | }); 47 | -------------------------------------------------------------------------------- /test/while/label.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("break with label", () => { 8 | const i = evaluate( 9 | ` 10 | var i = 1; 11 | loop1: 12 | while(true){ 13 | i++; 14 | break loop1; 15 | } 16 | 17 | i; 18 | ` 19 | ); 20 | deepEqual(i, 2); 21 | }); 22 | 23 | test("continue with label", () => { 24 | const { i, arr } = evaluate( 25 | ` 26 | var i = 10; 27 | var arr = []; 28 | loop1: 29 | while(i > 0){ 30 | if (i % 2 === 1){ 31 | i--; 32 | continue; 33 | } 34 | arr.push(i); 35 | i--; 36 | } 37 | 38 | d = {i:i, arr:arr}; 39 | ` 40 | ); 41 | deepEqual(i, 0); 42 | deepEqual(arr, [10, 8, 6, 4, 2]); 43 | }); 44 | -------------------------------------------------------------------------------- /test/with/withStatement.test.ts: -------------------------------------------------------------------------------- 1 | import { evaluate, Interpreter } from "../../src"; 2 | 3 | function deepEqual(a, b) { 4 | expect(a).toEqual(b); 5 | } 6 | 7 | test("with1", () => { 8 | const a = evaluate(` 9 | var a1 = 1; 10 | var obj = { 11 | a1 :2 12 | } 13 | with(obj){ 14 | a1 15 | } 16 | `); 17 | 18 | deepEqual(a, 2); 19 | }); 20 | 21 | test("with2", () => { 22 | const a = evaluate(` 23 | function o1(){} 24 | o1.prototype.x = 100; 25 | var z = new o1(); 26 | 27 | with(z) { 28 | x 29 | } 30 | `); 31 | 32 | deepEqual(a, 100); 33 | }); 34 | 35 | test("with3", () => { 36 | var ctx: Record = {}; 37 | const result = evaluate( 38 | ` 39 | 40 | var obj = {} 41 | 42 | with(obj) { 43 | a = 10 44 | } 45 | 46 | obj.a 47 | 48 | `, 49 | ctx 50 | ); 51 | 52 | expect(result).toEqual(undefined); 53 | expect(ctx.a).toEqual(10); 54 | }); 55 | 56 | test("with4", () => { 57 | var ctx: Record = {}; 58 | const result = evaluate( 59 | ` 60 | 61 | var obj = { 62 | a: 10 63 | } 64 | 65 | with(obj) { 66 | a = 20 67 | } 68 | 69 | obj.a 70 | 71 | `, 72 | ctx 73 | ); 74 | 75 | expect(result).toEqual(20); 76 | }); 77 | 78 | test("with5", () => { 79 | var ctx: Record = {}; 80 | const result = evaluate( 81 | ` 82 | 83 | var obj = { 84 | a: 10 85 | } 86 | 87 | with(obj) { 88 | var a = 20 89 | } 90 | 91 | obj.a 92 | 93 | `, 94 | ctx 95 | ); 96 | 97 | expect(result).toEqual(20); 98 | }); 99 | 100 | test("with6", () => { 101 | var ctx: Record = {}; 102 | const result = evaluate( 103 | ` 104 | 105 | var obj = { 106 | a: 10 107 | } 108 | 109 | with(obj) { 110 | var b = 20 111 | } 112 | 113 | b 114 | 115 | `, 116 | ctx 117 | ); 118 | 119 | expect(result).toEqual(20); 120 | }); 121 | 122 | test("with7", () => { 123 | var ctx: Record = {}; 124 | const interpreter = new Interpreter(ctx); 125 | try { 126 | interpreter.evaluate( 127 | ` 128 | var obj = { 129 | a: 10 130 | } 131 | with(obj) { 132 | //throw error 133 | u.a = 1; 134 | } 135 | 136 | obj 137 | 138 | ` 139 | ); 140 | } catch (e) {} 141 | 142 | const result = interpreter.evaluate(` 143 | var b = 1; 144 | obj; 145 | `); 146 | 147 | expect(ctx.obj.a).toEqual(10); 148 | expect(result.b).toEqual(undefined); 149 | expect(ctx.b).toEqual(1); 150 | }); 151 | 152 | test("with8", () => { 153 | var ctx: Record = {}; 154 | const interpreter = new Interpreter(ctx); 155 | try { 156 | interpreter.evaluate( 157 | ` 158 | var obj = { 159 | a: 10 160 | } 161 | function abc() { 162 | //throw error 163 | u.a = 1; 164 | } 165 | 166 | abc.call(obj); 167 | 168 | ` 169 | ); 170 | } catch (e) {} 171 | 172 | const result = interpreter.evaluate(` 173 | var b = 1; 174 | obj; 175 | `); 176 | 177 | expect(ctx.obj.a).toEqual(10); 178 | expect(ctx.b).toEqual(1); 179 | expect(result.b).toEqual(undefined); 180 | expect(result.a).toEqual(10); 181 | }); 182 | 183 | test("with9", () => { 184 | var ctx: Record = {}; 185 | const result = evaluate( 186 | ` 187 | var obj = { 188 | a: 10, 189 | f1: function(){ 190 | return this; 191 | }, 192 | f2: function(){ 193 | return this.a; 194 | } 195 | } 196 | 197 | with(obj) { 198 | var f = f1; 199 | [f(),(0,f1)(),f2()] 200 | } 201 | `, 202 | ctx 203 | ); 204 | 205 | expect(result).toEqual([undefined, undefined, 10]); 206 | }); 207 | 208 | test("with10", () => { 209 | var ctx: Record = {}; 210 | const result = evaluate( 211 | ` 212 | var obj = { 213 | a: 10, 214 | f1: function(){ 215 | return this; 216 | }, 217 | f2: function(){ 218 | return this.a; 219 | } 220 | } 221 | var obj2 = { 222 | a: 11, 223 | f3: function(){ 224 | return this; 225 | }, 226 | f4: function(){ 227 | return this.a; 228 | } 229 | } 230 | 231 | 232 | with(obj) { 233 | with(obj2) { 234 | var f = f1; 235 | [f(),(0,f1)(),f2(), f4(), (0,f3)()] 236 | } 237 | } 238 | `, 239 | ctx 240 | ); 241 | 242 | expect(result).toEqual([undefined, undefined, 10, 11, undefined]); 243 | }); 244 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "lib", 4 | "declaration": true, 5 | "module": "esnext", 6 | "target": "esnext", 7 | "lib": ["es6", "dom"], 8 | "jsx": "react", 9 | "allowSyntheticDefaultImports": true, 10 | "moduleResolution": "node", 11 | "forceConsistentCasingInFileNames": true, 12 | "noImplicitReturns": true, 13 | "noImplicitThis": false, 14 | "experimentalDecorators": true, 15 | "strictNullChecks": true 16 | }, 17 | "include": ["src/**/*"] 18 | } 19 | --------------------------------------------------------------------------------