@builder.io/sdk-react-nextjs 0.5.3 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/functions/evaluate/acorn-interpreter.js +3111 -0
- package/dist/functions/evaluate/evaluate.js +1 -19
- package/dist/functions/evaluate/non-node-runtime/index.d.ts +1 -0
- package/dist/functions/evaluate/non-node-runtime/index.js +2 -0
- package/dist/functions/evaluate/{non-node-runtime.d.ts → non-node-runtime/non-node-runtime.d.ts} +1 -1
- package/dist/functions/evaluate/{non-node-runtime.js → non-node-runtime/non-node-runtime.js} +3 -3
- package/package.json +4 -4
- package/dist/functions/evaluate/acorn.d.ts +0 -0
- package/dist/functions/evaluate/acorn.js +0 -2432
- package/dist/functions/evaluate/interpreter.js +0 -3853
- /package/dist/functions/evaluate/{interpreter.d.ts → acorn-interpreter.d.ts} +0 -0
|
@@ -1,3853 +0,0 @@
|
|
|
1
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
2
|
-
// @ts-nocheck
|
|
3
|
-
/* eslint-disable */
|
|
4
|
-
/**
|
|
5
|
-
* Copied from source: https://github.com/NeilFraser/JS-Interpreter/blob/master/interpreter.js
|
|
6
|
-
* Any changes made here have a comment above them starting with: "// BUILDER.IO"
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* @license
|
|
10
|
-
* Copyright 2013 Google LLC
|
|
11
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
12
|
-
*/
|
|
13
|
-
/**
|
|
14
|
-
* @fileoverview Interpreting JavaScript in JavaScript.
|
|
15
|
-
* @author fraser@google.com (Neil Fraser)
|
|
16
|
-
*/
|
|
17
|
-
/**
|
|
18
|
-
* Create a new interpreter.
|
|
19
|
-
* @param {string|!Object} code Raw JavaScript text or AST.
|
|
20
|
-
* @param {Function=} opt_initFunc Optional initialization function. Used to
|
|
21
|
-
* define APIs. When called it is passed the interpreter object and the
|
|
22
|
-
* global scope object.
|
|
23
|
-
* @constructor
|
|
24
|
-
*/
|
|
25
|
-
var Interpreter = function (code, opt_initFunc) {
|
|
26
|
-
if (typeof code === 'string') {
|
|
27
|
-
code = this.parse_(code, 'code');
|
|
28
|
-
}
|
|
29
|
-
// Get a handle on Acorn's node_t object.
|
|
30
|
-
var nodeConstructor = code.constructor;
|
|
31
|
-
this.newNode = function () {
|
|
32
|
-
return new nodeConstructor({
|
|
33
|
-
options: {}
|
|
34
|
-
});
|
|
35
|
-
};
|
|
36
|
-
// Clone the root 'Program' node so that the AST may be modified.
|
|
37
|
-
var ast = this.newNode();
|
|
38
|
-
for (var prop in code) {
|
|
39
|
-
ast[prop] = prop === 'body' ? code[prop].slice() : code[prop];
|
|
40
|
-
}
|
|
41
|
-
this.ast = ast;
|
|
42
|
-
this.initFunc_ = opt_initFunc;
|
|
43
|
-
this.paused_ = false;
|
|
44
|
-
this.polyfills_ = [];
|
|
45
|
-
// Unique identifier for native functions. Used in serialization.
|
|
46
|
-
this.functionCounter_ = 0;
|
|
47
|
-
// Map node types to our step function names; a property lookup is faster
|
|
48
|
-
// than string concatenation with "step" prefix.
|
|
49
|
-
this.stepFunctions_ = Object.create(null);
|
|
50
|
-
var stepMatch = /^step([A-Z]\w*)$/;
|
|
51
|
-
var m;
|
|
52
|
-
for (var methodName in this) {
|
|
53
|
-
if (typeof this[methodName] === 'function' && (m = methodName.match(stepMatch))) {
|
|
54
|
-
this.stepFunctions_[m[1]] = this[methodName].bind(this);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
// Create and initialize the global scope.
|
|
58
|
-
this.globalScope = this.createScope(this.ast, null);
|
|
59
|
-
this.globalObject = this.globalScope.object;
|
|
60
|
-
// Run the polyfills.
|
|
61
|
-
this.ast = this.parse_(this.polyfills_.join('\n'), 'polyfills');
|
|
62
|
-
this.polyfills_ = undefined; // Allow polyfill strings to garbage collect.
|
|
63
|
-
Interpreter.stripLocations_(this.ast, undefined, undefined);
|
|
64
|
-
var state = new Interpreter.State(this.ast, this.globalScope);
|
|
65
|
-
state.done = false;
|
|
66
|
-
this.stateStack = [state];
|
|
67
|
-
this.run();
|
|
68
|
-
this.value = undefined;
|
|
69
|
-
// Point at the main program.
|
|
70
|
-
this.ast = ast;
|
|
71
|
-
state = new Interpreter.State(this.ast, this.globalScope);
|
|
72
|
-
state.done = false;
|
|
73
|
-
this.stateStack.length = 0;
|
|
74
|
-
this.stateStack[0] = state;
|
|
75
|
-
};
|
|
76
|
-
/**
|
|
77
|
-
* Completion Value Types.
|
|
78
|
-
* @enum {number}
|
|
79
|
-
*/
|
|
80
|
-
Interpreter.Completion = {
|
|
81
|
-
NORMAL: 0,
|
|
82
|
-
BREAK: 1,
|
|
83
|
-
CONTINUE: 2,
|
|
84
|
-
RETURN: 3,
|
|
85
|
-
THROW: 4
|
|
86
|
-
};
|
|
87
|
-
/**
|
|
88
|
-
* @const {!Object} Configuration used for all Acorn parsing.
|
|
89
|
-
*/
|
|
90
|
-
Interpreter.PARSE_OPTIONS = {
|
|
91
|
-
locations: true,
|
|
92
|
-
ecmaVersion: 5 // Needed in the event a version > 0.5.0 of Acorn is used.
|
|
93
|
-
};
|
|
94
|
-
/**
|
|
95
|
-
* Property descriptor of readonly properties.
|
|
96
|
-
*/
|
|
97
|
-
Interpreter.READONLY_DESCRIPTOR = {
|
|
98
|
-
configurable: true,
|
|
99
|
-
enumerable: true,
|
|
100
|
-
writable: false
|
|
101
|
-
};
|
|
102
|
-
/**
|
|
103
|
-
* Property descriptor of non-enumerable properties.
|
|
104
|
-
*/
|
|
105
|
-
Interpreter.NONENUMERABLE_DESCRIPTOR = {
|
|
106
|
-
configurable: true,
|
|
107
|
-
enumerable: false,
|
|
108
|
-
writable: true
|
|
109
|
-
};
|
|
110
|
-
/**
|
|
111
|
-
* Property descriptor of readonly, non-enumerable properties.
|
|
112
|
-
*/
|
|
113
|
-
Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR = {
|
|
114
|
-
configurable: true,
|
|
115
|
-
enumerable: false,
|
|
116
|
-
writable: false
|
|
117
|
-
};
|
|
118
|
-
/**
|
|
119
|
-
* Property descriptor of non-configurable, readonly, non-enumerable properties.
|
|
120
|
-
* E.g. NaN, Infinity.
|
|
121
|
-
*/
|
|
122
|
-
Interpreter.NONCONFIGURABLE_READONLY_NONENUMERABLE_DESCRIPTOR = {
|
|
123
|
-
configurable: false,
|
|
124
|
-
enumerable: false,
|
|
125
|
-
writable: false
|
|
126
|
-
};
|
|
127
|
-
/**
|
|
128
|
-
* Property descriptor of variables.
|
|
129
|
-
*/
|
|
130
|
-
Interpreter.VARIABLE_DESCRIPTOR = {
|
|
131
|
-
configurable: false,
|
|
132
|
-
enumerable: true,
|
|
133
|
-
writable: true
|
|
134
|
-
};
|
|
135
|
-
/**
|
|
136
|
-
* Unique symbol for indicating that a step has encountered an error, has
|
|
137
|
-
* added it to the stack, and will be thrown within the user's program.
|
|
138
|
-
* When STEP_ERROR is thrown in the JS-Interpreter, the error can be ignored.
|
|
139
|
-
*/
|
|
140
|
-
Interpreter.STEP_ERROR = {
|
|
141
|
-
STEP_ERROR: true
|
|
142
|
-
};
|
|
143
|
-
/**
|
|
144
|
-
* Unique symbol for indicating that a reference is a variable on the scope,
|
|
145
|
-
* not an object property.
|
|
146
|
-
*/
|
|
147
|
-
Interpreter.SCOPE_REFERENCE = {
|
|
148
|
-
SCOPE_REFERENCE: true
|
|
149
|
-
};
|
|
150
|
-
/**
|
|
151
|
-
* Unique symbol for indicating, when used as the value of the value
|
|
152
|
-
* parameter in calls to setProperty and friends, that the value
|
|
153
|
-
* should be taken from the property descriptor instead.
|
|
154
|
-
*/
|
|
155
|
-
Interpreter.VALUE_IN_DESCRIPTOR = /** @type {!Interpreter.Value} */ {
|
|
156
|
-
VALUE_IN_DESCRIPTOR: true
|
|
157
|
-
};
|
|
158
|
-
/**
|
|
159
|
-
* Unique symbol for indicating that a RegExp timeout has occurred in a VM.
|
|
160
|
-
*/
|
|
161
|
-
Interpreter.REGEXP_TIMEOUT = {
|
|
162
|
-
REGEXP_TIMEOUT: true
|
|
163
|
-
};
|
|
164
|
-
/**
|
|
165
|
-
* For cycle detection in array to string and error conversion;
|
|
166
|
-
* see spec bug github.com/tc39/ecma262/issues/289
|
|
167
|
-
* Since this is for atomic actions only, it can be a class property.
|
|
168
|
-
*/
|
|
169
|
-
Interpreter.toStringCycles_ = [];
|
|
170
|
-
/**
|
|
171
|
-
* Node's vm module, if loaded and required.
|
|
172
|
-
* @type {Object}
|
|
173
|
-
*/
|
|
174
|
-
Interpreter.vm = null;
|
|
175
|
-
/**
|
|
176
|
-
* Currently executing interpreter. Needed so Interpreter.Object instances
|
|
177
|
-
* can know their environment.
|
|
178
|
-
* @type {Interpreter}
|
|
179
|
-
*/
|
|
180
|
-
Interpreter.currentInterpreter_ = null;
|
|
181
|
-
/**
|
|
182
|
-
* The global object (`window` in a browser, `global` in node.js) is usually
|
|
183
|
-
* `globalThis`, but older systems use `this`.
|
|
184
|
-
*/
|
|
185
|
-
Interpreter.nativeGlobal = typeof globalThis === 'undefined' ? this : globalThis;
|
|
186
|
-
/**
|
|
187
|
-
* Code for executing regular expressions in a thread.
|
|
188
|
-
*/
|
|
189
|
-
Interpreter.WORKER_CODE = ['onmessage = function(e) {', 'var result;', 'var data = e.data;', 'switch (data[0]) {', "case 'split':",
|
|
190
|
-
// ['split', string, separator, limit]
|
|
191
|
-
'result = data[1].split(data[2], data[3]);', 'break;', "case 'match':",
|
|
192
|
-
// ['match', string, regexp]
|
|
193
|
-
'result = data[1].match(data[2]);', 'break;', "case 'search':",
|
|
194
|
-
// ['search', string, regexp]
|
|
195
|
-
'result = data[1].search(data[2]);', 'break;', "case 'replace':",
|
|
196
|
-
// ['replace', string, regexp, newSubstr]
|
|
197
|
-
'result = data[1].replace(data[2], data[3]);', 'break;', "case 'exec':",
|
|
198
|
-
// ['exec', regexp, lastIndex, string]
|
|
199
|
-
'var regexp = data[1];', 'regexp.lastIndex = data[2];', 'result = [regexp.exec(data[3]), data[1].lastIndex];', 'break;', 'default:', "throw Error('Unknown RegExp operation: ' + data[0]);", '}', 'postMessage(result);', 'close();', '};'];
|
|
200
|
-
/**
|
|
201
|
-
* Is a value a legal integer for an array length?
|
|
202
|
-
* @param {Interpreter.Value} x Value to check.
|
|
203
|
-
* @returns {number} Zero, or a positive integer if the value can be
|
|
204
|
-
* converted to such. NaN otherwise.
|
|
205
|
-
*/
|
|
206
|
-
Interpreter.legalArrayLength = function (x) {
|
|
207
|
-
var n = x >>> 0;
|
|
208
|
-
// Array length must be between 0 and 2^32-1 (inclusive).
|
|
209
|
-
return n === Number(x) ? n : NaN;
|
|
210
|
-
};
|
|
211
|
-
/**
|
|
212
|
-
* Is a value a legal integer for an array index?
|
|
213
|
-
* @param {Interpreter.Value} x Value to check.
|
|
214
|
-
* @returns {number} Zero, or a positive integer if the value can be
|
|
215
|
-
* converted to such. NaN otherwise.
|
|
216
|
-
*/
|
|
217
|
-
Interpreter.legalArrayIndex = function (x) {
|
|
218
|
-
var n = x >>> 0;
|
|
219
|
-
// Array index cannot be 2^32-1, otherwise length would be 2^32.
|
|
220
|
-
// 0xffffffff is 2^32-1.
|
|
221
|
-
return String(n) === String(x) && n !== 0xffffffff ? n : NaN;
|
|
222
|
-
};
|
|
223
|
-
/**
|
|
224
|
-
* Remove start and end values from AST, or set start and end values to a
|
|
225
|
-
* constant value. Used to remove highlighting from polyfills and to set
|
|
226
|
-
* highlighting in an eval to cover the entire eval expression.
|
|
227
|
-
* @param {!Object} node AST node.
|
|
228
|
-
* @param {number=} start Starting character of all nodes, or undefined.
|
|
229
|
-
* @param {number=} end Ending character of all nodes, or undefined.
|
|
230
|
-
* @private
|
|
231
|
-
*/
|
|
232
|
-
Interpreter.stripLocations_ = function (node, start, end) {
|
|
233
|
-
if (start) {
|
|
234
|
-
node.start = start;
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
delete node.start;
|
|
238
|
-
}
|
|
239
|
-
if (end) {
|
|
240
|
-
node.end = end;
|
|
241
|
-
}
|
|
242
|
-
else {
|
|
243
|
-
delete node.end;
|
|
244
|
-
}
|
|
245
|
-
for (var name in node) {
|
|
246
|
-
if (name !== 'loc' && node.hasOwnProperty(name)) {
|
|
247
|
-
var prop = node[name];
|
|
248
|
-
if (prop && typeof prop === 'object') {
|
|
249
|
-
Interpreter.stripLocations_(/** @type {!Object} */ prop, start, end);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
};
|
|
254
|
-
/**
|
|
255
|
-
* Some pathological regular expressions can take geometric time.
|
|
256
|
-
* Regular expressions are handled in one of three ways:
|
|
257
|
-
* 0 - throw as invalid.
|
|
258
|
-
* 1 - execute natively (risk of unresponsive program).
|
|
259
|
-
* 2 - execute in separate thread (not supported by IE 9).
|
|
260
|
-
*/
|
|
261
|
-
Interpreter.prototype['REGEXP_MODE'] = 2;
|
|
262
|
-
/**
|
|
263
|
-
* If REGEXP_MODE = 2, the length of time (in ms) to allow a RegExp
|
|
264
|
-
* thread to execute before terminating it.
|
|
265
|
-
*/
|
|
266
|
-
Interpreter.prototype['REGEXP_THREAD_TIMEOUT'] = 1000;
|
|
267
|
-
/**
|
|
268
|
-
* Length of time (in ms) to allow a polyfill to run before ending step.
|
|
269
|
-
* If set to 0, polyfills will execute step by step.
|
|
270
|
-
* If set to 1000, polyfills will run for up to a second per step
|
|
271
|
-
* (execution will resume in the polyfill in the next step).
|
|
272
|
-
* If set to Infinity, polyfills will run to completion in a single step.
|
|
273
|
-
*/
|
|
274
|
-
Interpreter.prototype['POLYFILL_TIMEOUT'] = 1000;
|
|
275
|
-
/**
|
|
276
|
-
* Flag indicating that a getter function needs to be called immediately.
|
|
277
|
-
* @private
|
|
278
|
-
*/
|
|
279
|
-
Interpreter.prototype.getterStep_ = false;
|
|
280
|
-
/**
|
|
281
|
-
* Flag indicating that a setter function needs to be called immediately.
|
|
282
|
-
* @private
|
|
283
|
-
*/
|
|
284
|
-
Interpreter.prototype.setterStep_ = false;
|
|
285
|
-
/**
|
|
286
|
-
* Number of code chunks appended to the interpreter.
|
|
287
|
-
* @private
|
|
288
|
-
*/
|
|
289
|
-
Interpreter.prototype.appendCodeNumber_ = 0;
|
|
290
|
-
/**
|
|
291
|
-
* Parse JavaScript code into an AST using Acorn.
|
|
292
|
-
* @param {string} code Raw JavaScript text.
|
|
293
|
-
* @param {string} sourceFile Name of filename (for stack trace).
|
|
294
|
-
* @returns {!Object} AST.
|
|
295
|
-
* @private
|
|
296
|
-
*/
|
|
297
|
-
Interpreter.prototype.parse_ = function (code, sourceFile) {
|
|
298
|
-
// Create a new options object, since Acorn will modify this object.
|
|
299
|
-
// Inheritance can't be used since Acorn uses hasOwnProperty.
|
|
300
|
-
// Object.assign can't be used since that's ES6.
|
|
301
|
-
var options = {};
|
|
302
|
-
for (var name in Interpreter.PARSE_OPTIONS) {
|
|
303
|
-
options[name] = Interpreter.PARSE_OPTIONS[name];
|
|
304
|
-
}
|
|
305
|
-
options.sourceFile = sourceFile;
|
|
306
|
-
// BUILDER.IO: modified import here
|
|
307
|
-
return Interpreter.nativeGlobal.acornParse(code, options);
|
|
308
|
-
};
|
|
309
|
-
/**
|
|
310
|
-
* Add more code to the interpreter.
|
|
311
|
-
* @param {string|!Object} code Raw JavaScript text or AST.
|
|
312
|
-
*/
|
|
313
|
-
Interpreter.prototype.appendCode = function (code) {
|
|
314
|
-
var state = this.stateStack[0];
|
|
315
|
-
if (!state || state.node.type !== 'Program') {
|
|
316
|
-
throw Error('Expecting original AST to start with a Program node');
|
|
317
|
-
}
|
|
318
|
-
if (typeof code === 'string') {
|
|
319
|
-
code = this.parse_(code, 'appendCode' + this.appendCodeNumber_++);
|
|
320
|
-
}
|
|
321
|
-
if (!code || code.type !== 'Program') {
|
|
322
|
-
throw Error('Expecting new AST to start with a Program node');
|
|
323
|
-
}
|
|
324
|
-
this.populateScope_(code, state.scope);
|
|
325
|
-
// Append the new program to the old one.
|
|
326
|
-
Array.prototype.push.apply(state.node.body, code.body);
|
|
327
|
-
state.node.body.variableCache_ = null;
|
|
328
|
-
state.done = false;
|
|
329
|
-
};
|
|
330
|
-
/**
|
|
331
|
-
* Execute one step of the interpreter.
|
|
332
|
-
* @returns {boolean} True if a step was executed, false if no more instructions.
|
|
333
|
-
*/
|
|
334
|
-
Interpreter.prototype.step = function () {
|
|
335
|
-
var stack = this.stateStack;
|
|
336
|
-
var endTime;
|
|
337
|
-
do {
|
|
338
|
-
var state = stack[stack.length - 1];
|
|
339
|
-
if (!state) {
|
|
340
|
-
return false;
|
|
341
|
-
}
|
|
342
|
-
var node = state.node, type = node.type;
|
|
343
|
-
if (type === 'Program' && state.done) {
|
|
344
|
-
return false;
|
|
345
|
-
}
|
|
346
|
-
else if (this.paused_) {
|
|
347
|
-
return true;
|
|
348
|
-
}
|
|
349
|
-
// Record the interpreter in a global property so calls to toString/valueOf
|
|
350
|
-
// can execute in the proper context.
|
|
351
|
-
var oldInterpreterValue = Interpreter.currentInterpreter_;
|
|
352
|
-
Interpreter.currentInterpreter_ = this;
|
|
353
|
-
try {
|
|
354
|
-
var nextState = this.stepFunctions_[type](stack, state, node);
|
|
355
|
-
}
|
|
356
|
-
catch (e) {
|
|
357
|
-
// Eat any step errors. They have been thrown on the stack.
|
|
358
|
-
if (e !== Interpreter.STEP_ERROR) {
|
|
359
|
-
// This is a real error, either in the JS-Interpreter, or an uncaught
|
|
360
|
-
// error in the interpreted code. Rethrow.
|
|
361
|
-
if (this.value !== e) {
|
|
362
|
-
// Uh oh. Internal error in the JS-Interpreter.
|
|
363
|
-
this.value = undefined;
|
|
364
|
-
}
|
|
365
|
-
throw e;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
finally {
|
|
369
|
-
// Restore to previous value (probably null, maybe nested toString calls).
|
|
370
|
-
Interpreter.currentInterpreter_ = oldInterpreterValue;
|
|
371
|
-
}
|
|
372
|
-
if (nextState) {
|
|
373
|
-
stack.push(nextState);
|
|
374
|
-
}
|
|
375
|
-
if (this.getterStep_) {
|
|
376
|
-
// Getter from this step was not handled.
|
|
377
|
-
this.value = undefined;
|
|
378
|
-
throw Error('Getter not supported in this context');
|
|
379
|
-
}
|
|
380
|
-
if (this.setterStep_) {
|
|
381
|
-
// Setter from this step was not handled.
|
|
382
|
-
this.value = undefined;
|
|
383
|
-
throw Error('Setter not supported in this context');
|
|
384
|
-
}
|
|
385
|
-
// This may be polyfill code. Keep executing until we arrive at user code.
|
|
386
|
-
if (!endTime && !node.end) {
|
|
387
|
-
// Ideally this would be defined at the top of the function, but that
|
|
388
|
-
// wastes time if the step isn't a polyfill.
|
|
389
|
-
endTime = Date.now() + this['POLYFILL_TIMEOUT'];
|
|
390
|
-
}
|
|
391
|
-
} while (!node.end && endTime > Date.now());
|
|
392
|
-
return true;
|
|
393
|
-
};
|
|
394
|
-
/**
|
|
395
|
-
* Execute the interpreter to program completion. Vulnerable to infinite loops.
|
|
396
|
-
* @returns {boolean} True if a execution is asynchronously blocked,
|
|
397
|
-
* false if no more instructions.
|
|
398
|
-
*/
|
|
399
|
-
Interpreter.prototype.run = function () {
|
|
400
|
-
while (!this.paused_ && this.step()) { }
|
|
401
|
-
return this.paused_;
|
|
402
|
-
};
|
|
403
|
-
/**
|
|
404
|
-
* Initialize the global object with buitin properties and functions.
|
|
405
|
-
* @param {!Interpreter.Object} globalObject Global object.
|
|
406
|
-
*/
|
|
407
|
-
Interpreter.prototype.initGlobal = function (globalObject) {
|
|
408
|
-
// Initialize uneditable global properties.
|
|
409
|
-
this.setProperty(globalObject, 'NaN', NaN, Interpreter.NONCONFIGURABLE_READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
410
|
-
this.setProperty(globalObject, 'Infinity', Infinity, Interpreter.NONCONFIGURABLE_READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
411
|
-
this.setProperty(globalObject, 'undefined', undefined, Interpreter.NONCONFIGURABLE_READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
412
|
-
this.setProperty(globalObject, 'window', globalObject, Interpreter.READONLY_DESCRIPTOR);
|
|
413
|
-
this.setProperty(globalObject, 'this', globalObject, Interpreter.NONCONFIGURABLE_READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
414
|
-
this.setProperty(globalObject, 'self', globalObject); // Editable.
|
|
415
|
-
// Create the objects which will become Object.prototype and
|
|
416
|
-
// Function.prototype, which are needed to bootstrap everything else.
|
|
417
|
-
this.OBJECT_PROTO = new Interpreter.Object(null);
|
|
418
|
-
this.FUNCTION_PROTO = new Interpreter.Object(this.OBJECT_PROTO);
|
|
419
|
-
// Initialize global objects.
|
|
420
|
-
this.initFunction(globalObject);
|
|
421
|
-
this.initObject(globalObject);
|
|
422
|
-
// Unable to set globalObject's parent prior (OBJECT did not exist).
|
|
423
|
-
// Note that in a browser this would be `Window`, whereas in Node.js it would
|
|
424
|
-
// be `Object`. This interpreter is closer to Node in that it has no DOM.
|
|
425
|
-
globalObject.proto = this.OBJECT_PROTO;
|
|
426
|
-
this.setProperty(globalObject, 'constructor', this.OBJECT, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
427
|
-
this.initArray(globalObject);
|
|
428
|
-
this.initString(globalObject);
|
|
429
|
-
this.initBoolean(globalObject);
|
|
430
|
-
this.initNumber(globalObject);
|
|
431
|
-
this.initDate(globalObject);
|
|
432
|
-
this.initRegExp(globalObject);
|
|
433
|
-
this.initError(globalObject);
|
|
434
|
-
this.initMath(globalObject);
|
|
435
|
-
this.initJSON(globalObject);
|
|
436
|
-
// Initialize global functions.
|
|
437
|
-
var thisInterpreter = this;
|
|
438
|
-
var func = this.createNativeFunction(function (_x) {
|
|
439
|
-
throw EvalError("Can't happen");
|
|
440
|
-
}, false);
|
|
441
|
-
func.eval = true;
|
|
442
|
-
this.setProperty(globalObject, 'eval', func, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
443
|
-
this.setProperty(globalObject, 'parseInt', this.createNativeFunction(parseInt, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
444
|
-
this.setProperty(globalObject, 'parseFloat', this.createNativeFunction(parseFloat, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
445
|
-
this.setProperty(globalObject, 'isNaN', this.createNativeFunction(isNaN, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
446
|
-
this.setProperty(globalObject, 'isFinite', this.createNativeFunction(isFinite, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
447
|
-
var strFunctions = [[escape, 'escape'], [unescape, 'unescape'], [decodeURI, 'decodeURI'], [decodeURIComponent, 'decodeURIComponent'], [encodeURI, 'encodeURI'], [encodeURIComponent, 'encodeURIComponent']];
|
|
448
|
-
for (var i = 0; i < strFunctions.length; i++) {
|
|
449
|
-
var wrapper = function (nativeFunc) {
|
|
450
|
-
return function (str) {
|
|
451
|
-
try {
|
|
452
|
-
return nativeFunc(str);
|
|
453
|
-
}
|
|
454
|
-
catch (e) {
|
|
455
|
-
// decodeURI('%xy') will throw an error. Catch and rethrow.
|
|
456
|
-
thisInterpreter.throwException(thisInterpreter.URI_ERROR, e.message);
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
}(strFunctions[i][0]);
|
|
460
|
-
this.setProperty(globalObject, strFunctions[i][1], this.createNativeFunction(wrapper, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
461
|
-
}
|
|
462
|
-
// Preserve public properties from being pruned/renamed by JS compilers.
|
|
463
|
-
// Add others as needed.
|
|
464
|
-
this['OBJECT'] = this.OBJECT;
|
|
465
|
-
this['OBJECT_PROTO'] = this.OBJECT_PROTO;
|
|
466
|
-
this['FUNCTION'] = this.FUNCTION;
|
|
467
|
-
this['FUNCTION_PROTO'] = this.FUNCTION_PROTO;
|
|
468
|
-
this['ARRAY'] = this.ARRAY;
|
|
469
|
-
this['ARRAY_PROTO'] = this.ARRAY_PROTO;
|
|
470
|
-
this['REGEXP'] = this.REGEXP;
|
|
471
|
-
this['REGEXP_PROTO'] = this.REGEXP_PROTO;
|
|
472
|
-
this['DATE'] = this.DATE;
|
|
473
|
-
this['DATE_PROTO'] = this.DATE_PROTO;
|
|
474
|
-
// Run any user-provided initialization.
|
|
475
|
-
if (this.initFunc_) {
|
|
476
|
-
this.initFunc_(this, globalObject);
|
|
477
|
-
}
|
|
478
|
-
};
|
|
479
|
-
/**
|
|
480
|
-
* Number of functions created by the interpreter.
|
|
481
|
-
* @private
|
|
482
|
-
*/
|
|
483
|
-
Interpreter.prototype.functionCodeNumber_ = 0;
|
|
484
|
-
/**
|
|
485
|
-
* Initialize the Function class.
|
|
486
|
-
* @param {!Interpreter.Object} globalObject Global object.
|
|
487
|
-
*/
|
|
488
|
-
Interpreter.prototype.initFunction = function (globalObject) {
|
|
489
|
-
var thisInterpreter = this;
|
|
490
|
-
var wrapper;
|
|
491
|
-
var identifierRegexp = /^[A-Za-z_$][\w$]*$/;
|
|
492
|
-
// Function constructor.
|
|
493
|
-
wrapper = function Function(var_args) {
|
|
494
|
-
if (arguments.length) {
|
|
495
|
-
var code = String(arguments[arguments.length - 1]);
|
|
496
|
-
}
|
|
497
|
-
else {
|
|
498
|
-
var code = '';
|
|
499
|
-
}
|
|
500
|
-
var argsStr = Array.prototype.slice.call(arguments, 0, -1).join(',').trim();
|
|
501
|
-
if (argsStr) {
|
|
502
|
-
var args = argsStr.split(/\s*,\s*/);
|
|
503
|
-
for (var i = 0; i < args.length; i++) {
|
|
504
|
-
var name = args[i];
|
|
505
|
-
if (!identifierRegexp.test(name)) {
|
|
506
|
-
thisInterpreter.throwException(thisInterpreter.SYNTAX_ERROR, 'Invalid function argument: ' + name);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
argsStr = args.join(', ');
|
|
510
|
-
}
|
|
511
|
-
// Acorn needs to parse code in the context of a function or else `return`
|
|
512
|
-
// statements will be syntax errors.
|
|
513
|
-
try {
|
|
514
|
-
var ast = thisInterpreter.parse_('(function(' + argsStr + ') {' + code + '})', 'function' + thisInterpreter.functionCodeNumber_++);
|
|
515
|
-
}
|
|
516
|
-
catch (e) {
|
|
517
|
-
// Acorn threw a SyntaxError. Rethrow as a trappable error.
|
|
518
|
-
thisInterpreter.throwException(thisInterpreter.SYNTAX_ERROR, 'Invalid code: ' + e.message);
|
|
519
|
-
}
|
|
520
|
-
if (ast.body.length !== 1) {
|
|
521
|
-
// Function('a', 'return a + 6;}; {alert(1);');
|
|
522
|
-
thisInterpreter.throwException(thisInterpreter.SYNTAX_ERROR, 'Invalid code in function body');
|
|
523
|
-
}
|
|
524
|
-
var node = ast.body[0].expression;
|
|
525
|
-
// Note that if this constructor is called as `new Function()` the function
|
|
526
|
-
// object created by stepCallExpression and assigned to `this` is discarded.
|
|
527
|
-
// Interestingly, the scope for constructed functions is the global scope,
|
|
528
|
-
// even if they were constructed in some other scope.
|
|
529
|
-
return thisInterpreter.createFunction(node, thisInterpreter.globalScope, 'anonymous');
|
|
530
|
-
};
|
|
531
|
-
this.FUNCTION = this.createNativeFunction(wrapper, true);
|
|
532
|
-
this.setProperty(globalObject, 'Function', this.FUNCTION, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
533
|
-
// Throw away the created prototype and use the root prototype.
|
|
534
|
-
this.setProperty(this.FUNCTION, 'prototype', this.FUNCTION_PROTO, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
535
|
-
// Configure Function.prototype.
|
|
536
|
-
this.setProperty(this.FUNCTION_PROTO, 'constructor', this.FUNCTION, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
537
|
-
this.FUNCTION_PROTO.nativeFunc = function () { };
|
|
538
|
-
this.FUNCTION_PROTO.nativeFunc.id = this.functionCounter_++;
|
|
539
|
-
this.FUNCTION_PROTO.illegalConstructor = true;
|
|
540
|
-
this.setProperty(this.FUNCTION_PROTO, 'length', 0, Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
541
|
-
this.FUNCTION_PROTO.class = 'Function';
|
|
542
|
-
wrapper = function apply(thisArg, args) {
|
|
543
|
-
var state = thisInterpreter.stateStack[thisInterpreter.stateStack.length - 1];
|
|
544
|
-
// Rewrite the current CallExpression state to apply a different function.
|
|
545
|
-
state.func_ = this;
|
|
546
|
-
// Assign the `this` object.
|
|
547
|
-
state.funcThis_ = thisArg;
|
|
548
|
-
// Bind any provided arguments.
|
|
549
|
-
state.arguments_ = [];
|
|
550
|
-
if (args !== null && args !== undefined) {
|
|
551
|
-
if (args instanceof Interpreter.Object) {
|
|
552
|
-
state.arguments_ = thisInterpreter.arrayPseudoToNative(args);
|
|
553
|
-
}
|
|
554
|
-
else {
|
|
555
|
-
thisInterpreter.throwException(thisInterpreter.TYPE_ERROR, 'CreateListFromArrayLike called on non-object');
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
state.doneExec_ = false;
|
|
559
|
-
};
|
|
560
|
-
this.setNativeFunctionPrototype(this.FUNCTION, 'apply', wrapper);
|
|
561
|
-
wrapper = function call(thisArg /*, var_args */) {
|
|
562
|
-
var state = thisInterpreter.stateStack[thisInterpreter.stateStack.length - 1];
|
|
563
|
-
// Rewrite the current CallExpression state to call a different function.
|
|
564
|
-
state.func_ = this;
|
|
565
|
-
// Assign the `this` object.
|
|
566
|
-
state.funcThis_ = thisArg;
|
|
567
|
-
// Bind any provided arguments.
|
|
568
|
-
state.arguments_ = [];
|
|
569
|
-
for (var i = 1; i < arguments.length; i++) {
|
|
570
|
-
state.arguments_.push(arguments[i]);
|
|
571
|
-
}
|
|
572
|
-
state.doneExec_ = false;
|
|
573
|
-
};
|
|
574
|
-
this.setNativeFunctionPrototype(this.FUNCTION, 'call', wrapper);
|
|
575
|
-
this.polyfills_.push(
|
|
576
|
-
// Polyfill copied from:
|
|
577
|
-
// developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind
|
|
578
|
-
"Object.defineProperty(Function.prototype, 'bind',", '{configurable: true, writable: true, value:', 'function bind(oThis) {', "if (typeof this !== 'function') {", "throw TypeError('What is trying to be bound is not callable');", '}', 'var aArgs = Array.prototype.slice.call(arguments, 1),', 'fToBind = this,', 'fNOP = function() {},', 'fBound = function() {', 'return fToBind.apply(this instanceof fNOP', '? this', ': oThis,', 'aArgs.concat(Array.prototype.slice.call(arguments)));', '};', 'if (this.prototype) {', 'fNOP.prototype = this.prototype;', '}', 'fBound.prototype = new fNOP();', 'return fBound;', '}', '});', '');
|
|
579
|
-
// Function has no parent to inherit from, so it needs its own mandatory
|
|
580
|
-
// toString and valueOf functions.
|
|
581
|
-
wrapper = function toString() {
|
|
582
|
-
return String(this);
|
|
583
|
-
};
|
|
584
|
-
this.setNativeFunctionPrototype(this.FUNCTION, 'toString', wrapper);
|
|
585
|
-
this.setProperty(this.FUNCTION, 'toString', this.createNativeFunction(wrapper, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
586
|
-
wrapper = function valueOf() {
|
|
587
|
-
return this.valueOf();
|
|
588
|
-
};
|
|
589
|
-
this.setNativeFunctionPrototype(this.FUNCTION, 'valueOf', wrapper);
|
|
590
|
-
this.setProperty(this.FUNCTION, 'valueOf', this.createNativeFunction(wrapper, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
591
|
-
};
|
|
592
|
-
/**
|
|
593
|
-
* Initialize the Object class.
|
|
594
|
-
* @param {!Interpreter.Object} globalObject Global object.
|
|
595
|
-
*/
|
|
596
|
-
Interpreter.prototype.initObject = function (globalObject) {
|
|
597
|
-
var thisInterpreter = this;
|
|
598
|
-
var wrapper;
|
|
599
|
-
// Object constructor.
|
|
600
|
-
wrapper = function Object(value) {
|
|
601
|
-
if (value === undefined || value === null) {
|
|
602
|
-
// Create a new object.
|
|
603
|
-
if (thisInterpreter.calledWithNew()) {
|
|
604
|
-
// Called as `new Object()`.
|
|
605
|
-
return this;
|
|
606
|
-
}
|
|
607
|
-
else {
|
|
608
|
-
// Called as `Object()`.
|
|
609
|
-
return thisInterpreter.createObjectProto(thisInterpreter.OBJECT_PROTO);
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
if (!(value instanceof Interpreter.Object)) {
|
|
613
|
-
// Wrap the value as an object.
|
|
614
|
-
var box = thisInterpreter.createObjectProto(thisInterpreter.getPrototype(value));
|
|
615
|
-
box.data = value;
|
|
616
|
-
return box;
|
|
617
|
-
}
|
|
618
|
-
// Return the provided object.
|
|
619
|
-
return value;
|
|
620
|
-
};
|
|
621
|
-
this.OBJECT = this.createNativeFunction(wrapper, true);
|
|
622
|
-
// Throw away the created prototype and use the root prototype.
|
|
623
|
-
this.setProperty(this.OBJECT, 'prototype', this.OBJECT_PROTO, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
624
|
-
this.setProperty(this.OBJECT_PROTO, 'constructor', this.OBJECT, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
625
|
-
this.setProperty(globalObject, 'Object', this.OBJECT, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
626
|
-
/**
|
|
627
|
-
* Checks if the provided value is null or undefined.
|
|
628
|
-
* If so, then throw an error in the call stack.
|
|
629
|
-
* @param {Interpreter.Value} value Value to check.
|
|
630
|
-
*/
|
|
631
|
-
var throwIfNullUndefined = function (value) {
|
|
632
|
-
if (value === undefined || value === null) {
|
|
633
|
-
thisInterpreter.throwException(thisInterpreter.TYPE_ERROR, "Cannot convert '" + value + "' to object");
|
|
634
|
-
}
|
|
635
|
-
};
|
|
636
|
-
// Static methods on Object.
|
|
637
|
-
wrapper = function getOwnPropertyNames(obj) {
|
|
638
|
-
throwIfNullUndefined(obj);
|
|
639
|
-
var props = obj instanceof Interpreter.Object ? obj.properties : obj;
|
|
640
|
-
return thisInterpreter.arrayNativeToPseudo(Object.getOwnPropertyNames(props));
|
|
641
|
-
};
|
|
642
|
-
this.setProperty(this.OBJECT, 'getOwnPropertyNames', this.createNativeFunction(wrapper, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
643
|
-
wrapper = function keys(obj) {
|
|
644
|
-
throwIfNullUndefined(obj);
|
|
645
|
-
if (obj instanceof Interpreter.Object) {
|
|
646
|
-
obj = obj.properties;
|
|
647
|
-
}
|
|
648
|
-
return thisInterpreter.arrayNativeToPseudo(Object.keys(obj));
|
|
649
|
-
};
|
|
650
|
-
this.setProperty(this.OBJECT, 'keys', this.createNativeFunction(wrapper, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
651
|
-
wrapper = function create_(proto) {
|
|
652
|
-
// Support for the second argument is the responsibility of a polyfill.
|
|
653
|
-
if (proto === null) {
|
|
654
|
-
return thisInterpreter.createObjectProto(null);
|
|
655
|
-
}
|
|
656
|
-
if (!(proto instanceof Interpreter.Object)) {
|
|
657
|
-
thisInterpreter.throwException(thisInterpreter.TYPE_ERROR, 'Object prototype may only be an Object or null, not ' + proto);
|
|
658
|
-
}
|
|
659
|
-
return thisInterpreter.createObjectProto(proto);
|
|
660
|
-
};
|
|
661
|
-
this.setProperty(this.OBJECT, 'create', this.createNativeFunction(wrapper, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
662
|
-
// Add a polyfill to handle create's second argument.
|
|
663
|
-
this.polyfills_.push('(function() {', 'var create_ = Object.create;', 'Object.create = function create(proto, props) {', 'var obj = create_(proto);', 'props && Object.defineProperties(obj, props);', 'return obj;', '};', '})();', '');
|
|
664
|
-
wrapper = function defineProperty(obj, prop, descriptor) {
|
|
665
|
-
prop = String(prop);
|
|
666
|
-
if (!(obj instanceof Interpreter.Object)) {
|
|
667
|
-
thisInterpreter.throwException(thisInterpreter.TYPE_ERROR, 'Object.defineProperty called on non-object: ' + obj);
|
|
668
|
-
}
|
|
669
|
-
if (!(descriptor instanceof Interpreter.Object)) {
|
|
670
|
-
thisInterpreter.throwException(thisInterpreter.TYPE_ERROR, 'Property description must be an object');
|
|
671
|
-
}
|
|
672
|
-
if (obj.preventExtensions && !(prop in obj.properties)) {
|
|
673
|
-
thisInterpreter.throwException(thisInterpreter.TYPE_ERROR, "Can't define property '" + prop + "', object is not extensible");
|
|
674
|
-
}
|
|
675
|
-
// The polyfill guarantees no inheritance and no getter functions.
|
|
676
|
-
// Therefore the descriptor properties map is the native object needed.
|
|
677
|
-
thisInterpreter.setProperty(obj, prop, Interpreter.VALUE_IN_DESCRIPTOR, descriptor.properties);
|
|
678
|
-
return obj;
|
|
679
|
-
};
|
|
680
|
-
this.setProperty(this.OBJECT, 'defineProperty', this.createNativeFunction(wrapper, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
681
|
-
this.polyfills_.push(
|
|
682
|
-
// Flatten the descriptor to remove any inheritance or getter functions.
|
|
683
|
-
'(function() {', 'var defineProperty_ = Object.defineProperty;', 'Object.defineProperty = function defineProperty(obj, prop, d1) {', 'var d2 = {};', "if ('configurable' in d1) d2.configurable = d1.configurable;", "if ('enumerable' in d1) d2.enumerable = d1.enumerable;", "if ('writable' in d1) d2.writable = d1.writable;", "if ('value' in d1) d2.value = d1.value;", "if ('get' in d1) d2.get = d1.get;", "if ('set' in d1) d2.set = d1.set;", 'return defineProperty_(obj, prop, d2);', '};', '})();', "Object.defineProperty(Object, 'defineProperties',", '{configurable: true, writable: true, value:', 'function defineProperties(obj, props) {', 'var keys = Object.keys(props);', 'for (var i = 0; i < keys.length; i++) {', 'Object.defineProperty(obj, keys[i], props[keys[i]]);', '}', 'return obj;', '}', '});', '');
|
|
684
|
-
wrapper = function getOwnPropertyDescriptor(obj, prop) {
|
|
685
|
-
if (!(obj instanceof Interpreter.Object)) {
|
|
686
|
-
thisInterpreter.throwException(thisInterpreter.TYPE_ERROR, 'Object.getOwnPropertyDescriptor called on non-object: ' + obj);
|
|
687
|
-
}
|
|
688
|
-
prop = String(prop);
|
|
689
|
-
if (!(prop in obj.properties)) {
|
|
690
|
-
return undefined;
|
|
691
|
-
}
|
|
692
|
-
var descriptor = Object.getOwnPropertyDescriptor(obj.properties, prop);
|
|
693
|
-
var getter = obj.getter[prop];
|
|
694
|
-
var setter = obj.setter[prop];
|
|
695
|
-
var pseudoDescriptor = thisInterpreter.createObjectProto(thisInterpreter.OBJECT_PROTO);
|
|
696
|
-
if (getter || setter) {
|
|
697
|
-
thisInterpreter.setProperty(pseudoDescriptor, 'get', getter);
|
|
698
|
-
thisInterpreter.setProperty(pseudoDescriptor, 'set', setter);
|
|
699
|
-
}
|
|
700
|
-
else {
|
|
701
|
-
thisInterpreter.setProperty(pseudoDescriptor, 'value', /** @type {!Interpreter.Value} */ descriptor['value']);
|
|
702
|
-
thisInterpreter.setProperty(pseudoDescriptor, 'writable', descriptor['writable']);
|
|
703
|
-
}
|
|
704
|
-
thisInterpreter.setProperty(pseudoDescriptor, 'configurable', descriptor['configurable']);
|
|
705
|
-
thisInterpreter.setProperty(pseudoDescriptor, 'enumerable', descriptor['enumerable']);
|
|
706
|
-
return pseudoDescriptor;
|
|
707
|
-
};
|
|
708
|
-
this.setProperty(this.OBJECT, 'getOwnPropertyDescriptor', this.createNativeFunction(wrapper, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
709
|
-
wrapper = function getPrototypeOf(obj) {
|
|
710
|
-
throwIfNullUndefined(obj);
|
|
711
|
-
return thisInterpreter.getPrototype(obj);
|
|
712
|
-
};
|
|
713
|
-
this.setProperty(this.OBJECT, 'getPrototypeOf', this.createNativeFunction(wrapper, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
714
|
-
wrapper = function isExtensible(obj) {
|
|
715
|
-
return Boolean(obj) && !obj.preventExtensions;
|
|
716
|
-
};
|
|
717
|
-
this.setProperty(this.OBJECT, 'isExtensible', this.createNativeFunction(wrapper, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
718
|
-
wrapper = function preventExtensions(obj) {
|
|
719
|
-
if (obj instanceof Interpreter.Object) {
|
|
720
|
-
obj.preventExtensions = true;
|
|
721
|
-
}
|
|
722
|
-
return obj;
|
|
723
|
-
};
|
|
724
|
-
this.setProperty(this.OBJECT, 'preventExtensions', this.createNativeFunction(wrapper, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
725
|
-
// Instance methods on Object.
|
|
726
|
-
this.setNativeFunctionPrototype(this.OBJECT, 'toString', Interpreter.Object.prototype.toString);
|
|
727
|
-
this.setNativeFunctionPrototype(this.OBJECT, 'toLocaleString', Interpreter.Object.prototype.toString);
|
|
728
|
-
this.setNativeFunctionPrototype(this.OBJECT, 'valueOf', Interpreter.Object.prototype.valueOf);
|
|
729
|
-
wrapper = function hasOwnProperty(prop) {
|
|
730
|
-
throwIfNullUndefined(this);
|
|
731
|
-
if (this instanceof Interpreter.Object) {
|
|
732
|
-
return String(prop) in this.properties;
|
|
733
|
-
}
|
|
734
|
-
// Primitive.
|
|
735
|
-
return this.hasOwnProperty(prop);
|
|
736
|
-
};
|
|
737
|
-
this.setNativeFunctionPrototype(this.OBJECT, 'hasOwnProperty', wrapper);
|
|
738
|
-
wrapper = function propertyIsEnumerable(prop) {
|
|
739
|
-
throwIfNullUndefined(this);
|
|
740
|
-
if (this instanceof Interpreter.Object) {
|
|
741
|
-
return Object.prototype.propertyIsEnumerable.call(this.properties, prop);
|
|
742
|
-
}
|
|
743
|
-
// Primitive.
|
|
744
|
-
return this.propertyIsEnumerable(prop);
|
|
745
|
-
};
|
|
746
|
-
this.setNativeFunctionPrototype(this.OBJECT, 'propertyIsEnumerable', wrapper);
|
|
747
|
-
wrapper = function isPrototypeOf(obj) {
|
|
748
|
-
while (true) {
|
|
749
|
-
// Note, circular loops shouldn't be possible.
|
|
750
|
-
obj = thisInterpreter.getPrototype(obj);
|
|
751
|
-
if (!obj) {
|
|
752
|
-
// No parent; reached the top.
|
|
753
|
-
return false;
|
|
754
|
-
}
|
|
755
|
-
if (obj === this) {
|
|
756
|
-
return true;
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
};
|
|
760
|
-
this.setNativeFunctionPrototype(this.OBJECT, 'isPrototypeOf', wrapper);
|
|
761
|
-
};
|
|
762
|
-
/**
|
|
763
|
-
* Initialize the Array class.
|
|
764
|
-
* @param {!Interpreter.Object} globalObject Global object.
|
|
765
|
-
*/
|
|
766
|
-
Interpreter.prototype.initArray = function (globalObject) {
|
|
767
|
-
var thisInterpreter = this;
|
|
768
|
-
var wrapper;
|
|
769
|
-
// Array constructor.
|
|
770
|
-
wrapper = function Array(var_args) {
|
|
771
|
-
if (thisInterpreter.calledWithNew()) {
|
|
772
|
-
// Called as `new Array()`.
|
|
773
|
-
var newArray = this;
|
|
774
|
-
}
|
|
775
|
-
else {
|
|
776
|
-
// Called as `Array()`.
|
|
777
|
-
var newArray = thisInterpreter.createArray();
|
|
778
|
-
}
|
|
779
|
-
var first = arguments[0];
|
|
780
|
-
if (arguments.length === 1 && typeof first === 'number') {
|
|
781
|
-
if (isNaN(Interpreter.legalArrayLength(first))) {
|
|
782
|
-
thisInterpreter.throwException(thisInterpreter.RANGE_ERROR, 'Invalid array length: ' + first);
|
|
783
|
-
}
|
|
784
|
-
newArray.properties.length = first;
|
|
785
|
-
}
|
|
786
|
-
else {
|
|
787
|
-
for (var i = 0; i < arguments.length; i++) {
|
|
788
|
-
newArray.properties[i] = arguments[i];
|
|
789
|
-
}
|
|
790
|
-
newArray.properties.length = i;
|
|
791
|
-
}
|
|
792
|
-
return newArray;
|
|
793
|
-
};
|
|
794
|
-
this.ARRAY = this.createNativeFunction(wrapper, true);
|
|
795
|
-
this.ARRAY_PROTO = this.ARRAY.properties['prototype'];
|
|
796
|
-
this.setProperty(globalObject, 'Array', this.ARRAY, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
797
|
-
// Static methods on Array.
|
|
798
|
-
wrapper = function isArray(obj) {
|
|
799
|
-
return obj && obj.class === 'Array';
|
|
800
|
-
};
|
|
801
|
-
this.setProperty(this.ARRAY, 'isArray', this.createNativeFunction(wrapper, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
802
|
-
// Instance methods on Array.
|
|
803
|
-
this.setProperty(this.ARRAY_PROTO, 'length', 0, {
|
|
804
|
-
configurable: false,
|
|
805
|
-
enumerable: false,
|
|
806
|
-
writable: true
|
|
807
|
-
});
|
|
808
|
-
this.ARRAY_PROTO.class = 'Array';
|
|
809
|
-
this.polyfills_.push('(function() {', 'function createArrayMethod_(f) {', 'Object.defineProperty(Array.prototype, f.name,', '{configurable: true, writable: true, value: f});', '}', 'createArrayMethod_(', 'function pop() {', 'if (!this) throw TypeError();', 'var o = Object(this), len = o.length >>> 0;', 'if (!len || len < 0) {', 'o.length = 0;', 'return undefined;', '}', 'len--;', 'var x = o[len];', 'delete o[len];',
|
|
810
|
-
// Needed for non-arrays.
|
|
811
|
-
'o.length = len;', 'return x;', '}', ');', 'createArrayMethod_(', 'function push(var_args) {', 'if (!this) throw TypeError();', 'var o = Object(this), len = o.length >>> 0;', 'for (var i = 0; i < arguments.length; i++) {', 'o[len] = arguments[i];', 'len++;', '}', 'o.length = len;', 'return len;', '}', ');', 'createArrayMethod_(', 'function shift() {', 'if (!this) throw TypeError();', 'var o = Object(this), len = o.length >>> 0;', 'if (!len || len < 0) {', 'o.length = 0;', 'return undefined;', '}', 'var value = o[0];', 'for (var i = 0; i < len - 1; i++) {', 'if ((i + 1) in o) {', 'o[i] = o[i + 1];', '} else {', 'delete o[i];', '}', '}', 'delete o[i];',
|
|
812
|
-
// Needed for non-arrays.
|
|
813
|
-
'o.length = len - 1;', 'return value;', '}', ');', 'createArrayMethod_(', 'function unshift(var_args) {', 'if (!this) throw TypeError();', 'var o = Object(this), len = o.length >>> 0;', 'if (!len || len < 0) {', 'len = 0;', '}', 'for (var i = len - 1; i >= 0; i--) {', 'if (i in o) {', 'o[i + arguments.length] = o[i];', '} else {', 'delete o[i + arguments.length];', '}', '}', 'for (var i = 0; i < arguments.length; i++) {', 'o[i] = arguments[i];', '}', 'return (o.length = len + arguments.length);', '}', ');', 'createArrayMethod_(', 'function reverse() {', 'if (!this) throw TypeError();', 'var o = Object(this), len = o.length >>> 0;', 'if (!len || len < 2) {', 'return o;',
|
|
814
|
-
// Not an array, or too short to reverse.
|
|
815
|
-
'}', 'for (var i = 0; i < len / 2 - 0.5; i++) {', 'var x = o[i];', 'var hasX = i in o;', 'if ((len - i - 1) in o) {', 'o[i] = o[len - i - 1];', '} else {', 'delete o[i];', '}', 'if (hasX) {', 'o[len - i - 1] = x;', '} else {', 'delete o[len - i - 1];', '}', '}', 'return o;', '}', ');', 'createArrayMethod_(', 'function indexOf(searchElement, fromIndex) {', 'if (!this) throw TypeError();', 'var o = Object(this), len = o.length >>> 0;', 'var n = fromIndex | 0;', 'if (!len || n >= len) {', 'return -1;', '}', 'var i = Math.max(n >= 0 ? n : len - Math.abs(n), 0);', 'while (i < len) {', 'if (i in o && o[i] === searchElement) {', 'return i;', '}', 'i++;', '}', 'return -1;', '}', ');', 'createArrayMethod_(', 'function lastIndexOf(searchElement, fromIndex) {', 'if (!this) throw TypeError();', 'var o = Object(this), len = o.length >>> 0;', 'if (!len) {', 'return -1;', '}', 'var n = len - 1;', 'if (arguments.length > 1) {', 'n = fromIndex | 0;', 'if (n) {', 'n = (n > 0 || -1) * Math.floor(Math.abs(n));', '}', '}', 'var i = n >= 0 ? Math.min(n, len - 1) : len - Math.abs(n);', 'while (i >= 0) {', 'if (i in o && o[i] === searchElement) {', 'return i;', '}', 'i--;', '}', 'return -1;', '}', ');', 'createArrayMethod_(', 'function slice(start, end) {', 'if (!this) throw TypeError();', 'var o = Object(this), len = o.length >>> 0;',
|
|
816
|
-
// Handle negative value for "start"
|
|
817
|
-
'start |= 0;', 'start = (start >= 0) ? start : Math.max(0, len + start);',
|
|
818
|
-
// Handle negative value for "end"
|
|
819
|
-
"if (typeof end !== 'undefined') {", 'if (end !== Infinity) {', 'end |= 0;', '}', 'if (end < 0) {', 'end = len + end;', '} else {', 'end = Math.min(end, len);', '}', '} else {', 'end = len;', '}', 'var size = end - start;', 'var cloned = new Array(size);', 'for (var i = 0; i < size; i++) {', 'if ((start + i) in o) {', 'cloned[i] = o[start + i];', '}', '}', 'return cloned;', '}', ');', 'createArrayMethod_(', 'function splice(start, deleteCount, var_args) {', 'if (!this) throw TypeError();', 'var o = Object(this), len = o.length >>> 0;', 'start |= 0;', 'if (start < 0) {', 'start = Math.max(len + start, 0);', '} else {', 'start = Math.min(start, len);', '}', 'if (arguments.length < 1) {', 'deleteCount = len - start;', '} else {', 'deleteCount |= 0;', 'deleteCount = Math.max(0, Math.min(deleteCount, len - start));', '}', 'var removed = [];',
|
|
820
|
-
// Remove specified elements.
|
|
821
|
-
'for (var i = start; i < start + deleteCount; i++) {', 'if (i in o) {', 'removed.push(o[i]);', '} else {', 'removed.length++;', '}', 'if ((i + deleteCount) in o) {', 'o[i] = o[i + deleteCount];', '} else {', 'delete o[i];', '}', '}',
|
|
822
|
-
// Move other element to fill the gap.
|
|
823
|
-
'for (var i = start + deleteCount; i < len - deleteCount; i++) {', 'if ((i + deleteCount) in o) {', 'o[i] = o[i + deleteCount];', '} else {', 'delete o[i];', '}', '}',
|
|
824
|
-
// Delete superfluous properties.
|
|
825
|
-
'for (var i = len - deleteCount; i < len; i++) {', 'delete o[i];', '}', 'len -= deleteCount;',
|
|
826
|
-
// Insert specified items.
|
|
827
|
-
'var arl = arguments.length - 2;', 'for (var i = len - 1; i >= start; i--) {', 'if (i in o) {', 'o[i + arl] = o[i];', '} else {', 'delete o[i + arl];', '}', '}', 'len += arl;', 'for (var i = 2; i < arguments.length; i++) {', 'o[start + i - 2] = arguments[i];', '}', 'o.length = len;', 'return removed;', '}', ');', 'createArrayMethod_(', 'function concat(var_args) {', 'if (!this) throw TypeError();', 'var o = Object(this);', 'var cloned = [];', 'for (var i = -1; i < arguments.length; i++) {', 'var value = (i === -1) ? o : arguments[i];', 'if (Array.isArray(value)) {', 'for (var j = 0, l = value.length; j < l; j++) {', 'if (j in value) {', 'cloned.push(value[j]);', '} else {', 'cloned.length++;', '}', '}', '} else {', 'cloned.push(value);', '}', '}', 'return cloned;', '}', ');', 'createArrayMethod_(', 'function join(opt_separator) {', 'if (!this) throw TypeError();', 'var o = Object(this), len = o.length >>> 0;', "var sep = typeof opt_separator === 'undefined' ?", "',' : ('' + opt_separator);", "var str = '';", 'for (var i = 0; i < len; i++) {', 'if (i && sep) {', 'str += sep;', '}', "str += (o[i] === null || o[i] === undefined) ? '' : o[i];", '}', 'return str;', '}', ');',
|
|
828
|
-
// Polyfill copied from:
|
|
829
|
-
// developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every
|
|
830
|
-
'createArrayMethod_(', 'function every(callbackfn, thisArg) {', "if (!this || typeof callbackfn !== 'function') throw TypeError();", 'var t, k = 0;', 'var o = Object(this), len = o.length >>> 0;', 'if (arguments.length > 1) t = thisArg;', 'while (k < len) {', 'if (k in o && !callbackfn.call(t, o[k], k, o)) return false;', 'k++;', '}', 'return true;', '}', ');',
|
|
831
|
-
// Polyfill copied from:
|
|
832
|
-
// developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
|
|
833
|
-
'createArrayMethod_(', 'function filter(fun, var_args) {', "if (this === void 0 || this === null || typeof fun !== 'function') throw TypeError();", 'var o = Object(this), len = o.length >>> 0;', 'var res = [];', 'var thisArg = arguments.length >= 2 ? arguments[1] : void 0;', 'for (var i = 0; i < len; i++) {', 'if (i in o) {', 'var val = o[i];', 'if (fun.call(thisArg, val, i, o)) res.push(val);', '}', '}', 'return res;', '}', ');',
|
|
834
|
-
// Polyfill copied from:
|
|
835
|
-
// developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
|
|
836
|
-
'createArrayMethod_(', 'function forEach(callback, thisArg) {', "if (!this || typeof callback !== 'function') throw TypeError();", 'var t, k = 0;', 'var o = Object(this), len = o.length >>> 0;', 'if (arguments.length > 1) t = thisArg;', 'while (k < len) {', 'if (k in o) callback.call(t, o[k], k, o);', 'k++;', '}', '}', ');',
|
|
837
|
-
// Polyfill copied from:
|
|
838
|
-
// developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map
|
|
839
|
-
'createArrayMethod_(', 'function map(callback, thisArg) {', "if (!this || typeof callback !== 'function') throw TypeError();", 'var t, k = 0;', 'var o = Object(this), len = o.length >>> 0;', 'if (arguments.length > 1) t = thisArg;', 'var a = new Array(len);', 'while (k < len) {', 'if (k in o) a[k] = callback.call(t, o[k], k, o);', 'k++;', '}', 'return a;', '}', ');',
|
|
840
|
-
// Polyfill copied from:
|
|
841
|
-
// developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
|
|
842
|
-
'createArrayMethod_(', 'function reduce(callback /*, initialValue*/) {', "if (!this || typeof callback !== 'function') throw TypeError();", 'var o = Object(this), len = o.length >>> 0;', 'var k = 0, value;', 'if (arguments.length === 2) {', 'value = arguments[1];', '} else {', 'while (k < len && !(k in o)) k++;', 'if (k >= len) {', "throw TypeError('Reduce of empty array with no initial value');", '}', 'value = o[k++];', '}', 'for (; k < len; k++) {', 'if (k in o) value = callback(value, o[k], k, o);', '}', 'return value;', '}', ');',
|
|
843
|
-
// Polyfill copied from:
|
|
844
|
-
// developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/ReduceRight
|
|
845
|
-
'createArrayMethod_(', 'function reduceRight(callback /*, initialValue*/) {', "if (null === this || 'undefined' === typeof this || 'function' !== typeof callback) throw TypeError();", 'var o = Object(this), len = o.length >>> 0;', 'var k = len - 1, value;', 'if (arguments.length >= 2) {', 'value = arguments[1];', '} else {', 'while (k >= 0 && !(k in o)) k--;', 'if (k < 0) {', "throw TypeError('Reduce of empty array with no initial value');", '}', 'value = o[k--];', '}', 'for (; k >= 0; k--) {', 'if (k in o) value = callback(value, o[k], k, o);', '}', 'return value;', '}', ');',
|
|
846
|
-
// Polyfill copied from:
|
|
847
|
-
// developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/some
|
|
848
|
-
'createArrayMethod_(', 'function some(fun/*, thisArg*/) {', "if (!this || typeof fun !== 'function') throw TypeError();", 'var o = Object(this), len = o.length >>> 0;', 'var thisArg = arguments.length >= 2 ? arguments[1] : void 0;', 'for (var i = 0; i < len; i++) {', 'if (i in o && fun.call(thisArg, o[i], i, o)) {', 'return true;', '}', '}', 'return false;', '}', ');', 'createArrayMethod_(', 'function sort(opt_comp) {',
|
|
849
|
-
// Bubble sort!
|
|
850
|
-
'if (!this) throw TypeError();', "if (typeof opt_comp !== 'function') {", 'opt_comp = undefined;', '}', 'for (var i = 0; i < this.length; i++) {', 'var changes = 0;', 'for (var j = 0; j < this.length - i - 1; j++) {', 'if (opt_comp ? (opt_comp(this[j], this[j + 1]) > 0) :', '(String(this[j]) > String(this[j + 1]))) {', 'var swap = this[j];', 'var hasSwap = j in this;', 'if ((j + 1) in this) {', 'this[j] = this[j + 1];', '} else {', 'delete this[j];', '}', 'if (hasSwap) {', 'this[j + 1] = swap;', '} else {', 'delete this[j + 1];', '}', 'changes++;', '}', '}', 'if (!changes) break;', '}', 'return this;', '}', ');', 'createArrayMethod_(', 'function toLocaleString() {', 'if (!this) throw TypeError();', 'var o = Object(this), len = o.length >>> 0;', 'var out = [];', 'for (var i = 0; i < len; i++) {', "out[i] = (o[i] === null || o[i] === undefined) ? '' : o[i].toLocaleString();", '}', "return out.join(',');", '}', ');', '})();', '');
|
|
851
|
-
};
|
|
852
|
-
/**
|
|
853
|
-
* Initialize the String class.
|
|
854
|
-
* @param {!Interpreter.Object} globalObject Global object.
|
|
855
|
-
*/
|
|
856
|
-
Interpreter.prototype.initString = function (globalObject) {
|
|
857
|
-
var thisInterpreter = this;
|
|
858
|
-
var wrapper;
|
|
859
|
-
// String constructor.
|
|
860
|
-
wrapper = function String(value) {
|
|
861
|
-
value = arguments.length ? Interpreter.nativeGlobal.String(value) : '';
|
|
862
|
-
if (thisInterpreter.calledWithNew()) {
|
|
863
|
-
// Called as `new String()`.
|
|
864
|
-
this.data = value;
|
|
865
|
-
return this;
|
|
866
|
-
}
|
|
867
|
-
else {
|
|
868
|
-
// Called as `String()`.
|
|
869
|
-
return value;
|
|
870
|
-
}
|
|
871
|
-
};
|
|
872
|
-
this.STRING = this.createNativeFunction(wrapper, true);
|
|
873
|
-
this.setProperty(globalObject, 'String', this.STRING, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
874
|
-
// Static methods on String.
|
|
875
|
-
this.setProperty(this.STRING, 'fromCharCode', this.createNativeFunction(String.fromCharCode, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
876
|
-
// Instance methods on String.
|
|
877
|
-
// Methods with exclusively primitive arguments.
|
|
878
|
-
var functions = ['charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf', 'slice', 'substr', 'substring', 'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toUpperCase', 'trim'];
|
|
879
|
-
for (var i = 0; i < functions.length; i++) {
|
|
880
|
-
this.setNativeFunctionPrototype(this.STRING, functions[i], String.prototype[functions[i]]);
|
|
881
|
-
}
|
|
882
|
-
wrapper = function localeCompare(compareString, locales, options) {
|
|
883
|
-
locales = thisInterpreter.pseudoToNative(locales);
|
|
884
|
-
options = thisInterpreter.pseudoToNative(options);
|
|
885
|
-
try {
|
|
886
|
-
return String(this).localeCompare(compareString, /** @type {?} */ locales, /** @type {?} */ options);
|
|
887
|
-
}
|
|
888
|
-
catch (e) {
|
|
889
|
-
thisInterpreter.throwException(thisInterpreter.ERROR, 'localeCompare: ' + e.message);
|
|
890
|
-
}
|
|
891
|
-
};
|
|
892
|
-
this.setNativeFunctionPrototype(this.STRING, 'localeCompare', wrapper);
|
|
893
|
-
wrapper = function split(separator, limit, callback) {
|
|
894
|
-
var string = String(this);
|
|
895
|
-
limit = limit ? Number(limit) : undefined;
|
|
896
|
-
// Example of catastrophic split RegExp:
|
|
897
|
-
// 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaac'.split(/^(a+)+b/)
|
|
898
|
-
if (thisInterpreter.isa(separator, thisInterpreter.REGEXP)) {
|
|
899
|
-
separator = separator.data;
|
|
900
|
-
thisInterpreter.maybeThrowRegExp(separator, callback);
|
|
901
|
-
if (thisInterpreter['REGEXP_MODE'] === 2) {
|
|
902
|
-
if (Interpreter.vm) {
|
|
903
|
-
// Run split in vm.
|
|
904
|
-
var sandbox = {
|
|
905
|
-
string: string,
|
|
906
|
-
separator: separator,
|
|
907
|
-
limit: limit
|
|
908
|
-
};
|
|
909
|
-
var code = 'string.split(separator, limit)';
|
|
910
|
-
var jsList = thisInterpreter.vmCall(code, sandbox, separator, callback);
|
|
911
|
-
if (jsList !== Interpreter.REGEXP_TIMEOUT) {
|
|
912
|
-
callback(thisInterpreter.arrayNativeToPseudo(jsList));
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
else {
|
|
916
|
-
// Run split in separate thread.
|
|
917
|
-
var splitWorker = thisInterpreter.createWorker();
|
|
918
|
-
var pid = thisInterpreter.regExpTimeout(separator, splitWorker, callback);
|
|
919
|
-
splitWorker.onmessage = function (e) {
|
|
920
|
-
clearTimeout(pid);
|
|
921
|
-
callback(thisInterpreter.arrayNativeToPseudo(e.data));
|
|
922
|
-
};
|
|
923
|
-
splitWorker.postMessage(['split', string, separator, limit]);
|
|
924
|
-
}
|
|
925
|
-
return;
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
// Run split natively.
|
|
929
|
-
var jsList = string.split(separator, limit);
|
|
930
|
-
callback(thisInterpreter.arrayNativeToPseudo(jsList));
|
|
931
|
-
};
|
|
932
|
-
this.setAsyncFunctionPrototype(this.STRING, 'split', wrapper);
|
|
933
|
-
wrapper = function match(regexp, callback) {
|
|
934
|
-
var string = String(this);
|
|
935
|
-
regexp = thisInterpreter.isa(regexp, thisInterpreter.REGEXP) ? regexp.data : new RegExp(regexp);
|
|
936
|
-
// Example of catastrophic match RegExp:
|
|
937
|
-
// 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaac'.match(/^(a+)+b/)
|
|
938
|
-
thisInterpreter.maybeThrowRegExp(regexp, callback);
|
|
939
|
-
if (thisInterpreter['REGEXP_MODE'] === 2) {
|
|
940
|
-
if (Interpreter.vm) {
|
|
941
|
-
// Run match in vm.
|
|
942
|
-
var sandbox = {
|
|
943
|
-
string: string,
|
|
944
|
-
regexp: regexp
|
|
945
|
-
};
|
|
946
|
-
var code = 'string.match(regexp)';
|
|
947
|
-
var m = thisInterpreter.vmCall(code, sandbox, regexp, callback);
|
|
948
|
-
if (m !== Interpreter.REGEXP_TIMEOUT) {
|
|
949
|
-
callback(m && thisInterpreter.arrayNativeToPseudo(m));
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
else {
|
|
953
|
-
// Run match in separate thread.
|
|
954
|
-
var matchWorker = thisInterpreter.createWorker();
|
|
955
|
-
var pid = thisInterpreter.regExpTimeout(regexp, matchWorker, callback);
|
|
956
|
-
matchWorker.onmessage = function (e) {
|
|
957
|
-
clearTimeout(pid);
|
|
958
|
-
callback(e.data && thisInterpreter.arrayNativeToPseudo(e.data));
|
|
959
|
-
};
|
|
960
|
-
matchWorker.postMessage(['match', string, regexp]);
|
|
961
|
-
}
|
|
962
|
-
return;
|
|
963
|
-
}
|
|
964
|
-
// Run match natively.
|
|
965
|
-
var m = string.match(regexp);
|
|
966
|
-
callback(m && thisInterpreter.arrayNativeToPseudo(m));
|
|
967
|
-
};
|
|
968
|
-
this.setAsyncFunctionPrototype(this.STRING, 'match', wrapper);
|
|
969
|
-
wrapper = function search(regexp, callback) {
|
|
970
|
-
var string = String(this);
|
|
971
|
-
if (thisInterpreter.isa(regexp, thisInterpreter.REGEXP)) {
|
|
972
|
-
regexp = regexp.data;
|
|
973
|
-
}
|
|
974
|
-
else {
|
|
975
|
-
regexp = new RegExp(regexp);
|
|
976
|
-
}
|
|
977
|
-
// Example of catastrophic search RegExp:
|
|
978
|
-
// 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaac'.search(/^(a+)+b/)
|
|
979
|
-
thisInterpreter.maybeThrowRegExp(regexp, callback);
|
|
980
|
-
if (thisInterpreter['REGEXP_MODE'] === 2) {
|
|
981
|
-
if (Interpreter.vm) {
|
|
982
|
-
// Run search in vm.
|
|
983
|
-
var sandbox = {
|
|
984
|
-
string: string,
|
|
985
|
-
regexp: regexp
|
|
986
|
-
};
|
|
987
|
-
var code = 'string.search(regexp)';
|
|
988
|
-
var n = thisInterpreter.vmCall(code, sandbox, regexp, callback);
|
|
989
|
-
if (n !== Interpreter.REGEXP_TIMEOUT) {
|
|
990
|
-
callback(n);
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
else {
|
|
994
|
-
// Run search in separate thread.
|
|
995
|
-
var searchWorker = thisInterpreter.createWorker();
|
|
996
|
-
var pid = thisInterpreter.regExpTimeout(regexp, searchWorker, callback);
|
|
997
|
-
searchWorker.onmessage = function (e) {
|
|
998
|
-
clearTimeout(pid);
|
|
999
|
-
callback(e.data);
|
|
1000
|
-
};
|
|
1001
|
-
searchWorker.postMessage(['search', string, regexp]);
|
|
1002
|
-
}
|
|
1003
|
-
return;
|
|
1004
|
-
}
|
|
1005
|
-
// Run search natively.
|
|
1006
|
-
callback(string.search(regexp));
|
|
1007
|
-
};
|
|
1008
|
-
this.setAsyncFunctionPrototype(this.STRING, 'search', wrapper);
|
|
1009
|
-
wrapper = function replace_(substr, newSubstr, callback) {
|
|
1010
|
-
// Support for function replacements is the responsibility of a polyfill.
|
|
1011
|
-
var string = String(this);
|
|
1012
|
-
newSubstr = String(newSubstr);
|
|
1013
|
-
// Example of catastrophic replace RegExp:
|
|
1014
|
-
// 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaac'.replace(/^(a+)+b/, '')
|
|
1015
|
-
if (thisInterpreter.isa(substr, thisInterpreter.REGEXP)) {
|
|
1016
|
-
substr = substr.data;
|
|
1017
|
-
thisInterpreter.maybeThrowRegExp(substr, callback);
|
|
1018
|
-
if (thisInterpreter['REGEXP_MODE'] === 2) {
|
|
1019
|
-
if (Interpreter.vm) {
|
|
1020
|
-
// Run replace in vm.
|
|
1021
|
-
var sandbox = {
|
|
1022
|
-
string: string,
|
|
1023
|
-
substr: substr,
|
|
1024
|
-
newSubstr: newSubstr
|
|
1025
|
-
};
|
|
1026
|
-
var code = 'string.replace(substr, newSubstr)';
|
|
1027
|
-
var str = thisInterpreter.vmCall(code, sandbox, substr, callback);
|
|
1028
|
-
if (str !== Interpreter.REGEXP_TIMEOUT) {
|
|
1029
|
-
callback(str);
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
else {
|
|
1033
|
-
// Run replace in separate thread.
|
|
1034
|
-
var replaceWorker = thisInterpreter.createWorker();
|
|
1035
|
-
var pid = thisInterpreter.regExpTimeout(substr, replaceWorker, callback);
|
|
1036
|
-
replaceWorker.onmessage = function (e) {
|
|
1037
|
-
clearTimeout(pid);
|
|
1038
|
-
callback(e.data);
|
|
1039
|
-
};
|
|
1040
|
-
replaceWorker.postMessage(['replace', string, substr, newSubstr]);
|
|
1041
|
-
}
|
|
1042
|
-
return;
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
// Run replace natively.
|
|
1046
|
-
callback(string.replace(substr, newSubstr));
|
|
1047
|
-
};
|
|
1048
|
-
this.setAsyncFunctionPrototype(this.STRING, 'replace', wrapper);
|
|
1049
|
-
// Add a polyfill to handle replace's second argument being a function.
|
|
1050
|
-
this.polyfills_.push('(function() {', 'var replace_ = String.prototype.replace;', 'String.prototype.replace = function replace(substr, newSubstr) {', "if (typeof newSubstr !== 'function') {",
|
|
1051
|
-
// string.replace(string|regexp, string)
|
|
1052
|
-
'return replace_.call(this, substr, newSubstr);', '}', 'var str = this;', 'if (substr instanceof RegExp) {',
|
|
1053
|
-
// string.replace(regexp, function)
|
|
1054
|
-
'var subs = [];', 'var m = substr.exec(str);', 'while (m) {', 'm.push(m.index, str);', 'var inject = newSubstr.apply(null, m);', 'subs.push([m.index, m[0].length, inject]);', 'm = substr.global ? substr.exec(str) : null;', '}', 'for (var i = subs.length - 1; i >= 0; i--) {', 'str = str.substring(0, subs[i][0]) + subs[i][2] + ' + 'str.substring(subs[i][0] + subs[i][1]);', '}', '} else {',
|
|
1055
|
-
// string.replace(string, function)
|
|
1056
|
-
'var i = str.indexOf(substr);', 'if (i !== -1) {', 'var inject = newSubstr(str.substr(i, substr.length), i, str);', 'str = str.substring(0, i) + inject + ' + 'str.substring(i + substr.length);', '}', '}', 'return str;', '};', '})();', '');
|
|
1057
|
-
};
|
|
1058
|
-
/**
|
|
1059
|
-
* Initialize the Boolean class.
|
|
1060
|
-
* @param {!Interpreter.Object} globalObject Global object.
|
|
1061
|
-
*/
|
|
1062
|
-
Interpreter.prototype.initBoolean = function (globalObject) {
|
|
1063
|
-
var thisInterpreter = this;
|
|
1064
|
-
var wrapper;
|
|
1065
|
-
// Boolean constructor.
|
|
1066
|
-
wrapper = function Boolean(value) {
|
|
1067
|
-
value = Interpreter.nativeGlobal.Boolean(value);
|
|
1068
|
-
if (thisInterpreter.calledWithNew()) {
|
|
1069
|
-
// Called as `new Boolean()`.
|
|
1070
|
-
this.data = value;
|
|
1071
|
-
return this;
|
|
1072
|
-
}
|
|
1073
|
-
else {
|
|
1074
|
-
// Called as `Boolean()`.
|
|
1075
|
-
return value;
|
|
1076
|
-
}
|
|
1077
|
-
};
|
|
1078
|
-
this.BOOLEAN = this.createNativeFunction(wrapper, true);
|
|
1079
|
-
this.setProperty(globalObject, 'Boolean', this.BOOLEAN, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1080
|
-
};
|
|
1081
|
-
/**
|
|
1082
|
-
* Initialize the Number class.
|
|
1083
|
-
* @param {!Interpreter.Object} globalObject Global object.
|
|
1084
|
-
*/
|
|
1085
|
-
Interpreter.prototype.initNumber = function (globalObject) {
|
|
1086
|
-
var thisInterpreter = this;
|
|
1087
|
-
var wrapper;
|
|
1088
|
-
// Number constructor.
|
|
1089
|
-
wrapper = function Number(value) {
|
|
1090
|
-
value = arguments.length ? Interpreter.nativeGlobal.Number(value) : 0;
|
|
1091
|
-
if (thisInterpreter.calledWithNew()) {
|
|
1092
|
-
// Called as `new Number()`.
|
|
1093
|
-
this.data = value;
|
|
1094
|
-
return this;
|
|
1095
|
-
}
|
|
1096
|
-
else {
|
|
1097
|
-
// Called as `Number()`.
|
|
1098
|
-
return value;
|
|
1099
|
-
}
|
|
1100
|
-
};
|
|
1101
|
-
this.NUMBER = this.createNativeFunction(wrapper, true);
|
|
1102
|
-
this.setProperty(globalObject, 'Number', this.NUMBER, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1103
|
-
var numConsts = ['MAX_VALUE', 'MIN_VALUE', 'NaN', 'NEGATIVE_INFINITY', 'POSITIVE_INFINITY'];
|
|
1104
|
-
for (var i = 0; i < numConsts.length; i++) {
|
|
1105
|
-
this.setProperty(this.NUMBER, numConsts[i], Number[numConsts[i]], Interpreter.NONCONFIGURABLE_READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
1106
|
-
}
|
|
1107
|
-
// Instance methods on Number.
|
|
1108
|
-
wrapper = function toExponential(fractionDigits) {
|
|
1109
|
-
try {
|
|
1110
|
-
return Number(this).toExponential(fractionDigits);
|
|
1111
|
-
}
|
|
1112
|
-
catch (e) {
|
|
1113
|
-
// Throws if fractionDigits isn't within 0-20.
|
|
1114
|
-
thisInterpreter.throwException(thisInterpreter.ERROR, e.message);
|
|
1115
|
-
}
|
|
1116
|
-
};
|
|
1117
|
-
this.setNativeFunctionPrototype(this.NUMBER, 'toExponential', wrapper);
|
|
1118
|
-
wrapper = function toFixed(digits) {
|
|
1119
|
-
try {
|
|
1120
|
-
return Number(this).toFixed(digits);
|
|
1121
|
-
}
|
|
1122
|
-
catch (e) {
|
|
1123
|
-
// Throws if digits isn't within 0-20.
|
|
1124
|
-
thisInterpreter.throwException(thisInterpreter.ERROR, e.message);
|
|
1125
|
-
}
|
|
1126
|
-
};
|
|
1127
|
-
this.setNativeFunctionPrototype(this.NUMBER, 'toFixed', wrapper);
|
|
1128
|
-
wrapper = function toPrecision(precision) {
|
|
1129
|
-
try {
|
|
1130
|
-
return Number(this).toPrecision(precision);
|
|
1131
|
-
}
|
|
1132
|
-
catch (e) {
|
|
1133
|
-
// Throws if precision isn't within range (depends on implementation).
|
|
1134
|
-
thisInterpreter.throwException(thisInterpreter.ERROR, e.message);
|
|
1135
|
-
}
|
|
1136
|
-
};
|
|
1137
|
-
this.setNativeFunctionPrototype(this.NUMBER, 'toPrecision', wrapper);
|
|
1138
|
-
wrapper = function toString(radix) {
|
|
1139
|
-
try {
|
|
1140
|
-
return Number(this).toString(radix);
|
|
1141
|
-
}
|
|
1142
|
-
catch (e) {
|
|
1143
|
-
// Throws if radix isn't within 2-36.
|
|
1144
|
-
thisInterpreter.throwException(thisInterpreter.ERROR, e.message);
|
|
1145
|
-
}
|
|
1146
|
-
};
|
|
1147
|
-
this.setNativeFunctionPrototype(this.NUMBER, 'toString', wrapper);
|
|
1148
|
-
wrapper = function toLocaleString(locales, options) {
|
|
1149
|
-
locales = locales ? thisInterpreter.pseudoToNative(locales) : undefined;
|
|
1150
|
-
options = options ? thisInterpreter.pseudoToNative(options) : undefined;
|
|
1151
|
-
try {
|
|
1152
|
-
return Number(this).toLocaleString(/** @type {?} */ locales, /** @type {?} */ options);
|
|
1153
|
-
}
|
|
1154
|
-
catch (e) {
|
|
1155
|
-
thisInterpreter.throwException(thisInterpreter.ERROR, 'toLocaleString: ' + e.message);
|
|
1156
|
-
}
|
|
1157
|
-
};
|
|
1158
|
-
this.setNativeFunctionPrototype(this.NUMBER, 'toLocaleString', wrapper);
|
|
1159
|
-
};
|
|
1160
|
-
/**
|
|
1161
|
-
* Initialize the Date class.
|
|
1162
|
-
* @param {!Interpreter.Object} globalObject Global object.
|
|
1163
|
-
*/
|
|
1164
|
-
Interpreter.prototype.initDate = function (globalObject) {
|
|
1165
|
-
var thisInterpreter = this;
|
|
1166
|
-
var wrapper;
|
|
1167
|
-
// Date constructor.
|
|
1168
|
-
wrapper = function Date(_value, var_args) {
|
|
1169
|
-
if (!thisInterpreter.calledWithNew()) {
|
|
1170
|
-
// Called as `Date()`.
|
|
1171
|
-
// Calling Date() as a function returns a string, no arguments are heeded.
|
|
1172
|
-
return Interpreter.nativeGlobal.Date();
|
|
1173
|
-
}
|
|
1174
|
-
// Called as `new Date(...)`.
|
|
1175
|
-
var args = [null].concat(Array.from(arguments));
|
|
1176
|
-
this.data = new (Function.prototype.bind.apply(Interpreter.nativeGlobal.Date, args))();
|
|
1177
|
-
return this;
|
|
1178
|
-
};
|
|
1179
|
-
this.DATE = this.createNativeFunction(wrapper, true);
|
|
1180
|
-
this.DATE_PROTO = this.DATE.properties['prototype'];
|
|
1181
|
-
this.setProperty(globalObject, 'Date', this.DATE, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1182
|
-
// Static methods on Date.
|
|
1183
|
-
this.setProperty(this.DATE, 'now', this.createNativeFunction(Date.now, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1184
|
-
this.setProperty(this.DATE, 'parse', this.createNativeFunction(Date.parse, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1185
|
-
this.setProperty(this.DATE, 'UTC', this.createNativeFunction(Date.UTC, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1186
|
-
// Instance methods on Date.
|
|
1187
|
-
var functions = ['getDate', 'getDay', 'getFullYear', 'getHours', 'getMilliseconds', 'getMinutes', 'getMonth', 'getSeconds', 'getTime', 'getTimezoneOffset', 'getUTCDate', 'getUTCDay', 'getUTCFullYear', 'getUTCHours', 'getUTCMilliseconds', 'getUTCMinutes', 'getUTCMonth', 'getUTCSeconds', 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear', 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds', 'setYear', 'toDateString', 'toISOString', 'toJSON', 'toGMTString', 'toLocaleDateString', 'toLocaleString', 'toLocaleTimeString', 'toTimeString', 'toUTCString'];
|
|
1188
|
-
for (var i = 0; i < functions.length; i++) {
|
|
1189
|
-
wrapper = function (nativeFunc) {
|
|
1190
|
-
return function (var_args) {
|
|
1191
|
-
var date = this.data;
|
|
1192
|
-
if (!(date instanceof Date)) {
|
|
1193
|
-
thisInterpreter.throwException(thisInterpreter.TYPE_ERROR, nativeFunc + ' not called on a Date');
|
|
1194
|
-
}
|
|
1195
|
-
var args = [];
|
|
1196
|
-
for (var i = 0; i < arguments.length; i++) {
|
|
1197
|
-
args[i] = thisInterpreter.pseudoToNative(arguments[i]);
|
|
1198
|
-
}
|
|
1199
|
-
return date[nativeFunc].apply(date, args);
|
|
1200
|
-
};
|
|
1201
|
-
}(functions[i]);
|
|
1202
|
-
this.setNativeFunctionPrototype(this.DATE, functions[i], wrapper);
|
|
1203
|
-
}
|
|
1204
|
-
};
|
|
1205
|
-
/**
|
|
1206
|
-
* Initialize Regular Expression object.
|
|
1207
|
-
* @param {!Interpreter.Object} globalObject Global object.
|
|
1208
|
-
*/
|
|
1209
|
-
Interpreter.prototype.initRegExp = function (globalObject) {
|
|
1210
|
-
var thisInterpreter = this;
|
|
1211
|
-
var wrapper;
|
|
1212
|
-
// RegExp constructor.
|
|
1213
|
-
wrapper = function RegExp(pattern, flags) {
|
|
1214
|
-
if (thisInterpreter.calledWithNew()) {
|
|
1215
|
-
// Called as `new RegExp()`.
|
|
1216
|
-
var rgx = this;
|
|
1217
|
-
}
|
|
1218
|
-
else {
|
|
1219
|
-
// Called as `RegExp()`.
|
|
1220
|
-
if (flags === undefined && thisInterpreter.isa(pattern, thisInterpreter.REGEXP)) {
|
|
1221
|
-
// Regexp(/foo/) returns the same obj.
|
|
1222
|
-
return pattern;
|
|
1223
|
-
}
|
|
1224
|
-
var rgx = thisInterpreter.createObjectProto(thisInterpreter.REGEXP_PROTO);
|
|
1225
|
-
}
|
|
1226
|
-
pattern = pattern === undefined ? '' : String(pattern);
|
|
1227
|
-
flags = flags ? String(flags) : '';
|
|
1228
|
-
if (!/^[gmi]*$/.test(flags)) {
|
|
1229
|
-
// Don't allow ES6 flags 'y' and 's' to pass through.
|
|
1230
|
-
thisInterpreter.throwException(thisInterpreter.SYNTAX_ERROR, 'Invalid regexp flag: ' + flags);
|
|
1231
|
-
}
|
|
1232
|
-
try {
|
|
1233
|
-
var nativeRegExp = new Interpreter.nativeGlobal.RegExp(pattern, flags);
|
|
1234
|
-
}
|
|
1235
|
-
catch (e) {
|
|
1236
|
-
// Throws if flags are repeated.
|
|
1237
|
-
thisInterpreter.throwException(thisInterpreter.SYNTAX_ERROR, e.message);
|
|
1238
|
-
}
|
|
1239
|
-
thisInterpreter.populateRegExp(rgx, nativeRegExp);
|
|
1240
|
-
return rgx;
|
|
1241
|
-
};
|
|
1242
|
-
this.REGEXP = this.createNativeFunction(wrapper, true);
|
|
1243
|
-
this.REGEXP_PROTO = this.REGEXP.properties['prototype'];
|
|
1244
|
-
this.setProperty(globalObject, 'RegExp', this.REGEXP, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1245
|
-
this.setProperty(this.REGEXP.properties['prototype'], 'global', undefined, Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
1246
|
-
this.setProperty(this.REGEXP.properties['prototype'], 'ignoreCase', undefined, Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
1247
|
-
this.setProperty(this.REGEXP.properties['prototype'], 'multiline', undefined, Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
1248
|
-
this.setProperty(this.REGEXP.properties['prototype'], 'source', '(?:)', Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
1249
|
-
// Use polyfill to avoid complexity of regexp threads.
|
|
1250
|
-
this.polyfills_.push("Object.defineProperty(RegExp.prototype, 'test',", '{configurable: true, writable: true, value:', 'function test(str) {', 'return !!this.exec(str);', '}', '});');
|
|
1251
|
-
wrapper = function exec(string, callback) {
|
|
1252
|
-
var regexp = this.data;
|
|
1253
|
-
string = String(string);
|
|
1254
|
-
// Get lastIndex from wrapped regexp, since this is settable.
|
|
1255
|
-
regexp.lastIndex = Number(thisInterpreter.getProperty(this, 'lastIndex'));
|
|
1256
|
-
// Example of catastrophic exec RegExp:
|
|
1257
|
-
// /^(a+)+b/.exec('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaac')
|
|
1258
|
-
thisInterpreter.maybeThrowRegExp(regexp, callback);
|
|
1259
|
-
if (thisInterpreter['REGEXP_MODE'] === 2) {
|
|
1260
|
-
if (Interpreter.vm) {
|
|
1261
|
-
// Run exec in vm.
|
|
1262
|
-
var sandbox = {
|
|
1263
|
-
string: string,
|
|
1264
|
-
regexp: regexp
|
|
1265
|
-
};
|
|
1266
|
-
var code = 'regexp.exec(string)';
|
|
1267
|
-
var match = thisInterpreter.vmCall(code, sandbox, regexp, callback);
|
|
1268
|
-
if (match !== Interpreter.REGEXP_TIMEOUT) {
|
|
1269
|
-
thisInterpreter.setProperty(this, 'lastIndex', regexp.lastIndex);
|
|
1270
|
-
callback(matchToPseudo(match));
|
|
1271
|
-
}
|
|
1272
|
-
}
|
|
1273
|
-
else {
|
|
1274
|
-
// Run exec in separate thread.
|
|
1275
|
-
// Note that lastIndex is not preserved when a RegExp is passed to a
|
|
1276
|
-
// Web Worker. Thus it needs to be passed back and forth separately.
|
|
1277
|
-
var execWorker = thisInterpreter.createWorker();
|
|
1278
|
-
var pid = thisInterpreter.regExpTimeout(regexp, execWorker, callback);
|
|
1279
|
-
var thisPseudoRegExp = this;
|
|
1280
|
-
execWorker.onmessage = function (e) {
|
|
1281
|
-
clearTimeout(pid);
|
|
1282
|
-
// Return tuple: [result, lastIndex]
|
|
1283
|
-
thisInterpreter.setProperty(thisPseudoRegExp, 'lastIndex', e.data[1]);
|
|
1284
|
-
callback(matchToPseudo(e.data[0]));
|
|
1285
|
-
};
|
|
1286
|
-
execWorker.postMessage(['exec', regexp, regexp.lastIndex, string]);
|
|
1287
|
-
}
|
|
1288
|
-
return;
|
|
1289
|
-
}
|
|
1290
|
-
// Run exec natively.
|
|
1291
|
-
var match = regexp.exec(string);
|
|
1292
|
-
thisInterpreter.setProperty(this, 'lastIndex', regexp.lastIndex);
|
|
1293
|
-
callback(matchToPseudo(match));
|
|
1294
|
-
function matchToPseudo(match) {
|
|
1295
|
-
if (match) {
|
|
1296
|
-
var result = thisInterpreter.arrayNativeToPseudo(match);
|
|
1297
|
-
// match has additional properties.
|
|
1298
|
-
thisInterpreter.setProperty(result, 'index', match.index);
|
|
1299
|
-
thisInterpreter.setProperty(result, 'input', match.input);
|
|
1300
|
-
return result;
|
|
1301
|
-
}
|
|
1302
|
-
return null;
|
|
1303
|
-
}
|
|
1304
|
-
};
|
|
1305
|
-
this.setAsyncFunctionPrototype(this.REGEXP, 'exec', wrapper);
|
|
1306
|
-
};
|
|
1307
|
-
/**
|
|
1308
|
-
* Initialize the Error class.
|
|
1309
|
-
* @param {!Interpreter.Object} globalObject Global object.
|
|
1310
|
-
*/
|
|
1311
|
-
Interpreter.prototype.initError = function (globalObject) {
|
|
1312
|
-
var thisInterpreter = this;
|
|
1313
|
-
// Error constructor.
|
|
1314
|
-
this.ERROR = this.createNativeFunction(function Error(opt_message) {
|
|
1315
|
-
if (thisInterpreter.calledWithNew()) {
|
|
1316
|
-
// Called as `new Error()`.
|
|
1317
|
-
var newError = this;
|
|
1318
|
-
}
|
|
1319
|
-
else {
|
|
1320
|
-
// Called as `Error()`.
|
|
1321
|
-
var newError = thisInterpreter.createObject(thisInterpreter.ERROR);
|
|
1322
|
-
}
|
|
1323
|
-
thisInterpreter.populateError(newError, opt_message);
|
|
1324
|
-
return newError;
|
|
1325
|
-
}, true);
|
|
1326
|
-
this.setProperty(globalObject, 'Error', this.ERROR, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1327
|
-
this.setProperty(this.ERROR.properties['prototype'], 'message', '', Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1328
|
-
this.setProperty(this.ERROR.properties['prototype'], 'name', 'Error', Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1329
|
-
var createErrorSubclass = function (name) {
|
|
1330
|
-
var constructor = thisInterpreter.createNativeFunction(function (opt_message) {
|
|
1331
|
-
if (thisInterpreter.calledWithNew()) {
|
|
1332
|
-
// Called as `new XyzError()`.
|
|
1333
|
-
var newError = this;
|
|
1334
|
-
}
|
|
1335
|
-
else {
|
|
1336
|
-
// Called as `XyzError()`.
|
|
1337
|
-
var newError = thisInterpreter.createObject(constructor);
|
|
1338
|
-
}
|
|
1339
|
-
thisInterpreter.populateError(newError, opt_message);
|
|
1340
|
-
return newError;
|
|
1341
|
-
}, true);
|
|
1342
|
-
thisInterpreter.setProperty(constructor, 'prototype', thisInterpreter.createObject(thisInterpreter.ERROR), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1343
|
-
thisInterpreter.setProperty(constructor.properties['prototype'], 'name', name, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1344
|
-
thisInterpreter.setProperty(globalObject, name, constructor, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1345
|
-
return constructor;
|
|
1346
|
-
};
|
|
1347
|
-
this.EVAL_ERROR = createErrorSubclass('EvalError');
|
|
1348
|
-
this.RANGE_ERROR = createErrorSubclass('RangeError');
|
|
1349
|
-
this.REFERENCE_ERROR = createErrorSubclass('ReferenceError');
|
|
1350
|
-
this.SYNTAX_ERROR = createErrorSubclass('SyntaxError');
|
|
1351
|
-
this.TYPE_ERROR = createErrorSubclass('TypeError');
|
|
1352
|
-
this.URI_ERROR = createErrorSubclass('URIError');
|
|
1353
|
-
};
|
|
1354
|
-
/**
|
|
1355
|
-
* Initialize Math object.
|
|
1356
|
-
* @param {!Interpreter.Object} globalObject Global object.
|
|
1357
|
-
*/
|
|
1358
|
-
Interpreter.prototype.initMath = function (globalObject) {
|
|
1359
|
-
var myMath = this.createObjectProto(this.OBJECT_PROTO);
|
|
1360
|
-
this.setProperty(globalObject, 'Math', myMath, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1361
|
-
var mathConsts = ['E', 'LN2', 'LN10', 'LOG2E', 'LOG10E', 'PI', 'SQRT1_2', 'SQRT2'];
|
|
1362
|
-
for (var i = 0; i < mathConsts.length; i++) {
|
|
1363
|
-
this.setProperty(myMath, mathConsts[i], Math[mathConsts[i]], Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
1364
|
-
}
|
|
1365
|
-
var numFunctions = ['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor', 'log', 'max', 'min', 'pow', 'random', 'round', 'sin', 'sqrt', 'tan'];
|
|
1366
|
-
for (var i = 0; i < numFunctions.length; i++) {
|
|
1367
|
-
this.setProperty(myMath, numFunctions[i], this.createNativeFunction(Math[numFunctions[i]], false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1368
|
-
}
|
|
1369
|
-
};
|
|
1370
|
-
/**
|
|
1371
|
-
* Initialize JSON object.
|
|
1372
|
-
* @param {!Interpreter.Object} globalObject Global object.
|
|
1373
|
-
*/
|
|
1374
|
-
Interpreter.prototype.initJSON = function (globalObject) {
|
|
1375
|
-
var wrapper;
|
|
1376
|
-
var thisInterpreter = this;
|
|
1377
|
-
var myJSON = thisInterpreter.createObjectProto(this.OBJECT_PROTO);
|
|
1378
|
-
this.setProperty(globalObject, 'JSON', myJSON, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1379
|
-
wrapper = function parse(text) {
|
|
1380
|
-
try {
|
|
1381
|
-
var nativeObj = JSON.parse(String(text));
|
|
1382
|
-
}
|
|
1383
|
-
catch (e) {
|
|
1384
|
-
thisInterpreter.throwException(thisInterpreter.SYNTAX_ERROR, e.message);
|
|
1385
|
-
}
|
|
1386
|
-
return thisInterpreter.nativeToPseudo(nativeObj);
|
|
1387
|
-
};
|
|
1388
|
-
this.setProperty(myJSON, 'parse', this.createNativeFunction(wrapper, false));
|
|
1389
|
-
wrapper = function stringify(value, replacer, space) {
|
|
1390
|
-
if (replacer && replacer.class === 'Function') {
|
|
1391
|
-
thisInterpreter.throwException(thisInterpreter.TYPE_ERROR, 'Function replacer on JSON.stringify not supported');
|
|
1392
|
-
}
|
|
1393
|
-
else if (replacer && replacer.class === 'Array') {
|
|
1394
|
-
replacer = thisInterpreter.arrayPseudoToNative(replacer);
|
|
1395
|
-
replacer = replacer.filter(function (word) {
|
|
1396
|
-
// Spec says we should also support boxed primitives here.
|
|
1397
|
-
return typeof word === 'string' || typeof word === 'number';
|
|
1398
|
-
});
|
|
1399
|
-
}
|
|
1400
|
-
else {
|
|
1401
|
-
replacer = null;
|
|
1402
|
-
}
|
|
1403
|
-
// Spec says we should also support boxed primitives here.
|
|
1404
|
-
if (typeof space !== 'string' && typeof space !== 'number') {
|
|
1405
|
-
space = undefined;
|
|
1406
|
-
}
|
|
1407
|
-
var nativeObj = thisInterpreter.pseudoToNative(value);
|
|
1408
|
-
try {
|
|
1409
|
-
var str = JSON.stringify(nativeObj, replacer, space);
|
|
1410
|
-
}
|
|
1411
|
-
catch (e) {
|
|
1412
|
-
thisInterpreter.throwException(thisInterpreter.TYPE_ERROR, e.message);
|
|
1413
|
-
}
|
|
1414
|
-
return str;
|
|
1415
|
-
};
|
|
1416
|
-
this.setProperty(myJSON, 'stringify', this.createNativeFunction(wrapper, false));
|
|
1417
|
-
};
|
|
1418
|
-
/**
|
|
1419
|
-
* Is an object of a certain class?
|
|
1420
|
-
* @param {Interpreter.Value} child Object to check.
|
|
1421
|
-
* @param {Interpreter.Object} constructor Constructor of object.
|
|
1422
|
-
* @returns {boolean} True if object is the class or inherits from it.
|
|
1423
|
-
* False otherwise.
|
|
1424
|
-
*/
|
|
1425
|
-
Interpreter.prototype.isa = function (child, constructor) {
|
|
1426
|
-
if (child === null || child === undefined || !constructor) {
|
|
1427
|
-
return false;
|
|
1428
|
-
}
|
|
1429
|
-
var proto = constructor.properties['prototype'];
|
|
1430
|
-
if (child === proto) {
|
|
1431
|
-
return true;
|
|
1432
|
-
}
|
|
1433
|
-
// The first step up the prototype chain is harder since the child might be
|
|
1434
|
-
// a primitive value. Subsequent steps can just follow the .proto property.
|
|
1435
|
-
child = this.getPrototype(child);
|
|
1436
|
-
while (child) {
|
|
1437
|
-
if (child === proto) {
|
|
1438
|
-
return true;
|
|
1439
|
-
}
|
|
1440
|
-
child = child.proto;
|
|
1441
|
-
}
|
|
1442
|
-
return false;
|
|
1443
|
-
};
|
|
1444
|
-
/**
|
|
1445
|
-
* Initialize a pseudo regular expression object based on a native regular
|
|
1446
|
-
* expression object.
|
|
1447
|
-
* @param {!Interpreter.Object} pseudoRegexp The existing object to set.
|
|
1448
|
-
* @param {!RegExp} nativeRegexp The native regular expression.
|
|
1449
|
-
*/
|
|
1450
|
-
Interpreter.prototype.populateRegExp = function (pseudoRegexp, nativeRegexp) {
|
|
1451
|
-
pseudoRegexp.data = new RegExp(nativeRegexp.source, nativeRegexp.flags);
|
|
1452
|
-
// lastIndex is settable, all others are read-only attributes
|
|
1453
|
-
this.setProperty(pseudoRegexp, 'lastIndex', nativeRegexp.lastIndex, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1454
|
-
this.setProperty(pseudoRegexp, 'source', nativeRegexp.source, Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
1455
|
-
this.setProperty(pseudoRegexp, 'global', nativeRegexp.global, Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
1456
|
-
this.setProperty(pseudoRegexp, 'ignoreCase', nativeRegexp.ignoreCase, Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
1457
|
-
this.setProperty(pseudoRegexp, 'multiline', nativeRegexp.multiline, Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
1458
|
-
};
|
|
1459
|
-
/**
|
|
1460
|
-
* Initialize a pseudo error object.
|
|
1461
|
-
* @param {!Interpreter.Object} pseudoError The existing object to set.
|
|
1462
|
-
* @param {string=} opt_message Error's message.
|
|
1463
|
-
*/
|
|
1464
|
-
Interpreter.prototype.populateError = function (pseudoError, opt_message) {
|
|
1465
|
-
if (opt_message) {
|
|
1466
|
-
this.setProperty(pseudoError, 'message', String(opt_message), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1467
|
-
}
|
|
1468
|
-
var tracebackData = [];
|
|
1469
|
-
for (var i = this.stateStack.length - 1; i >= 0; i--) {
|
|
1470
|
-
var state = this.stateStack[i];
|
|
1471
|
-
var node = state.node;
|
|
1472
|
-
if (node.type === 'CallExpression') {
|
|
1473
|
-
var func = state.func_;
|
|
1474
|
-
if (func && tracebackData.length) {
|
|
1475
|
-
tracebackData[tracebackData.length - 1].datumName = this.getProperty(func, 'name');
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
if (node.loc && (!tracebackData.length || node.type === 'CallExpression')) {
|
|
1479
|
-
tracebackData.push({
|
|
1480
|
-
datumLoc: node.loc
|
|
1481
|
-
});
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
|
-
var errorName = String(this.getProperty(pseudoError, 'name'));
|
|
1485
|
-
var errorMessage = String(this.getProperty(pseudoError, 'message'));
|
|
1486
|
-
var stackString = errorName + ': ' + errorMessage + '\n';
|
|
1487
|
-
for (var i = 0; i < tracebackData.length; i++) {
|
|
1488
|
-
var loc = tracebackData[i].datumLoc;
|
|
1489
|
-
var name = tracebackData[i].datumName;
|
|
1490
|
-
var locString = loc.source + ':' + loc.start.line + ':' + loc.start.column;
|
|
1491
|
-
if (name) {
|
|
1492
|
-
stackString += ' at ' + name + ' (' + locString + ')\n';
|
|
1493
|
-
}
|
|
1494
|
-
else {
|
|
1495
|
-
stackString += ' at ' + locString + '\n';
|
|
1496
|
-
}
|
|
1497
|
-
}
|
|
1498
|
-
this.setProperty(pseudoError, 'stack', stackString.trim(), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1499
|
-
};
|
|
1500
|
-
/**
|
|
1501
|
-
* Create a Web Worker to execute regular expressions.
|
|
1502
|
-
* Using a separate file fails in Chrome when run locally on a file:// URI.
|
|
1503
|
-
* Using a data encoded URI fails in IE and Edge.
|
|
1504
|
-
* Using a blob works in IE11 and all other browsers.
|
|
1505
|
-
* @returns {!Worker} Web Worker with regexp execution code loaded.
|
|
1506
|
-
*/
|
|
1507
|
-
Interpreter.prototype.createWorker = function () {
|
|
1508
|
-
var blob = this.createWorker.blob_;
|
|
1509
|
-
if (!blob) {
|
|
1510
|
-
blob = new Blob([Interpreter.WORKER_CODE.join('\n')], {
|
|
1511
|
-
type: 'application/javascript'
|
|
1512
|
-
});
|
|
1513
|
-
// Cache the blob, so it doesn't need to be created next time.
|
|
1514
|
-
this.createWorker.blob_ = blob;
|
|
1515
|
-
}
|
|
1516
|
-
return new Worker(URL.createObjectURL(blob));
|
|
1517
|
-
};
|
|
1518
|
-
/**
|
|
1519
|
-
* Execute regular expressions in a node vm.
|
|
1520
|
-
* @param {string} code Code to execute.
|
|
1521
|
-
* @param {!Object} sandbox Global variables for new vm.
|
|
1522
|
-
* @param {!RegExp} nativeRegExp Regular expression.
|
|
1523
|
-
* @param {!Function} callback Asynchronous callback function.
|
|
1524
|
-
*/
|
|
1525
|
-
Interpreter.prototype.vmCall = function (code, sandbox, nativeRegExp, callback) {
|
|
1526
|
-
var options = {
|
|
1527
|
-
timeout: this['REGEXP_THREAD_TIMEOUT']
|
|
1528
|
-
};
|
|
1529
|
-
try {
|
|
1530
|
-
return Interpreter.vm['runInNewContext'](code, sandbox, options);
|
|
1531
|
-
}
|
|
1532
|
-
catch (_e) {
|
|
1533
|
-
callback(null);
|
|
1534
|
-
this.throwException(this.ERROR, 'RegExp Timeout: ' + nativeRegExp);
|
|
1535
|
-
}
|
|
1536
|
-
return Interpreter.REGEXP_TIMEOUT;
|
|
1537
|
-
};
|
|
1538
|
-
/**
|
|
1539
|
-
* If REGEXP_MODE is 0, then throw an error.
|
|
1540
|
-
* Also throw if REGEXP_MODE is 2 and JS doesn't support Web Workers or vm.
|
|
1541
|
-
* @param {!RegExp} nativeRegExp Regular expression.
|
|
1542
|
-
* @param {!Function} callback Asynchronous callback function.
|
|
1543
|
-
*/
|
|
1544
|
-
Interpreter.prototype.maybeThrowRegExp = function (nativeRegExp, callback) {
|
|
1545
|
-
var ok;
|
|
1546
|
-
if (this['REGEXP_MODE'] === 0) {
|
|
1547
|
-
// Fail: No RegExp support.
|
|
1548
|
-
ok = false;
|
|
1549
|
-
}
|
|
1550
|
-
else if (this['REGEXP_MODE'] === 1) {
|
|
1551
|
-
// Ok: Native RegExp support.
|
|
1552
|
-
ok = true;
|
|
1553
|
-
}
|
|
1554
|
-
else {
|
|
1555
|
-
// Sandboxed RegExp handling.
|
|
1556
|
-
if (Interpreter.vm) {
|
|
1557
|
-
// Ok: Node's vm module already loaded.
|
|
1558
|
-
ok = true;
|
|
1559
|
-
}
|
|
1560
|
-
else if (typeof Worker === 'function' && typeof URL === 'function') {
|
|
1561
|
-
// Ok: Web Workers available.
|
|
1562
|
-
ok = true;
|
|
1563
|
-
}
|
|
1564
|
-
else if (typeof require === 'function') {
|
|
1565
|
-
// Try to load Node's vm module.
|
|
1566
|
-
try {
|
|
1567
|
-
Interpreter.vm = require('vm');
|
|
1568
|
-
}
|
|
1569
|
-
catch (_e) { }
|
|
1570
|
-
ok = !!Interpreter.vm;
|
|
1571
|
-
}
|
|
1572
|
-
else {
|
|
1573
|
-
// Fail: Neither Web Workers nor vm available.
|
|
1574
|
-
ok = false;
|
|
1575
|
-
}
|
|
1576
|
-
}
|
|
1577
|
-
if (!ok) {
|
|
1578
|
-
callback(null);
|
|
1579
|
-
this.throwException(this.ERROR, 'Regular expressions not supported: ' + nativeRegExp);
|
|
1580
|
-
}
|
|
1581
|
-
};
|
|
1582
|
-
/**
|
|
1583
|
-
* Set a timeout for regular expression threads. Unless cancelled, this will
|
|
1584
|
-
* terminate the thread and throw an error.
|
|
1585
|
-
* @param {!RegExp} nativeRegExp Regular expression (used for error message).
|
|
1586
|
-
* @param {!Worker} worker Thread to terminate.
|
|
1587
|
-
* @param {!Function} callback Async callback function to continue execution.
|
|
1588
|
-
* @returns {number} PID of timeout. Used to cancel if thread completes.
|
|
1589
|
-
*/
|
|
1590
|
-
Interpreter.prototype.regExpTimeout = function (nativeRegExp, worker, callback) {
|
|
1591
|
-
var thisInterpreter = this;
|
|
1592
|
-
return setTimeout(function () {
|
|
1593
|
-
worker.terminate();
|
|
1594
|
-
callback(null);
|
|
1595
|
-
try {
|
|
1596
|
-
thisInterpreter.throwException(thisInterpreter.ERROR, 'RegExp Timeout: ' + nativeRegExp);
|
|
1597
|
-
}
|
|
1598
|
-
catch (_e) {
|
|
1599
|
-
// Eat the expected Interpreter.STEP_ERROR.
|
|
1600
|
-
}
|
|
1601
|
-
}, this['REGEXP_THREAD_TIMEOUT']);
|
|
1602
|
-
};
|
|
1603
|
-
/**
|
|
1604
|
-
* Create a new data object based on a constructor's prototype.
|
|
1605
|
-
* @param {Interpreter.Object} constructor Parent constructor function,
|
|
1606
|
-
* or null if scope object.
|
|
1607
|
-
* @returns {!Interpreter.Object} New data object.
|
|
1608
|
-
*/
|
|
1609
|
-
Interpreter.prototype.createObject = function (constructor) {
|
|
1610
|
-
return this.createObjectProto(constructor && constructor.properties['prototype']);
|
|
1611
|
-
};
|
|
1612
|
-
/**
|
|
1613
|
-
* Create a new data object based on a prototype.
|
|
1614
|
-
* @param {Interpreter.Object} proto Prototype object.
|
|
1615
|
-
* @returns {!Interpreter.Object} New data object.
|
|
1616
|
-
*/
|
|
1617
|
-
Interpreter.prototype.createObjectProto = function (proto) {
|
|
1618
|
-
if (typeof proto !== 'object') {
|
|
1619
|
-
throw Error('Non object prototype');
|
|
1620
|
-
}
|
|
1621
|
-
var obj = new Interpreter.Object(proto);
|
|
1622
|
-
if (this.isa(obj, this.ERROR)) {
|
|
1623
|
-
// Record this object as being an error so that its toString function can
|
|
1624
|
-
// process it correctly (toString has no access to the interpreter and could
|
|
1625
|
-
// not otherwise determine that the object is an error).
|
|
1626
|
-
obj.class = 'Error';
|
|
1627
|
-
}
|
|
1628
|
-
return obj;
|
|
1629
|
-
};
|
|
1630
|
-
/**
|
|
1631
|
-
* Create a new array.
|
|
1632
|
-
* @returns {!Interpreter.Object} New array.
|
|
1633
|
-
*/
|
|
1634
|
-
Interpreter.prototype.createArray = function () {
|
|
1635
|
-
var array = this.createObjectProto(this.ARRAY_PROTO);
|
|
1636
|
-
// Arrays have length.
|
|
1637
|
-
this.setProperty(array, 'length', 0, {
|
|
1638
|
-
configurable: false,
|
|
1639
|
-
enumerable: false,
|
|
1640
|
-
writable: true
|
|
1641
|
-
});
|
|
1642
|
-
array.class = 'Array';
|
|
1643
|
-
return array;
|
|
1644
|
-
};
|
|
1645
|
-
/**
|
|
1646
|
-
* Create a new function object (could become interpreted or native or async).
|
|
1647
|
-
* @param {number} argumentLength Number of arguments.
|
|
1648
|
-
* @param {boolean} isConstructor True if function can be used with 'new'.
|
|
1649
|
-
* @returns {!Interpreter.Object} New function.
|
|
1650
|
-
* @private
|
|
1651
|
-
*/
|
|
1652
|
-
Interpreter.prototype.createFunctionBase_ = function (argumentLength, isConstructor) {
|
|
1653
|
-
var func = this.createObjectProto(this.FUNCTION_PROTO);
|
|
1654
|
-
if (isConstructor) {
|
|
1655
|
-
var proto = this.createObjectProto(this.OBJECT_PROTO);
|
|
1656
|
-
this.setProperty(func, 'prototype', proto, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1657
|
-
this.setProperty(proto, 'constructor', func, Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
1658
|
-
}
|
|
1659
|
-
else {
|
|
1660
|
-
func.illegalConstructor = true;
|
|
1661
|
-
}
|
|
1662
|
-
this.setProperty(func, 'length', argumentLength, Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
1663
|
-
func.class = 'Function';
|
|
1664
|
-
// When making changes to this function, check to see if those changes also
|
|
1665
|
-
// need to be made to the creation of FUNCTION_PROTO in initFunction.
|
|
1666
|
-
return func;
|
|
1667
|
-
};
|
|
1668
|
-
/**
|
|
1669
|
-
* Create a new interpreted function.
|
|
1670
|
-
* @param {!Object} node AST node defining the function.
|
|
1671
|
-
* @param {!Interpreter.Scope} scope Parent scope.
|
|
1672
|
-
* @param {string=} opt_name Optional name for function.
|
|
1673
|
-
* @returns {!Interpreter.Object} New function.
|
|
1674
|
-
*/
|
|
1675
|
-
Interpreter.prototype.createFunction = function (node, scope, opt_name) {
|
|
1676
|
-
var func = this.createFunctionBase_(node.params.length, true);
|
|
1677
|
-
func.parentScope = scope;
|
|
1678
|
-
func.node = node;
|
|
1679
|
-
// Choose a name for this function.
|
|
1680
|
-
// function foo() {} -> 'foo'
|
|
1681
|
-
// var bar = function() {}; -> 'bar'
|
|
1682
|
-
// var bar = function foo() {}; -> 'foo'
|
|
1683
|
-
// foo.bar = function() {}; -> ''
|
|
1684
|
-
// var bar = new Function(''); -> 'anonymous'
|
|
1685
|
-
var name = node.id ? String(node.id.name) : opt_name || '';
|
|
1686
|
-
this.setProperty(func, 'name', name, Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
1687
|
-
return func;
|
|
1688
|
-
};
|
|
1689
|
-
/**
|
|
1690
|
-
* Create a new native function.
|
|
1691
|
-
* @param {!Function} nativeFunc JavaScript function.
|
|
1692
|
-
* @param {boolean} isConstructor True if function can be used with 'new'.
|
|
1693
|
-
* @returns {!Interpreter.Object} New function.
|
|
1694
|
-
*/
|
|
1695
|
-
Interpreter.prototype.createNativeFunction = function (nativeFunc, isConstructor) {
|
|
1696
|
-
var func = this.createFunctionBase_(nativeFunc.length, isConstructor);
|
|
1697
|
-
func.nativeFunc = nativeFunc;
|
|
1698
|
-
nativeFunc.id = this.functionCounter_++;
|
|
1699
|
-
this.setProperty(func, 'name', nativeFunc.name, Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
1700
|
-
return func;
|
|
1701
|
-
};
|
|
1702
|
-
/**
|
|
1703
|
-
* Create a new native asynchronous function.
|
|
1704
|
-
* @param {!Function} asyncFunc JavaScript function.
|
|
1705
|
-
* @returns {!Interpreter.Object} New function.
|
|
1706
|
-
*/
|
|
1707
|
-
Interpreter.prototype.createAsyncFunction = function (asyncFunc) {
|
|
1708
|
-
var func = this.createFunctionBase_(asyncFunc.length, true);
|
|
1709
|
-
func.asyncFunc = asyncFunc;
|
|
1710
|
-
asyncFunc.id = this.functionCounter_++;
|
|
1711
|
-
this.setProperty(func, 'name', asyncFunc.name, Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
|
|
1712
|
-
return func;
|
|
1713
|
-
};
|
|
1714
|
-
/**
|
|
1715
|
-
* Converts from a native JavaScript object or value to a JS-Interpreter object.
|
|
1716
|
-
* Can handle JSON-style values, regular expressions, dates and functions.
|
|
1717
|
-
* Does NOT handle cycles.
|
|
1718
|
-
* @param {*} nativeObj The native JavaScript object to be converted.
|
|
1719
|
-
* @returns {Interpreter.Value} The equivalent JS-Interpreter object.
|
|
1720
|
-
*/
|
|
1721
|
-
Interpreter.prototype.nativeToPseudo = function (nativeObj) {
|
|
1722
|
-
if (nativeObj instanceof Interpreter.Object) {
|
|
1723
|
-
throw Error('Object is already pseudo');
|
|
1724
|
-
}
|
|
1725
|
-
if (nativeObj === null || nativeObj === undefined || nativeObj === true || nativeObj === false || typeof nativeObj === 'string' || typeof nativeObj === 'number') {
|
|
1726
|
-
return nativeObj;
|
|
1727
|
-
}
|
|
1728
|
-
if (nativeObj instanceof RegExp) {
|
|
1729
|
-
var pseudoRegexp = this.createObjectProto(this.REGEXP_PROTO);
|
|
1730
|
-
this.populateRegExp(pseudoRegexp, nativeObj);
|
|
1731
|
-
return pseudoRegexp;
|
|
1732
|
-
}
|
|
1733
|
-
if (nativeObj instanceof Date) {
|
|
1734
|
-
var pseudoDate = this.createObjectProto(this.DATE_PROTO);
|
|
1735
|
-
pseudoDate.data = new Date(nativeObj.valueOf());
|
|
1736
|
-
return pseudoDate;
|
|
1737
|
-
}
|
|
1738
|
-
if (typeof nativeObj === 'function') {
|
|
1739
|
-
var thisInterpreter = this;
|
|
1740
|
-
var wrapper = function () {
|
|
1741
|
-
var args = Array.prototype.slice.call(arguments).map(function (i) {
|
|
1742
|
-
return thisInterpreter.pseudoToNative(i);
|
|
1743
|
-
});
|
|
1744
|
-
var value = nativeObj.apply(thisInterpreter, args);
|
|
1745
|
-
return thisInterpreter.nativeToPseudo(value);
|
|
1746
|
-
};
|
|
1747
|
-
var prototype = Object.getOwnPropertyDescriptor(nativeObj, 'prototype');
|
|
1748
|
-
return this.createNativeFunction(wrapper, !!prototype);
|
|
1749
|
-
}
|
|
1750
|
-
if (Array.isArray(nativeObj)) {
|
|
1751
|
-
// Array.
|
|
1752
|
-
var pseudoArray = this.createArray();
|
|
1753
|
-
for (var i = 0; i < nativeObj.length; i++) {
|
|
1754
|
-
if (i in nativeObj) {
|
|
1755
|
-
this.setProperty(pseudoArray, i, this.nativeToPseudo(nativeObj[i]));
|
|
1756
|
-
}
|
|
1757
|
-
}
|
|
1758
|
-
return pseudoArray;
|
|
1759
|
-
}
|
|
1760
|
-
// Object.
|
|
1761
|
-
var pseudoObj = this.createObjectProto(this.OBJECT_PROTO);
|
|
1762
|
-
for (var key in nativeObj) {
|
|
1763
|
-
this.setProperty(pseudoObj, key, this.nativeToPseudo(nativeObj[key]));
|
|
1764
|
-
}
|
|
1765
|
-
return pseudoObj;
|
|
1766
|
-
};
|
|
1767
|
-
/**
|
|
1768
|
-
* Converts from a JS-Interpreter object to native JavaScript object.
|
|
1769
|
-
* Can handle JSON-style values, regular expressions, and dates.
|
|
1770
|
-
* Does handle cycles.
|
|
1771
|
-
* @param {Interpreter.Value} pseudoObj The JS-Interpreter object to be
|
|
1772
|
-
* converted.
|
|
1773
|
-
* @param {Object=} opt_cycles Cycle detection object (used by recursive calls).
|
|
1774
|
-
* @returns {*} The equivalent native JavaScript object or value.
|
|
1775
|
-
*/
|
|
1776
|
-
Interpreter.prototype.pseudoToNative = function (pseudoObj, opt_cycles) {
|
|
1777
|
-
if (typeof pseudoObj !== 'object' && typeof pseudoObj !== 'function' || pseudoObj === null) {
|
|
1778
|
-
return pseudoObj;
|
|
1779
|
-
}
|
|
1780
|
-
if (!(pseudoObj instanceof Interpreter.Object)) {
|
|
1781
|
-
throw Error('Object is not pseudo');
|
|
1782
|
-
}
|
|
1783
|
-
if (this.isa(pseudoObj, this.REGEXP)) {
|
|
1784
|
-
// Regular expression.
|
|
1785
|
-
var nativeRegExp = new RegExp(pseudoObj.data.source, pseudoObj.data.flags);
|
|
1786
|
-
nativeRegExp.lastIndex = pseudoObj.data.lastIndex;
|
|
1787
|
-
return nativeRegExp;
|
|
1788
|
-
}
|
|
1789
|
-
if (this.isa(pseudoObj, this.DATE)) {
|
|
1790
|
-
// Date.
|
|
1791
|
-
return new Date(pseudoObj.data.valueOf());
|
|
1792
|
-
}
|
|
1793
|
-
var cycles = opt_cycles || {
|
|
1794
|
-
pseudo: [],
|
|
1795
|
-
native: []
|
|
1796
|
-
};
|
|
1797
|
-
var index = cycles.pseudo.indexOf(pseudoObj);
|
|
1798
|
-
if (index !== -1) {
|
|
1799
|
-
return cycles.native[index];
|
|
1800
|
-
}
|
|
1801
|
-
cycles.pseudo.push(pseudoObj);
|
|
1802
|
-
var nativeObj;
|
|
1803
|
-
if (this.isa(pseudoObj, this.ARRAY)) {
|
|
1804
|
-
// Array.
|
|
1805
|
-
nativeObj = [];
|
|
1806
|
-
cycles.native.push(nativeObj);
|
|
1807
|
-
var len = this.getProperty(pseudoObj, 'length');
|
|
1808
|
-
for (var i = 0; i < len; i++) {
|
|
1809
|
-
if (this.hasProperty(pseudoObj, i)) {
|
|
1810
|
-
nativeObj[i] = this.pseudoToNative(this.getProperty(pseudoObj, i), cycles);
|
|
1811
|
-
}
|
|
1812
|
-
}
|
|
1813
|
-
}
|
|
1814
|
-
else {
|
|
1815
|
-
// Object.
|
|
1816
|
-
nativeObj = {};
|
|
1817
|
-
cycles.native.push(nativeObj);
|
|
1818
|
-
var val;
|
|
1819
|
-
for (var key in pseudoObj.properties) {
|
|
1820
|
-
val = this.pseudoToNative(pseudoObj.properties[key], cycles);
|
|
1821
|
-
// Use defineProperty to avoid side effects if setting '__proto__'.
|
|
1822
|
-
Object.defineProperty(nativeObj, key, {
|
|
1823
|
-
value: val,
|
|
1824
|
-
writable: true,
|
|
1825
|
-
enumerable: true,
|
|
1826
|
-
configurable: true
|
|
1827
|
-
});
|
|
1828
|
-
}
|
|
1829
|
-
}
|
|
1830
|
-
cycles.pseudo.pop();
|
|
1831
|
-
cycles.native.pop();
|
|
1832
|
-
return nativeObj;
|
|
1833
|
-
};
|
|
1834
|
-
/**
|
|
1835
|
-
* Converts from a native JavaScript array to a JS-Interpreter array.
|
|
1836
|
-
* Does handle non-numeric properties (like str.match's index prop).
|
|
1837
|
-
* Does NOT recurse into the array's contents.
|
|
1838
|
-
* @param {!Array} nativeArray The JavaScript array to be converted.
|
|
1839
|
-
* @returns {!Interpreter.Object} The equivalent JS-Interpreter array.
|
|
1840
|
-
*/
|
|
1841
|
-
Interpreter.prototype.arrayNativeToPseudo = function (nativeArray) {
|
|
1842
|
-
var pseudoArray = this.createArray();
|
|
1843
|
-
var props = /** @type {!Array<?>} */ Object.getOwnPropertyNames(nativeArray);
|
|
1844
|
-
for (var i = 0; i < props.length; i++) {
|
|
1845
|
-
this.setProperty(pseudoArray, props[i], nativeArray[props[i]]);
|
|
1846
|
-
}
|
|
1847
|
-
return pseudoArray;
|
|
1848
|
-
};
|
|
1849
|
-
/**
|
|
1850
|
-
* Converts from a JS-Interpreter array to native JavaScript array.
|
|
1851
|
-
* Does handle non-numeric properties (like str.match's index prop).
|
|
1852
|
-
* Does NOT recurse into the array's contents.
|
|
1853
|
-
* @param {!Interpreter.Object} pseudoArray The JS-Interpreter array,
|
|
1854
|
-
* or JS-Interpreter object pretending to be an array.
|
|
1855
|
-
* @returns {!Array} The equivalent native JavaScript array.
|
|
1856
|
-
*/
|
|
1857
|
-
Interpreter.prototype.arrayPseudoToNative = function (pseudoArray) {
|
|
1858
|
-
var nativeArray = [];
|
|
1859
|
-
for (var key in pseudoArray.properties) {
|
|
1860
|
-
nativeArray[ /** @type {?} */key] = this.getProperty(pseudoArray, key);
|
|
1861
|
-
}
|
|
1862
|
-
// pseudoArray might be an object pretending to be an array. In this case
|
|
1863
|
-
// it's possible that length is non-existent, invalid, or smaller than the
|
|
1864
|
-
// largest defined numeric property. Set length explicitly here.
|
|
1865
|
-
nativeArray.length = Interpreter.legalArrayLength(this.getProperty(pseudoArray, 'length')) || 0;
|
|
1866
|
-
return nativeArray;
|
|
1867
|
-
};
|
|
1868
|
-
/**
|
|
1869
|
-
* Look up the prototype for this value.
|
|
1870
|
-
* @param {Interpreter.Value} value Data object.
|
|
1871
|
-
* @returns {Interpreter.Object} Prototype object, null if none.
|
|
1872
|
-
*/
|
|
1873
|
-
Interpreter.prototype.getPrototype = function (value) {
|
|
1874
|
-
switch (typeof value) {
|
|
1875
|
-
case 'number':
|
|
1876
|
-
return this.NUMBER.properties['prototype'];
|
|
1877
|
-
case 'boolean':
|
|
1878
|
-
return this.BOOLEAN.properties['prototype'];
|
|
1879
|
-
case 'string':
|
|
1880
|
-
return this.STRING.properties['prototype'];
|
|
1881
|
-
}
|
|
1882
|
-
if (value) {
|
|
1883
|
-
return value.proto;
|
|
1884
|
-
}
|
|
1885
|
-
return null;
|
|
1886
|
-
};
|
|
1887
|
-
/**
|
|
1888
|
-
* Fetch a property value from a data object.
|
|
1889
|
-
* @param {Interpreter.Value} obj Data object.
|
|
1890
|
-
* @param {Interpreter.Value} name Name of property.
|
|
1891
|
-
* @returns {Interpreter.Value} Property value (may be undefined).
|
|
1892
|
-
*/
|
|
1893
|
-
Interpreter.prototype.getProperty = function (obj, name) {
|
|
1894
|
-
if (this.getterStep_) {
|
|
1895
|
-
throw Error('Getter not supported in that context');
|
|
1896
|
-
}
|
|
1897
|
-
name = String(name);
|
|
1898
|
-
if (obj === undefined || obj === null) {
|
|
1899
|
-
this.throwException(this.TYPE_ERROR, "Cannot read property '" + name + "' of " + obj);
|
|
1900
|
-
}
|
|
1901
|
-
if (typeof obj === 'object' && !(obj instanceof Interpreter.Object)) {
|
|
1902
|
-
throw TypeError('Expecting native value or pseudo object');
|
|
1903
|
-
}
|
|
1904
|
-
if (name === 'length') {
|
|
1905
|
-
// Special cases for magic length property.
|
|
1906
|
-
if (this.isa(obj, this.STRING)) {
|
|
1907
|
-
return String(obj).length;
|
|
1908
|
-
}
|
|
1909
|
-
}
|
|
1910
|
-
else if (name.charCodeAt(0) < 0x40) {
|
|
1911
|
-
// Might have numbers in there?
|
|
1912
|
-
// Special cases for string array indexing
|
|
1913
|
-
if (this.isa(obj, this.STRING)) {
|
|
1914
|
-
var n = Interpreter.legalArrayIndex(name);
|
|
1915
|
-
if (!isNaN(n) && n < String(obj).length) {
|
|
1916
|
-
return String(obj)[n];
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
}
|
|
1920
|
-
do {
|
|
1921
|
-
if (obj.properties && name in obj.properties) {
|
|
1922
|
-
var getter = obj.getter[name];
|
|
1923
|
-
if (getter) {
|
|
1924
|
-
// Flag this function as being a getter and thus needing immediate
|
|
1925
|
-
// execution (rather than being the value of the property).
|
|
1926
|
-
this.getterStep_ = true;
|
|
1927
|
-
return getter;
|
|
1928
|
-
}
|
|
1929
|
-
return obj.properties[name];
|
|
1930
|
-
}
|
|
1931
|
-
} while (obj = this.getPrototype(obj));
|
|
1932
|
-
return undefined;
|
|
1933
|
-
};
|
|
1934
|
-
/**
|
|
1935
|
-
* Does the named property exist on a data object.
|
|
1936
|
-
* @param {Interpreter.Object} obj Data object.
|
|
1937
|
-
* @param {Interpreter.Value} name Name of property.
|
|
1938
|
-
* @returns {boolean} True if property exists.
|
|
1939
|
-
*/
|
|
1940
|
-
Interpreter.prototype.hasProperty = function (obj, name) {
|
|
1941
|
-
if (!(obj instanceof Interpreter.Object)) {
|
|
1942
|
-
throw TypeError('Primitive data type has no properties');
|
|
1943
|
-
}
|
|
1944
|
-
name = String(name);
|
|
1945
|
-
if (name === 'length' && this.isa(obj, this.STRING)) {
|
|
1946
|
-
return true;
|
|
1947
|
-
}
|
|
1948
|
-
if (this.isa(obj, this.STRING)) {
|
|
1949
|
-
var n = Interpreter.legalArrayIndex(name);
|
|
1950
|
-
if (!isNaN(n) && n < String(obj).length) {
|
|
1951
|
-
return true;
|
|
1952
|
-
}
|
|
1953
|
-
}
|
|
1954
|
-
do {
|
|
1955
|
-
if (obj.properties && name in obj.properties) {
|
|
1956
|
-
return true;
|
|
1957
|
-
}
|
|
1958
|
-
} while (obj = this.getPrototype(obj));
|
|
1959
|
-
return false;
|
|
1960
|
-
};
|
|
1961
|
-
/**
|
|
1962
|
-
* Set a property value on a data object.
|
|
1963
|
-
* @param {Interpreter.Value} obj Data object.
|
|
1964
|
-
* @param {Interpreter.Value} name Name of property.
|
|
1965
|
-
* @param {Interpreter.Value} value New property value.
|
|
1966
|
-
* Use Interpreter.VALUE_IN_DESCRIPTOR if value is handled by
|
|
1967
|
-
* descriptor instead.
|
|
1968
|
-
* @param {Object=} opt_descriptor Optional descriptor object.
|
|
1969
|
-
* @returns {!Interpreter.Object|undefined} Returns a setter function if one
|
|
1970
|
-
* needs to be called, otherwise undefined.
|
|
1971
|
-
*/
|
|
1972
|
-
Interpreter.prototype.setProperty = function (obj, name, value, opt_descriptor) {
|
|
1973
|
-
if (this.setterStep_) {
|
|
1974
|
-
// Getter from previous call to setProperty was not handled.
|
|
1975
|
-
throw Error('Setter not supported in that context');
|
|
1976
|
-
}
|
|
1977
|
-
name = String(name);
|
|
1978
|
-
if (obj === undefined || obj === null) {
|
|
1979
|
-
this.throwException(this.TYPE_ERROR, "Cannot set property '" + name + "' of " + obj);
|
|
1980
|
-
}
|
|
1981
|
-
if (typeof obj === 'object' && !(obj instanceof Interpreter.Object)) {
|
|
1982
|
-
throw TypeError('Expecting native value or pseudo object');
|
|
1983
|
-
}
|
|
1984
|
-
if (opt_descriptor && ('get' in opt_descriptor || 'set' in opt_descriptor) && ('value' in opt_descriptor || 'writable' in opt_descriptor)) {
|
|
1985
|
-
this.throwException(this.TYPE_ERROR, 'Invalid property descriptor. ' + 'Cannot both specify accessors and a value or writable attribute');
|
|
1986
|
-
}
|
|
1987
|
-
var strict = !this.stateStack || this.getScope().strict;
|
|
1988
|
-
if (!(obj instanceof Interpreter.Object)) {
|
|
1989
|
-
if (strict) {
|
|
1990
|
-
this.throwException(this.TYPE_ERROR, "Can't create property '" + name + "' on '" + obj + "'");
|
|
1991
|
-
}
|
|
1992
|
-
return;
|
|
1993
|
-
}
|
|
1994
|
-
if (this.isa(obj, this.STRING)) {
|
|
1995
|
-
var n = Interpreter.legalArrayIndex(name);
|
|
1996
|
-
if (name === 'length' || !isNaN(n) && n < String(obj).length) {
|
|
1997
|
-
// Can't set length or letters on String objects.
|
|
1998
|
-
if (strict) {
|
|
1999
|
-
this.throwException(this.TYPE_ERROR, 'Cannot assign to read only ' + "property '" + name + "' of String '" + obj.data + "'");
|
|
2000
|
-
}
|
|
2001
|
-
return;
|
|
2002
|
-
}
|
|
2003
|
-
}
|
|
2004
|
-
if (obj.class === 'Array') {
|
|
2005
|
-
// Arrays have a magic length variable that is bound to the elements.
|
|
2006
|
-
var len = obj.properties.length;
|
|
2007
|
-
var i;
|
|
2008
|
-
if (name === 'length') {
|
|
2009
|
-
// Delete elements if length is smaller.
|
|
2010
|
-
if (opt_descriptor) {
|
|
2011
|
-
if (!('value' in opt_descriptor)) {
|
|
2012
|
-
return;
|
|
2013
|
-
}
|
|
2014
|
-
value = opt_descriptor['value'];
|
|
2015
|
-
}
|
|
2016
|
-
value = Interpreter.legalArrayLength(value);
|
|
2017
|
-
if (isNaN(value)) {
|
|
2018
|
-
this.throwException(this.RANGE_ERROR, 'Invalid array length');
|
|
2019
|
-
}
|
|
2020
|
-
if (value < len) {
|
|
2021
|
-
for (i in obj.properties) {
|
|
2022
|
-
i = Interpreter.legalArrayIndex(i);
|
|
2023
|
-
if (!isNaN(i) && value <= i) {
|
|
2024
|
-
delete obj.properties[i];
|
|
2025
|
-
}
|
|
2026
|
-
}
|
|
2027
|
-
}
|
|
2028
|
-
}
|
|
2029
|
-
else if (!isNaN(i = Interpreter.legalArrayIndex(name))) {
|
|
2030
|
-
// Increase length if this index is larger.
|
|
2031
|
-
obj.properties.length = Math.max(len, i + 1);
|
|
2032
|
-
}
|
|
2033
|
-
}
|
|
2034
|
-
if (obj.preventExtensions && !(name in obj.properties)) {
|
|
2035
|
-
if (strict) {
|
|
2036
|
-
this.throwException(this.TYPE_ERROR, "Can't add property '" + name + "', object is not extensible");
|
|
2037
|
-
}
|
|
2038
|
-
return;
|
|
2039
|
-
}
|
|
2040
|
-
if (opt_descriptor) {
|
|
2041
|
-
// Define the property.
|
|
2042
|
-
var descriptor = {};
|
|
2043
|
-
if ('get' in opt_descriptor && opt_descriptor['get']) {
|
|
2044
|
-
obj.getter[name] = opt_descriptor['get'];
|
|
2045
|
-
descriptor['get'] = this.setProperty.placeholderGet_;
|
|
2046
|
-
}
|
|
2047
|
-
if ('set' in opt_descriptor && opt_descriptor['set']) {
|
|
2048
|
-
obj.setter[name] = opt_descriptor['set'];
|
|
2049
|
-
descriptor['set'] = this.setProperty.placeholderSet_;
|
|
2050
|
-
}
|
|
2051
|
-
if ('configurable' in opt_descriptor) {
|
|
2052
|
-
descriptor['configurable'] = opt_descriptor['configurable'];
|
|
2053
|
-
}
|
|
2054
|
-
if ('enumerable' in opt_descriptor) {
|
|
2055
|
-
descriptor['enumerable'] = opt_descriptor['enumerable'];
|
|
2056
|
-
}
|
|
2057
|
-
if ('writable' in opt_descriptor) {
|
|
2058
|
-
descriptor['writable'] = opt_descriptor['writable'];
|
|
2059
|
-
delete obj.getter[name];
|
|
2060
|
-
delete obj.setter[name];
|
|
2061
|
-
}
|
|
2062
|
-
if ('value' in opt_descriptor) {
|
|
2063
|
-
descriptor['value'] = opt_descriptor['value'];
|
|
2064
|
-
delete obj.getter[name];
|
|
2065
|
-
delete obj.setter[name];
|
|
2066
|
-
}
|
|
2067
|
-
else if (value !== Interpreter.VALUE_IN_DESCRIPTOR) {
|
|
2068
|
-
descriptor['value'] = value;
|
|
2069
|
-
delete obj.getter[name];
|
|
2070
|
-
delete obj.setter[name];
|
|
2071
|
-
}
|
|
2072
|
-
try {
|
|
2073
|
-
Object.defineProperty(obj.properties, name, descriptor);
|
|
2074
|
-
}
|
|
2075
|
-
catch (e) {
|
|
2076
|
-
this.throwException(this.TYPE_ERROR, 'Cannot redefine property: ' + name);
|
|
2077
|
-
}
|
|
2078
|
-
// Now that the definition has suceeded, clean up any obsolete get/set funcs.
|
|
2079
|
-
if ('get' in opt_descriptor && !opt_descriptor['get']) {
|
|
2080
|
-
delete obj.getter[name];
|
|
2081
|
-
}
|
|
2082
|
-
if ('set' in opt_descriptor && !opt_descriptor['set']) {
|
|
2083
|
-
delete obj.setter[name];
|
|
2084
|
-
}
|
|
2085
|
-
}
|
|
2086
|
-
else {
|
|
2087
|
-
// Set the property.
|
|
2088
|
-
if (value === Interpreter.VALUE_IN_DESCRIPTOR) {
|
|
2089
|
-
throw ReferenceError('Value not specified');
|
|
2090
|
-
}
|
|
2091
|
-
// Determine the parent (possibly self) where the property is defined.
|
|
2092
|
-
var defObj = obj;
|
|
2093
|
-
while (!(name in defObj.properties)) {
|
|
2094
|
-
defObj = this.getPrototype(defObj);
|
|
2095
|
-
if (!defObj) {
|
|
2096
|
-
// This is a new property.
|
|
2097
|
-
defObj = obj;
|
|
2098
|
-
break;
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
|
-
if (defObj.setter && defObj.setter[name]) {
|
|
2102
|
-
this.setterStep_ = true;
|
|
2103
|
-
return defObj.setter[name];
|
|
2104
|
-
}
|
|
2105
|
-
if (defObj.getter && defObj.getter[name]) {
|
|
2106
|
-
if (strict) {
|
|
2107
|
-
this.throwException(this.TYPE_ERROR, "Cannot set property '" + name + "' of object '" + obj + "' which only has a getter");
|
|
2108
|
-
}
|
|
2109
|
-
}
|
|
2110
|
-
else {
|
|
2111
|
-
// No setter, simple assignment.
|
|
2112
|
-
try {
|
|
2113
|
-
obj.properties[name] = value;
|
|
2114
|
-
}
|
|
2115
|
-
catch (_e) {
|
|
2116
|
-
if (strict) {
|
|
2117
|
-
this.throwException(this.TYPE_ERROR, 'Cannot assign to read only ' + "property '" + name + "' of object '" + obj + "'");
|
|
2118
|
-
}
|
|
2119
|
-
}
|
|
2120
|
-
}
|
|
2121
|
-
}
|
|
2122
|
-
};
|
|
2123
|
-
Interpreter.prototype.setProperty.placeholderGet_ = function () {
|
|
2124
|
-
throw Error('Placeholder getter');
|
|
2125
|
-
};
|
|
2126
|
-
Interpreter.prototype.setProperty.placeholderSet_ = function () {
|
|
2127
|
-
throw Error('Placeholder setter');
|
|
2128
|
-
};
|
|
2129
|
-
/**
|
|
2130
|
-
* Convenience method for adding a native function as a non-enumerable property
|
|
2131
|
-
* onto an object's prototype.
|
|
2132
|
-
* @param {!Interpreter.Object} obj Data object.
|
|
2133
|
-
* @param {Interpreter.Value} name Name of property.
|
|
2134
|
-
* @param {!Function} wrapper Function object.
|
|
2135
|
-
*/
|
|
2136
|
-
Interpreter.prototype.setNativeFunctionPrototype = function (obj, name, wrapper) {
|
|
2137
|
-
this.setProperty(obj.properties['prototype'], name, this.createNativeFunction(wrapper, false), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
2138
|
-
};
|
|
2139
|
-
/**
|
|
2140
|
-
* Convenience method for adding an async function as a non-enumerable property
|
|
2141
|
-
* onto an object's prototype.
|
|
2142
|
-
* @param {!Interpreter.Object} obj Data object.
|
|
2143
|
-
* @param {Interpreter.Value} name Name of property.
|
|
2144
|
-
* @param {!Function} wrapper Function object.
|
|
2145
|
-
*/
|
|
2146
|
-
Interpreter.prototype.setAsyncFunctionPrototype = function (obj, name, wrapper) {
|
|
2147
|
-
this.setProperty(obj.properties['prototype'], name, this.createAsyncFunction(wrapper), Interpreter.NONENUMERABLE_DESCRIPTOR);
|
|
2148
|
-
};
|
|
2149
|
-
/**
|
|
2150
|
-
* Returns the current scope from the stateStack.
|
|
2151
|
-
* @returns {!Interpreter.Scope} Current scope.
|
|
2152
|
-
*/
|
|
2153
|
-
Interpreter.prototype.getScope = function () {
|
|
2154
|
-
var scope = this.stateStack[this.stateStack.length - 1].scope;
|
|
2155
|
-
if (!scope) {
|
|
2156
|
-
throw Error('No scope found');
|
|
2157
|
-
}
|
|
2158
|
-
return scope;
|
|
2159
|
-
};
|
|
2160
|
-
/**
|
|
2161
|
-
* Create a new scope dictionary.
|
|
2162
|
-
* @param {!Object} node AST node defining the scope container
|
|
2163
|
-
* (e.g. a function).
|
|
2164
|
-
* @param {Interpreter.Scope} parentScope Scope to link to.
|
|
2165
|
-
* @returns {!Interpreter.Scope} New scope.
|
|
2166
|
-
*/
|
|
2167
|
-
Interpreter.prototype.createScope = function (node, parentScope) {
|
|
2168
|
-
// Determine if this scope starts with `use strict`.
|
|
2169
|
-
var strict = false;
|
|
2170
|
-
if (parentScope && parentScope.strict) {
|
|
2171
|
-
strict = true;
|
|
2172
|
-
}
|
|
2173
|
-
else {
|
|
2174
|
-
var firstNode = node.body && node.body[0];
|
|
2175
|
-
if (firstNode && firstNode.expression && firstNode.expression.type === 'Literal' && firstNode.expression.value === 'use strict') {
|
|
2176
|
-
strict = true;
|
|
2177
|
-
}
|
|
2178
|
-
}
|
|
2179
|
-
var object = this.createObjectProto(null);
|
|
2180
|
-
var scope = new Interpreter.Scope(parentScope, strict, object);
|
|
2181
|
-
if (!parentScope) {
|
|
2182
|
-
this.initGlobal(scope.object);
|
|
2183
|
-
}
|
|
2184
|
-
this.populateScope_(node, scope);
|
|
2185
|
-
return scope;
|
|
2186
|
-
};
|
|
2187
|
-
/**
|
|
2188
|
-
* Create a new special scope dictionary. Similar to createScope(), but
|
|
2189
|
-
* doesn't assume that the scope is for a function body.
|
|
2190
|
-
* This is used for 'catch' clauses, 'with' statements,
|
|
2191
|
-
* and named function expressions.
|
|
2192
|
-
* @param {!Interpreter.Scope} parentScope Scope to link to.
|
|
2193
|
-
* @param {Interpreter.Object=} opt_object Optional object to transform into
|
|
2194
|
-
* scope.
|
|
2195
|
-
* @returns {!Interpreter.Scope} New scope.
|
|
2196
|
-
*/
|
|
2197
|
-
Interpreter.prototype.createSpecialScope = function (parentScope, opt_object) {
|
|
2198
|
-
if (!parentScope) {
|
|
2199
|
-
throw Error('parentScope required');
|
|
2200
|
-
}
|
|
2201
|
-
var object = opt_object || this.createObjectProto(null);
|
|
2202
|
-
return new Interpreter.Scope(parentScope, parentScope.strict, object);
|
|
2203
|
-
};
|
|
2204
|
-
/**
|
|
2205
|
-
* Retrieves a value from the scope chain.
|
|
2206
|
-
* @param {string} name Name of variable.
|
|
2207
|
-
* @returns {Interpreter.Value} Any value.
|
|
2208
|
-
* May be flagged as being a getter and thus needing immediate execution
|
|
2209
|
-
* (rather than being the value of the property).
|
|
2210
|
-
*/
|
|
2211
|
-
Interpreter.prototype.getValueFromScope = function (name) {
|
|
2212
|
-
var scope = this.getScope();
|
|
2213
|
-
while (scope && scope !== this.globalScope) {
|
|
2214
|
-
if (name in scope.object.properties) {
|
|
2215
|
-
return scope.object.properties[name];
|
|
2216
|
-
}
|
|
2217
|
-
scope = scope.parentScope;
|
|
2218
|
-
}
|
|
2219
|
-
// The root scope is also an object which has inherited properties and
|
|
2220
|
-
// could also have getters.
|
|
2221
|
-
if (scope === this.globalScope && this.hasProperty(scope.object, name)) {
|
|
2222
|
-
return this.getProperty(scope.object, name);
|
|
2223
|
-
}
|
|
2224
|
-
// Typeof operator is unique: it can safely look at non-defined variables.
|
|
2225
|
-
var prevNode = this.stateStack[this.stateStack.length - 1].node;
|
|
2226
|
-
if (prevNode.type === 'UnaryExpression' && prevNode.operator === 'typeof') {
|
|
2227
|
-
return undefined;
|
|
2228
|
-
}
|
|
2229
|
-
this.throwException(this.REFERENCE_ERROR, name + ' is not defined');
|
|
2230
|
-
};
|
|
2231
|
-
/**
|
|
2232
|
-
* Sets a value to the current scope.
|
|
2233
|
-
* @param {string} name Name of variable.
|
|
2234
|
-
* @param {Interpreter.Value} value Value.
|
|
2235
|
-
* @returns {!Interpreter.Object|undefined} Returns a setter function if one
|
|
2236
|
-
* needs to be called, otherwise undefined.
|
|
2237
|
-
*/
|
|
2238
|
-
Interpreter.prototype.setValueToScope = function (name, value) {
|
|
2239
|
-
var scope = this.getScope();
|
|
2240
|
-
var strict = scope.strict;
|
|
2241
|
-
while (scope && scope !== this.globalScope) {
|
|
2242
|
-
if (name in scope.object.properties) {
|
|
2243
|
-
try {
|
|
2244
|
-
scope.object.properties[name] = value;
|
|
2245
|
-
}
|
|
2246
|
-
catch (_e) {
|
|
2247
|
-
if (strict) {
|
|
2248
|
-
this.throwException(this.TYPE_ERROR, "Cannot assign to read only variable '" + name + "'");
|
|
2249
|
-
}
|
|
2250
|
-
}
|
|
2251
|
-
return undefined;
|
|
2252
|
-
}
|
|
2253
|
-
scope = scope.parentScope;
|
|
2254
|
-
}
|
|
2255
|
-
// The root scope is also an object which has readonly properties and
|
|
2256
|
-
// could also have setters.
|
|
2257
|
-
if (scope === this.globalScope && (!strict || this.hasProperty(scope.object, name))) {
|
|
2258
|
-
return this.setProperty(scope.object, name, value);
|
|
2259
|
-
}
|
|
2260
|
-
this.throwException(this.REFERENCE_ERROR, name + ' is not defined');
|
|
2261
|
-
};
|
|
2262
|
-
/**
|
|
2263
|
-
* Create a new scope for the given node and populate it with all variables
|
|
2264
|
-
* and named functions.
|
|
2265
|
-
* @param {!Object} node AST node (usually a program or function when initally
|
|
2266
|
-
* calling this function, though it recurses to scan many child nodes).
|
|
2267
|
-
* @param {!Interpreter.Scope} scope Scope dictionary to populate.
|
|
2268
|
-
* @returns {!Object} Map of all variable and function names.
|
|
2269
|
-
* @private
|
|
2270
|
-
*/
|
|
2271
|
-
Interpreter.prototype.populateScope_ = function (node, scope) {
|
|
2272
|
-
var variableCache;
|
|
2273
|
-
if (!node.variableCache_) {
|
|
2274
|
-
variableCache = Object.create(null);
|
|
2275
|
-
switch (node.type) {
|
|
2276
|
-
case 'VariableDeclaration':
|
|
2277
|
-
for (var i = 0; i < node.declarations.length; i++) {
|
|
2278
|
-
variableCache[node.declarations[i].id.name] = true;
|
|
2279
|
-
}
|
|
2280
|
-
break;
|
|
2281
|
-
case 'FunctionDeclaration':
|
|
2282
|
-
variableCache[node.id.name] = node;
|
|
2283
|
-
break;
|
|
2284
|
-
case 'BlockStatement':
|
|
2285
|
-
case 'CatchClause':
|
|
2286
|
-
case 'DoWhileStatement':
|
|
2287
|
-
case 'ForInStatement':
|
|
2288
|
-
case 'ForStatement':
|
|
2289
|
-
case 'IfStatement':
|
|
2290
|
-
case 'LabeledStatement':
|
|
2291
|
-
case 'Program':
|
|
2292
|
-
case 'SwitchCase':
|
|
2293
|
-
case 'SwitchStatement':
|
|
2294
|
-
case 'TryStatement':
|
|
2295
|
-
case 'WithStatement':
|
|
2296
|
-
case 'WhileStatement':
|
|
2297
|
-
// All the structures within which a variable or function could hide.
|
|
2298
|
-
var nodeClass = node.constructor;
|
|
2299
|
-
for (var name in node) {
|
|
2300
|
-
if (name === 'loc')
|
|
2301
|
-
continue;
|
|
2302
|
-
var prop = node[name];
|
|
2303
|
-
if (prop && typeof prop === 'object') {
|
|
2304
|
-
var childCache;
|
|
2305
|
-
if (Array.isArray(prop)) {
|
|
2306
|
-
for (var i = 0; i < prop.length; i++) {
|
|
2307
|
-
if (prop[i] && prop[i].constructor === nodeClass) {
|
|
2308
|
-
childCache = this.populateScope_(prop[i], scope);
|
|
2309
|
-
for (var name in childCache) {
|
|
2310
|
-
variableCache[name] = childCache[name];
|
|
2311
|
-
}
|
|
2312
|
-
}
|
|
2313
|
-
}
|
|
2314
|
-
}
|
|
2315
|
-
else {
|
|
2316
|
-
if (prop.constructor === nodeClass) {
|
|
2317
|
-
childCache = this.populateScope_(prop, scope);
|
|
2318
|
-
for (var name in childCache) {
|
|
2319
|
-
variableCache[name] = childCache[name];
|
|
2320
|
-
}
|
|
2321
|
-
}
|
|
2322
|
-
}
|
|
2323
|
-
}
|
|
2324
|
-
}
|
|
2325
|
-
}
|
|
2326
|
-
node.variableCache_ = variableCache;
|
|
2327
|
-
}
|
|
2328
|
-
else {
|
|
2329
|
-
variableCache = node.variableCache_;
|
|
2330
|
-
}
|
|
2331
|
-
for (var name in variableCache) {
|
|
2332
|
-
if (variableCache[name] === true) {
|
|
2333
|
-
this.setProperty(scope.object, name, undefined, Interpreter.VARIABLE_DESCRIPTOR);
|
|
2334
|
-
}
|
|
2335
|
-
else {
|
|
2336
|
-
this.setProperty(scope.object, name, this.createFunction(variableCache[name], scope), Interpreter.VARIABLE_DESCRIPTOR);
|
|
2337
|
-
}
|
|
2338
|
-
}
|
|
2339
|
-
return variableCache;
|
|
2340
|
-
};
|
|
2341
|
-
/**
|
|
2342
|
-
* Is the current state directly being called with as a construction with 'new'.
|
|
2343
|
-
* @returns {boolean} True if 'new foo()', false if 'foo()'.
|
|
2344
|
-
*/
|
|
2345
|
-
Interpreter.prototype.calledWithNew = function () {
|
|
2346
|
-
return this.stateStack[this.stateStack.length - 1].isConstructor;
|
|
2347
|
-
};
|
|
2348
|
-
/**
|
|
2349
|
-
* Gets a value from the scope chain or from an object property.
|
|
2350
|
-
* @param {!Array} ref Name of variable or object/propname tuple.
|
|
2351
|
-
* @returns {Interpreter.Value} Any value.
|
|
2352
|
-
* May be flagged as being a getter and thus needing immediate execution
|
|
2353
|
-
* (rather than being the value of the property).
|
|
2354
|
-
*/
|
|
2355
|
-
Interpreter.prototype.getValue = function (ref) {
|
|
2356
|
-
if (ref[0] === Interpreter.SCOPE_REFERENCE) {
|
|
2357
|
-
// A null/varname variable lookup.
|
|
2358
|
-
return this.getValueFromScope(ref[1]);
|
|
2359
|
-
}
|
|
2360
|
-
else {
|
|
2361
|
-
// An obj/prop components tuple (foo.bar).
|
|
2362
|
-
return this.getProperty(ref[0], ref[1]);
|
|
2363
|
-
}
|
|
2364
|
-
};
|
|
2365
|
-
/**
|
|
2366
|
-
* Sets a value to the scope chain or to an object property.
|
|
2367
|
-
* @param {!Array} ref Name of variable or object/propname tuple.
|
|
2368
|
-
* @param {Interpreter.Value} value Value.
|
|
2369
|
-
* @returns {!Interpreter.Object|undefined} Returns a setter function if one
|
|
2370
|
-
* needs to be called, otherwise undefined.
|
|
2371
|
-
*/
|
|
2372
|
-
Interpreter.prototype.setValue = function (ref, value) {
|
|
2373
|
-
if (ref[0] === Interpreter.SCOPE_REFERENCE) {
|
|
2374
|
-
// A null/varname variable lookup.
|
|
2375
|
-
return this.setValueToScope(ref[1], value);
|
|
2376
|
-
}
|
|
2377
|
-
// An obj/prop components tuple (foo.bar).
|
|
2378
|
-
return this.setProperty(ref[0], ref[1], value);
|
|
2379
|
-
};
|
|
2380
|
-
/**
|
|
2381
|
-
* Throw an exception in the interpreter that can be handled by an
|
|
2382
|
-
* interpreter try/catch statement. If unhandled, a real exception will
|
|
2383
|
-
* be thrown. Can be called with either an error class and a message, or
|
|
2384
|
-
* with an actual object to be thrown.
|
|
2385
|
-
* @param {!Interpreter.Object|Interpreter.Value} errorClass Type of error
|
|
2386
|
-
* (if message is provided) or the value to throw (if no message).
|
|
2387
|
-
* @param {string=} opt_message Message being thrown.
|
|
2388
|
-
*/
|
|
2389
|
-
Interpreter.prototype.throwException = function (errorClass, opt_message) {
|
|
2390
|
-
if (!this.globalScope) {
|
|
2391
|
-
// This is an error being thrown in the initialization, throw a real error.
|
|
2392
|
-
throw opt_message === undefined ? errorClass : opt_message;
|
|
2393
|
-
}
|
|
2394
|
-
if (opt_message === undefined || !(errorClass instanceof Interpreter.Object)) {
|
|
2395
|
-
var error = errorClass; // This is a value to throw, not an error class.
|
|
2396
|
-
}
|
|
2397
|
-
else {
|
|
2398
|
-
var error = this.createObject(errorClass);
|
|
2399
|
-
this.populateError(error, opt_message);
|
|
2400
|
-
}
|
|
2401
|
-
this.unwind(Interpreter.Completion.THROW, error, undefined);
|
|
2402
|
-
// Abort anything related to the current step.
|
|
2403
|
-
throw Interpreter.STEP_ERROR;
|
|
2404
|
-
};
|
|
2405
|
-
/**
|
|
2406
|
-
* Unwind the stack to the innermost relevant enclosing TryStatement,
|
|
2407
|
-
* For/ForIn/WhileStatement or Call/NewExpression. If this results in
|
|
2408
|
-
* the stack being completely unwound the thread will be terminated
|
|
2409
|
-
* and the appropriate error being thrown.
|
|
2410
|
-
* @param {Interpreter.Completion} type Completion type.
|
|
2411
|
-
* @param {Interpreter.Value} value Value computed, returned or thrown.
|
|
2412
|
-
* @param {string|undefined} label Target label for break or return.
|
|
2413
|
-
*/
|
|
2414
|
-
Interpreter.prototype.unwind = function (type, value, label) {
|
|
2415
|
-
if (type === Interpreter.Completion.NORMAL) {
|
|
2416
|
-
throw TypeError('Should not unwind for NORMAL completions');
|
|
2417
|
-
}
|
|
2418
|
-
loop: for (var stack = this.stateStack; stack.length > 0; stack.pop()) {
|
|
2419
|
-
var state = stack[stack.length - 1];
|
|
2420
|
-
switch (state.node.type) {
|
|
2421
|
-
case 'TryStatement':
|
|
2422
|
-
state.cv = {
|
|
2423
|
-
type: type,
|
|
2424
|
-
value: value,
|
|
2425
|
-
label: label
|
|
2426
|
-
};
|
|
2427
|
-
return;
|
|
2428
|
-
case 'CallExpression':
|
|
2429
|
-
case 'NewExpression':
|
|
2430
|
-
if (type === Interpreter.Completion.RETURN) {
|
|
2431
|
-
state.value = value;
|
|
2432
|
-
return;
|
|
2433
|
-
}
|
|
2434
|
-
else if (type !== Interpreter.Completion.THROW) {
|
|
2435
|
-
throw Error('Unsynatctic break/continue not rejected by Acorn');
|
|
2436
|
-
}
|
|
2437
|
-
break;
|
|
2438
|
-
case 'Program':
|
|
2439
|
-
// Don't pop the stateStack.
|
|
2440
|
-
// Leave the root scope on the tree in case the program is appended to.
|
|
2441
|
-
state.done = true;
|
|
2442
|
-
break loop;
|
|
2443
|
-
}
|
|
2444
|
-
if (type === Interpreter.Completion.BREAK) {
|
|
2445
|
-
if (label ? state.labels && state.labels.indexOf(label) !== -1 : state.isLoop || state.isSwitch) {
|
|
2446
|
-
stack.pop();
|
|
2447
|
-
return;
|
|
2448
|
-
}
|
|
2449
|
-
}
|
|
2450
|
-
else if (type === Interpreter.Completion.CONTINUE) {
|
|
2451
|
-
if (label ? state.labels && state.labels.indexOf(label) !== -1 : state.isLoop) {
|
|
2452
|
-
return;
|
|
2453
|
-
}
|
|
2454
|
-
}
|
|
2455
|
-
}
|
|
2456
|
-
// Unhandled completion. Throw a real error.
|
|
2457
|
-
var realError;
|
|
2458
|
-
if (this.isa(value, this.ERROR)) {
|
|
2459
|
-
var errorTable = {
|
|
2460
|
-
EvalError: EvalError,
|
|
2461
|
-
RangeError: RangeError,
|
|
2462
|
-
ReferenceError: ReferenceError,
|
|
2463
|
-
SyntaxError: SyntaxError,
|
|
2464
|
-
TypeError: TypeError,
|
|
2465
|
-
URIError: URIError
|
|
2466
|
-
};
|
|
2467
|
-
var name = String(this.getProperty(value, 'name'));
|
|
2468
|
-
var message = this.getProperty(value, 'message').valueOf();
|
|
2469
|
-
var errorConstructor = errorTable[name] || Error;
|
|
2470
|
-
realError = errorConstructor(message);
|
|
2471
|
-
realError.stack = String(this.getProperty(value, 'stack'));
|
|
2472
|
-
}
|
|
2473
|
-
else {
|
|
2474
|
-
realError = String(value);
|
|
2475
|
-
}
|
|
2476
|
-
// Overwrite the previous (more or less random) interpreter return value.
|
|
2477
|
-
// Replace it with the error.
|
|
2478
|
-
this.value = realError;
|
|
2479
|
-
throw realError;
|
|
2480
|
-
};
|
|
2481
|
-
/**
|
|
2482
|
-
* AST to code. Summarizes the expression at the given node. Currently
|
|
2483
|
-
* not guaranteed to be correct or complete. Used for error messages.
|
|
2484
|
-
* E.g. `escape('hello') + 42` -> 'escape(...) + 42'
|
|
2485
|
-
* @param {!Object} node AST node.
|
|
2486
|
-
* @returns {string} Code string.
|
|
2487
|
-
*/
|
|
2488
|
-
Interpreter.prototype.nodeSummary = function (node) {
|
|
2489
|
-
switch (node.type) {
|
|
2490
|
-
case 'ArrayExpression':
|
|
2491
|
-
return '[...]';
|
|
2492
|
-
case 'BinaryExpression':
|
|
2493
|
-
case 'LogicalExpression':
|
|
2494
|
-
return this.nodeSummary(node.left) + ' ' + node.operator + ' ' + this.nodeSummary(node.right);
|
|
2495
|
-
case 'CallExpression':
|
|
2496
|
-
return this.nodeSummary(node.callee) + '(...)';
|
|
2497
|
-
case 'ConditionalExpression':
|
|
2498
|
-
return this.nodeSummary(node.test) + ' ? ' + this.nodeSummary(node.consequent) + ' : ' + this.nodeSummary(node.alternate);
|
|
2499
|
-
case 'Identifier':
|
|
2500
|
-
return node.name;
|
|
2501
|
-
case 'Literal':
|
|
2502
|
-
return node.raw;
|
|
2503
|
-
case 'MemberExpression':
|
|
2504
|
-
var obj = this.nodeSummary(node.object);
|
|
2505
|
-
var prop = this.nodeSummary(node.property);
|
|
2506
|
-
return node.computed ? obj + '[' + prop + ']' : obj + '.' + prop;
|
|
2507
|
-
case 'NewExpression':
|
|
2508
|
-
return 'new ' + this.nodeSummary(node.callee) + '(...)';
|
|
2509
|
-
case 'ObjectExpression':
|
|
2510
|
-
return '{...}';
|
|
2511
|
-
case 'ThisExpression':
|
|
2512
|
-
return 'this';
|
|
2513
|
-
case 'UnaryExpression':
|
|
2514
|
-
return node.operator + ' ' + this.nodeSummary(node.argument);
|
|
2515
|
-
case 'UpdateExpression':
|
|
2516
|
-
var argument = this.nodeSummary(node.argument);
|
|
2517
|
-
return node.prefix ? node.operator + argument : argument + node.operator;
|
|
2518
|
-
}
|
|
2519
|
-
return '???';
|
|
2520
|
-
};
|
|
2521
|
-
/**
|
|
2522
|
-
* Create a call to a getter function.
|
|
2523
|
-
* @param {!Interpreter.Object} func Function to execute.
|
|
2524
|
-
* @param {!Interpreter.Object|!Array} left
|
|
2525
|
-
* Name of variable or object/propname tuple.
|
|
2526
|
-
* @private
|
|
2527
|
-
*/
|
|
2528
|
-
Interpreter.prototype.createGetter_ = function (func, left) {
|
|
2529
|
-
if (!this.getterStep_) {
|
|
2530
|
-
throw Error('Unexpected call to createGetter');
|
|
2531
|
-
}
|
|
2532
|
-
// Clear the getter flag.
|
|
2533
|
-
this.getterStep_ = false;
|
|
2534
|
-
// Normally `this` will be specified as the object component (o.x).
|
|
2535
|
-
// Sometimes `this` is explicitly provided (o).
|
|
2536
|
-
var funcThis = Array.isArray(left) ? left[0] : left;
|
|
2537
|
-
var node = this.newNode();
|
|
2538
|
-
node.type = 'CallExpression';
|
|
2539
|
-
var state = new Interpreter.State(node, this.stateStack[this.stateStack.length - 1].scope);
|
|
2540
|
-
state.doneCallee_ = 2;
|
|
2541
|
-
state.funcThis_ = funcThis;
|
|
2542
|
-
state.func_ = func;
|
|
2543
|
-
state.doneArgs_ = true;
|
|
2544
|
-
state.arguments_ = [];
|
|
2545
|
-
return state;
|
|
2546
|
-
};
|
|
2547
|
-
/**
|
|
2548
|
-
* Create a call to a setter function.
|
|
2549
|
-
* @param {!Interpreter.Object} func Function to execute.
|
|
2550
|
-
* @param {!Interpreter.Object|!Array} left
|
|
2551
|
-
* Name of variable or object/propname tuple.
|
|
2552
|
-
* @param {Interpreter.Value} value Value to set.
|
|
2553
|
-
* @private
|
|
2554
|
-
*/
|
|
2555
|
-
Interpreter.prototype.createSetter_ = function (func, left, value) {
|
|
2556
|
-
if (!this.setterStep_) {
|
|
2557
|
-
throw Error('Unexpected call to createSetter');
|
|
2558
|
-
}
|
|
2559
|
-
// Clear the setter flag.
|
|
2560
|
-
this.setterStep_ = false;
|
|
2561
|
-
// Normally `this` will be specified as the object component (o.x).
|
|
2562
|
-
// Sometimes `this` is implicitly the global object (x).
|
|
2563
|
-
var funcThis = Array.isArray(left) ? left[0] : this.globalObject;
|
|
2564
|
-
var node = this.newNode();
|
|
2565
|
-
node.type = 'CallExpression';
|
|
2566
|
-
var state = new Interpreter.State(node, this.stateStack[this.stateStack.length - 1].scope);
|
|
2567
|
-
state.doneCallee_ = 2;
|
|
2568
|
-
state.funcThis_ = funcThis;
|
|
2569
|
-
state.func_ = func;
|
|
2570
|
-
state.doneArgs_ = true;
|
|
2571
|
-
state.arguments_ = [value];
|
|
2572
|
-
return state;
|
|
2573
|
-
};
|
|
2574
|
-
/**
|
|
2575
|
-
* In non-strict mode `this` must be an object.
|
|
2576
|
-
* Must not be called in strict mode.
|
|
2577
|
-
* @param {Interpreter.Value} value Proposed value for `this`.
|
|
2578
|
-
* @returns {!Interpreter.Object} Final value for `this`.
|
|
2579
|
-
* @private
|
|
2580
|
-
*/
|
|
2581
|
-
Interpreter.prototype.boxThis_ = function (value) {
|
|
2582
|
-
if (value === undefined || value === null) {
|
|
2583
|
-
// `Undefined` and `null` are changed to the global object.
|
|
2584
|
-
return this.globalObject;
|
|
2585
|
-
}
|
|
2586
|
-
if (!(value instanceof Interpreter.Object)) {
|
|
2587
|
-
// Primitives must be boxed.
|
|
2588
|
-
var box = this.createObjectProto(this.getPrototype(value));
|
|
2589
|
-
box.data = value;
|
|
2590
|
-
return box;
|
|
2591
|
-
}
|
|
2592
|
-
return value;
|
|
2593
|
-
};
|
|
2594
|
-
/**
|
|
2595
|
-
* Return the global scope object.
|
|
2596
|
-
* @returns {!Interpreter.Scope} Scope object.
|
|
2597
|
-
*/
|
|
2598
|
-
Interpreter.prototype.getGlobalScope = function () {
|
|
2599
|
-
return this.globalScope;
|
|
2600
|
-
};
|
|
2601
|
-
/**
|
|
2602
|
-
* Return the state stack.
|
|
2603
|
-
* @returns {!Array<!Interpreter.State>} State stack.
|
|
2604
|
-
*/
|
|
2605
|
-
Interpreter.prototype.getStateStack = function () {
|
|
2606
|
-
return this.stateStack;
|
|
2607
|
-
};
|
|
2608
|
-
/**
|
|
2609
|
-
* Replace the state stack with a new one.
|
|
2610
|
-
* @param {!Array<!Interpreter.State>} newStack New state stack.
|
|
2611
|
-
*/
|
|
2612
|
-
Interpreter.prototype.setStateStack = function (newStack) {
|
|
2613
|
-
this.stateStack = newStack;
|
|
2614
|
-
};
|
|
2615
|
-
/**
|
|
2616
|
-
* Typedef for JS values.
|
|
2617
|
-
* @typedef {!Interpreter.Object|boolean|number|string|undefined|null}
|
|
2618
|
-
*/
|
|
2619
|
-
Interpreter.Value;
|
|
2620
|
-
/**
|
|
2621
|
-
* Class for a state.
|
|
2622
|
-
* @param {!Object} node AST node for the state.
|
|
2623
|
-
* @param {!Interpreter.Scope} scope Scope object for the state.
|
|
2624
|
-
* @constructor
|
|
2625
|
-
*/
|
|
2626
|
-
Interpreter.State = function (node, scope) {
|
|
2627
|
-
this.node = node;
|
|
2628
|
-
this.scope = scope;
|
|
2629
|
-
};
|
|
2630
|
-
/**
|
|
2631
|
-
* Class for a scope.
|
|
2632
|
-
* @param {Interpreter.Scope} parentScope Parent scope.
|
|
2633
|
-
* @param {boolean} strict True if "use strict".
|
|
2634
|
-
* @param {!Interpreter.Object} object Object containing scope's variables.
|
|
2635
|
-
* @struct
|
|
2636
|
-
* @constructor
|
|
2637
|
-
*/
|
|
2638
|
-
Interpreter.Scope = function (parentScope, strict, object) {
|
|
2639
|
-
this.parentScope = parentScope;
|
|
2640
|
-
this.strict = strict;
|
|
2641
|
-
this.object = object;
|
|
2642
|
-
};
|
|
2643
|
-
/**
|
|
2644
|
-
* Class for an object.
|
|
2645
|
-
* @param {Interpreter.Object} proto Prototype object or null.
|
|
2646
|
-
* @constructor
|
|
2647
|
-
*/
|
|
2648
|
-
Interpreter.Object = function (proto) {
|
|
2649
|
-
this.getter = Object.create(null);
|
|
2650
|
-
this.setter = Object.create(null);
|
|
2651
|
-
this.properties = Object.create(null);
|
|
2652
|
-
this.proto = proto;
|
|
2653
|
-
};
|
|
2654
|
-
/** @type {Interpreter.Object} */
|
|
2655
|
-
Interpreter.Object.prototype.proto = null;
|
|
2656
|
-
/** @type {string} */
|
|
2657
|
-
Interpreter.Object.prototype.class = 'Object';
|
|
2658
|
-
/** @type {Date|RegExp|boolean|number|string|null} */
|
|
2659
|
-
Interpreter.Object.prototype.data = null;
|
|
2660
|
-
/**
|
|
2661
|
-
* Convert this object into a string.
|
|
2662
|
-
* @returns {string} String value.
|
|
2663
|
-
* @override
|
|
2664
|
-
*/
|
|
2665
|
-
Interpreter.Object.prototype.toString = function () {
|
|
2666
|
-
if (!Interpreter.currentInterpreter_) {
|
|
2667
|
-
// Called from outside an interpreter.
|
|
2668
|
-
return '[object Interpreter.Object]';
|
|
2669
|
-
}
|
|
2670
|
-
if (!(this instanceof Interpreter.Object)) {
|
|
2671
|
-
// Primitive value.
|
|
2672
|
-
return String(this);
|
|
2673
|
-
}
|
|
2674
|
-
if (this.class === 'Array') {
|
|
2675
|
-
// Array contents must not have cycles.
|
|
2676
|
-
var cycles = Interpreter.toStringCycles_;
|
|
2677
|
-
cycles.push(this);
|
|
2678
|
-
try {
|
|
2679
|
-
var strs = [];
|
|
2680
|
-
// Truncate very long strings. This is not part of the spec,
|
|
2681
|
-
// but it prevents hanging the interpreter for gigantic arrays.
|
|
2682
|
-
var maxLength = this.properties.length;
|
|
2683
|
-
var truncated = false;
|
|
2684
|
-
if (maxLength > 1024) {
|
|
2685
|
-
maxLength = 1000;
|
|
2686
|
-
truncated = true;
|
|
2687
|
-
}
|
|
2688
|
-
for (var i = 0; i < maxLength; i++) {
|
|
2689
|
-
var value = this.properties[i];
|
|
2690
|
-
strs[i] = value instanceof Interpreter.Object && cycles.indexOf(value) !== -1 ? '...' : value;
|
|
2691
|
-
}
|
|
2692
|
-
if (truncated) {
|
|
2693
|
-
strs.push('...');
|
|
2694
|
-
}
|
|
2695
|
-
}
|
|
2696
|
-
finally {
|
|
2697
|
-
cycles.pop();
|
|
2698
|
-
}
|
|
2699
|
-
return strs.join(',');
|
|
2700
|
-
}
|
|
2701
|
-
if (this.class === 'Error') {
|
|
2702
|
-
// Error name and message properties must not have cycles.
|
|
2703
|
-
var cycles = Interpreter.toStringCycles_;
|
|
2704
|
-
if (cycles.indexOf(this) !== -1) {
|
|
2705
|
-
return '[object Error]';
|
|
2706
|
-
}
|
|
2707
|
-
var name, message;
|
|
2708
|
-
// Bug: Does not support getters and setters for name or message.
|
|
2709
|
-
var obj = this;
|
|
2710
|
-
do {
|
|
2711
|
-
if ('name' in obj.properties) {
|
|
2712
|
-
name = obj.properties.name;
|
|
2713
|
-
break;
|
|
2714
|
-
}
|
|
2715
|
-
} while (obj = obj.proto);
|
|
2716
|
-
obj = this;
|
|
2717
|
-
do {
|
|
2718
|
-
if ('message' in obj.properties) {
|
|
2719
|
-
message = obj.properties.message;
|
|
2720
|
-
break;
|
|
2721
|
-
}
|
|
2722
|
-
} while (obj = obj.proto);
|
|
2723
|
-
cycles.push(this);
|
|
2724
|
-
try {
|
|
2725
|
-
name = name && String(name);
|
|
2726
|
-
message = message && String(message);
|
|
2727
|
-
}
|
|
2728
|
-
finally {
|
|
2729
|
-
cycles.pop();
|
|
2730
|
-
}
|
|
2731
|
-
return message ? name + ': ' + message : String(name);
|
|
2732
|
-
}
|
|
2733
|
-
if (this.data !== null) {
|
|
2734
|
-
// RegExp, Date, and boxed primitives.
|
|
2735
|
-
return String(this.data);
|
|
2736
|
-
}
|
|
2737
|
-
return '[object ' + this.class + ']';
|
|
2738
|
-
};
|
|
2739
|
-
/**
|
|
2740
|
-
* Return the object's value.
|
|
2741
|
-
* @returns {Interpreter.Value} Value.
|
|
2742
|
-
* @override
|
|
2743
|
-
*/
|
|
2744
|
-
Interpreter.Object.prototype.valueOf = function () {
|
|
2745
|
-
if (!Interpreter.currentInterpreter_) {
|
|
2746
|
-
// Called from outside an interpreter.
|
|
2747
|
-
return this;
|
|
2748
|
-
}
|
|
2749
|
-
if (this.data === undefined || this.data === null || this.data instanceof RegExp) {
|
|
2750
|
-
return this; // An Object, RegExp, or primitive.
|
|
2751
|
-
}
|
|
2752
|
-
if (this.data instanceof Date) {
|
|
2753
|
-
return this.data.valueOf(); // Milliseconds.
|
|
2754
|
-
}
|
|
2755
|
-
return ( /** @type {(boolean|number|string)} */this.data); // Boxed primitive.
|
|
2756
|
-
};
|
|
2757
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
2758
|
-
// Functions to handle each node type.
|
|
2759
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
2760
|
-
Interpreter.prototype['stepArrayExpression'] = function (stack, state, node) {
|
|
2761
|
-
var elements = node.elements;
|
|
2762
|
-
var n = state.n_ || 0;
|
|
2763
|
-
if (!state.array_) {
|
|
2764
|
-
state.array_ = this.createArray();
|
|
2765
|
-
state.array_.properties.length = elements.length;
|
|
2766
|
-
}
|
|
2767
|
-
else {
|
|
2768
|
-
this.setProperty(state.array_, n, state.value);
|
|
2769
|
-
n++;
|
|
2770
|
-
}
|
|
2771
|
-
while (n < elements.length) {
|
|
2772
|
-
// Skip missing elements - they're not defined, not undefined.
|
|
2773
|
-
if (elements[n]) {
|
|
2774
|
-
state.n_ = n;
|
|
2775
|
-
return new Interpreter.State(elements[n], state.scope);
|
|
2776
|
-
}
|
|
2777
|
-
n++;
|
|
2778
|
-
}
|
|
2779
|
-
stack.pop();
|
|
2780
|
-
stack[stack.length - 1].value = state.array_;
|
|
2781
|
-
};
|
|
2782
|
-
Interpreter.prototype['stepAssignmentExpression'] = function (stack, state, node) {
|
|
2783
|
-
if (!state.doneLeft_) {
|
|
2784
|
-
state.doneLeft_ = true;
|
|
2785
|
-
var nextState = new Interpreter.State(node.left, state.scope);
|
|
2786
|
-
nextState.components = true;
|
|
2787
|
-
return nextState;
|
|
2788
|
-
}
|
|
2789
|
-
if (!state.doneRight_) {
|
|
2790
|
-
if (!state.leftReference_) {
|
|
2791
|
-
state.leftReference_ = state.value;
|
|
2792
|
-
}
|
|
2793
|
-
if (state.doneGetter_) {
|
|
2794
|
-
state.leftValue_ = state.value;
|
|
2795
|
-
}
|
|
2796
|
-
if (!state.doneGetter_ && node.operator !== '=') {
|
|
2797
|
-
var leftValue = this.getValue(state.leftReference_);
|
|
2798
|
-
state.leftValue_ = leftValue;
|
|
2799
|
-
if (this.getterStep_) {
|
|
2800
|
-
// Call the getter function.
|
|
2801
|
-
state.doneGetter_ = true;
|
|
2802
|
-
var func = /** @type {!Interpreter.Object} */ leftValue;
|
|
2803
|
-
return this.createGetter_(func, state.leftReference_);
|
|
2804
|
-
}
|
|
2805
|
-
}
|
|
2806
|
-
state.doneRight_ = true;
|
|
2807
|
-
// When assigning an unnamed function to a variable, the function's name
|
|
2808
|
-
// is set to the variable name. Record the variable name in case the
|
|
2809
|
-
// right side is a functionExpression.
|
|
2810
|
-
// E.g. foo = function() {};
|
|
2811
|
-
if (node.operator === '=' && node.left.type === 'Identifier') {
|
|
2812
|
-
state.destinationName = node.left.name;
|
|
2813
|
-
}
|
|
2814
|
-
return new Interpreter.State(node.right, state.scope);
|
|
2815
|
-
}
|
|
2816
|
-
if (state.doneSetter_) {
|
|
2817
|
-
// Return if setter function.
|
|
2818
|
-
// Setter method on property has completed.
|
|
2819
|
-
// Ignore its return value, and use the original set value instead.
|
|
2820
|
-
stack.pop();
|
|
2821
|
-
stack[stack.length - 1].value = state.setterValue_;
|
|
2822
|
-
return;
|
|
2823
|
-
}
|
|
2824
|
-
var value = state.leftValue_;
|
|
2825
|
-
var rightValue = state.value;
|
|
2826
|
-
switch (node.operator) {
|
|
2827
|
-
case '=':
|
|
2828
|
-
value = rightValue;
|
|
2829
|
-
break;
|
|
2830
|
-
case '+=':
|
|
2831
|
-
value += rightValue;
|
|
2832
|
-
break;
|
|
2833
|
-
case '-=':
|
|
2834
|
-
value -= rightValue;
|
|
2835
|
-
break;
|
|
2836
|
-
case '*=':
|
|
2837
|
-
value *= rightValue;
|
|
2838
|
-
break;
|
|
2839
|
-
case '/=':
|
|
2840
|
-
value /= rightValue;
|
|
2841
|
-
break;
|
|
2842
|
-
case '%=':
|
|
2843
|
-
value %= rightValue;
|
|
2844
|
-
break;
|
|
2845
|
-
case '<<=':
|
|
2846
|
-
value <<= rightValue;
|
|
2847
|
-
break;
|
|
2848
|
-
case '>>=':
|
|
2849
|
-
value >>= rightValue;
|
|
2850
|
-
break;
|
|
2851
|
-
case '>>>=':
|
|
2852
|
-
value >>>= rightValue;
|
|
2853
|
-
break;
|
|
2854
|
-
case '&=':
|
|
2855
|
-
value &= rightValue;
|
|
2856
|
-
break;
|
|
2857
|
-
case '^=':
|
|
2858
|
-
value ^= rightValue;
|
|
2859
|
-
break;
|
|
2860
|
-
case '|=':
|
|
2861
|
-
value |= rightValue;
|
|
2862
|
-
break;
|
|
2863
|
-
default:
|
|
2864
|
-
throw SyntaxError('Unknown assignment expression: ' + node.operator);
|
|
2865
|
-
}
|
|
2866
|
-
var setter = this.setValue(state.leftReference_, value);
|
|
2867
|
-
if (setter) {
|
|
2868
|
-
state.doneSetter_ = true;
|
|
2869
|
-
state.setterValue_ = value;
|
|
2870
|
-
return this.createSetter_(setter, state.leftReference_, value);
|
|
2871
|
-
}
|
|
2872
|
-
// Return if no setter function.
|
|
2873
|
-
stack.pop();
|
|
2874
|
-
stack[stack.length - 1].value = value;
|
|
2875
|
-
};
|
|
2876
|
-
Interpreter.prototype['stepBinaryExpression'] = function (stack, state, node) {
|
|
2877
|
-
if (!state.doneLeft_) {
|
|
2878
|
-
state.doneLeft_ = true;
|
|
2879
|
-
return new Interpreter.State(node.left, state.scope);
|
|
2880
|
-
}
|
|
2881
|
-
if (!state.doneRight_) {
|
|
2882
|
-
state.doneRight_ = true;
|
|
2883
|
-
state.leftValue_ = state.value;
|
|
2884
|
-
return new Interpreter.State(node.right, state.scope);
|
|
2885
|
-
}
|
|
2886
|
-
stack.pop();
|
|
2887
|
-
var leftValue = state.leftValue_;
|
|
2888
|
-
var rightValue = state.value;
|
|
2889
|
-
var value;
|
|
2890
|
-
switch (node.operator) {
|
|
2891
|
-
case '==':
|
|
2892
|
-
value = leftValue == rightValue;
|
|
2893
|
-
break;
|
|
2894
|
-
case '!=':
|
|
2895
|
-
value = leftValue != rightValue;
|
|
2896
|
-
break;
|
|
2897
|
-
case '===':
|
|
2898
|
-
value = leftValue === rightValue;
|
|
2899
|
-
break;
|
|
2900
|
-
case '!==':
|
|
2901
|
-
value = leftValue !== rightValue;
|
|
2902
|
-
break;
|
|
2903
|
-
case '>':
|
|
2904
|
-
value = leftValue > rightValue;
|
|
2905
|
-
break;
|
|
2906
|
-
case '>=':
|
|
2907
|
-
value = leftValue >= rightValue;
|
|
2908
|
-
break;
|
|
2909
|
-
case '<':
|
|
2910
|
-
value = leftValue < rightValue;
|
|
2911
|
-
break;
|
|
2912
|
-
case '<=':
|
|
2913
|
-
value = leftValue <= rightValue;
|
|
2914
|
-
break;
|
|
2915
|
-
case '+':
|
|
2916
|
-
value = leftValue + rightValue;
|
|
2917
|
-
break;
|
|
2918
|
-
case '-':
|
|
2919
|
-
value = leftValue - rightValue;
|
|
2920
|
-
break;
|
|
2921
|
-
case '*':
|
|
2922
|
-
value = leftValue * rightValue;
|
|
2923
|
-
break;
|
|
2924
|
-
case '/':
|
|
2925
|
-
value = leftValue / rightValue;
|
|
2926
|
-
break;
|
|
2927
|
-
case '%':
|
|
2928
|
-
value = leftValue % rightValue;
|
|
2929
|
-
break;
|
|
2930
|
-
case '&':
|
|
2931
|
-
value = leftValue & rightValue;
|
|
2932
|
-
break;
|
|
2933
|
-
case '|':
|
|
2934
|
-
value = leftValue | rightValue;
|
|
2935
|
-
break;
|
|
2936
|
-
case '^':
|
|
2937
|
-
value = leftValue ^ rightValue;
|
|
2938
|
-
break;
|
|
2939
|
-
case '<<':
|
|
2940
|
-
value = leftValue << rightValue;
|
|
2941
|
-
break;
|
|
2942
|
-
case '>>':
|
|
2943
|
-
value = leftValue >> rightValue;
|
|
2944
|
-
break;
|
|
2945
|
-
case '>>>':
|
|
2946
|
-
value = leftValue >>> rightValue;
|
|
2947
|
-
break;
|
|
2948
|
-
case 'in':
|
|
2949
|
-
if (!(rightValue instanceof Interpreter.Object)) {
|
|
2950
|
-
this.throwException(this.TYPE_ERROR, "'in' expects an object, not '" + rightValue + "'");
|
|
2951
|
-
}
|
|
2952
|
-
value = this.hasProperty(rightValue, leftValue);
|
|
2953
|
-
break;
|
|
2954
|
-
case 'instanceof':
|
|
2955
|
-
if (!this.isa(rightValue, this.FUNCTION)) {
|
|
2956
|
-
this.throwException(this.TYPE_ERROR, "'instanceof' expects an object, not '" + rightValue + "'");
|
|
2957
|
-
}
|
|
2958
|
-
value = leftValue instanceof Interpreter.Object ? this.isa(leftValue, rightValue) : false;
|
|
2959
|
-
break;
|
|
2960
|
-
default:
|
|
2961
|
-
throw SyntaxError('Unknown binary operator: ' + node.operator);
|
|
2962
|
-
}
|
|
2963
|
-
stack[stack.length - 1].value = value;
|
|
2964
|
-
};
|
|
2965
|
-
Interpreter.prototype['stepBlockStatement'] = function (stack, state, node) {
|
|
2966
|
-
var n = state.n_ || 0;
|
|
2967
|
-
var expression = node.body[n];
|
|
2968
|
-
if (expression) {
|
|
2969
|
-
state.n_ = n + 1;
|
|
2970
|
-
return new Interpreter.State(expression, state.scope);
|
|
2971
|
-
}
|
|
2972
|
-
stack.pop();
|
|
2973
|
-
};
|
|
2974
|
-
Interpreter.prototype['stepBreakStatement'] = function (stack, state, node) {
|
|
2975
|
-
var label = node.label && node.label.name;
|
|
2976
|
-
this.unwind(Interpreter.Completion.BREAK, undefined, label);
|
|
2977
|
-
};
|
|
2978
|
-
/**
|
|
2979
|
-
* Number of evals called by the interpreter.
|
|
2980
|
-
* @private
|
|
2981
|
-
*/
|
|
2982
|
-
Interpreter.prototype.evalCodeNumber_ = 0;
|
|
2983
|
-
Interpreter.prototype['stepCallExpression'] = function (stack, state, node) {
|
|
2984
|
-
// Handles both CallExpression and NewExpression.
|
|
2985
|
-
if (!state.doneCallee_) {
|
|
2986
|
-
state.doneCallee_ = 1;
|
|
2987
|
-
// Components needed to determine value of `this`.
|
|
2988
|
-
var nextState = new Interpreter.State(node.callee, state.scope);
|
|
2989
|
-
nextState.components = true;
|
|
2990
|
-
return nextState;
|
|
2991
|
-
}
|
|
2992
|
-
if (state.doneCallee_ === 1) {
|
|
2993
|
-
// Determine value of the function.
|
|
2994
|
-
state.doneCallee_ = 2;
|
|
2995
|
-
var func = state.value;
|
|
2996
|
-
if (Array.isArray(func)) {
|
|
2997
|
-
state.func_ = this.getValue(func);
|
|
2998
|
-
if (func[0] === Interpreter.SCOPE_REFERENCE) {
|
|
2999
|
-
// (Globally or locally) named function. Is it named 'eval'?
|
|
3000
|
-
state.directEval_ = func[1] === 'eval';
|
|
3001
|
-
}
|
|
3002
|
-
else {
|
|
3003
|
-
// Method function, `this` is object (ignored if invoked as `new`).
|
|
3004
|
-
state.funcThis_ = func[0];
|
|
3005
|
-
}
|
|
3006
|
-
func = state.func_;
|
|
3007
|
-
if (this.getterStep_) {
|
|
3008
|
-
// Call the getter function.
|
|
3009
|
-
state.doneCallee_ = 1;
|
|
3010
|
-
return this.createGetter_(/** @type {!Interpreter.Object} */ func, state.value);
|
|
3011
|
-
}
|
|
3012
|
-
}
|
|
3013
|
-
else {
|
|
3014
|
-
// Already evaluated function: (function(){...})();
|
|
3015
|
-
state.func_ = func;
|
|
3016
|
-
}
|
|
3017
|
-
state.arguments_ = [];
|
|
3018
|
-
state.n_ = 0;
|
|
3019
|
-
}
|
|
3020
|
-
var func = state.func_;
|
|
3021
|
-
if (!state.doneArgs_) {
|
|
3022
|
-
if (state.n_ !== 0) {
|
|
3023
|
-
state.arguments_.push(state.value);
|
|
3024
|
-
}
|
|
3025
|
-
if (node.arguments[state.n_]) {
|
|
3026
|
-
return new Interpreter.State(node.arguments[state.n_++], state.scope);
|
|
3027
|
-
}
|
|
3028
|
-
// Determine value of `this` in function.
|
|
3029
|
-
if (node.type === 'NewExpression') {
|
|
3030
|
-
if (!(func instanceof Interpreter.Object) || func.illegalConstructor) {
|
|
3031
|
-
// Illegal: new escape();
|
|
3032
|
-
this.throwException(this.TYPE_ERROR, this.nodeSummary(node.callee) + ' is not a constructor');
|
|
3033
|
-
}
|
|
3034
|
-
// Constructor, `this` is new object.
|
|
3035
|
-
if (func === this.ARRAY) {
|
|
3036
|
-
state.funcThis_ = this.createArray();
|
|
3037
|
-
}
|
|
3038
|
-
else {
|
|
3039
|
-
var proto = func.properties['prototype'];
|
|
3040
|
-
if (typeof proto !== 'object' || proto === null) {
|
|
3041
|
-
// Non-object prototypes default to `Object.prototype`.
|
|
3042
|
-
proto = this.OBJECT_PROTO;
|
|
3043
|
-
}
|
|
3044
|
-
state.funcThis_ = this.createObjectProto(proto);
|
|
3045
|
-
}
|
|
3046
|
-
state.isConstructor = true;
|
|
3047
|
-
}
|
|
3048
|
-
state.doneArgs_ = true;
|
|
3049
|
-
}
|
|
3050
|
-
if (!state.doneExec_) {
|
|
3051
|
-
state.doneExec_ = true;
|
|
3052
|
-
if (!(func instanceof Interpreter.Object)) {
|
|
3053
|
-
this.throwException(this.TYPE_ERROR, this.nodeSummary(node.callee) + ' is not a function');
|
|
3054
|
-
}
|
|
3055
|
-
var funcNode = func.node;
|
|
3056
|
-
if (funcNode) {
|
|
3057
|
-
var scope = this.createScope(funcNode.body, func.parentScope);
|
|
3058
|
-
// Build arguments variable.
|
|
3059
|
-
var argsList = this.createArray();
|
|
3060
|
-
for (var i = 0; i < state.arguments_.length; i++) {
|
|
3061
|
-
this.setProperty(argsList, i, state.arguments_[i]);
|
|
3062
|
-
}
|
|
3063
|
-
this.setProperty(scope.object, 'arguments', argsList);
|
|
3064
|
-
// Add all arguments (may clobber 'arguments' if a param is named such).
|
|
3065
|
-
for (var i = 0; i < funcNode.params.length; i++) {
|
|
3066
|
-
var paramName = funcNode.params[i].name;
|
|
3067
|
-
var paramValue = state.arguments_.length > i ? state.arguments_[i] : undefined;
|
|
3068
|
-
this.setProperty(scope.object, paramName, paramValue);
|
|
3069
|
-
}
|
|
3070
|
-
if (!scope.strict) {
|
|
3071
|
-
state.funcThis_ = this.boxThis_(state.funcThis_);
|
|
3072
|
-
}
|
|
3073
|
-
this.setProperty(scope.object, 'this', state.funcThis_, Interpreter.READONLY_DESCRIPTOR);
|
|
3074
|
-
state.value = undefined; // Default value if no explicit return.
|
|
3075
|
-
return new Interpreter.State(funcNode.body, scope);
|
|
3076
|
-
}
|
|
3077
|
-
else if (func.eval) {
|
|
3078
|
-
var code = state.arguments_[0];
|
|
3079
|
-
if (typeof code !== 'string') {
|
|
3080
|
-
// JS does not parse String objects:
|
|
3081
|
-
// eval(new String('1 + 1')) -> '1 + 1'
|
|
3082
|
-
state.value = code;
|
|
3083
|
-
}
|
|
3084
|
-
else {
|
|
3085
|
-
try {
|
|
3086
|
-
var ast = this.parse_(String(code), 'eval' + this.evalCodeNumber_++);
|
|
3087
|
-
}
|
|
3088
|
-
catch (e) {
|
|
3089
|
-
// Acorn threw a SyntaxError. Rethrow as a trappable error.
|
|
3090
|
-
this.throwException(this.SYNTAX_ERROR, 'Invalid code: ' + e.message);
|
|
3091
|
-
}
|
|
3092
|
-
var evalNode = this.newNode();
|
|
3093
|
-
evalNode.type = 'EvalProgram_';
|
|
3094
|
-
evalNode.body = ast.body;
|
|
3095
|
-
Interpreter.stripLocations_(evalNode, node.start, node.end);
|
|
3096
|
-
// Create new scope and update it with definitions in eval().
|
|
3097
|
-
var scope = state.directEval_ ? state.scope : this.globalScope;
|
|
3098
|
-
if (scope.strict) {
|
|
3099
|
-
// Strict mode get its own scope in eval.
|
|
3100
|
-
scope = this.createScope(ast, scope);
|
|
3101
|
-
}
|
|
3102
|
-
else {
|
|
3103
|
-
// Non-strict mode pollutes the current scope.
|
|
3104
|
-
this.populateScope_(ast, scope);
|
|
3105
|
-
}
|
|
3106
|
-
this.value = undefined; // Default value if no code.
|
|
3107
|
-
return new Interpreter.State(evalNode, scope);
|
|
3108
|
-
}
|
|
3109
|
-
}
|
|
3110
|
-
else if (func.nativeFunc) {
|
|
3111
|
-
if (!state.scope.strict) {
|
|
3112
|
-
state.funcThis_ = this.boxThis_(state.funcThis_);
|
|
3113
|
-
}
|
|
3114
|
-
state.value = func.nativeFunc.apply(state.funcThis_, state.arguments_);
|
|
3115
|
-
}
|
|
3116
|
-
else if (func.asyncFunc) {
|
|
3117
|
-
var thisInterpreter = this;
|
|
3118
|
-
var callback = function (value) {
|
|
3119
|
-
state.value = value;
|
|
3120
|
-
thisInterpreter.paused_ = false;
|
|
3121
|
-
};
|
|
3122
|
-
// Force the argument lengths to match, then append the callback.
|
|
3123
|
-
var argLength = func.asyncFunc.length - 1;
|
|
3124
|
-
var argsWithCallback = state.arguments_.concat(new Array(argLength)).slice(0, argLength);
|
|
3125
|
-
argsWithCallback.push(callback);
|
|
3126
|
-
this.paused_ = true;
|
|
3127
|
-
if (!state.scope.strict) {
|
|
3128
|
-
state.funcThis_ = this.boxThis_(state.funcThis_);
|
|
3129
|
-
}
|
|
3130
|
-
func.asyncFunc.apply(state.funcThis_, argsWithCallback);
|
|
3131
|
-
return;
|
|
3132
|
-
}
|
|
3133
|
-
else {
|
|
3134
|
-
/* A child of a function is a function but is not callable. For example:
|
|
3135
|
-
var F = function() {};
|
|
3136
|
-
F.prototype = escape;
|
|
3137
|
-
var f = new F();
|
|
3138
|
-
f();
|
|
3139
|
-
*/
|
|
3140
|
-
this.throwException(this.TYPE_ERROR, this.nodeSummary(node.callee) + ' is not callable');
|
|
3141
|
-
}
|
|
3142
|
-
}
|
|
3143
|
-
else {
|
|
3144
|
-
// Execution complete. Put the return value on the stack.
|
|
3145
|
-
stack.pop();
|
|
3146
|
-
if (state.isConstructor && typeof state.value !== 'object') {
|
|
3147
|
-
// Normal case for a constructor is to use the `this` value.
|
|
3148
|
-
stack[stack.length - 1].value = state.funcThis_;
|
|
3149
|
-
}
|
|
3150
|
-
else {
|
|
3151
|
-
// Non-constructors or constructions explicitly returning objects use
|
|
3152
|
-
// the return value.
|
|
3153
|
-
stack[stack.length - 1].value = state.value;
|
|
3154
|
-
}
|
|
3155
|
-
}
|
|
3156
|
-
};
|
|
3157
|
-
Interpreter.prototype['stepConditionalExpression'] = function (stack, state, node) {
|
|
3158
|
-
// Handles both ConditionalExpression and IfStatement.
|
|
3159
|
-
var mode = state.mode_ || 0;
|
|
3160
|
-
if (mode === 0) {
|
|
3161
|
-
state.mode_ = 1;
|
|
3162
|
-
return new Interpreter.State(node.test, state.scope);
|
|
3163
|
-
}
|
|
3164
|
-
if (mode === 1) {
|
|
3165
|
-
state.mode_ = 2;
|
|
3166
|
-
var value = Boolean(state.value);
|
|
3167
|
-
if (value && node.consequent) {
|
|
3168
|
-
// Execute `if` block.
|
|
3169
|
-
return new Interpreter.State(node.consequent, state.scope);
|
|
3170
|
-
}
|
|
3171
|
-
else if (!value && node.alternate) {
|
|
3172
|
-
// Execute `else` block.
|
|
3173
|
-
return new Interpreter.State(node.alternate, state.scope);
|
|
3174
|
-
}
|
|
3175
|
-
// eval('1;if(false){2}') -> undefined
|
|
3176
|
-
this.value = undefined;
|
|
3177
|
-
}
|
|
3178
|
-
stack.pop();
|
|
3179
|
-
if (node.type === 'ConditionalExpression') {
|
|
3180
|
-
stack[stack.length - 1].value = state.value;
|
|
3181
|
-
}
|
|
3182
|
-
};
|
|
3183
|
-
Interpreter.prototype['stepContinueStatement'] = function (stack, state, node) {
|
|
3184
|
-
var label = node.label && node.label.name;
|
|
3185
|
-
this.unwind(Interpreter.Completion.CONTINUE, undefined, label);
|
|
3186
|
-
};
|
|
3187
|
-
Interpreter.prototype['stepDebuggerStatement'] = function (stack, state, node) {
|
|
3188
|
-
// Do nothing. May be overridden by developers.
|
|
3189
|
-
stack.pop();
|
|
3190
|
-
};
|
|
3191
|
-
Interpreter.prototype['stepDoWhileStatement'] = function (stack, state, node) {
|
|
3192
|
-
// Handles both DoWhileStatement and WhileStatement.
|
|
3193
|
-
if (node.type === 'DoWhileStatement' && state.test_ === undefined) {
|
|
3194
|
-
// First iteration of do/while executes without checking test.
|
|
3195
|
-
state.value = true;
|
|
3196
|
-
state.test_ = true;
|
|
3197
|
-
}
|
|
3198
|
-
if (!state.test_) {
|
|
3199
|
-
state.test_ = true;
|
|
3200
|
-
return new Interpreter.State(node.test, state.scope);
|
|
3201
|
-
}
|
|
3202
|
-
if (!state.value) {
|
|
3203
|
-
// Done, exit loop.
|
|
3204
|
-
stack.pop();
|
|
3205
|
-
}
|
|
3206
|
-
else if (node.body) {
|
|
3207
|
-
// Execute the body.
|
|
3208
|
-
state.test_ = false;
|
|
3209
|
-
state.isLoop = true;
|
|
3210
|
-
return new Interpreter.State(node.body, state.scope);
|
|
3211
|
-
}
|
|
3212
|
-
};
|
|
3213
|
-
Interpreter.prototype['stepEmptyStatement'] = function (stack, state, node) {
|
|
3214
|
-
stack.pop();
|
|
3215
|
-
};
|
|
3216
|
-
Interpreter.prototype['stepEvalProgram_'] = function (stack, state, node) {
|
|
3217
|
-
var n = state.n_ || 0;
|
|
3218
|
-
var expression = node.body[n];
|
|
3219
|
-
if (expression) {
|
|
3220
|
-
state.n_ = n + 1;
|
|
3221
|
-
return new Interpreter.State(expression, state.scope);
|
|
3222
|
-
}
|
|
3223
|
-
stack.pop();
|
|
3224
|
-
stack[stack.length - 1].value = this.value;
|
|
3225
|
-
};
|
|
3226
|
-
Interpreter.prototype['stepExpressionStatement'] = function (stack, state, node) {
|
|
3227
|
-
if (!state.done_) {
|
|
3228
|
-
this.value = undefined;
|
|
3229
|
-
state.done_ = true;
|
|
3230
|
-
return new Interpreter.State(node.expression, state.scope);
|
|
3231
|
-
}
|
|
3232
|
-
stack.pop();
|
|
3233
|
-
// Save this value to interpreter.value for use as a return value if
|
|
3234
|
-
// this code is inside an eval function.
|
|
3235
|
-
this.value = state.value;
|
|
3236
|
-
};
|
|
3237
|
-
Interpreter.prototype['stepForInStatement'] = function (stack, state, node) {
|
|
3238
|
-
// First, initialize a variable if exists. Only do so once, ever.
|
|
3239
|
-
if (!state.doneInit_) {
|
|
3240
|
-
state.doneInit_ = true;
|
|
3241
|
-
if (node.left.declarations && node.left.declarations[0].init) {
|
|
3242
|
-
if (state.scope.strict) {
|
|
3243
|
-
this.throwException(this.SYNTAX_ERROR, 'for-in loop variable declaration may not have an initializer');
|
|
3244
|
-
}
|
|
3245
|
-
// Variable initialization: for (var x = 4 in y)
|
|
3246
|
-
return new Interpreter.State(node.left, state.scope);
|
|
3247
|
-
}
|
|
3248
|
-
}
|
|
3249
|
-
// Second, look up the object. Only do so once, ever.
|
|
3250
|
-
if (!state.doneObject_) {
|
|
3251
|
-
state.doneObject_ = true;
|
|
3252
|
-
if (!state.variable_) {
|
|
3253
|
-
state.variable_ = state.value;
|
|
3254
|
-
}
|
|
3255
|
-
return new Interpreter.State(node.right, state.scope);
|
|
3256
|
-
}
|
|
3257
|
-
if (!state.isLoop) {
|
|
3258
|
-
// First iteration.
|
|
3259
|
-
state.isLoop = true;
|
|
3260
|
-
state.object_ = state.value;
|
|
3261
|
-
state.visited_ = Object.create(null);
|
|
3262
|
-
}
|
|
3263
|
-
// Third, find the property name for this iteration.
|
|
3264
|
-
if (state.name_ === undefined) {
|
|
3265
|
-
gotPropName: while (true) {
|
|
3266
|
-
if (state.object_ instanceof Interpreter.Object) {
|
|
3267
|
-
if (!state.props_) {
|
|
3268
|
-
state.props_ = Object.getOwnPropertyNames(state.object_.properties);
|
|
3269
|
-
}
|
|
3270
|
-
while (true) {
|
|
3271
|
-
var prop = state.props_.shift();
|
|
3272
|
-
if (prop === undefined) {
|
|
3273
|
-
break; // Reached end of this object's properties.
|
|
3274
|
-
}
|
|
3275
|
-
if (!Object.prototype.hasOwnProperty.call(state.object_.properties, prop)) {
|
|
3276
|
-
continue; // Property has been deleted in the loop.
|
|
3277
|
-
}
|
|
3278
|
-
if (state.visited_[prop]) {
|
|
3279
|
-
continue; // Already seen this property on a child.
|
|
3280
|
-
}
|
|
3281
|
-
state.visited_[prop] = true;
|
|
3282
|
-
if (!Object.prototype.propertyIsEnumerable.call(state.object_.properties, prop)) {
|
|
3283
|
-
continue; // Skip non-enumerable property.
|
|
3284
|
-
}
|
|
3285
|
-
state.name_ = prop;
|
|
3286
|
-
break gotPropName;
|
|
3287
|
-
}
|
|
3288
|
-
}
|
|
3289
|
-
else if (state.object_ !== null && state.object_ !== undefined) {
|
|
3290
|
-
// Primitive value (other than null or undefined).
|
|
3291
|
-
if (!state.props_) {
|
|
3292
|
-
state.props_ = Object.getOwnPropertyNames(state.object_);
|
|
3293
|
-
}
|
|
3294
|
-
while (true) {
|
|
3295
|
-
var prop = state.props_.shift();
|
|
3296
|
-
if (prop === undefined) {
|
|
3297
|
-
break; // Reached end of this value's properties.
|
|
3298
|
-
}
|
|
3299
|
-
state.visited_[prop] = true;
|
|
3300
|
-
if (!Object.prototype.propertyIsEnumerable.call(state.object_, prop)) {
|
|
3301
|
-
continue; // Skip non-enumerable property.
|
|
3302
|
-
}
|
|
3303
|
-
state.name_ = prop;
|
|
3304
|
-
break gotPropName;
|
|
3305
|
-
}
|
|
3306
|
-
}
|
|
3307
|
-
state.object_ = this.getPrototype(state.object_);
|
|
3308
|
-
state.props_ = null;
|
|
3309
|
-
if (state.object_ === null) {
|
|
3310
|
-
// Done, exit loop.
|
|
3311
|
-
stack.pop();
|
|
3312
|
-
return;
|
|
3313
|
-
}
|
|
3314
|
-
}
|
|
3315
|
-
}
|
|
3316
|
-
// Fourth, find the variable
|
|
3317
|
-
if (!state.doneVariable_) {
|
|
3318
|
-
state.doneVariable_ = true;
|
|
3319
|
-
var left = node.left;
|
|
3320
|
-
if (left.type === 'VariableDeclaration') {
|
|
3321
|
-
// Inline variable declaration: for (var x in y)
|
|
3322
|
-
state.variable_ = [Interpreter.SCOPE_REFERENCE, left.declarations[0].id.name];
|
|
3323
|
-
}
|
|
3324
|
-
else {
|
|
3325
|
-
// Arbitrary left side: for (foo().bar in y)
|
|
3326
|
-
state.variable_ = null;
|
|
3327
|
-
var nextState = new Interpreter.State(left, state.scope);
|
|
3328
|
-
nextState.components = true;
|
|
3329
|
-
return nextState;
|
|
3330
|
-
}
|
|
3331
|
-
}
|
|
3332
|
-
if (!state.variable_) {
|
|
3333
|
-
state.variable_ = state.value;
|
|
3334
|
-
}
|
|
3335
|
-
// Fifth, set the variable.
|
|
3336
|
-
if (!state.doneSetter_) {
|
|
3337
|
-
state.doneSetter_ = true;
|
|
3338
|
-
var value = state.name_;
|
|
3339
|
-
var setter = this.setValue(state.variable_, value);
|
|
3340
|
-
if (setter) {
|
|
3341
|
-
return this.createSetter_(setter, state.variable_, value);
|
|
3342
|
-
}
|
|
3343
|
-
}
|
|
3344
|
-
// Next step will be step three.
|
|
3345
|
-
state.name_ = undefined;
|
|
3346
|
-
// Reevaluate the variable since it could be a setter on the global object.
|
|
3347
|
-
state.doneVariable_ = false;
|
|
3348
|
-
state.doneSetter_ = false;
|
|
3349
|
-
// Sixth and finally, execute the body if there was one.
|
|
3350
|
-
if (node.body) {
|
|
3351
|
-
return new Interpreter.State(node.body, state.scope);
|
|
3352
|
-
}
|
|
3353
|
-
};
|
|
3354
|
-
Interpreter.prototype['stepForStatement'] = function (stack, state, node) {
|
|
3355
|
-
switch (state.mode_) {
|
|
3356
|
-
default:
|
|
3357
|
-
state.mode_ = 1;
|
|
3358
|
-
if (node.init) {
|
|
3359
|
-
return new Interpreter.State(node.init, state.scope);
|
|
3360
|
-
}
|
|
3361
|
-
break;
|
|
3362
|
-
case 1:
|
|
3363
|
-
state.mode_ = 2;
|
|
3364
|
-
if (node.test) {
|
|
3365
|
-
return new Interpreter.State(node.test, state.scope);
|
|
3366
|
-
}
|
|
3367
|
-
break;
|
|
3368
|
-
case 2:
|
|
3369
|
-
state.mode_ = 3;
|
|
3370
|
-
if (node.test && !state.value) {
|
|
3371
|
-
// Done, exit loop.
|
|
3372
|
-
stack.pop();
|
|
3373
|
-
}
|
|
3374
|
-
else {
|
|
3375
|
-
// Execute the body.
|
|
3376
|
-
state.isLoop = true;
|
|
3377
|
-
return new Interpreter.State(node.body, state.scope);
|
|
3378
|
-
}
|
|
3379
|
-
break;
|
|
3380
|
-
case 3:
|
|
3381
|
-
state.mode_ = 1;
|
|
3382
|
-
if (node.update) {
|
|
3383
|
-
return new Interpreter.State(node.update, state.scope);
|
|
3384
|
-
}
|
|
3385
|
-
break;
|
|
3386
|
-
}
|
|
3387
|
-
};
|
|
3388
|
-
Interpreter.prototype['stepFunctionDeclaration'] = function (stack, state, node) {
|
|
3389
|
-
// This was found and handled when the scope was populated.
|
|
3390
|
-
stack.pop();
|
|
3391
|
-
};
|
|
3392
|
-
Interpreter.prototype['stepFunctionExpression'] = function (stack, state, node) {
|
|
3393
|
-
stack.pop();
|
|
3394
|
-
state = stack[stack.length - 1];
|
|
3395
|
-
var parentScope = state.scope;
|
|
3396
|
-
if (node.id) {
|
|
3397
|
-
// Create a tiny scope to store the function name.
|
|
3398
|
-
// E.g. var x = function foo(){};
|
|
3399
|
-
parentScope = this.createSpecialScope(parentScope);
|
|
3400
|
-
}
|
|
3401
|
-
state.value = this.createFunction(node, parentScope, state.destinationName);
|
|
3402
|
-
if (node.id) {
|
|
3403
|
-
// Record the function name, read-only.
|
|
3404
|
-
this.setProperty(parentScope.object, node.id.name, state.value, Interpreter.READONLY_DESCRIPTOR);
|
|
3405
|
-
}
|
|
3406
|
-
};
|
|
3407
|
-
Interpreter.prototype['stepIdentifier'] = function (stack, state, node) {
|
|
3408
|
-
stack.pop();
|
|
3409
|
-
if (state.components) {
|
|
3410
|
-
stack[stack.length - 1].value = [Interpreter.SCOPE_REFERENCE, node.name];
|
|
3411
|
-
return;
|
|
3412
|
-
}
|
|
3413
|
-
var value = this.getValueFromScope(node.name);
|
|
3414
|
-
// An identifier could be a getter if it's a property on the global object.
|
|
3415
|
-
if (this.getterStep_) {
|
|
3416
|
-
// Call the getter function.
|
|
3417
|
-
var func = /** @type {!Interpreter.Object} */ value;
|
|
3418
|
-
return this.createGetter_(func, this.globalObject);
|
|
3419
|
-
}
|
|
3420
|
-
stack[stack.length - 1].value = value;
|
|
3421
|
-
};
|
|
3422
|
-
Interpreter.prototype['stepIfStatement'] = Interpreter.prototype['stepConditionalExpression'];
|
|
3423
|
-
Interpreter.prototype['stepLabeledStatement'] = function (stack, state, node) {
|
|
3424
|
-
// No need to hit this node again on the way back up the stack.
|
|
3425
|
-
stack.pop();
|
|
3426
|
-
// Note that a statement might have multiple labels.
|
|
3427
|
-
var labels = state.labels || [];
|
|
3428
|
-
labels.push(node.label.name);
|
|
3429
|
-
var nextState = new Interpreter.State(node.body, state.scope);
|
|
3430
|
-
nextState.labels = labels;
|
|
3431
|
-
return nextState;
|
|
3432
|
-
};
|
|
3433
|
-
Interpreter.prototype['stepLiteral'] = function (stack, state, node) {
|
|
3434
|
-
stack.pop();
|
|
3435
|
-
var value = node.value;
|
|
3436
|
-
if (value instanceof RegExp) {
|
|
3437
|
-
var pseudoRegexp = this.createObjectProto(this.REGEXP_PROTO);
|
|
3438
|
-
this.populateRegExp(pseudoRegexp, value);
|
|
3439
|
-
value = pseudoRegexp;
|
|
3440
|
-
}
|
|
3441
|
-
stack[stack.length - 1].value = value;
|
|
3442
|
-
};
|
|
3443
|
-
Interpreter.prototype['stepLogicalExpression'] = function (stack, state, node) {
|
|
3444
|
-
if (node.operator !== '&&' && node.operator !== '||') {
|
|
3445
|
-
throw SyntaxError('Unknown logical operator: ' + node.operator);
|
|
3446
|
-
}
|
|
3447
|
-
if (!state.doneLeft_) {
|
|
3448
|
-
state.doneLeft_ = true;
|
|
3449
|
-
return new Interpreter.State(node.left, state.scope);
|
|
3450
|
-
}
|
|
3451
|
-
if (!state.doneRight_) {
|
|
3452
|
-
if (node.operator === '&&' && !state.value || node.operator === '||' && state.value) {
|
|
3453
|
-
// Shortcut evaluation.
|
|
3454
|
-
stack.pop();
|
|
3455
|
-
stack[stack.length - 1].value = state.value;
|
|
3456
|
-
}
|
|
3457
|
-
else {
|
|
3458
|
-
state.doneRight_ = true;
|
|
3459
|
-
return new Interpreter.State(node.right, state.scope);
|
|
3460
|
-
}
|
|
3461
|
-
}
|
|
3462
|
-
else {
|
|
3463
|
-
stack.pop();
|
|
3464
|
-
stack[stack.length - 1].value = state.value;
|
|
3465
|
-
}
|
|
3466
|
-
};
|
|
3467
|
-
Interpreter.prototype['stepMemberExpression'] = function (stack, state, node) {
|
|
3468
|
-
if (!state.doneObject_) {
|
|
3469
|
-
state.doneObject_ = true;
|
|
3470
|
-
return new Interpreter.State(node.object, state.scope);
|
|
3471
|
-
}
|
|
3472
|
-
var propName;
|
|
3473
|
-
if (!node.computed) {
|
|
3474
|
-
state.object_ = state.value;
|
|
3475
|
-
// obj.foo -- Just access `foo` directly.
|
|
3476
|
-
propName = node.property.name;
|
|
3477
|
-
}
|
|
3478
|
-
else if (!state.doneProperty_) {
|
|
3479
|
-
state.object_ = state.value;
|
|
3480
|
-
// obj[foo] -- Compute value of `foo`.
|
|
3481
|
-
state.doneProperty_ = true;
|
|
3482
|
-
return new Interpreter.State(node.property, state.scope);
|
|
3483
|
-
}
|
|
3484
|
-
else {
|
|
3485
|
-
propName = state.value;
|
|
3486
|
-
}
|
|
3487
|
-
stack.pop();
|
|
3488
|
-
if (state.components) {
|
|
3489
|
-
stack[stack.length - 1].value = [state.object_, propName];
|
|
3490
|
-
}
|
|
3491
|
-
else {
|
|
3492
|
-
var value = this.getProperty(state.object_, propName);
|
|
3493
|
-
if (this.getterStep_) {
|
|
3494
|
-
// Call the getter function.
|
|
3495
|
-
var func = /** @type {!Interpreter.Object} */ value;
|
|
3496
|
-
return this.createGetter_(func, state.object_);
|
|
3497
|
-
}
|
|
3498
|
-
stack[stack.length - 1].value = value;
|
|
3499
|
-
}
|
|
3500
|
-
};
|
|
3501
|
-
Interpreter.prototype['stepNewExpression'] = Interpreter.prototype['stepCallExpression'];
|
|
3502
|
-
Interpreter.prototype['stepObjectExpression'] = function (stack, state, node) {
|
|
3503
|
-
var n = state.n_ || 0;
|
|
3504
|
-
var property = node.properties[n];
|
|
3505
|
-
if (!state.object_) {
|
|
3506
|
-
// First execution.
|
|
3507
|
-
state.object_ = this.createObjectProto(this.OBJECT_PROTO);
|
|
3508
|
-
state.properties_ = Object.create(null);
|
|
3509
|
-
}
|
|
3510
|
-
else {
|
|
3511
|
-
// Set the property computed in the previous execution.
|
|
3512
|
-
var propName = state.destinationName;
|
|
3513
|
-
if (!state.properties_[propName]) {
|
|
3514
|
-
// Create temp object to collect value, getter, and/or setter.
|
|
3515
|
-
state.properties_[propName] = {};
|
|
3516
|
-
}
|
|
3517
|
-
state.properties_[propName][property.kind] = state.value;
|
|
3518
|
-
state.n_ = ++n;
|
|
3519
|
-
property = node.properties[n];
|
|
3520
|
-
}
|
|
3521
|
-
if (property) {
|
|
3522
|
-
// Determine property name.
|
|
3523
|
-
var key = property.key;
|
|
3524
|
-
if (key.type === 'Identifier') {
|
|
3525
|
-
var propName = key.name;
|
|
3526
|
-
}
|
|
3527
|
-
else if (key.type === 'Literal') {
|
|
3528
|
-
var propName = key.value;
|
|
3529
|
-
}
|
|
3530
|
-
else {
|
|
3531
|
-
throw SyntaxError('Unknown object structure: ' + key.type);
|
|
3532
|
-
}
|
|
3533
|
-
// When assigning an unnamed function to a property, the function's name
|
|
3534
|
-
// is set to the property name. Record the property name in case the
|
|
3535
|
-
// value is a functionExpression.
|
|
3536
|
-
// E.g. {foo: function() {}}
|
|
3537
|
-
state.destinationName = propName;
|
|
3538
|
-
return new Interpreter.State(property.value, state.scope);
|
|
3539
|
-
}
|
|
3540
|
-
for (var key in state.properties_) {
|
|
3541
|
-
var kinds = state.properties_[key];
|
|
3542
|
-
if ('get' in kinds || 'set' in kinds) {
|
|
3543
|
-
// Set a property with a getter or setter.
|
|
3544
|
-
var descriptor = {
|
|
3545
|
-
configurable: true,
|
|
3546
|
-
enumerable: true,
|
|
3547
|
-
get: kinds['get'],
|
|
3548
|
-
set: kinds['set']
|
|
3549
|
-
};
|
|
3550
|
-
this.setProperty(state.object_, key, Interpreter.VALUE_IN_DESCRIPTOR, descriptor);
|
|
3551
|
-
}
|
|
3552
|
-
else {
|
|
3553
|
-
// Set a normal property with a value.
|
|
3554
|
-
this.setProperty(state.object_, key, kinds['init']);
|
|
3555
|
-
}
|
|
3556
|
-
}
|
|
3557
|
-
stack.pop();
|
|
3558
|
-
stack[stack.length - 1].value = state.object_;
|
|
3559
|
-
};
|
|
3560
|
-
Interpreter.prototype['stepProgram'] = function (stack, state, node) {
|
|
3561
|
-
var expression = node.body.shift();
|
|
3562
|
-
if (expression) {
|
|
3563
|
-
state.done = false;
|
|
3564
|
-
return new Interpreter.State(expression, state.scope);
|
|
3565
|
-
}
|
|
3566
|
-
state.done = true;
|
|
3567
|
-
// Don't pop the stateStack.
|
|
3568
|
-
// Leave the root scope on the tree in case the program is appended to.
|
|
3569
|
-
};
|
|
3570
|
-
Interpreter.prototype['stepReturnStatement'] = function (stack, state, node) {
|
|
3571
|
-
if (node.argument && !state.done_) {
|
|
3572
|
-
state.done_ = true;
|
|
3573
|
-
return new Interpreter.State(node.argument, state.scope);
|
|
3574
|
-
}
|
|
3575
|
-
this.unwind(Interpreter.Completion.RETURN, state.value, undefined);
|
|
3576
|
-
};
|
|
3577
|
-
Interpreter.prototype['stepSequenceExpression'] = function (stack, state, node) {
|
|
3578
|
-
var n = state.n_ || 0;
|
|
3579
|
-
var expression = node.expressions[n];
|
|
3580
|
-
if (expression) {
|
|
3581
|
-
state.n_ = n + 1;
|
|
3582
|
-
return new Interpreter.State(expression, state.scope);
|
|
3583
|
-
}
|
|
3584
|
-
stack.pop();
|
|
3585
|
-
stack[stack.length - 1].value = state.value;
|
|
3586
|
-
};
|
|
3587
|
-
Interpreter.prototype['stepSwitchStatement'] = function (stack, state, node) {
|
|
3588
|
-
if (!state.test_) {
|
|
3589
|
-
state.test_ = 1;
|
|
3590
|
-
return new Interpreter.State(node.discriminant, state.scope);
|
|
3591
|
-
}
|
|
3592
|
-
if (state.test_ === 1) {
|
|
3593
|
-
state.test_ = 2;
|
|
3594
|
-
// Preserve switch value between case tests.
|
|
3595
|
-
state.switchValue_ = state.value;
|
|
3596
|
-
state.defaultCase_ = -1;
|
|
3597
|
-
}
|
|
3598
|
-
while (true) {
|
|
3599
|
-
var index = state.index_ || 0;
|
|
3600
|
-
var switchCase = node.cases[index];
|
|
3601
|
-
if (!state.matched_ && switchCase && !switchCase.test) {
|
|
3602
|
-
// Test on the default case is null.
|
|
3603
|
-
// Bypass (but store) the default case, and get back to it later.
|
|
3604
|
-
state.defaultCase_ = index;
|
|
3605
|
-
state.index_ = index + 1;
|
|
3606
|
-
continue;
|
|
3607
|
-
}
|
|
3608
|
-
if (!switchCase && !state.matched_ && state.defaultCase_ !== -1) {
|
|
3609
|
-
// Ran through all cases, no match. Jump to the default.
|
|
3610
|
-
state.matched_ = true;
|
|
3611
|
-
state.index_ = state.defaultCase_;
|
|
3612
|
-
continue;
|
|
3613
|
-
}
|
|
3614
|
-
if (switchCase) {
|
|
3615
|
-
if (!state.matched_ && !state.tested_ && switchCase.test) {
|
|
3616
|
-
state.tested_ = true;
|
|
3617
|
-
return new Interpreter.State(switchCase.test, state.scope);
|
|
3618
|
-
}
|
|
3619
|
-
if (state.matched_ || state.value === state.switchValue_) {
|
|
3620
|
-
state.matched_ = true;
|
|
3621
|
-
var n = state.n_ || 0;
|
|
3622
|
-
if (switchCase.consequent[n]) {
|
|
3623
|
-
state.isSwitch = true;
|
|
3624
|
-
state.n_ = n + 1;
|
|
3625
|
-
return new Interpreter.State(switchCase.consequent[n], state.scope);
|
|
3626
|
-
}
|
|
3627
|
-
}
|
|
3628
|
-
// Move on to next case.
|
|
3629
|
-
state.tested_ = false;
|
|
3630
|
-
state.n_ = 0;
|
|
3631
|
-
state.index_ = index + 1;
|
|
3632
|
-
}
|
|
3633
|
-
else {
|
|
3634
|
-
stack.pop();
|
|
3635
|
-
return;
|
|
3636
|
-
}
|
|
3637
|
-
}
|
|
3638
|
-
};
|
|
3639
|
-
Interpreter.prototype['stepThisExpression'] = function (stack, state, node) {
|
|
3640
|
-
stack.pop();
|
|
3641
|
-
stack[stack.length - 1].value = this.getValueFromScope('this');
|
|
3642
|
-
};
|
|
3643
|
-
Interpreter.prototype['stepThrowStatement'] = function (stack, state, node) {
|
|
3644
|
-
if (!state.done_) {
|
|
3645
|
-
state.done_ = true;
|
|
3646
|
-
return new Interpreter.State(node.argument, state.scope);
|
|
3647
|
-
}
|
|
3648
|
-
else {
|
|
3649
|
-
this.throwException(state.value);
|
|
3650
|
-
}
|
|
3651
|
-
};
|
|
3652
|
-
Interpreter.prototype['stepTryStatement'] = function (stack, state, node) {
|
|
3653
|
-
// This step also handles all CatchClause nodes, since these nodes can
|
|
3654
|
-
// only appear inside the `handler` property of a TryStatement node.
|
|
3655
|
-
if (!state.doneBlock_) {
|
|
3656
|
-
state.doneBlock_ = true;
|
|
3657
|
-
return new Interpreter.State(node.block, state.scope);
|
|
3658
|
-
}
|
|
3659
|
-
if (state.cv && state.cv.type === Interpreter.Completion.THROW && !state.doneHandler_ && node.handler) {
|
|
3660
|
-
state.doneHandler_ = true;
|
|
3661
|
-
// Create an new scope and add the error variable.
|
|
3662
|
-
var scope = this.createSpecialScope(state.scope);
|
|
3663
|
-
this.setProperty(scope.object, node.handler.param.name, state.cv.value);
|
|
3664
|
-
state.cv = undefined; // This error has been handled, don't rethrow.
|
|
3665
|
-
// Execute catch clause.
|
|
3666
|
-
return new Interpreter.State(node.handler.body, scope);
|
|
3667
|
-
}
|
|
3668
|
-
if (!state.doneFinalizer_ && node.finalizer) {
|
|
3669
|
-
state.doneFinalizer_ = true;
|
|
3670
|
-
return new Interpreter.State(node.finalizer, state.scope);
|
|
3671
|
-
}
|
|
3672
|
-
stack.pop();
|
|
3673
|
-
if (state.cv) {
|
|
3674
|
-
// There was no catch handler, or the catch/finally threw an error.
|
|
3675
|
-
// Throw the error up to a higher try.
|
|
3676
|
-
this.unwind(state.cv.type, state.cv.value, state.cv.label);
|
|
3677
|
-
}
|
|
3678
|
-
};
|
|
3679
|
-
Interpreter.prototype['stepUnaryExpression'] = function (stack, state, node) {
|
|
3680
|
-
if (!state.done_) {
|
|
3681
|
-
state.done_ = true;
|
|
3682
|
-
var nextState = new Interpreter.State(node.argument, state.scope);
|
|
3683
|
-
nextState.components = node.operator === 'delete';
|
|
3684
|
-
return nextState;
|
|
3685
|
-
}
|
|
3686
|
-
stack.pop();
|
|
3687
|
-
var value = state.value;
|
|
3688
|
-
switch (node.operator) {
|
|
3689
|
-
case '-':
|
|
3690
|
-
value = -value;
|
|
3691
|
-
break;
|
|
3692
|
-
case '+':
|
|
3693
|
-
value = +value;
|
|
3694
|
-
break;
|
|
3695
|
-
case '!':
|
|
3696
|
-
value = !value;
|
|
3697
|
-
break;
|
|
3698
|
-
case '~':
|
|
3699
|
-
value = ~value;
|
|
3700
|
-
break;
|
|
3701
|
-
case 'delete':
|
|
3702
|
-
var result = true;
|
|
3703
|
-
// If value is not an array, then it is a primitive, or some other value.
|
|
3704
|
-
// If so, skip the delete and return true.
|
|
3705
|
-
if (Array.isArray(value)) {
|
|
3706
|
-
var obj = value[0];
|
|
3707
|
-
if (obj === Interpreter.SCOPE_REFERENCE) {
|
|
3708
|
-
// `delete foo;` is the same as `delete window.foo;`.
|
|
3709
|
-
obj = state.scope;
|
|
3710
|
-
}
|
|
3711
|
-
var name = String(value[1]);
|
|
3712
|
-
try {
|
|
3713
|
-
delete obj.properties[name];
|
|
3714
|
-
}
|
|
3715
|
-
catch (_e) {
|
|
3716
|
-
if (state.scope.strict) {
|
|
3717
|
-
this.throwException(this.TYPE_ERROR, "Cannot delete property '" + name + "' of '" + obj + "'");
|
|
3718
|
-
}
|
|
3719
|
-
else {
|
|
3720
|
-
result = false;
|
|
3721
|
-
}
|
|
3722
|
-
}
|
|
3723
|
-
}
|
|
3724
|
-
value = result;
|
|
3725
|
-
break;
|
|
3726
|
-
case 'typeof':
|
|
3727
|
-
value = value && value.class === 'Function' ? 'function' : typeof value;
|
|
3728
|
-
break;
|
|
3729
|
-
case 'void':
|
|
3730
|
-
value = undefined;
|
|
3731
|
-
break;
|
|
3732
|
-
default:
|
|
3733
|
-
throw SyntaxError('Unknown unary operator: ' + node.operator);
|
|
3734
|
-
}
|
|
3735
|
-
stack[stack.length - 1].value = value;
|
|
3736
|
-
};
|
|
3737
|
-
Interpreter.prototype['stepUpdateExpression'] = function (stack, state, node) {
|
|
3738
|
-
if (!state.doneLeft_) {
|
|
3739
|
-
state.doneLeft_ = true;
|
|
3740
|
-
var nextState = new Interpreter.State(node.argument, state.scope);
|
|
3741
|
-
nextState.components = true;
|
|
3742
|
-
return nextState;
|
|
3743
|
-
}
|
|
3744
|
-
if (!state.leftSide_) {
|
|
3745
|
-
state.leftSide_ = state.value;
|
|
3746
|
-
}
|
|
3747
|
-
if (state.doneGetter_) {
|
|
3748
|
-
state.leftValue_ = state.value;
|
|
3749
|
-
}
|
|
3750
|
-
if (!state.doneGetter_) {
|
|
3751
|
-
var leftValue = this.getValue(state.leftSide_);
|
|
3752
|
-
state.leftValue_ = leftValue;
|
|
3753
|
-
if (this.getterStep_) {
|
|
3754
|
-
// Call the getter function.
|
|
3755
|
-
state.doneGetter_ = true;
|
|
3756
|
-
var func = /** @type {!Interpreter.Object} */ leftValue;
|
|
3757
|
-
return this.createGetter_(func, state.leftSide_);
|
|
3758
|
-
}
|
|
3759
|
-
}
|
|
3760
|
-
if (state.doneSetter_) {
|
|
3761
|
-
// Return if setter function.
|
|
3762
|
-
// Setter method on property has completed.
|
|
3763
|
-
// Ignore its return value, and use the original set value instead.
|
|
3764
|
-
stack.pop();
|
|
3765
|
-
stack[stack.length - 1].value = state.setterValue_;
|
|
3766
|
-
return;
|
|
3767
|
-
}
|
|
3768
|
-
var leftValue = Number(state.leftValue_);
|
|
3769
|
-
var changeValue;
|
|
3770
|
-
if (node.operator === '++') {
|
|
3771
|
-
changeValue = leftValue + 1;
|
|
3772
|
-
}
|
|
3773
|
-
else if (node.operator === '--') {
|
|
3774
|
-
changeValue = leftValue - 1;
|
|
3775
|
-
}
|
|
3776
|
-
else {
|
|
3777
|
-
throw SyntaxError('Unknown update expression: ' + node.operator);
|
|
3778
|
-
}
|
|
3779
|
-
var returnValue = node.prefix ? changeValue : leftValue;
|
|
3780
|
-
var setter = this.setValue(state.leftSide_, changeValue);
|
|
3781
|
-
if (setter) {
|
|
3782
|
-
state.doneSetter_ = true;
|
|
3783
|
-
state.setterValue_ = returnValue;
|
|
3784
|
-
return this.createSetter_(setter, state.leftSide_, changeValue);
|
|
3785
|
-
}
|
|
3786
|
-
// Return if no setter function.
|
|
3787
|
-
stack.pop();
|
|
3788
|
-
stack[stack.length - 1].value = returnValue;
|
|
3789
|
-
};
|
|
3790
|
-
Interpreter.prototype['stepVariableDeclaration'] = function (stack, state, node) {
|
|
3791
|
-
// This step also handles all VariableDeclarator nodes, since these nodes can
|
|
3792
|
-
// only appear inside the `declarations` array of a VariableDeclaration node.
|
|
3793
|
-
var declarations = node.declarations;
|
|
3794
|
-
var n = state.n_ || 0;
|
|
3795
|
-
var declarationNode = declarations[n];
|
|
3796
|
-
if (state.init_ && declarationNode) {
|
|
3797
|
-
// This setValue call never needs to deal with calling a setter function.
|
|
3798
|
-
// Note that this is setting the init value, not defining the variable.
|
|
3799
|
-
// Variable definition is done when scope is populated.
|
|
3800
|
-
this.setValueToScope(declarationNode.id.name, state.value);
|
|
3801
|
-
state.init_ = false;
|
|
3802
|
-
declarationNode = declarations[++n];
|
|
3803
|
-
}
|
|
3804
|
-
while (declarationNode) {
|
|
3805
|
-
// Skip any declarations that are not initialized. They have already
|
|
3806
|
-
// been defined as undefined in populateScope_.
|
|
3807
|
-
if (declarationNode.init) {
|
|
3808
|
-
state.n_ = n;
|
|
3809
|
-
state.init_ = true;
|
|
3810
|
-
// When assigning an unnamed function to a variable, the function's name
|
|
3811
|
-
// is set to the variable name. Record the variable name in case the
|
|
3812
|
-
// right side is a functionExpression.
|
|
3813
|
-
// E.g. var foo = function() {};
|
|
3814
|
-
state.destinationName = declarationNode.id.name;
|
|
3815
|
-
return new Interpreter.State(declarationNode.init, state.scope);
|
|
3816
|
-
}
|
|
3817
|
-
declarationNode = declarations[++n];
|
|
3818
|
-
}
|
|
3819
|
-
stack.pop();
|
|
3820
|
-
};
|
|
3821
|
-
Interpreter.prototype['stepWithStatement'] = function (stack, state, node) {
|
|
3822
|
-
if (!state.doneObject_) {
|
|
3823
|
-
state.doneObject_ = true;
|
|
3824
|
-
return new Interpreter.State(node.object, state.scope);
|
|
3825
|
-
}
|
|
3826
|
-
stack.pop();
|
|
3827
|
-
var scope = this.createSpecialScope(state.scope, state.value);
|
|
3828
|
-
return new Interpreter.State(node.body, scope);
|
|
3829
|
-
};
|
|
3830
|
-
Interpreter.prototype['stepWhileStatement'] = Interpreter.prototype['stepDoWhileStatement'];
|
|
3831
|
-
// Preserve top-level API functions from being pruned/renamed by JS compilers.
|
|
3832
|
-
// Add others as needed.
|
|
3833
|
-
Interpreter.nativeGlobal['Interpreter'] = Interpreter;
|
|
3834
|
-
Interpreter.prototype['step'] = Interpreter.prototype.step;
|
|
3835
|
-
Interpreter.prototype['run'] = Interpreter.prototype.run;
|
|
3836
|
-
Interpreter.prototype['appendCode'] = Interpreter.prototype.appendCode;
|
|
3837
|
-
Interpreter.prototype['createObject'] = Interpreter.prototype.createObject;
|
|
3838
|
-
Interpreter.prototype['createObjectProto'] = Interpreter.prototype.createObjectProto;
|
|
3839
|
-
Interpreter.prototype['createAsyncFunction'] = Interpreter.prototype.createAsyncFunction;
|
|
3840
|
-
Interpreter.prototype['createNativeFunction'] = Interpreter.prototype.createNativeFunction;
|
|
3841
|
-
Interpreter.prototype['getProperty'] = Interpreter.prototype.getProperty;
|
|
3842
|
-
Interpreter.prototype['setProperty'] = Interpreter.prototype.setProperty;
|
|
3843
|
-
Interpreter.prototype['nativeToPseudo'] = Interpreter.prototype.nativeToPseudo;
|
|
3844
|
-
Interpreter.prototype['pseudoToNative'] = Interpreter.prototype.pseudoToNative;
|
|
3845
|
-
Interpreter.prototype['getGlobalScope'] = Interpreter.prototype.getGlobalScope;
|
|
3846
|
-
Interpreter.prototype['getStateStack'] = Interpreter.prototype.getStateStack;
|
|
3847
|
-
Interpreter.prototype['setStateStack'] = Interpreter.prototype.setStateStack;
|
|
3848
|
-
Interpreter['VALUE_IN_DESCRIPTOR'] = Interpreter.VALUE_IN_DESCRIPTOR;
|
|
3849
|
-
// BUILDER.IO: added acorn.parse import here
|
|
3850
|
-
import { parse } from './acorn';
|
|
3851
|
-
Interpreter.nativeGlobal.acornParse = parse;
|
|
3852
|
-
// BUILDER.IO: exported Interpreter
|
|
3853
|
-
export default Interpreter;
|