-
Notifications
You must be signed in to change notification settings - Fork 353
/
docs.html
345 lines (276 loc) · 13.4 KB
/
docs.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS-Interpreter Documentation</title>
<link href="demos/style.css" rel="stylesheet" type="text/css">
<style>
h2 {
background-color: #f7df1e;
padding-left: 1ex;
}
</style>
</head>
<body>
<img src="logo.png" height=200 width=200 style="float: right">
<h1>JS-Interpreter Documentation</h1>
<p>JS-Interpreter is a sandboxed JavaScript interpreter written in JavaScript.
It allows for execution of arbitrary ES5 JavaScript code line by line.
Execution is completely isolated from the main JavaScript environment.
Multiple instances of the JS-Interpreter allow for multi-threaded concurrent
JavaScript without the use of Web Workers.</p>
<p>Play with the <a href="index.html">JS-Interpreter demo</a>.</p>
<p>Get the <a href="https://github.com/NeilFraser/JS-Interpreter">source code</a>.</p>
<h2 id="usage">Usage</h2>
<p>Start by including the two JavaScript source files:</p>
<pre>
<script src="acorn.js"></script>
<script src="interpreter.js"></script>
</pre>
<p>Alternatively, use the compressed bundle (80kb):</p>
<pre>
<script src="acorn_interpreter.js"></script>
</pre>
<p>Next, instantiate an interpreter with the JavaScript code that needs to be
parsed:</p>
<pre>
var myCode = 'var a=1; for(var i=0;i<4;i++){a*=i;} a;';
var myInterpreter = new Interpreter(myCode);
</pre>
<p>Additional JavaScript code may be added at any time (frequently used to
interactively call previously defined functions):</p>
<pre>
myInterpreter.appendCode('foo();');
</pre>
<p>To run the code step by step, call the <code>step</code> function
repeatedly until it returns false:</p>
<pre>
function nextStep() {
if (myInterpreter.step()) {
window.setTimeout(nextStep, 0);
}
}
nextStep();
</pre>
<p>By default, one step is a single operation. For an example of how to
step line by line, see the <a href="demos/line.html">line step demo</a>.</p>
<p>Alternatively, if the code is known to be safe from infinite loops, it may
be executed to completion by calling the <code>run</code> function once:</p>
<pre>
myInterpreter.run();
</pre>
<p>In cases where the code encounters asynchronous API calls (see below),
or there are pending tasks created by <code>setTimeout</code>/<code>setInterval</code>,
<code>run</code> will return true if it is blocked and needs to be called
again at a later time.</p>
<h2 id="external_api">External API</h2>
<p>Similar to the <code>eval</code> function, the result of the last
statement executed is available in <code>myInterpreter.value</code>:</p>
<pre>
var myInterpreter = new Interpreter('6 * 7');
myInterpreter.run();
alert(myInterpreter.value);
</pre>
<p>Additionally, API calls may be added to the interpreter's global object
during creation. Here is the addition of <code>alert()</code> and a
<code>url</code> variable:</p>
<pre>
var myCode = 'alert(url);';
var initFunc = function(interpreter, globalObject) {
interpreter.setProperty(globalObject, 'url', String(location));
var wrapper = function alert(text) {
return window.alert(text);
};
interpreter.setProperty(globalObject, 'alert',
interpreter.createNativeFunction(wrapper));
};
var myInterpreter = new Interpreter(myCode, initFunc);
</pre>
<p>Alternatively, API calls may be added to an object:</p>
<pre>
var myCode = 'robot.forwards(robot.fast);';
var initFunc = function(interpreter, globalObject) {
// Create 'robot' global object.
var robot = interpreter.nativeToPseudo({});
interpreter.setProperty(globalObject, 'robot', robot);
// Define 'robot.fast' property.
interpreter.setProperty(robot, 'fast', 99);
// Define 'robot.forwards' function.
var wrapper = function forwards(speed) {
return realRobot.forwards(speed);
};
interpreter.setProperty(robot, 'forwards',
interpreter.createNativeFunction(wrapper));
};
var myInterpreter = new Interpreter(myCode, initFunc);
</pre>
<p>Only primitives (numbers, strings, booleans, null and undefined) may be
passed in and out of the interpreter during execution. Objects and functions
are not compatible between native and interpreted code.
See the <a href="demos/json.html">JSON demo</a> for an example of exchanging
JSON between the browser and the interpreter.</p>
<p>Asynchronous API functions may wrapped so that they appear to be
synchronous to interpreter. For example, a <code>getXhr(url)</code> function
that returns the contents of an XMLHttpRequest could be defined in
<code>initFunc</code> like this:</p>
<pre>
var wrapper = function getXhr(href, callback) {
var req = new XMLHttpRequest();
req.open('GET', href, true);
req.onreadystatechange = function() {
if (req.readyState === 4 && req.status === 200) {
callback(req.responseText);
}
};
req.send(null);
};
interpreter.setProperty(globalObject, 'getXhr',
interpreter.createAsyncFunction(wrapper));
</pre>
<p>This snippet uses <code>createAsyncFunction</code> in the same way that
<code>createNativeFunction</code> was used earlier. The difference is that
the wrapped asynchronous function's return value is ignored. Instead, an
extra callback function is passed in when the wrapper is called. When the
wrapper is ready to return, it calls the callback function with the value it
wishes to return. From the point of view of the code running inside the
JS-Interpreter, a function call was made and the result was returned
immediately.</p>
<p>For a working example, see the
<a href="demos/async.html">async demo</a>.</p>
<h2 id="serialization">Serialization</h2>
<p>A unique feature of the JS-Interpreter is its ability to pause execution,
serialize the current state, then resume the execution at that point at a
later time. Loops, variables, closures, and all other state is preserved.</p>
<p>Uses of this feature include continuously executing programs that survive
a server reboot, or loading a stack image that has been computed up to a
certain point, or forking execution, or rolling back to a stored state.</p>
<p>Serialization has several limitations. One is that the serialized format
is not human-readable. It is also not guaranteed that future versions of the
JS-Interpreter will be able to parse the serialization from older versions.
Another limitation is that serialization reaches beyond the public API, and
thus does not work with the compressed bundle (acorn_interpreter.js). Yet
another limitation is that the serialization format is rather large; it has a
300 kb overhead due to the standard polyfills.</p>
<p>For a working example, see the
<a href="demos/serialize.html">serialization demo</a>.</p>
<p>Another example shows how to use serialization of every step to enable
<a href="demos/backwards.html">stepping backwards</a>.</p>
<h2 id="threading">Threading</h2>
<p>JavaScript is single-threaded, but the JS-Interpreter allows one to run
multiple threads at the same time. Creating two or more completely
independent threads that run separately from each other is trivial: just
create two or more instances of the Interpreter, each with its own code,
and alternate calling each interpreter's step function. They may
communicate indirectly with each other through any external APIs that are
provided.</p>
<p>A more complex case is where two or more threads should share the same
global scope. To implement this,</p>
<ol>
<li>create one main JS-Interpreter with no code but with any needed API
definitions,</li>
<li>create an empty array to store the thread stacks,</li>
<li>for each thread:
<ol type="a">
<li>create a empty, temporary JS-Interpreter (no code, no APIs).</li>
<li>replace this interpreter's global scope with the main interpreter's global
(using <code>getGlobalScope</code> and <code>setGlobalScope</code>),
<li>inject the thread's code into this interpreter
(using <code>appendCode</code>),</li>
<li>extract the stack from this interpreter (using <code>getStateStack</code>)
and add the stack to the array of thread stacks,</li>
</ol>
</li>
<li>then assign the desired stack to the main interpreter (using
<code>setStateStack</code>) before calling <code>step</code>.</li>
</ol>
<p>For a working example, see the
<a href="demos/thread.html">thread demo</a>.</p>
<h2 id="status">Status</h2>
<p>A JS-Intepreter instance has a <code>getStatus</code> function that returns
information about what the interpreter is doing. The return values are one of:</p>
<dl>
<dt><code>Interpreter.Status.DONE</code></dt>
<dd>The interpreter has finished executing all code.
Unless <code>appendCode</code> is used, it's done.</dd>
<dt><code>Interpreter.Status.STEP</code></dt>
<dd>The interpreter has code to be executed and is ready to take another step.</dd>
<dt><code>Interpreter.Status.TASK</code></dt>
<dd>The interpreter doesn't have any code to be executed at the moment, but
there's a pending setTimeout/setInterval task which will provide code at
some future time.</dd>
<dt><code>Interpreter.Status.ASYNC</code></dt>
<dd>The interpreter's execution is blocked by an asynchonous function call
(created with <code>createAsyncFunction</code>) which is currently executing.</dd>
</dl>
<p>This status information might be used to determine whether to take another
step immediately, or call setTimeout and step later, or stop.</p>
<h2 id="security">Security</h2>
<p>A common use-case of the JS-Interpreter is to sandbox potentially hostile
code. The interpreter is secure by default: it does not use blacklists to
prevent dangerous actions, instead it creates its own virtual machine with no
external APIs except as provided by the developer.</p>
<p>Infinite loops are handled by calling the <code>step</code> function
a maximum number of times, or by calling <code>step</code> indefinitely many
times but using a setTimeout between each call to ensure other tasks have a
chance to execute.</p>
<p>Memory bombs (e.g. <code>var x='X'; while(1) x=x+x;</code>) can be detected
by periodically measuring the size of the stack, and aborting execution if it
grows too large. For a working example, see the
<a href="demos/size.html">size demo</a>.</p>
<h2 id="regular_expressions">Regular Expressions</h2>
<p>Pathological regular expressions can execute in geometric time. For example
<code>'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac'.match(/^(a+)+b/)</code>
will effectively crash any JavaScript runtime. JS-Interpreter has a
variety of methods for <a href="demos/regexp.html">safely dealing with
regular expressions</a> depending on the platform.</p>
<h2 id="limitations">Limitations</h2>
<p>The version of JavaScript implemented by the interpreter has a few
differences from that which executes in a browser:</p>
<dl>
<dt>API</dt>
<dd>None of the DOM APIs are exposed. That's the point of a sandbox.
If you need these, write your own interfaces. For a working example,
see the <a href="demos/dom.html">DOM demo</a>.</dd>
<dt>ES6</dt>
<dd>More recent additions to JavaScript such as <code>let</code> or
<code>Set</code> aren't implemented. Babel is a good method of transpiling
newer versions of JavaScript to ES5. For a working example, see the
<a href="demos/babel.html">Babel demo</a>.</dd>
<dt>toString & valueOf</dt>
<dd>User-created functions are not called when casting objects to
primitives.</dd>
<dt>Performance</dt>
<dd>The interpreter is not particularly efficient. It currently runs about
200 times slower than native JavaScript.</dd>
</dl>
<h2 id="node_js">Node.js</h2>
<p>JS-Intepreter can be run server-side using Node.js. For a minimal demo,
just run: <code>node <a href="demos/node.js">demos/node.js</a></code></p>
<h2 id="dependency">Dependency</h2>
<p>The only dependency is <a href="https://github.com/acornjs/acorn">Acorn</a>,
a beautifully written JavaScript parser by Marijn Haverbeke. A minimal
version of Acorn for ES5 is included in the JS-Interpreter package.</p>
<p>Alternatively, one could use latest version from the Acorn repo. However,
it's three times the size due to features (such as ES6+) which JS-Interpreter
doesn't use. There's no benefit in doing so unless your project already uses
Acorn for something else.</p>
<h2 id="compatibility">Compatibility</h2>
<p>The limiting factor for browser support is the use of
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create"><code>Object.create(null)</code></a>
to create hash objects in both Acorn and JS-Interpreter.
This results in the following minimum browser requirements:</p>
<ul>
<li>Chrome 5</li>
<li>Firefox 4.0</li>
<li>IE 9</li>
<li>Edge (all versions)</li>
<li>Opera 11.6</li>
<li>Safari 5</li>
<li>Node.js (all versions)</li>
<li>JS-Interpreter
(<a href="demos/self-interpreter.html">self-interpreter demo</a>)</li>
</ul>
<p>Additionally, if safely executing regular expressions is required, then
IE 11 is the minimum version of IE that supports Web Workers via Blobs.</p>
</body>
</html>