• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

jcubic / cortex / 11295358550

11 Oct 2024 03:47PM UTC coverage: 74.887% (+0.2%) from 74.693%
11295358550

push

github

jcubic
add another unit test for regex match

482 of 669 branches covered (72.05%)

Branch coverage included in aggregate %.

3675 of 4882 relevant lines covered (75.28%)

4113.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.89
/interpreter.js
1
/**
1✔
2
 * @license
1✔
3
 * Copyright 2013 Neil Fraser
1✔
4
 * SPDX-License-Identifier: Apache-2.0
1✔
5
 */
1✔
6

1✔
7
/**
1✔
8
 * @fileoverview Interpreting JavaScript in JavaScript.
1✔
9
 * @author interpreter@neil.fraser.name (Neil Fraser)
1✔
10
 */
1✔
11
'use strict';
1✔
12

1✔
13
/**
1✔
14
 * Create a new interpreter.
1✔
15
 * @param {string|!Object} code Raw JavaScript text or AST.
1✔
16
 * @param {Function=} opt_initFunc Optional initialization function.  Used to
1✔
17
 *     define APIs.  When called it is passed the interpreter object and the
1✔
18
 *     global scope object.
1✔
19
 * @constructor
1✔
20
 */
1✔
21
var Interpreter = function(code, opt_initFunc) {
1✔
22
  if (typeof code === 'string') {
47✔
23
    code = this.parse_(code, 'code');
47✔
24
  }
47✔
25
  // Get a handle on Acorn's node_t object.
47✔
26
  var nodeConstructor = code.constructor;
47✔
27
  this.newNode = function() {
47✔
28
    return new nodeConstructor({'options': {}});
48✔
29
  };
47✔
30
  // Clone the root 'Program' node so that the AST may be modified.
47✔
31
  var ast = this.newNode();
47✔
32
  for (var prop in code) {
47✔
33
    ast[prop] = (prop === 'body') ? code[prop].slice() : code[prop];
282✔
34
  }
282✔
35
  this.ast = ast;
47✔
36
  /**
47✔
37
   * Sorted array of setTimeout/setInterval tasks waiting to execute.
47✔
38
   * @type {!Array<!Interpreter.Task>}
47✔
39
   */
47✔
40
  this.tasks = [];
47✔
41
  this.initFunc_ = opt_initFunc;
47✔
42
  /**
47✔
43
   * True if the interpreter is paused while waiting for an async function.
47✔
44
   */
47✔
45
  this.paused_ = false;
47✔
46
  /**
47✔
47
   * Lines of code to execute during startup of Interpreter.
47✔
48
   * @type {!Array<string>|undefined}
47✔
49
   */
47✔
50
  this.polyfills_ = [];
47✔
51
  // Unique identifier for native functions.  Used in serialization.
47✔
52
  this.functionCounter_ = 0;
47✔
53
  // Map node types to our step function names; a property lookup is faster
47✔
54
  // than string concatenation with "step" prefix.
47✔
55
  this.stepFunctions_ = Object.create(null);
47✔
56
  var stepMatch = /^step([A-Z]\w*)$/;
47✔
57
  var m;
47✔
58
  for (var methodName in this) {
47✔
59
    if ((typeof this[methodName] === 'function') &&
5,499✔
60
        (m = methodName.match(stepMatch))) {
5,499✔
61
      this.stepFunctions_[m[1]] = this[methodName].bind(this);
1,739✔
62
    }
1,739✔
63
  }
5,499✔
64
  // Create and initialize the global scope.
47✔
65
  this.globalScope = this.createScope(this.ast, null);
47✔
66
  this.globalObject = this.globalScope.object;
47✔
67
  // Run the polyfills.
47✔
68
  this.ast = this.parse_(this.polyfills_.join('\n'), 'polyfills');
47✔
69
  this.polyfills_ = undefined;  // Allow polyfill strings to garbage collect.
47✔
70
  Interpreter.stripLocations_(this.ast, undefined, undefined);
47✔
71
  var state = new Interpreter.State(this.ast, this.globalScope);
47✔
72
  state.done = false;
47✔
73
  this.stateStack = [state];
47✔
74
  this.run();
47✔
75
  this.value = undefined;
47✔
76
  // Point at the main program.
47✔
77
  this.ast = ast;
47✔
78
  state = new Interpreter.State(this.ast, this.globalScope);
47✔
79
  state.done = false;
47✔
80
  this.stateStack.length = 0;
47✔
81
  this.stateStack[0] = state;
47✔
82
};
47✔
83

1✔
84
/**
1✔
85
 * Completion Value Types.
1✔
86
 * @enum {number}
1✔
87
 */
1✔
88
Interpreter.Completion = {
1✔
89
  NORMAL: 0,
1✔
90
  BREAK: 1,
1✔
91
  CONTINUE: 2,
1✔
92
  RETURN: 3,
1✔
93
  THROW: 4,
1✔
94
};
1✔
95

1✔
96
/**
1✔
97
 * Interpreter status values.
1✔
98
 * @enum {number}
1✔
99
 */
1✔
100
Interpreter.Status = {
1✔
101
  'DONE': 0,
1✔
102
  'STEP': 1,
1✔
103
  'TASK': 2,
1✔
104
  'ASYNC': 3,
1✔
105
};
1✔
106

1✔
107
/**
1✔
108
 * @const {!Object} Configuration used for all Acorn parsing.
1✔
109
 */
1✔
110
Interpreter.PARSE_OPTIONS = {
1✔
111
  locations: true,
1✔
112
  ecmaVersion: 5,  // Needed in the event a version > 0.5.0 of Acorn is used.
1✔
113
};
1✔
114

1✔
115
/**
1✔
116
 * Property descriptor of readonly properties.
1✔
117
 */
1✔
118
Interpreter.READONLY_DESCRIPTOR = {
1✔
119
  'configurable': true,
1✔
120
  'enumerable': true,
1✔
121
  'writable': false,
1✔
122
};
1✔
123

1✔
124
/**
1✔
125
 * Property descriptor of non-enumerable properties.
1✔
126
 */
1✔
127
Interpreter.NONENUMERABLE_DESCRIPTOR = {
1✔
128
  'configurable': true,
1✔
129
  'enumerable': false,
1✔
130
  'writable': true,
1✔
131
};
1✔
132

1✔
133
/**
1✔
134
 * Property descriptor of readonly, non-enumerable properties.
1✔
135
 */
1✔
136
Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR = {
1✔
137
  'configurable': true,
1✔
138
  'enumerable': false,
1✔
139
  'writable': false,
1✔
140
};
1✔
141

1✔
142
/**
1✔
143
 * Property descriptor of non-configurable, readonly, non-enumerable properties.
1✔
144
 * E.g. NaN, Infinity.
1✔
145
 */
1✔
146
Interpreter.NONCONFIGURABLE_READONLY_NONENUMERABLE_DESCRIPTOR = {
1✔
147
  'configurable': false,
1✔
148
  'enumerable': false,
1✔
149
  'writable': false,
1✔
150
};
1✔
151

1✔
152
/**
1✔
153
 * Property descriptor of variables.
1✔
154
 */
1✔
155
Interpreter.VARIABLE_DESCRIPTOR = {
1✔
156
  'configurable': false,
1✔
157
  'enumerable': true,
1✔
158
  'writable': true,
1✔
159
};
1✔
160

1✔
161
/**
1✔
162
 * Unique symbol for indicating that a step has encountered an error, has
1✔
163
 * added it to the stack, and will be thrown within the user's program.
1✔
164
 * When STEP_ERROR is thrown in the JS-Interpreter, the error can be ignored.
1✔
165
 */
1✔
166
Interpreter.STEP_ERROR = {'STEP_ERROR': true};
1✔
167

1✔
168
/**
1✔
169
 * Unique symbol for indicating that a reference is a variable on the scope,
1✔
170
 * not an object property.
1✔
171
 */
1✔
172
Interpreter.SCOPE_REFERENCE = {'SCOPE_REFERENCE': true};
1✔
173

1✔
174
/**
1✔
175
 * Unique symbol for indicating, when used as the value of the value
1✔
176
 * parameter in calls to setProperty and friends, that the value
1✔
177
 * should be taken from the property descriptor instead.
1✔
178
 */
1✔
179
Interpreter.VALUE_IN_DESCRIPTOR = /** @type {!Interpreter.Value} */({
1✔
180
  'VALUE_IN_DESCRIPTOR': true
1✔
181
});
1✔
182

1✔
183
/**
1✔
184
 * Unique symbol for indicating that a RegExp timeout has occurred in a VM.
1✔
185
 */
1✔
186
Interpreter.REGEXP_TIMEOUT = {'REGEXP_TIMEOUT': true};
1✔
187

1✔
188
/**
1✔
189
 * For cycle detection in array to string and error conversion;
1✔
190
 * see spec bug github.com/tc39/ecma262/issues/289
1✔
191
 * Since this is for atomic actions only, it can be a class property.
1✔
192
 */
1✔
193
Interpreter.toStringCycles_ = [];
1✔
194

1✔
195
/**
1✔
196
 * Node's vm module, if loaded and required.
1✔
197
 * @type {Object}
1✔
198
 */
1✔
199
Interpreter.vm = null;
1✔
200

1✔
201
/**
1✔
202
 * Currently executing interpreter.  Needed so Interpreter.Object instances
1✔
203
 * can know their environment.
1✔
204
 * @type {Interpreter}
1✔
205
 */
1✔
206
Interpreter.currentInterpreter_ = null;
1✔
207

1✔
208
/**
1✔
209
 * The global object.  Ideally use `globalThis`.  Failing that try `this` or
1✔
210
 * `window`.  Other options to consider are `self` and `global`.
1✔
211
 * Same logic as in Acorn.
1✔
212
 */
1✔
213
Interpreter.nativeGlobal =
1✔
214
    (typeof globalThis === 'undefined') ? this || window : globalThis;
1!
215

1✔
216
/**
1✔
217
 * Code for executing regular expressions in a thread.
1✔
218
 */
1✔
219
Interpreter.WORKER_CODE = [
1✔
220
  "onmessage = function(e) {",
1✔
221
    "var result;",
1✔
222
    "var data = e.data;",
1✔
223
    "switch (data[0]) {",
1✔
224
      "case 'split':",
1✔
225
        // ['split', string, separator, limit]
1✔
226
        "result = data[1].split(data[2], data[3]);",
1✔
227
        "break;",
1✔
228
      "case 'match':",
1✔
229
        // ['match', string, regexp]
1✔
230
        "result = data[1].match(data[2]);",
1✔
231
        "break;",
1✔
232
      "case 'search':",
1✔
233
        // ['search', string, regexp]
1✔
234
        "result = data[1].search(data[2]);",
1✔
235
        "break;",
1✔
236
      "case 'replace':",
1✔
237
        // ['replace', string, regexp, newSubstr]
1✔
238
        "result = data[1].replace(data[2], data[3]);",
1✔
239
        "break;",
1✔
240
      "case 'exec':",
1✔
241
        // ['exec', regexp, lastIndex, string]
1✔
242
        "var regexp = data[1];",
1✔
243
        "regexp.lastIndex = data[2];",
1✔
244
        "result = [regexp.exec(data[3]), data[1].lastIndex];",
1✔
245
        "break;",
1✔
246
      "default:",
1✔
247
        "throw Error('Unknown RegExp operation: ' + data[0]);",
1✔
248
    "}",
1✔
249
    "postMessage(result);",
1✔
250
    "close();",
1✔
251
  "};"];
1✔
252

1✔
253
/**
1✔
254
 * Is a value a legal integer for an array length?
1✔
255
 * @param {Interpreter.Value} x Value to check.
1✔
256
 * @returns {number} Zero, or a positive integer if the value can be
1✔
257
 *     converted to such.  NaN otherwise.
1✔
258
 */
1✔
259
Interpreter.legalArrayLength = function(x) {
1✔
260
  var n = x >>> 0;
3✔
261
  // Array length must be between 0 and 2^32-1 (inclusive).
3✔
262
  return (n === Number(x)) ? n : NaN;
3✔
263
};
3✔
264

1✔
265
/**
1✔
266
 * Is a value a legal integer for an array index?
1✔
267
 * @param {Interpreter.Value} x Value to check.
1✔
268
 * @returns {number} Zero, or a positive integer if the value can be
1✔
269
 *     converted to such.  NaN otherwise.
1✔
270
 */
1✔
271
Interpreter.legalArrayIndex = function(x) {
1✔
272
  var n = x >>> 0;
9,689✔
273
  // Array index cannot be 2^32-1, otherwise length would be 2^32.
9,689✔
274
  // 0xffffffff is 2^32-1.
9,689✔
275
  return (String(n) === String(x) && n !== 0xffffffff) ? n : NaN;
9,689✔
276
};
9,689✔
277

1✔
278
/**
1✔
279
 * Remove start and end values from AST, or set start and end values to a
1✔
280
 * constant value.  Used to remove highlighting from polyfills and to set
1✔
281
 * highlighting in an eval to cover the entire eval expression.
1✔
282
 * @param {!Object} node AST node.
1✔
283
 * @param {number=} start Starting character of all nodes, or undefined.
1✔
284
 * @param {number=} end Ending character of all nodes, or undefined.
1✔
285
 * @private
1✔
286
 */
1✔
287
Interpreter.stripLocations_ = function(node, start, end) {
1✔
288
  if (start) {
146,029!
289
    node.start = start;
×
290
  } else {
146,029✔
291
    delete node.start;
146,029✔
292
  }
146,029✔
293
  if (end) {
146,029!
294
    node.end = end;
×
295
  } else {
146,029✔
296
    delete node.end;
146,029✔
297
  }
146,029✔
298
  for (var name in node) {
146,029✔
299
    if (name !== 'loc' && node.hasOwnProperty(name)) {
512,253✔
300
      var prop = node[name];
384,460✔
301
      if (prop && typeof prop === 'object') {
384,460✔
302
        Interpreter.stripLocations_(/** @type {!Object} */(prop), start, end);
145,982✔
303
      }
145,982✔
304
    }
384,460✔
305
  }
512,253✔
306
};
146,029✔
307

1✔
308
/**
1✔
309
 * Some pathological regular expressions can take geometric time.
1✔
310
 * Regular expressions are handled in one of three ways:
1✔
311
 * 0 - throw as invalid.
1✔
312
 * 1 - execute natively (risk of unresponsive program).
1✔
313
 * 2 - execute in separate thread (not supported by IE 9).
1✔
314
 */
1✔
315
Interpreter.prototype['REGEXP_MODE'] = 2;
1✔
316

1✔
317
/**
1✔
318
 * If REGEXP_MODE = 2, the length of time (in ms) to allow a RegExp
1✔
319
 * thread to execute before terminating it.
1✔
320
 */
1✔
321
Interpreter.prototype['REGEXP_THREAD_TIMEOUT'] = 1000;
1✔
322

1✔
323
/**
1✔
324
 * Length of time (in ms) to allow a polyfill to run before ending step.
1✔
325
 * If set to 0, polyfills will execute step by step.
1✔
326
 * If set to 1000, polyfills will run for up to a second per step
1✔
327
 * (execution will resume in the polyfill in the next step).
1✔
328
 * If set to Infinity, polyfills will run to completion in a single step.
1✔
329
 */
1✔
330
Interpreter.prototype['POLYFILL_TIMEOUT'] = 1000;
1✔
331

1✔
332
/**
1✔
333
 * Flag indicating that a getter function needs to be called immediately.
1✔
334
 * @private
1✔
335
 */
1✔
336
Interpreter.prototype.getterStep_ = false;
1✔
337

1✔
338
/**
1✔
339
 * Flag indicating that a setter function needs to be called immediately.
1✔
340
 * @private
1✔
341
 */
1✔
342
Interpreter.prototype.setterStep_ = false;
1✔
343

1✔
344
/**
1✔
345
 * Number of code chunks appended to the interpreter.
1✔
346
 * @private
1✔
347
 */
1✔
348
Interpreter.prototype.appendCodeNumber_ = 0;
1✔
349

1✔
350
/**
1✔
351
 * Number of parsed tasks.
1✔
352
 * @private
1✔
353
 */
1✔
354
Interpreter.prototype.taskCodeNumber_ = 0;
1✔
355

1✔
356
/**
1✔
357
 * Parse JavaScript code into an AST using Acorn.
1✔
358
 * @param {string} code Raw JavaScript text.
1✔
359
 * @param {string} sourceFile Name of filename (for stack trace).
1✔
360
 * @returns {!Object} AST.
1✔
361
 * @private
1✔
362
 */
1✔
363
Interpreter.prototype.parse_ = function(code, sourceFile) {
1✔
364
   // Create a new options object, since Acorn will modify this object.
95✔
365
   // Inheritance can't be used since Acorn uses hasOwnProperty.
95✔
366
   // Object.assign can't be used since that's ES6.
95✔
367
   var options = {};
95✔
368
   for (var name in Interpreter.PARSE_OPTIONS) {
95✔
369
     options[name] = Interpreter.PARSE_OPTIONS[name];
190✔
370
   }
190✔
371
   options.sourceFile = sourceFile;
95✔
372
   return Interpreter.nativeGlobal.acorn.parse(code, options);
95✔
373
};
95✔
374

1✔
375
/**
1✔
376
 * Add more code to the interpreter.
1✔
377
 * @param {string|!Object} code Raw JavaScript text or AST.
1✔
378
 */
1✔
379
Interpreter.prototype.appendCode = function(code) {
1✔
380
  var state = this.stateStack[0];
1✔
381
  if (!state || state.node.type !== 'Program') {
1!
382
    throw Error('Expecting original AST to start with a Program node');
×
383
  }
×
384
  if (typeof code === 'string') {
1✔
385
    code = this.parse_(code, 'appendCode' + (this.appendCodeNumber_++));
1✔
386
  }
1✔
387
  if (!code || code.type !== 'Program') {
1!
388
    throw Error('Expecting new AST to start with a Program node');
×
389
  }
×
390
  this.populateScope_(code, state.scope);
1✔
391
  // Append the new program to the old one.
1✔
392
  Array.prototype.push.apply(state.node.body, code.body);
1✔
393
  state.node.body.variableCache_ = null;
1✔
394
  state.done = false;
1✔
395
};
1✔
396

1✔
397
/**
1✔
398
 * Execute one step of the interpreter.
1✔
399
 * @returns {boolean} True if a step was executed, false if no more instructions.
1✔
400
 */
1✔
401
Interpreter.prototype.step = function() {
1✔
402
  var stack = this.stateStack;
1,873✔
403
  var endTime;
1,873✔
404
  do {
1,873✔
405
    var state = stack[stack.length - 1];
237,116✔
406
    if (this.paused_) {
237,116!
407
      // Blocked by an asynchronous function.
×
408
      return true;
×
409
    } else if (!state || (state.node.type === 'Program' && state.done)) {
237,116✔
410
      if (!this.tasks.length) {
87✔
411
        // Main program complete and no queued tasks.  We're done!
86✔
412
        return false;
86✔
413
      }
86✔
414
      state = this.nextTask_();
1✔
415
      if (!state) {
87!
416
        // Main program complete, queued tasks, but nothing to run right now.
×
417
        return true;
×
418
      }
×
419
      // Found a queued task, execute it.
87✔
420
    }
87✔
421
    var node = state.node;
237,030✔
422
    // Record the interpreter in a global property so calls to toString/valueOf
237,030✔
423
    // can execute in the proper context.
237,030✔
424
    var oldInterpreterValue = Interpreter.currentInterpreter_;
237,030✔
425
    Interpreter.currentInterpreter_ = this;
237,030✔
426
    try {
237,030✔
427
      var nextState = this.stepFunctions_[node.type](stack, state, node);
237,030✔
428
    } catch (e) {
237,116✔
429
      // Eat any step errors.  They have been thrown on the stack.
7✔
430
      if (e !== Interpreter.STEP_ERROR) {
7✔
431
        // This is a real error, either in the JS-Interpreter, or an uncaught
7✔
432
        // error in the interpreted code.  Rethrow.
7✔
433
        if (this.value !== e) {
7!
434
          // Uh oh.  Internal error in the JS-Interpreter.
×
435
          this.value = undefined;
×
436
        }
×
437
        throw e;
7✔
438
      }
7✔
439
    } finally {
237,116✔
440
      // Restore to previous value (probably null, maybe nested toString calls).
237,030✔
441
      Interpreter.currentInterpreter_ = oldInterpreterValue;
237,030✔
442
    }
237,030✔
443
    if (nextState) {
237,116✔
444
      stack.push(nextState);
118,423✔
445
    }
118,423✔
446
    if (this.getterStep_) {
237,116!
447
      // Getter from this step was not handled.
×
448
      this.value = undefined;
×
449
      throw Error('Getter not supported in this context');
×
450
    }
×
451
    if (this.setterStep_) {
237,116!
452
      // Setter from this step was not handled.
×
453
      this.value = undefined;
×
454
      throw Error('Setter not supported in this context');
×
455
    }
×
456
    // This may be polyfill code.  Keep executing until we arrive at user code.
237,023✔
457
    if (!endTime && !node.end) {
237,116✔
458
      // Ideally this would be defined at the top of the function, but that
63✔
459
      // wastes time if the step isn't a polyfill.
63✔
460
      endTime = Date.now() + this['POLYFILL_TIMEOUT'];
63✔
461
    }
63✔
462
  } while (!node.end && endTime > Date.now());
1,873✔
463
  return true;
1,873✔
464
};
1,873✔
465

1✔
466
/**
1✔
467
 * Execute the interpreter to program completion.  Vulnerable to infinite loops.
1✔
468
 * @returns {boolean} True if a execution is asynchronously blocked,
1✔
469
 *     false if no more instructions.
1✔
470
 */
1✔
471
Interpreter.prototype.run = function() {
1✔
472
  while (!this.paused_ && this.step()) {}
91✔
473
  return this.paused_;
84✔
474
};
84✔
475

1✔
476
/**
1✔
477
 * Current status of the interpreter.
1✔
478
 * @returns {Interpreter.Status} One of DONE, STEP, TASK, or ASYNC.
1✔
479
 */
1✔
480
Interpreter.prototype.getStatus = function() {
1✔
481
  if (this.paused_) {
×
482
    return Interpreter.Status['ASYNC'];
×
483
  }
×
484
  var stack = this.stateStack;
×
485
  var state = stack[stack.length - 1];
×
486
  if (state && (state.node.type !== 'Program' || !state.done)) {
×
487
    // There's a step ready to execute.
×
488
    return Interpreter.Status['STEP'];
×
489
  }
×
490
  var task = this.tasks[0];
×
491
  if (task) {
×
492
    if (task.time > Date.now()) {
×
493
      // There's a pending task, but it's not ready.
×
494
      return Interpreter.Status['TASK'];
×
495
    }
×
496
    // There's a task ready to execute.
×
497
    return Interpreter.Status['STEP'];
×
498
  }
×
499
  return Interpreter.Status['DONE'];
×
500
};
×
501

1✔
502
/**
1✔
503
 * Initialize the global object with built-in properties and functions.
1✔
504
 * @param {!Interpreter.Object} globalObject Global object.
1✔
505
 */
1✔
506
Interpreter.prototype.initGlobal = function(globalObject) {
1✔
507
  // Initialize uneditable global properties.
47✔
508
  this.setProperty(globalObject, 'NaN', NaN,
47✔
509
      Interpreter.NONCONFIGURABLE_READONLY_NONENUMERABLE_DESCRIPTOR);
47✔
510
  this.setProperty(globalObject, 'Infinity', Infinity,
47✔
511
      Interpreter.NONCONFIGURABLE_READONLY_NONENUMERABLE_DESCRIPTOR);
47✔
512
  this.setProperty(globalObject, 'undefined', undefined,
47✔
513
      Interpreter.NONCONFIGURABLE_READONLY_NONENUMERABLE_DESCRIPTOR);
47✔
514
  this.setProperty(globalObject, 'window', globalObject,
47✔
515
      Interpreter.READONLY_DESCRIPTOR);
47✔
516
  this.setProperty(globalObject, 'this', globalObject,
47✔
517
      Interpreter.NONCONFIGURABLE_READONLY_NONENUMERABLE_DESCRIPTOR);
47✔
518
  this.setProperty(globalObject, 'self', globalObject);  // Editable.
47✔
519

47✔
520
  // Create the objects which will become Object.prototype and
47✔
521
  // Function.prototype, which are needed to bootstrap everything else.
47✔
522
  this.OBJECT_PROTO = new Interpreter.Object(null);
47✔
523
  this.FUNCTION_PROTO = new Interpreter.Object(this.OBJECT_PROTO);
47✔
524
  // Initialize global objects.
47✔
525
  this.initFunction(globalObject);
47✔
526
  this.initObject(globalObject);
47✔
527
  // Unable to set globalObject's parent prior (OBJECT did not exist).
47✔
528
  // Note that in a browser this would be `Window`, whereas in Node.js it would
47✔
529
  // be `Object`.  This interpreter is closer to Node in that it has no DOM.
47✔
530
  globalObject.proto = this.OBJECT_PROTO;
47✔
531
  this.setProperty(globalObject, 'constructor', this.OBJECT,
47✔
532
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
533
  this.initArray(globalObject);
47✔
534
  this.initString(globalObject);
47✔
535
  this.initBoolean(globalObject);
47✔
536
  this.initNumber(globalObject);
47✔
537
  this.initDate(globalObject);
47✔
538
  this.initRegExp(globalObject);
47✔
539
  this.initError(globalObject);
47✔
540
  this.initMath(globalObject);
47✔
541
  this.initJSON(globalObject);
47✔
542

47✔
543
  // Initialize global functions.
47✔
544
  var thisInterpreter = this;
47✔
545
  var wrapper;
47✔
546
  var func = this.createNativeFunction(
47✔
547
      function(_x) {throw EvalError("Can't happen");}, false);
47✔
548
  func.eval = true;
47✔
549
  this.setProperty(globalObject, 'eval', func,
47✔
550
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
551

47✔
552
  this.setProperty(globalObject, 'parseInt',
47✔
553
      this.createNativeFunction(parseInt, false),
47✔
554
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
555
  this.setProperty(globalObject, 'parseFloat',
47✔
556
      this.createNativeFunction(parseFloat, false),
47✔
557
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
558

47✔
559
  this.setProperty(globalObject, 'isNaN',
47✔
560
      this.createNativeFunction(isNaN, false),
47✔
561
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
562

47✔
563
  this.setProperty(globalObject, 'isFinite',
47✔
564
      this.createNativeFunction(isFinite, false),
47✔
565
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
566

47✔
567
  var strFunctions = [
47✔
568
    [escape, 'escape'], [unescape, 'unescape'],
47✔
569
    [decodeURI, 'decodeURI'], [decodeURIComponent, 'decodeURIComponent'],
47✔
570
    [encodeURI, 'encodeURI'], [encodeURIComponent, 'encodeURIComponent']
47✔
571
  ];
47✔
572
  for (var i = 0; i < strFunctions.length; i++) {
47✔
573
    wrapper = (function(nativeFunc) {
282✔
574
      return function(str) {
282✔
575
        try {
×
576
          return nativeFunc(str);
×
577
        } catch (e) {
×
578
          // decodeURI('%xy') will throw an error.  Catch and rethrow.
×
579
          thisInterpreter.throwException(thisInterpreter.URI_ERROR, e.message);
×
580
        }
×
581
      };
282✔
582
    })(strFunctions[i][0]);
282✔
583
    this.setProperty(globalObject, strFunctions[i][1],
282✔
584
        this.createNativeFunction(wrapper, false),
282✔
585
        Interpreter.NONENUMERABLE_DESCRIPTOR);
282✔
586
  }
282✔
587

47✔
588
  wrapper = function setTimeout(var_args) {
47✔
589
    return thisInterpreter.createTask_(false, arguments);
1✔
590
  };
47✔
591
  this.setProperty(globalObject, 'setTimeout',
47✔
592
      this.createNativeFunction(wrapper, false),
47✔
593
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
594

47✔
595
  wrapper = function setInterval(var_args) {
47✔
596
    return thisInterpreter.createTask_(true, arguments);
×
597
  };
47✔
598
  this.setProperty(globalObject, 'setInterval',
47✔
599
      this.createNativeFunction(wrapper, false),
47✔
600
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
601

47✔
602
  wrapper = function clearTimeout(pid) {
47✔
603
    thisInterpreter.deleteTask_(pid);
×
604
  };
47✔
605
  this.setProperty(globalObject, 'clearTimeout',
47✔
606
      this.createNativeFunction(wrapper, false),
47✔
607
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
608

47✔
609
  wrapper = function clearInterval(pid) {
47✔
610
    thisInterpreter.deleteTask_(pid);
×
611
  };
47✔
612
  this.setProperty(globalObject, 'clearInterval',
47✔
613
      this.createNativeFunction(wrapper, false),
47✔
614
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
615

47✔
616
  // Preserve public properties from being pruned/renamed by JS compilers.
47✔
617
  // Add others as needed.
47✔
618
  this['OBJECT'] = this.OBJECT;     this['OBJECT_PROTO'] = this.OBJECT_PROTO;
47✔
619
  this['FUNCTION'] = this.FUNCTION; this['FUNCTION_PROTO'] = this.FUNCTION_PROTO;
47✔
620
  this['ARRAY'] = this.ARRAY;       this['ARRAY_PROTO'] = this.ARRAY_PROTO;
47✔
621
  this['REGEXP'] = this.REGEXP;     this['REGEXP_PROTO'] = this.REGEXP_PROTO;
47✔
622
  this['DATE'] = this.DATE;         this['DATE_PROTO'] = this.DATE_PROTO;
47✔
623

47✔
624
  // Run any user-provided initialization.
47✔
625
  if (this.initFunc_) {
47✔
626
    this.initFunc_(this, globalObject);
32✔
627
  }
32✔
628
};
47✔
629

1✔
630
/**
1✔
631
 * Number of functions created by the interpreter.
1✔
632
 * @private
1✔
633
 */
1✔
634
Interpreter.prototype.functionCodeNumber_ = 0;
1✔
635

1✔
636
/**
1✔
637
 * Initialize the Function class.
1✔
638
 * @param {!Interpreter.Object} globalObject Global object.
1✔
639
 */
1✔
640
Interpreter.prototype.initFunction = function(globalObject) {
1✔
641
  var thisInterpreter = this;
47✔
642
  var wrapper;
47✔
643
  var identifierRegexp = /^[A-Za-z_$][\w$]*$/;
47✔
644
  // Function constructor.
47✔
645
  wrapper = function Function(var_args) {
47✔
646
    if (arguments.length) {
×
647
      var code = String(arguments[arguments.length - 1]);
×
648
    } else {
×
649
      var code = '';
×
650
    }
×
651
    var argsStr = Array.prototype.slice.call(arguments, 0, -1).join(',').trim();
×
652
    if (argsStr) {
×
653
      var args = argsStr.split(/\s*,\s*/);
×
654
      for (var i = 0; i < args.length; i++) {
×
655
        var name = args[i];
×
656
        if (!identifierRegexp.test(name)) {
×
657
          thisInterpreter.throwException(thisInterpreter.SYNTAX_ERROR,
×
658
              'Invalid function argument: ' + name);
×
659
        }
×
660
      }
×
661
      argsStr = args.join(', ');
×
662
    }
×
663
    // Acorn needs to parse code in the context of a function or else `return`
×
664
    // statements will be syntax errors.
×
665
    try {
×
666
      var ast = thisInterpreter.parse_('(function(' + argsStr + ') {' + code + '})',
×
667
          'function' + (thisInterpreter.functionCodeNumber_++));
×
668
    } catch (e) {
×
669
      // Acorn threw a SyntaxError.  Rethrow as a trappable error.
×
670
      thisInterpreter.throwException(thisInterpreter.SYNTAX_ERROR,
×
671
          'Invalid code: ' + e.message);
×
672
    }
×
673
    if (ast.body.length !== 1) {
×
674
      // Function('a', 'return a + 6;}; {alert(1);');
×
675
      thisInterpreter.throwException(thisInterpreter.SYNTAX_ERROR,
×
676
          'Invalid code in function body');
×
677
    }
×
678
    var node = ast.body[0].expression;
×
679
    // Note that if this constructor is called as `new Function()` the function
×
680
    // object created by stepCallExpression and assigned to `this` is discarded.
×
681
    // Interestingly, the scope for constructed functions is the global scope,
×
682
    // even if they were constructed in some other scope.
×
683
    return thisInterpreter.createFunction(node, thisInterpreter.globalScope,
×
684
       'anonymous');
×
685
  };
47✔
686
  this.FUNCTION = this.createNativeFunction(wrapper, true);
47✔
687

47✔
688
  this.setProperty(globalObject, 'Function', this.FUNCTION,
47✔
689
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
690
  // Throw away the created prototype and use the root prototype.
47✔
691
  this.setProperty(this.FUNCTION, 'prototype', this.FUNCTION_PROTO,
47✔
692
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
693

47✔
694
  // Configure Function.prototype.
47✔
695
  this.setProperty(this.FUNCTION_PROTO, 'constructor', this.FUNCTION,
47✔
696
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
697
  this.FUNCTION_PROTO.nativeFunc = function() {};
47✔
698
  this.FUNCTION_PROTO.nativeFunc.id = this.functionCounter_++;
47✔
699
  this.FUNCTION_PROTO.illegalConstructor = true;
47✔
700
  this.setProperty(this.FUNCTION_PROTO, 'length', 0,
47✔
701
      Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
47✔
702
  this.FUNCTION_PROTO.class = 'Function';
47✔
703

47✔
704
  wrapper = function apply_(func, thisArg, args) {
47✔
705
    var state =
1✔
706
        thisInterpreter.stateStack[thisInterpreter.stateStack.length - 1];
1✔
707
    // Rewrite the current CallExpression state to apply a different function.
1✔
708
    // Note: 'func' is provided by the polyfill as a non-standard argument.
1✔
709
    state.func_ = func;
1✔
710
    // Assign the `this` object.
1✔
711
    state.funcThis_ = thisArg;
1✔
712
    // Bind any provided arguments.
1✔
713
    state.arguments_ = [];
1✔
714
    if (args !== null && args !== undefined) {
1✔
715
      if (args instanceof Interpreter.Object) {
1✔
716
        // Convert the pseudo array of args into a native array.
1✔
717
        // The pseudo array's properties object happens to be array-like.
1✔
718
        state.arguments_ = Array.from(args.properties);
1✔
719
      } else {
1!
720
        thisInterpreter.throwException(thisInterpreter.TYPE_ERROR,
×
721
            'CreateListFromArrayLike called on non-object');
×
722
      }
×
723
    }
1✔
724
    state.doneExec_ = false;
1✔
725
  };
47✔
726
  this.setNativeFunctionPrototype(this.FUNCTION, 'apply', wrapper);
47✔
727

47✔
728
  this.polyfills_.push(
47✔
729
/* POLYFILL START */
47✔
730
// Flatten the apply args list to remove any inheritance or getter functions.
47✔
731
"(function() {",
47✔
732
  "var apply_ = Function.prototype.apply;",
47✔
733
  "Function.prototype.apply = function apply(thisArg, args) {",
47✔
734
    "var a2 = [];",
47✔
735
    "for (var i = 0; i < args.length; i++) {",
47✔
736
      "a2[i] = args[i];",
47✔
737
    "}",
47✔
738
    "return apply_(this, thisArg, a2);",  // Note: Non-standard 'this' arg.
47✔
739
  "};",
47✔
740
"})();"
47✔
741
/* POLYFILL END */
47✔
742
);
47✔
743

47✔
744
  wrapper = function call(thisArg /*, var_args */) {
47✔
745
    var state =
1✔
746
        thisInterpreter.stateStack[thisInterpreter.stateStack.length - 1];
1✔
747
    // Rewrite the current CallExpression state to call a different function.
1✔
748
    state.func_ = this;
1✔
749
    // Assign the `this` object.
1✔
750
    state.funcThis_ = thisArg;
1✔
751
    // Bind any provided arguments.
1✔
752
    state.arguments_ = [];
1✔
753
    for (var i = 1; i < arguments.length; i++) {
1✔
754
      state.arguments_.push(arguments[i]);
3✔
755
    }
3✔
756
    state.doneExec_ = false;
1✔
757
  };
47✔
758
  this.setNativeFunctionPrototype(this.FUNCTION, 'call', wrapper);
47✔
759

47✔
760
  this.polyfills_.push(
47✔
761
/* POLYFILL START */
47✔
762
// Polyfill copied from:
47✔
763
// developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind
47✔
764
"Object.defineProperty(Function.prototype, 'bind',",
47✔
765
    "{configurable: true, writable: true, value:",
47✔
766
  "function bind(oThis) {",
47✔
767
    "if (typeof this !== 'function') {",
47✔
768
      "throw TypeError('What is trying to be bound is not callable');",
47✔
769
    "}",
47✔
770
    "var aArgs   = Array.prototype.slice.call(arguments, 1),",
47✔
771
        "fToBind = this,",
47✔
772
        "fNOP    = function() {},",
47✔
773
        "fBound  = function() {",
47✔
774
          "return fToBind.apply(this instanceof fNOP",
47✔
775
                 "? this",
47✔
776
                 ": oThis,",
47✔
777
                 "aArgs.concat(Array.prototype.slice.call(arguments)));",
47✔
778
        "};",
47✔
779
    "if (this.prototype) {",
47✔
780
      "fNOP.prototype = this.prototype;",
47✔
781
    "}",
47✔
782
    "fBound.prototype = new fNOP();",
47✔
783
    "return fBound;",
47✔
784
  "}",
47✔
785
"});",
47✔
786
""
47✔
787
/* POLYFILL END */
47✔
788
);
47✔
789

47✔
790
  // Function has no parent to inherit from, so it needs its own mandatory
47✔
791
  // toString and valueOf functions.
47✔
792
  wrapper = function toString() {
47✔
793
    return String(this);
1✔
794
  };
47✔
795
  this.setNativeFunctionPrototype(this.FUNCTION, 'toString', wrapper);
47✔
796
  this.setProperty(this.FUNCTION, 'toString',
47✔
797
      this.createNativeFunction(wrapper, false),
47✔
798
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
799
  wrapper = function valueOf() {
47✔
800
    return this.valueOf();
×
801
  };
47✔
802
  this.setNativeFunctionPrototype(this.FUNCTION, 'valueOf', wrapper);
47✔
803
  this.setProperty(this.FUNCTION, 'valueOf',
47✔
804
      this.createNativeFunction(wrapper, false),
47✔
805
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
806
};
47✔
807

1✔
808
/**
1✔
809
 * Initialize the Object class.
1✔
810
 * @param {!Interpreter.Object} globalObject Global object.
1✔
811
 */
1✔
812
Interpreter.prototype.initObject = function(globalObject) {
1✔
813
  var thisInterpreter = this;
47✔
814
  var wrapper;
47✔
815
  // Object constructor.
47✔
816
  wrapper = function Object(value) {
47✔
817
    if (value === undefined || value === null) {
6✔
818
      // Create a new object.
2✔
819
      if (thisInterpreter.calledWithNew()) {
2✔
820
        // Called as `new Object()`.
1✔
821
        return this;
1✔
822
      } else {
1✔
823
        // Called as `Object()`.
1✔
824
        return thisInterpreter.createObjectProto(thisInterpreter.OBJECT_PROTO);
1✔
825
      }
1✔
826
    }
2✔
827
    if (!(value instanceof Interpreter.Object)) {
6✔
828
      // Wrap the value as an object.
3✔
829
      var box = thisInterpreter.createObjectProto(
3✔
830
          thisInterpreter.getPrototype(value));
3✔
831
      box.data = value;
3✔
832
      return box;
3✔
833
    }
3✔
834
    // Return the provided object.
1✔
835
    return value;
1✔
836
  };
47✔
837
  this.OBJECT = this.createNativeFunction(wrapper, true);
47✔
838
  // Throw away the created prototype and use the root prototype.
47✔
839
  this.setProperty(this.OBJECT, 'prototype', this.OBJECT_PROTO,
47✔
840
                   Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
841
  this.setProperty(this.OBJECT_PROTO, 'constructor', this.OBJECT,
47✔
842
                   Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
843
  this.setProperty(globalObject, 'Object', this.OBJECT,
47✔
844
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
845

47✔
846
  /**
47✔
847
   * Checks if the provided value is null or undefined.
47✔
848
   * If so, then throw an error in the call stack.
47✔
849
   * @param {Interpreter.Value} value Value to check.
47✔
850
   */
47✔
851
  var throwIfNullUndefined = function(value) {
47✔
852
    if (value === undefined || value === null) {
5✔
853
      thisInterpreter.throwException(thisInterpreter.TYPE_ERROR,
2✔
854
          "Cannot convert '" + value + "' to object");
2✔
855
    }
2✔
856
  };
47✔
857

47✔
858
  // Static methods on Object.
47✔
859
  wrapper = function getOwnPropertyNames(obj) {
47✔
860
    throwIfNullUndefined(obj);
3✔
861
    var props = (obj instanceof Interpreter.Object) ? obj.properties : obj;
3!
862
    return thisInterpreter.nativeToPseudo(Object.getOwnPropertyNames(props));
3✔
863
  };
47✔
864
  this.setProperty(this.OBJECT, 'getOwnPropertyNames',
47✔
865
      this.createNativeFunction(wrapper, false),
47✔
866
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
867

47✔
868
  wrapper = function keys(obj) {
47✔
869
    throwIfNullUndefined(obj);
2✔
870
    if (obj instanceof Interpreter.Object) {
2✔
871
      obj = obj.properties;
2✔
872
    }
2✔
873
    return thisInterpreter.nativeToPseudo(Object.keys(obj));
2✔
874
  };
47✔
875
  this.setProperty(this.OBJECT, 'keys',
47✔
876
      this.createNativeFunction(wrapper, false),
47✔
877
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
878

47✔
879
  wrapper = function create_(proto) {
47✔
880
    // Support for the second argument is the responsibility of a polyfill.
3✔
881
    if (proto === null) {
3✔
882
      return thisInterpreter.createObjectProto(null);
1✔
883
    }
1✔
884
    if (!(proto instanceof Interpreter.Object)) {
3✔
885
      thisInterpreter.throwException(thisInterpreter.TYPE_ERROR,
1✔
886
          'Object prototype may only be an Object or null, not ' + proto);
1✔
887
    }
1✔
888
    return thisInterpreter.createObjectProto(proto);
1✔
889
  };
47✔
890
  this.setProperty(this.OBJECT, 'create',
47✔
891
      this.createNativeFunction(wrapper, false),
47✔
892
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
893

47✔
894
  // Add a polyfill to handle create's second argument.
47✔
895
  this.polyfills_.push(
47✔
896
/* POLYFILL START */
47✔
897
"(function() {",
47✔
898
  "var create_ = Object.create;",
47✔
899
  "Object.create = function create(proto, props) {",
47✔
900
    "var obj = create_(proto);",
47✔
901
    "props && Object.defineProperties(obj, props);",
47✔
902
    "return obj;",
47✔
903
  "};",
47✔
904
"})();",
47✔
905
""
47✔
906
/* POLYFILL END */
47✔
907
);
47✔
908

47✔
909
  wrapper = function defineProperty(obj, prop, descriptor) {
47✔
910
    prop = String(prop);
2,030✔
911
    if (!(obj instanceof Interpreter.Object)) {
2,030!
912
      thisInterpreter.throwException(thisInterpreter.TYPE_ERROR,
×
913
          'Object.defineProperty called on non-object: ' + obj);
×
914
    }
×
915
    if (!(descriptor instanceof Interpreter.Object)) {
2,030!
916
      thisInterpreter.throwException(thisInterpreter.TYPE_ERROR,
×
917
          'Property description must be an object');
×
918
    }
×
919
    if (obj.preventExtensions && !(prop in obj.properties)) {
2,030!
920
      thisInterpreter.throwException(thisInterpreter.TYPE_ERROR,
×
921
          "Can't define property '" + prop + "', object is not extensible");
×
922
    }
×
923
    // The polyfill guarantees no inheritance and no getter functions.
2,030✔
924
    // Therefore the descriptor properties map is the native object needed.
2,030✔
925
    thisInterpreter.setProperty(obj, prop, Interpreter.VALUE_IN_DESCRIPTOR,
2,030✔
926
                                descriptor.properties);
2,030✔
927
    return obj;
2,030✔
928
  };
47✔
929
  this.setProperty(this.OBJECT, 'defineProperty',
47✔
930
      this.createNativeFunction(wrapper, false),
47✔
931
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
932

47✔
933
  this.polyfills_.push(
47✔
934
// Flatten the descriptor to remove any inheritance or getter functions.
47✔
935
/* POLYFILL START */
47✔
936
"(function() {",
47✔
937
  "var defineProperty_ = Object.defineProperty;",
47✔
938
  "Object.defineProperty = function defineProperty(obj, prop, d1) {",
47✔
939
    "var d2 = {};",
47✔
940
    "if ('configurable' in d1) d2.configurable = d1.configurable;",
47✔
941
    "if ('enumerable' in d1) d2.enumerable = d1.enumerable;",
47✔
942
    "if ('writable' in d1) d2.writable = d1.writable;",
47✔
943
    "if ('value' in d1) d2.value = d1.value;",
47✔
944
    "if ('get' in d1) d2.get = d1.get;",
47✔
945
    "if ('set' in d1) d2.set = d1.set;",
47✔
946
    "return defineProperty_(obj, prop, d2);",
47✔
947
  "};",
47✔
948
"})();",
47✔
949

47✔
950
"Object.defineProperty(Object, 'defineProperties',",
47✔
951
    "{configurable: true, writable: true, value:",
47✔
952
  "function defineProperties(obj, props) {",
47✔
953
    "var keys = Object.keys(props);",
47✔
954
    "for (var i = 0; i < keys.length; i++) {",
47✔
955
      "Object.defineProperty(obj, keys[i], props[keys[i]]);",
47✔
956
    "}",
47✔
957
    "return obj;",
47✔
958
  "}",
47✔
959
"});",
47✔
960
""
47✔
961
/* POLYFILL END */
47✔
962
);
47✔
963

47✔
964
  wrapper = function getOwnPropertyDescriptor(obj, prop) {
47✔
965
    if (!(obj instanceof Interpreter.Object)) {
×
966
      thisInterpreter.throwException(thisInterpreter.TYPE_ERROR,
×
967
          'Object.getOwnPropertyDescriptor called on non-object: ' + obj);
×
968
    }
×
969
    prop = String(prop);
×
970
    if (!(prop in obj.properties)) {
×
971
      return undefined;
×
972
    }
×
973
    var descriptor = Object.getOwnPropertyDescriptor(obj.properties, prop);
×
974
    var getter = obj.getter[prop];
×
975
    var setter = obj.setter[prop];
×
976

×
977
    var pseudoDescriptor =
×
978
        thisInterpreter.createObjectProto(thisInterpreter.OBJECT_PROTO);
×
979
    if (getter || setter) {
×
980
      thisInterpreter.setProperty(pseudoDescriptor, 'get', getter);
×
981
      thisInterpreter.setProperty(pseudoDescriptor, 'set', setter);
×
982
    } else {
×
983
      thisInterpreter.setProperty(pseudoDescriptor, 'value',
×
984
          /** @type {!Interpreter.Value} */(descriptor['value']));
×
985
      thisInterpreter.setProperty(pseudoDescriptor, 'writable',
×
986
          descriptor['writable']);
×
987
    }
×
988
    thisInterpreter.setProperty(pseudoDescriptor, 'configurable',
×
989
        descriptor['configurable']);
×
990
    thisInterpreter.setProperty(pseudoDescriptor, 'enumerable',
×
991
        descriptor['enumerable']);
×
992
    return pseudoDescriptor;
×
993
  };
47✔
994
  this.setProperty(this.OBJECT, 'getOwnPropertyDescriptor',
47✔
995
      this.createNativeFunction(wrapper, false),
47✔
996
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
997

47✔
998
  wrapper = function getPrototypeOf(obj) {
47✔
999
    throwIfNullUndefined(obj);
×
1000
    return thisInterpreter.getPrototype(obj);
×
1001
  };
47✔
1002
  this.setProperty(this.OBJECT, 'getPrototypeOf',
47✔
1003
      this.createNativeFunction(wrapper, false),
47✔
1004
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
1005

47✔
1006
  wrapper = function isExtensible(obj) {
47✔
1007
    return Boolean(obj) && !obj.preventExtensions;
×
1008
  };
47✔
1009
  this.setProperty(this.OBJECT, 'isExtensible',
47✔
1010
      this.createNativeFunction(wrapper, false),
47✔
1011
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
1012

47✔
1013
  wrapper = function preventExtensions(obj) {
47✔
1014
    if (obj instanceof Interpreter.Object) {
×
1015
      obj.preventExtensions = true;
×
1016
    }
×
1017
    return obj;
×
1018
  };
47✔
1019
  this.setProperty(this.OBJECT, 'preventExtensions',
47✔
1020
      this.createNativeFunction(wrapper, false),
47✔
1021
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
1022

47✔
1023
  // Instance methods on Object.
47✔
1024
  this.setNativeFunctionPrototype(this.OBJECT, 'toString',
47✔
1025
      Interpreter.Object.prototype.toString);
47✔
1026
  this.setNativeFunctionPrototype(this.OBJECT, 'toLocaleString',
47✔
1027
      Interpreter.Object.prototype.toString);
47✔
1028
  this.setNativeFunctionPrototype(this.OBJECT, 'valueOf',
47✔
1029
      Interpreter.Object.prototype.valueOf);
47✔
1030

47✔
1031
  wrapper = function hasOwnProperty(prop) {
47✔
1032
    throwIfNullUndefined(this);
×
1033
    if (this instanceof Interpreter.Object) {
×
1034
      return String(prop) in this.properties;
×
1035
    }
×
1036
    // Primitive.
×
1037
    return this.hasOwnProperty(prop);
×
1038
  };
47✔
1039
  this.setNativeFunctionPrototype(this.OBJECT, 'hasOwnProperty', wrapper);
47✔
1040

47✔
1041
  wrapper = function propertyIsEnumerable(prop) {
47✔
1042
    throwIfNullUndefined(this);
×
1043
    if (this instanceof Interpreter.Object) {
×
1044
      return Object.prototype.propertyIsEnumerable.call(this.properties, prop);
×
1045
    }
×
1046
    // Primitive.
×
1047
    return this.propertyIsEnumerable(prop);
×
1048
  };
47✔
1049
  this.setNativeFunctionPrototype(this.OBJECT, 'propertyIsEnumerable', wrapper);
47✔
1050

47✔
1051
  wrapper = function isPrototypeOf(obj) {
47✔
1052
    while (true) {
×
1053
      // Note, circular loops shouldn't be possible.
×
1054
      obj = thisInterpreter.getPrototype(obj);
×
1055
      if (!obj) {
×
1056
        // No parent; reached the top.
×
1057
        return false;
×
1058
      }
×
1059
      if (obj === this) {
×
1060
        return true;
×
1061
      }
×
1062
    }
×
1063
  };
47✔
1064
  this.setNativeFunctionPrototype(this.OBJECT, 'isPrototypeOf',  wrapper);
47✔
1065
};
47✔
1066

1✔
1067
/**
1✔
1068
 * Initialize the Array class.
1✔
1069
 * @param {!Interpreter.Object} globalObject Global object.
1✔
1070
 */
1✔
1071
Interpreter.prototype.initArray = function(globalObject) {
1✔
1072
  var thisInterpreter = this;
47✔
1073
  var wrapper;
47✔
1074
  // Array constructor.
47✔
1075
  wrapper = function Array(var_args) {
47✔
1076
    if (thisInterpreter.calledWithNew()) {
9✔
1077
      // Called as `new Array()`.
4✔
1078
      var newArray = this;
4✔
1079
    } else {
9✔
1080
      // Called as `Array()`.
5✔
1081
      var newArray = thisInterpreter.createArray();
5✔
1082
    }
5✔
1083
    var first = arguments[0];
9✔
1084
    if (arguments.length === 1 && typeof first === 'number') {
9✔
1085
      if (isNaN(Interpreter.legalArrayLength(first))) {
3✔
1086
        thisInterpreter.throwException(thisInterpreter.RANGE_ERROR,
1✔
1087
                                       'Invalid array length: ' + first);
1✔
1088
      }
1✔
1089
      newArray.properties.length = first;
2✔
1090
    } else {
9✔
1091
      for (var i = 0; i < arguments.length; i++) {
6✔
1092
        newArray.properties[i] = arguments[i];
18✔
1093
      }
18✔
1094
      newArray.properties.length = i;
6✔
1095
    }
6✔
1096
    return newArray;
8✔
1097
  };
47✔
1098
  this.ARRAY = this.createNativeFunction(wrapper, true);
47✔
1099
  this.ARRAY_PROTO = this.ARRAY.properties['prototype'];
47✔
1100
  this.setProperty(globalObject, 'Array', this.ARRAY,
47✔
1101
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
1102

47✔
1103
  // Static methods on Array.
47✔
1104
  wrapper = function isArray(obj) {
47✔
1105
    return obj && obj.class === 'Array';
8✔
1106
  };
47✔
1107
  this.setProperty(this.ARRAY, 'isArray',
47✔
1108
                   this.createNativeFunction(wrapper, false),
47✔
1109
                   Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
1110

47✔
1111
  // Instance methods on Array.
47✔
1112
  this.setProperty(this.ARRAY_PROTO, 'length', 0,
47✔
1113
      {'configurable': false, 'enumerable': false, 'writable': true});
47✔
1114
  this.ARRAY_PROTO.class = 'Array';
47✔
1115

47✔
1116
  this.polyfills_.push(
47✔
1117
/* POLYFILL START */
47✔
1118
"(function() {",
47✔
1119
  "function createArrayMethod_(name, func) {",
47✔
1120
    "Object.defineProperty(func, 'name', {value: name});",
47✔
1121
    "Object.defineProperty(Array.prototype, name,",
47✔
1122
        "{configurable: true, writable: true, value: func});",
47✔
1123
  "}",
47✔
1124

47✔
1125
  "createArrayMethod_('pop',",
47✔
1126
    "function() {",
47✔
1127
      "if (!this) throw TypeError();",
47✔
1128
      "var o = Object(this), len = o.length >>> 0;",
47✔
1129
      "if (!len || len < 0) {",
47✔
1130
        "o.length = 0;",
47✔
1131
        "return undefined;",
47✔
1132
      "}",
47✔
1133
      "len--;",
47✔
1134
      "var x = o[len];",
47✔
1135
      "delete o[len];",  // Needed for non-arrays.
47✔
1136
      "o.length = len;",
47✔
1137
      "return x;",
47✔
1138
    "}",
47✔
1139
  ");",
47✔
1140

47✔
1141
  "createArrayMethod_('push',",
47✔
1142
    "function(var_args) {",
47✔
1143
      "if (!this) throw TypeError();",
47✔
1144
      "var o = Object(this), len = o.length >>> 0;",
47✔
1145
      "for (var i = 0; i < arguments.length; i++) {",
47✔
1146
        "o[len] = arguments[i];",
47✔
1147
        "len++;",
47✔
1148
      "}",
47✔
1149
      "o.length = len;",
47✔
1150
      "return len;",
47✔
1151
    "}",
47✔
1152
  ");",
47✔
1153

47✔
1154
  "createArrayMethod_('shift',",
47✔
1155
    "function() {",
47✔
1156
      "if (!this) throw TypeError();",
47✔
1157
      "var o = Object(this), len = o.length >>> 0;",
47✔
1158
      "if (!len || len < 0) {",
47✔
1159
        "o.length = 0;",
47✔
1160
        "return undefined;",
47✔
1161
      "}",
47✔
1162
      "var value = o[0];",
47✔
1163
      "for (var i = 0; i < len - 1; i++) {",
47✔
1164
        "if ((i + 1) in o) {",
47✔
1165
          "o[i] = o[i + 1];",
47✔
1166
        "} else {",
47✔
1167
          "delete o[i];",
47✔
1168
        "}",
47✔
1169
      "}",
47✔
1170
      "delete o[i];",  // Needed for non-arrays.
47✔
1171
      "o.length = len - 1;",
47✔
1172
      "return value;",
47✔
1173
    "}",
47✔
1174
  ");",
47✔
1175

47✔
1176
  "createArrayMethod_('unshift',",
47✔
1177
    "function(var_args) {",
47✔
1178
      "if (!this) throw TypeError();",
47✔
1179
      "var o = Object(this), len = o.length >>> 0;",
47✔
1180
      "if (!len || len < 0) {",
47✔
1181
        "len = 0;",
47✔
1182
      "}",
47✔
1183
      "for (var i = len - 1; i >= 0; i--) {",
47✔
1184
        "if (i in o) {",
47✔
1185
          "o[i + arguments.length] = o[i];",
47✔
1186
        "} else {",
47✔
1187
          "delete o[i + arguments.length];",
47✔
1188
        "}",
47✔
1189
      "}",
47✔
1190
      "for (var i = 0; i < arguments.length; i++) {",
47✔
1191
        "o[i] = arguments[i];",
47✔
1192
      "}",
47✔
1193
      "return (o.length = len + arguments.length);",
47✔
1194
    "}",
47✔
1195
  ");",
47✔
1196

47✔
1197
  "createArrayMethod_('reverse',",
47✔
1198
    "function() {",
47✔
1199
      "if (!this) throw TypeError();",
47✔
1200
      "var o = Object(this), len = o.length >>> 0;",
47✔
1201
      "if (!len || len < 2) {",
47✔
1202
        "return o;",  // Not an array, or too short to reverse.
47✔
1203
      "}",
47✔
1204
      "for (var i = 0; i < len / 2 - 0.5; i++) {",
47✔
1205
        "var x = o[i];",
47✔
1206
        "var hasX = i in o;",
47✔
1207
        "if ((len - i - 1) in o) {",
47✔
1208
          "o[i] = o[len - i - 1];",
47✔
1209
        "} else {",
47✔
1210
          "delete o[i];",
47✔
1211
        "}",
47✔
1212
        "if (hasX) {",
47✔
1213
          "o[len - i - 1] = x;",
47✔
1214
        "} else {",
47✔
1215
          "delete o[len - i - 1];",
47✔
1216
        "}",
47✔
1217
      "}",
47✔
1218
      "return o;",
47✔
1219
    "}",
47✔
1220
  ");",
47✔
1221

47✔
1222
  "createArrayMethod_('indexOf',",
47✔
1223
    "function(searchElement, fromIndex) {",
47✔
1224
      "if (!this) throw TypeError();",
47✔
1225
      "var o = Object(this), len = o.length >>> 0;",
47✔
1226
      "var n = fromIndex | 0;",
47✔
1227
      "if (!len || n >= len) {",
47✔
1228
        "return -1;",
47✔
1229
      "}",
47✔
1230
      "var i = Math.max(n >= 0 ? n : len - Math.abs(n), 0);",
47✔
1231
      "while (i < len) {",
47✔
1232
        "if (i in o && o[i] === searchElement) {",
47✔
1233
          "return i;",
47✔
1234
        "}",
47✔
1235
        "i++;",
47✔
1236
      "}",
47✔
1237
      "return -1;",
47✔
1238
    "}",
47✔
1239
  ");",
47✔
1240

47✔
1241
  "createArrayMethod_('lastIndexOf',",
47✔
1242
    "function(searchElement, fromIndex) {",
47✔
1243
      "if (!this) throw TypeError();",
47✔
1244
      "var o = Object(this), len = o.length >>> 0;",
47✔
1245
      "if (!len) {",
47✔
1246
        "return -1;",
47✔
1247
      "}",
47✔
1248
      "var n = len - 1;",
47✔
1249
      "if (arguments.length > 1) {",
47✔
1250
        "n = fromIndex | 0;",
47✔
1251
        "if (n) {",
47✔
1252
          "n = (n > 0 || -1) * Math.floor(Math.abs(n));",
47✔
1253
        "}",
47✔
1254
      "}",
47✔
1255
      "var i = n >= 0 ? Math.min(n, len - 1) : len - Math.abs(n);",
47✔
1256
      "while (i >= 0) {",
47✔
1257
        "if (i in o && o[i] === searchElement) {",
47✔
1258
          "return i;",
47✔
1259
        "}",
47✔
1260
        "i--;",
47✔
1261
      "}",
47✔
1262
      "return -1;",
47✔
1263
    "}",
47✔
1264
  ");",
47✔
1265

47✔
1266
  "createArrayMethod_('slice',",
47✔
1267
    "function(start, end) {",
47✔
1268
      "if (!this) throw TypeError();",
47✔
1269
      "var o = Object(this), len = o.length >>> 0;",
47✔
1270
      // Handle negative value for "start"
47✔
1271
      "start |= 0;",
47✔
1272
      "start = (start >= 0) ? start : Math.max(0, len + start);",
47✔
1273
      // Handle negative value for "end"
47✔
1274
      "if (typeof end !== 'undefined') {",
47✔
1275
        "if (end !== Infinity) {",
47✔
1276
          "end |= 0;",
47✔
1277
        "}",
47✔
1278
        "if (end < 0) {",
47✔
1279
          "end = len + end;",
47✔
1280
        "} else {",
47✔
1281
          "end = Math.min(end, len);",
47✔
1282
        "}",
47✔
1283
      "} else {",
47✔
1284
        "end = len;",
47✔
1285
      "}",
47✔
1286
      "var size = end - start;",
47✔
1287
      "var cloned = new Array(size);",
47✔
1288
      "for (var i = 0; i < size; i++) {",
47✔
1289
        "if ((start + i) in o) {",
47✔
1290
          "cloned[i] = o[start + i];",
47✔
1291
        "}",
47✔
1292
      "}",
47✔
1293
      "return cloned;",
47✔
1294
    "}",
47✔
1295
  ");",
47✔
1296

47✔
1297
  "createArrayMethod_('splice',",
47✔
1298
    "function(start, deleteCount, var_args) {",
47✔
1299
      "if (!this) throw TypeError();",
47✔
1300
      "var o = Object(this), len = o.length >>> 0;",
47✔
1301
      "start |= 0;",
47✔
1302
      "if (start < 0) {",
47✔
1303
        "start = Math.max(len + start, 0);",
47✔
1304
      "} else {",
47✔
1305
        "start = Math.min(start, len);",
47✔
1306
      "}",
47✔
1307
      "if (arguments.length < 2) {",
47✔
1308
        "deleteCount = len - start;",
47✔
1309
      "} else {",
47✔
1310
        "deleteCount |= 0;",
47✔
1311
        "deleteCount = Math.max(0, Math.min(deleteCount, len - start));",
47✔
1312
      "}",
47✔
1313
      "var removed = [];",
47✔
1314
      // Remove specified elements.
47✔
1315
      "for (var i = start; i < start + deleteCount; i++) {",
47✔
1316
        "if (i in o) {",
47✔
1317
          "removed.push(o[i]);",
47✔
1318
        "} else {",
47✔
1319
          "removed.length++;",
47✔
1320
        "}",
47✔
1321
        "if ((i + deleteCount) in o) {",
47✔
1322
          "o[i] = o[i + deleteCount];",
47✔
1323
        "} else {",
47✔
1324
          "delete o[i];",
47✔
1325
        "}",
47✔
1326
      "}",
47✔
1327
      // Move other element to fill the gap.
47✔
1328
      "for (var i = start + deleteCount; i < len - deleteCount; i++) {",
47✔
1329
        "if ((i + deleteCount) in o) {",
47✔
1330
          "o[i] = o[i + deleteCount];",
47✔
1331
        "} else {",
47✔
1332
          "delete o[i];",
47✔
1333
        "}",
47✔
1334
      "}",
47✔
1335
      // Delete superfluous properties.
47✔
1336
      "for (var i = len - deleteCount; i < len; i++) {",
47✔
1337
        "delete o[i];",
47✔
1338
      "}",
47✔
1339
      "len -= deleteCount;",
47✔
1340
      // Insert specified items.
47✔
1341
      "if (arguments.length > 2) {",
47✔
1342
        "var arl = arguments.length - 2;",
47✔
1343
        "for (var i = len - 1; i >= start; i--) {",
47✔
1344
          "if (i in o) {",
47✔
1345
            "o[i + arl] = o[i];",
47✔
1346
          "} else {",
47✔
1347
            "delete o[i + arl];",
47✔
1348
          "}",
47✔
1349
        "}",
47✔
1350
        "len += arl;",
47✔
1351
        "for (var i = 2; i < arguments.length; i++) {",
47✔
1352
          "o[start + i - 2] = arguments[i];",
47✔
1353
        "}",
47✔
1354
      "}",
47✔
1355
      "o.length = len;",
47✔
1356
      "return removed;",
47✔
1357
    "}",
47✔
1358
  ");",
47✔
1359

47✔
1360
  "createArrayMethod_('concat',",
47✔
1361
    "function(var_args) {",
47✔
1362
      "if (!this) throw TypeError();",
47✔
1363
      "var o = Object(this);",
47✔
1364
      "var cloned = [];",
47✔
1365
      "for (var i = -1; i < arguments.length; i++) {",
47✔
1366
        "var value = (i === -1) ? o : arguments[i];",
47✔
1367
        "if (Array.isArray(value)) {",
47✔
1368
          "for (var j = 0, l = value.length; j < l; j++) {",
47✔
1369
            "if (j in value) {",
47✔
1370
              "cloned.push(value[j]);",
47✔
1371
            "} else {",
47✔
1372
              "cloned.length++;",
47✔
1373
            "}",
47✔
1374
          "}",
47✔
1375
        "} else {",
47✔
1376
          "cloned.push(value);",
47✔
1377
        "}",
47✔
1378
      "}",
47✔
1379
      "return cloned;",
47✔
1380
    "}",
47✔
1381
  ");",
47✔
1382

47✔
1383
  "createArrayMethod_('join',",
47✔
1384
    "function(opt_separator) {",
47✔
1385
      "if (!this) throw TypeError();",
47✔
1386
      "var o = Object(this), len = o.length >>> 0;",
47✔
1387
      "var sep = typeof opt_separator === 'undefined' ?",
47✔
1388
          "',' : ('' + opt_separator);",
47✔
1389
      "var str = '';",
47✔
1390
      "for (var i = 0; i < len; i++) {",
47✔
1391
        "if (i && sep) str += sep;",
47✔
1392
        "str += (o[i] === null || o[i] === undefined) ? '' : o[i];",
47✔
1393
      "}",
47✔
1394
      "return str;",
47✔
1395
    "}",
47✔
1396
  ");",
47✔
1397

47✔
1398
  // Polyfill copied from:
47✔
1399
  // developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/every
47✔
1400
  "createArrayMethod_('every',",
47✔
1401
    "function(callback, thisArg) {",
47✔
1402
      "if (!this || typeof callback !== 'function') throw TypeError();",
47✔
1403
      "var t, k = 0;",
47✔
1404
      "var o = Object(this), len = o.length >>> 0;",
47✔
1405
      "if (arguments.length > 1) t = thisArg;",
47✔
1406
      "while (k < len) {",
47✔
1407
        "if (k in o && !callback.call(t, o[k], k, o)) return false;",
47✔
1408
        "k++;",
47✔
1409
      "}",
47✔
1410
      "return true;",
47✔
1411
    "}",
47✔
1412
  ");",
47✔
1413

47✔
1414
  // Polyfill copied from:
47✔
1415
  // developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
47✔
1416
  "createArrayMethod_('filter',",
47✔
1417
    "function(callback, var_args) {",
47✔
1418
      "if (!this || typeof callback !== 'function') throw TypeError();",
47✔
1419
      "var o = Object(this), len = o.length >>> 0;",
47✔
1420
      "var res = [];",
47✔
1421
      "var thisArg = arguments.length >= 2 ? arguments[1] : void 0;",
47✔
1422
      "for (var i = 0; i < len; i++) {",
47✔
1423
        "if (i in o) {",
47✔
1424
          "var val = o[i];",
47✔
1425
          "if (callback.call(thisArg, val, i, o)) res.push(val);",
47✔
1426
        "}",
47✔
1427
      "}",
47✔
1428
      "return res;",
47✔
1429
    "}",
47✔
1430
  ");",
47✔
1431

47✔
1432
  // Polyfill copied from:
47✔
1433
  // developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
47✔
1434
  "createArrayMethod_('forEach',",
47✔
1435
    "function(callback, thisArg) {",
47✔
1436
      "if (!this || typeof callback !== 'function') throw TypeError();",
47✔
1437
      "var t, k = 0;",
47✔
1438
      "var o = Object(this), len = o.length >>> 0;",
47✔
1439
      "if (arguments.length > 1) t = thisArg;",
47✔
1440
      "while (k < len) {",
47✔
1441
        "if (k in o) callback.call(t, o[k], k, o);",
47✔
1442
        "k++;",
47✔
1443
      "}",
47✔
1444
    "}",
47✔
1445
  ");",
47✔
1446

47✔
1447
  // Polyfill copied from:
47✔
1448
  // developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/map
47✔
1449
  "createArrayMethod_('map',",
47✔
1450
    "function(callback, thisArg) {",
47✔
1451
      "if (!this || typeof callback !== 'function') throw TypeError();",
47✔
1452
      "var t, k = 0;",
47✔
1453
      "var o = Object(this), len = o.length >>> 0;",
47✔
1454
      "if (arguments.length > 1) t = thisArg;",
47✔
1455
      "var a = new Array(len);",
47✔
1456
      "while (k < len) {",
47✔
1457
        "if (k in o) a[k] = callback.call(t, o[k], k, o);",
47✔
1458
        "k++;",
47✔
1459
      "}",
47✔
1460
      "return a;",
47✔
1461
    "}",
47✔
1462
  ");",
47✔
1463

47✔
1464
  // Polyfill copied from:
47✔
1465
  // developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
47✔
1466
  "createArrayMethod_('reduce',",
47✔
1467
    "function(callback /*, initialValue*/) {",
47✔
1468
      "if (!this || typeof callback !== 'function') throw TypeError();",
47✔
1469
      "var o = Object(this), len = o.length >>> 0;",
47✔
1470
      "var k = 0, value;",
47✔
1471
      "if (arguments.length === 2) {",
47✔
1472
        "value = arguments[1];",
47✔
1473
      "} else {",
47✔
1474
        "while (k < len && !(k in o)) k++;",
47✔
1475
        "if (k >= len) {",
47✔
1476
          "throw TypeError('Reduce of empty array with no initial value');",
47✔
1477
        "}",
47✔
1478
        "value = o[k++];",
47✔
1479
      "}",
47✔
1480
      "for (; k < len; k++) {",
47✔
1481
        "if (k in o) value = callback(value, o[k], k, o);",
47✔
1482
      "}",
47✔
1483
      "return value;",
47✔
1484
    "}",
47✔
1485
  ");",
47✔
1486

47✔
1487
  // Polyfill copied from:
47✔
1488
  // developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/ReduceRight
47✔
1489
  "createArrayMethod_('reduceRight',",
47✔
1490
    "function(callback /*, initialValue*/) {",
47✔
1491
      "if (!this || typeof callback !== 'function') throw TypeError();",
47✔
1492
      "var o = Object(this), len = o.length >>> 0;",
47✔
1493
      "var k = len - 1, value;",
47✔
1494
      "if (arguments.length >= 2) {",
47✔
1495
        "value = arguments[1];",
47✔
1496
      "} else {",
47✔
1497
        "while (k >= 0 && !(k in o)) k--;",
47✔
1498
        "if (k < 0) {",
47✔
1499
          "throw TypeError('Reduce of empty array with no initial value');",
47✔
1500
        "}",
47✔
1501
        "value = o[k--];",
47✔
1502
      "}",
47✔
1503
      "for (; k >= 0; k--) {",
47✔
1504
        "if (k in o) value = callback(value, o[k], k, o);",
47✔
1505
      "}",
47✔
1506
      "return value;",
47✔
1507
    "}",
47✔
1508
  ");",
47✔
1509

47✔
1510
  // Polyfill copied from:
47✔
1511
  // developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/some
47✔
1512
  "createArrayMethod_('some',",
47✔
1513
    "function(callback /*, thisArg*/) {",
47✔
1514
      "if (!this || typeof callback !== 'function') throw TypeError();",
47✔
1515
      "var o = Object(this), len = o.length >>> 0;",
47✔
1516
      "var thisArg = arguments.length >= 2 ? arguments[1] : void 0;",
47✔
1517
      "for (var i = 0; i < len; i++) {",
47✔
1518
        "if (i in o && callback.call(thisArg, o[i], i, o)) return true;",
47✔
1519
      "}",
47✔
1520
      "return false;",
47✔
1521
    "}",
47✔
1522
  ");",
47✔
1523

47✔
1524
  "createArrayMethod_('sort',",
47✔
1525
    "function(opt_comp) {",  // Bubble sort!
47✔
1526
      "if (!this) throw TypeError();",
47✔
1527
      "if (typeof opt_comp !== 'function') {",
47✔
1528
        "opt_comp = undefined;",
47✔
1529
      "}",
47✔
1530
      "for (var i = 0; i < this.length; i++) {",
47✔
1531
        "var changes = 0;",
47✔
1532
        "for (var j = 0; j < this.length - i - 1; j++) {",
47✔
1533
          "if (opt_comp ? (opt_comp(this[j], this[j + 1]) > 0) :",
47✔
1534
              "(String(this[j]) > String(this[j + 1]))) {",
47✔
1535
            "var swap = this[j];",
47✔
1536
            "var hasSwap = j in this;",
47✔
1537
            "if ((j + 1) in this) {",
47✔
1538
              "this[j] = this[j + 1];",
47✔
1539
            "} else {",
47✔
1540
              "delete this[j];",
47✔
1541
            "}",
47✔
1542
            "if (hasSwap) {",
47✔
1543
              "this[j + 1] = swap;",
47✔
1544
            "} else {",
47✔
1545
              "delete this[j + 1];",
47✔
1546
            "}",
47✔
1547
            "changes++;",
47✔
1548
          "}",
47✔
1549
        "}",
47✔
1550
        "if (!changes) break;",
47✔
1551
      "}",
47✔
1552
      "return this;",
47✔
1553
    "}",
47✔
1554
  ");",
47✔
1555

47✔
1556
  "createArrayMethod_('toLocaleString',",
47✔
1557
    "function() {",
47✔
1558
      "if (!this) throw TypeError();",
47✔
1559
      "var o = Object(this), len = o.length >>> 0;",
47✔
1560
      "var out = [];",
47✔
1561
      "for (var i = 0; i < len; i++) {",
47✔
1562
        "out[i] = (o[i] === null || o[i] === undefined) ? '' : o[i].toLocaleString();",
47✔
1563
      "}",
47✔
1564
      "return out.join(',');",
47✔
1565
    "}",
47✔
1566
  ");",
47✔
1567
"})();",
47✔
1568
""
47✔
1569
/* POLYFILL END */
47✔
1570
);
47✔
1571
};
47✔
1572

1✔
1573
/**
1✔
1574
 * Initialize the String class.
1✔
1575
 * @param {!Interpreter.Object} globalObject Global object.
1✔
1576
 */
1✔
1577
Interpreter.prototype.initString = function(globalObject) {
1✔
1578
  var thisInterpreter = this;
47✔
1579
  var wrapper;
47✔
1580
  // String constructor.
47✔
1581
  wrapper = function String(value) {
47✔
1582
    value = arguments.length ? Interpreter.nativeGlobal.String(value) : '';
×
1583
    if (thisInterpreter.calledWithNew()) {
×
1584
      // Called as `new String()`.
×
1585
      this.data = value;
×
1586
      return this;
×
1587
    } else {
×
1588
      // Called as `String()`.
×
1589
      return value;
×
1590
    }
×
1591
  };
47✔
1592
  this.STRING = this.createNativeFunction(wrapper, true);
47✔
1593
  this.setProperty(globalObject, 'String', this.STRING,
47✔
1594
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
1595

47✔
1596
  // Static methods on String.
47✔
1597
  this.setProperty(this.STRING, 'fromCharCode',
47✔
1598
      this.createNativeFunction(String.fromCharCode, false),
47✔
1599
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
1600

47✔
1601
  // Instance methods on String.
47✔
1602
  // Methods with exclusively primitive arguments.
47✔
1603
  var functions = ['charAt', 'charCodeAt', 'concat', 'indexOf', 'lastIndexOf',
47✔
1604
      'slice', 'substr', 'substring', 'toLocaleLowerCase', 'toLocaleUpperCase',
47✔
1605
      'toLowerCase', 'toUpperCase', 'trim'];
47✔
1606
  for (var i = 0; i < functions.length; i++) {
47✔
1607
    this.setNativeFunctionPrototype(this.STRING, functions[i],
611✔
1608
                                    String.prototype[functions[i]]);
611✔
1609
  }
611✔
1610

47✔
1611
  wrapper = function localeCompare(compareString, locales, options) {
47✔
1612
    locales = thisInterpreter.pseudoToNative(locales);
×
1613
    options = thisInterpreter.pseudoToNative(options);
×
1614
    try {
×
1615
      return String(this).localeCompare(compareString,
×
1616
          /** @type {?} */(locales), /** @type {?} */(options));
×
1617
    } catch (e) {
×
1618
      thisInterpreter.throwException(thisInterpreter.ERROR,
×
1619
          'localeCompare: ' + e.message);
×
1620
    }
×
1621
  };
47✔
1622
  this.setNativeFunctionPrototype(this.STRING, 'localeCompare', wrapper);
47✔
1623

47✔
1624
  wrapper = function split(separator, limit, callback) {
47✔
1625
    var string = String(this);
×
1626
    limit = limit ? Number(limit) : undefined;
×
1627
    // Example of catastrophic split RegExp:
×
1628
    // 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaac'.split(/^(a+)+b/)
×
1629
    if (thisInterpreter.isa(separator, thisInterpreter.REGEXP)) {
×
1630
      separator = separator.data;
×
1631
      thisInterpreter.maybeThrowRegExp(separator, callback);
×
1632
      if (thisInterpreter['REGEXP_MODE'] === 2) {
×
1633
        if (Interpreter.vm) {
×
1634
          // Run split in vm.
×
1635
          var sandbox = {
×
1636
            'string': string,
×
1637
            'separator': separator,
×
1638
            'limit': limit,
×
1639
          };
×
1640
          var code = 'string.split(separator, limit)';
×
1641
          var jsList =
×
1642
              thisInterpreter.vmCall(code, sandbox, separator, callback);
×
1643
          if (jsList !== Interpreter.REGEXP_TIMEOUT) {
×
1644
            callback(thisInterpreter.nativeToPseudo(jsList));
×
1645
          }
×
1646
        } else {
×
1647
          // Run split in separate thread.
×
1648
          var splitWorker = thisInterpreter.createWorker();
×
1649
          var pid = thisInterpreter.regExpTimeout(separator, splitWorker,
×
1650
              callback);
×
1651
          splitWorker.onmessage = function(e) {
×
1652
            clearTimeout(pid);
×
1653
            callback(thisInterpreter.nativeToPseudo(e.data));
×
1654
          };
×
1655
          splitWorker.postMessage(['split', string, separator, limit]);
×
1656
        }
×
1657
        return;
×
1658
      }
×
1659
    }
×
1660
    // Run split natively.
×
1661
    var jsList = string.split(separator, limit);
×
1662
    callback(thisInterpreter.nativeToPseudo(jsList));
×
1663
  };
47✔
1664
  this.setAsyncFunctionPrototype(this.STRING, 'split', wrapper);
47✔
1665

47✔
1666
  wrapper = function match(regexp, callback) {
47✔
1667
    var string = String(this);
2✔
1668
    regexp = thisInterpreter.isa(regexp, thisInterpreter.REGEXP) ?
2✔
1669
        regexp.data : new RegExp(regexp);
2!
1670
    // Example of catastrophic match RegExp:
2✔
1671
    // 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaac'.match(/^(a+)+b/)
2✔
1672
    thisInterpreter.maybeThrowRegExp(regexp, callback);
2✔
1673
    if (thisInterpreter['REGEXP_MODE'] === 2) {
2✔
1674
      if (Interpreter.vm) {
2✔
1675
        // Run match in vm.
2✔
1676
        var sandbox = {
2✔
1677
          'string': string,
2✔
1678
          'regexp': regexp,
2✔
1679
        };
2✔
1680
        var code = 'string.match(regexp)';
2✔
1681
        var m = thisInterpreter.vmCall(code, sandbox, regexp, callback);
2✔
1682
        if (m !== Interpreter.REGEXP_TIMEOUT) {
2✔
1683
          callback(m && thisInterpreter.matchToPseudo_(m));
1!
1684
        }
1✔
1685
      } else {
2!
1686
        // Run match in separate thread.
×
1687
        var matchWorker = thisInterpreter.createWorker();
×
1688
        var pid = thisInterpreter.regExpTimeout(regexp, matchWorker, callback);
×
1689
        matchWorker.onmessage = function(e) {
×
1690
          clearTimeout(pid);
×
1691
          callback(e.data && thisInterpreter.matchToPseudo_(e.data));
×
1692
        };
×
1693
        matchWorker.postMessage(['match', string, regexp]);
×
1694
      }
×
1695
      return;
1✔
1696
    }
1✔
1697
    // Run match natively.
×
1698
    var m = string.match(regexp);
×
1699
    callback(m && thisInterpreter.matchToPseudo_(m));
2✔
1700
  };
47✔
1701
  this.setAsyncFunctionPrototype(this.STRING, 'match', wrapper);
47✔
1702

47✔
1703
  wrapper = function search(regexp, callback) {
47✔
1704
    var string = String(this);
×
1705
    if (thisInterpreter.isa(regexp, thisInterpreter.REGEXP)) {
×
1706
      regexp = regexp.data;
×
1707
    } else {
×
1708
      regexp = new RegExp(regexp);
×
1709
    }
×
1710
    // Example of catastrophic search RegExp:
×
1711
    // 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaac'.search(/^(a+)+b/)
×
1712
    thisInterpreter.maybeThrowRegExp(regexp, callback);
×
1713
    if (thisInterpreter['REGEXP_MODE'] === 2) {
×
1714
      if (Interpreter.vm) {
×
1715
        // Run search in vm.
×
1716
        var sandbox = {
×
1717
          'string': string,
×
1718
          'regexp': regexp
×
1719
        };
×
1720
        var code = 'string.search(regexp)';
×
1721
        var n = thisInterpreter.vmCall(code, sandbox, regexp, callback);
×
1722
        if (n !== Interpreter.REGEXP_TIMEOUT) {
×
1723
          callback(n);
×
1724
        }
×
1725
      } else {
×
1726
        // Run search in separate thread.
×
1727
        var searchWorker = thisInterpreter.createWorker();
×
1728
        var pid = thisInterpreter.regExpTimeout(regexp, searchWorker, callback);
×
1729
        searchWorker.onmessage = function(e) {
×
1730
          clearTimeout(pid);
×
1731
          callback(e.data);
×
1732
        };
×
1733
        searchWorker.postMessage(['search', string, regexp]);
×
1734
      }
×
1735
      return;
×
1736
    }
×
1737
    // Run search natively.
×
1738
    callback(string.search(regexp));
×
1739
  };
47✔
1740
  this.setAsyncFunctionPrototype(this.STRING, 'search', wrapper);
47✔
1741

47✔
1742
  wrapper = function replace_(substr, newSubstr, callback) {
47✔
1743
    // Support for function replacements is the responsibility of a polyfill.
×
1744
    var string = String(this);
×
1745
    newSubstr = String(newSubstr);
×
1746
    // Example of catastrophic replace RegExp:
×
1747
    // 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaac'.replace(/^(a+)+b/, '')
×
1748
    if (thisInterpreter.isa(substr, thisInterpreter.REGEXP)) {
×
1749
      substr = substr.data;
×
1750
      thisInterpreter.maybeThrowRegExp(substr, callback);
×
1751
      if (thisInterpreter['REGEXP_MODE'] === 2) {
×
1752
        if (Interpreter.vm) {
×
1753
          // Run replace in vm.
×
1754
          var sandbox = {
×
1755
            'string': string,
×
1756
            'substr': substr,
×
1757
            'newSubstr': newSubstr,
×
1758
          };
×
1759
          var code = 'string.replace(substr, newSubstr)';
×
1760
          var str = thisInterpreter.vmCall(code, sandbox, substr, callback);
×
1761
          if (str !== Interpreter.REGEXP_TIMEOUT) {
×
1762
            callback(str);
×
1763
          }
×
1764
        } else {
×
1765
          // Run replace in separate thread.
×
1766
          var replaceWorker = thisInterpreter.createWorker();
×
1767
          var pid = thisInterpreter.regExpTimeout(substr, replaceWorker,
×
1768
              callback);
×
1769
          replaceWorker.onmessage = function(e) {
×
1770
            clearTimeout(pid);
×
1771
            callback(e.data);
×
1772
          };
×
1773
          replaceWorker.postMessage(['replace', string, substr, newSubstr]);
×
1774
        }
×
1775
        return;
×
1776
      }
×
1777
    }
×
1778
    // Run replace natively.
×
1779
    callback(string.replace(substr, newSubstr));
×
1780
  };
47✔
1781
  this.setAsyncFunctionPrototype(this.STRING, 'replace', wrapper);
47✔
1782
  // Add a polyfill to handle replace's second argument being a function.
47✔
1783
  this.polyfills_.push(
47✔
1784
/* POLYFILL START */
47✔
1785
"(function() {",
47✔
1786
  "var replace_ = String.prototype.replace;",
47✔
1787
  "String.prototype.replace = function replace(substr, newSubstr) {",
47✔
1788
    "if (typeof newSubstr !== 'function') {",
47✔
1789
      // string.replace(string|regexp, string)
47✔
1790
      "return replace_.call(this, substr, newSubstr);",
47✔
1791
    "}",
47✔
1792
    "var str = this;",
47✔
1793
    "if (substr instanceof RegExp) {",
47✔
1794
      // string.replace(regexp, function)
47✔
1795
      "var subs = [];",
47✔
1796
      "var m = substr.exec(str);",
47✔
1797
      "while (m) {",
47✔
1798
        "m.push(m.index, str);",
47✔
1799
        "var inject = newSubstr.apply(null, m);",
47✔
1800
        "subs.push([m.index, m[0].length, inject]);",
47✔
1801
        "m = substr.global ? substr.exec(str) : null;",
47✔
1802
      "}",
47✔
1803
      "for (var i = subs.length - 1; i >= 0; i--) {",
47✔
1804
        "str = str.substring(0, subs[i][0]) + subs[i][2] + ",
47✔
1805
            "str.substring(subs[i][0] + subs[i][1]);",
47✔
1806
      "}",
47✔
1807
    "} else {",
47✔
1808
      // string.replace(string, function)
47✔
1809
      "var i = str.indexOf(substr);",
47✔
1810
      "if (i !== -1) {",
47✔
1811
        "var inject = newSubstr(str.substr(i, substr.length), i, str);",
47✔
1812
        "str = str.substring(0, i) + inject + ",
47✔
1813
            "str.substring(i + substr.length);",
47✔
1814
      "}",
47✔
1815
    "}",
47✔
1816
    "return str;",
47✔
1817
  "};",
47✔
1818
"})();",
47✔
1819
""
47✔
1820
/* POLYFILL END */
47✔
1821
);
47✔
1822
};
47✔
1823

1✔
1824
/**
1✔
1825
 * Initialize the Boolean class.
1✔
1826
 * @param {!Interpreter.Object} globalObject Global object.
1✔
1827
 */
1✔
1828
Interpreter.prototype.initBoolean = function(globalObject) {
1✔
1829
  var thisInterpreter = this;
47✔
1830
  var wrapper;
47✔
1831
  // Boolean constructor.
47✔
1832
  wrapper = function Boolean(value) {
47✔
1833
    value = Interpreter.nativeGlobal.Boolean(value);
×
1834
    if (thisInterpreter.calledWithNew()) {
×
1835
      // Called as `new Boolean()`.
×
1836
      this.data = value;
×
1837
      return this;
×
1838
    } else {
×
1839
      // Called as `Boolean()`.
×
1840
      return value;
×
1841
    }
×
1842
  };
47✔
1843
  this.BOOLEAN = this.createNativeFunction(wrapper, true);
47✔
1844
  this.setProperty(globalObject, 'Boolean', this.BOOLEAN,
47✔
1845
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
1846
};
47✔
1847

1✔
1848
/**
1✔
1849
 * Initialize the Number class.
1✔
1850
 * @param {!Interpreter.Object} globalObject Global object.
1✔
1851
 */
1✔
1852
Interpreter.prototype.initNumber = function(globalObject) {
1✔
1853
  var thisInterpreter = this;
47✔
1854
  var wrapper;
47✔
1855
  // Number constructor.
47✔
1856
  wrapper = function Number(value) {
47✔
1857
    value = arguments.length ? Interpreter.nativeGlobal.Number(value) : 0;
×
1858
    if (thisInterpreter.calledWithNew()) {
×
1859
      // Called as `new Number()`.
×
1860
      this.data = value;
×
1861
      return this;
×
1862
    } else {
×
1863
      // Called as `Number()`.
×
1864
      return value;
×
1865
    }
×
1866
  };
47✔
1867
  this.NUMBER = this.createNativeFunction(wrapper, true);
47✔
1868
  this.setProperty(globalObject, 'Number', this.NUMBER,
47✔
1869
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
1870

47✔
1871
  var numConsts = ['MAX_VALUE', 'MIN_VALUE', 'NaN', 'NEGATIVE_INFINITY',
47✔
1872
                   'POSITIVE_INFINITY'];
47✔
1873
  for (var i = 0; i < numConsts.length; i++) {
47✔
1874
    this.setProperty(this.NUMBER, numConsts[i], Number[numConsts[i]],
235✔
1875
        Interpreter.NONCONFIGURABLE_READONLY_NONENUMERABLE_DESCRIPTOR);
235✔
1876
  }
235✔
1877

47✔
1878
  // Instance methods on Number.
47✔
1879
  wrapper = function toExponential(fractionDigits) {
47✔
1880
    try {
×
1881
      return Number(this).toExponential(fractionDigits);
×
1882
    } catch (e) {
×
1883
      // Throws if fractionDigits isn't within 0-20.
×
1884
      thisInterpreter.throwException(thisInterpreter.ERROR, e.message);
×
1885
    }
×
1886
  };
47✔
1887
  this.setNativeFunctionPrototype(this.NUMBER, 'toExponential', wrapper);
47✔
1888

47✔
1889
  wrapper = function toFixed(digits) {
47✔
1890
    try {
×
1891
      return Number(this).toFixed(digits);
×
1892
    } catch (e) {
×
1893
      // Throws if digits isn't within 0-20.
×
1894
      thisInterpreter.throwException(thisInterpreter.ERROR, e.message);
×
1895
    }
×
1896
  };
47✔
1897
  this.setNativeFunctionPrototype(this.NUMBER, 'toFixed', wrapper);
47✔
1898

47✔
1899
  wrapper = function toPrecision(precision) {
47✔
1900
    try {
×
1901
      return Number(this).toPrecision(precision);
×
1902
    } catch (e) {
×
1903
      // Throws if precision isn't within range (depends on implementation).
×
1904
      thisInterpreter.throwException(thisInterpreter.ERROR, e.message);
×
1905
    }
×
1906
  };
47✔
1907
  this.setNativeFunctionPrototype(this.NUMBER, 'toPrecision', wrapper);
47✔
1908

47✔
1909
  wrapper = function toString(radix) {
47✔
1910
    try {
×
1911
      return Number(this).toString(radix);
×
1912
    } catch (e) {
×
1913
      // Throws if radix isn't within 2-36.
×
1914
      thisInterpreter.throwException(thisInterpreter.ERROR, e.message);
×
1915
    }
×
1916
  };
47✔
1917
  this.setNativeFunctionPrototype(this.NUMBER, 'toString', wrapper);
47✔
1918

47✔
1919
  wrapper = function toLocaleString(locales, options) {
47✔
1920
    locales = locales ? thisInterpreter.pseudoToNative(locales) : undefined;
×
1921
    options = options ? thisInterpreter.pseudoToNative(options) : undefined;
×
1922
    try {
×
1923
      return Number(this).toLocaleString(
×
1924
          /** @type {?} */(locales), /** @type {?} */(options));
×
1925
    } catch (e) {
×
1926
      thisInterpreter.throwException(thisInterpreter.ERROR,
×
1927
          'toLocaleString: ' + e.message);
×
1928
    }
×
1929
  };
47✔
1930
  this.setNativeFunctionPrototype(this.NUMBER, 'toLocaleString', wrapper);
47✔
1931
};
47✔
1932

1✔
1933
/**
1✔
1934
 * Initialize the Date class.
1✔
1935
 * @param {!Interpreter.Object} globalObject Global object.
1✔
1936
 */
1✔
1937
Interpreter.prototype.initDate = function(globalObject) {
1✔
1938
  var thisInterpreter = this;
47✔
1939
  var wrapper;
47✔
1940
  // Date constructor.
47✔
1941
  wrapper = function Date(_value, var_args) {
47✔
1942
    if (!thisInterpreter.calledWithNew()) {
1!
1943
      // Called as `Date()`.
×
1944
      // Calling Date() as a function returns a string, no arguments are heeded.
×
1945
      return Interpreter.nativeGlobal.Date();
×
1946
    }
×
1947
    // Called as `new Date(...)`.
1✔
1948
    var args = [null].concat(Array.from(arguments));
1✔
1949
    this.data = new (Function.prototype.bind.apply(
1✔
1950
        Interpreter.nativeGlobal.Date, args));
1✔
1951
    return this;
1✔
1952
  };
47✔
1953
  this.DATE = this.createNativeFunction(wrapper, true);
47✔
1954
  this.DATE_PROTO = this.DATE.properties['prototype'];
47✔
1955
  this.setProperty(globalObject, 'Date', this.DATE,
47✔
1956
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
1957

47✔
1958
  // Static methods on Date.
47✔
1959
  this.setProperty(this.DATE, 'now', this.createNativeFunction(Date.now, false),
47✔
1960
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
1961

47✔
1962
  this.setProperty(this.DATE, 'parse',
47✔
1963
      this.createNativeFunction(Date.parse, false),
47✔
1964
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
1965

47✔
1966
  this.setProperty(this.DATE, 'UTC', this.createNativeFunction(Date.UTC, false),
47✔
1967
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
1968

47✔
1969
  // Instance methods on Date.
47✔
1970
  var functions = ['getDate', 'getDay', 'getFullYear', 'getHours',
47✔
1971
      'getMilliseconds', 'getMinutes', 'getMonth', 'getSeconds', 'getTime',
47✔
1972
      'getTimezoneOffset', 'getUTCDate', 'getUTCDay', 'getUTCFullYear',
47✔
1973
      'getUTCHours', 'getUTCMilliseconds', 'getUTCMinutes', 'getUTCMonth',
47✔
1974
      'getUTCSeconds', 'getYear',
47✔
1975
      'setDate', 'setFullYear', 'setHours', 'setMilliseconds',
47✔
1976
      'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate',
47✔
1977
      'setUTCFullYear', 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes',
47✔
1978
      'setUTCMonth', 'setUTCSeconds', 'setYear',
47✔
1979
      'toDateString', 'toJSON', 'toGMTString', 'toLocaleDateString',
47✔
1980
      'toLocaleString', 'toLocaleTimeString', 'toTimeString', 'toUTCString'];
47✔
1981
  for (var i = 0; i < functions.length; i++) {
47✔
1982
    wrapper = (function(nativeFunc) {
2,021✔
1983
      return function(var_args) {
2,021✔
1984
        var date = this.data;
×
1985
        if (!(date instanceof Date)) {
×
1986
          thisInterpreter.throwException(thisInterpreter.TYPE_ERROR,
×
1987
              nativeFunc + ' not called on a Date');
×
1988
        }
×
1989
        var args = [];
×
1990
        for (var i = 0; i < arguments.length; i++) {
×
1991
          args[i] = thisInterpreter.pseudoToNative(arguments[i]);
×
1992
        }
×
1993
        return date[nativeFunc].apply(date, args);
×
1994
      };
2,021✔
1995
    })(functions[i]);
2,021✔
1996
    this.setNativeFunctionPrototype(this.DATE, functions[i], wrapper);
2,021✔
1997
  }
2,021✔
1998

47✔
1999
  // Unlike the previous instance methods, toISOString may throw.
47✔
2000
  wrapper = function toISOString() {
47✔
2001
    try {
×
2002
      return this.data.toISOString();
×
2003
    } catch (e) {
×
2004
      thisInterpreter.throwException(thisInterpreter.RANGE_ERROR,
×
2005
          'toISOString: ' + e.message);
×
2006
    }
×
2007
  };
47✔
2008
  this.setNativeFunctionPrototype(this.DATE, 'toISOString', wrapper);
47✔
2009
};
47✔
2010

1✔
2011
/**
1✔
2012
 * Initialize Regular Expression object.
1✔
2013
 * @param {!Interpreter.Object} globalObject Global object.
1✔
2014
 */
1✔
2015
Interpreter.prototype.initRegExp = function(globalObject) {
1✔
2016
  var thisInterpreter = this;
47✔
2017
  var wrapper;
47✔
2018
  // RegExp constructor.
47✔
2019
  wrapper = function RegExp(pattern, flags) {
47✔
2020
    if (thisInterpreter.calledWithNew()) {
1✔
2021
      // Called as `new RegExp()`.
1✔
2022
      var rgx = this;
1✔
2023
    } else {
1!
2024
      // Called as `RegExp()`.
×
2025
      if (flags === undefined &&
×
2026
          thisInterpreter.isa(pattern, thisInterpreter.REGEXP)) {
×
2027
        // Regexp(/foo/) returns the same obj.
×
2028
        return pattern;
×
2029
      }
×
2030
      var rgx = thisInterpreter.createObjectProto(thisInterpreter.REGEXP_PROTO);
×
2031
    }
×
2032
    pattern = pattern === undefined ? '' : String(pattern);
1!
2033
    flags = flags ? String(flags) : '';
1!
2034
    if (!/^[gmi]*$/.test(flags)) {
1!
2035
      // Don't allow ES6 flags 'y' and 's' to pass through.
×
2036
      thisInterpreter.throwException(thisInterpreter.SYNTAX_ERROR,
×
2037
          'Invalid regexp flag: ' + flags);
×
2038
    }
×
2039
    try {
1✔
2040
      var nativeRegExp = new Interpreter.nativeGlobal.RegExp(pattern, flags)
1✔
2041
    } catch (e) {
1!
2042
      // Throws if flags are repeated.
×
2043
      thisInterpreter.throwException(thisInterpreter.SYNTAX_ERROR, e.message);
×
2044
    }
×
2045
    thisInterpreter.populateRegExp(rgx, nativeRegExp);
1✔
2046
    return rgx;
1✔
2047
  };
47✔
2048
  this.REGEXP = this.createNativeFunction(wrapper, true);
47✔
2049
  this.REGEXP_PROTO = this.REGEXP.properties['prototype'];
47✔
2050
  this.setProperty(globalObject, 'RegExp', this.REGEXP,
47✔
2051
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
2052

47✔
2053
  this.setProperty(this.REGEXP.properties['prototype'], 'global', undefined,
47✔
2054
      Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
47✔
2055
  this.setProperty(this.REGEXP.properties['prototype'], 'ignoreCase', undefined,
47✔
2056
      Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
47✔
2057
  this.setProperty(this.REGEXP.properties['prototype'], 'multiline', undefined,
47✔
2058
      Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
47✔
2059
  this.setProperty(this.REGEXP.properties['prototype'], 'source', '(?:)',
47✔
2060
      Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
47✔
2061

47✔
2062
  // Use polyfill to avoid complexity of regexp threads.
47✔
2063
  this.polyfills_.push(
47✔
2064
/* POLYFILL START */
47✔
2065
"Object.defineProperty(RegExp.prototype, 'test',",
47✔
2066
    "{configurable: true, writable: true, value:",
47✔
2067
  "function test(str) {",
47✔
2068
    "return !!this.exec(str);",
47✔
2069
  "}",
47✔
2070
"});"
47✔
2071
/* POLYFILL END */
47✔
2072
);
47✔
2073

47✔
2074
  wrapper = function exec(string, callback) {
47✔
2075
    var regexp = this.data;
×
2076
    string = String(string);
×
2077
    // Get lastIndex from wrapped regexp, since this is settable.
×
2078
    regexp.lastIndex = Number(thisInterpreter.getProperty(this, 'lastIndex'));
×
2079
    // Example of catastrophic exec RegExp:
×
2080
    // /^(a+)+b/.exec('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaac')
×
2081
    thisInterpreter.maybeThrowRegExp(regexp, callback);
×
2082
    if (thisInterpreter['REGEXP_MODE'] === 2) {
×
2083
      if (Interpreter.vm) {
×
2084
        // Run exec in vm.
×
2085
        var sandbox = {
×
2086
          'string': string,
×
2087
          'regexp': regexp,
×
2088
        };
×
2089
        var code = 'regexp.exec(string)';
×
2090
        var match = thisInterpreter.vmCall(code, sandbox, regexp, callback);
×
2091
        if (match !== Interpreter.REGEXP_TIMEOUT) {
×
2092
          thisInterpreter.setProperty(this, 'lastIndex', regexp.lastIndex);
×
2093
          callback(thisInterpreter.matchToPseudo_(match));
×
2094
        }
×
2095
      } else {
×
2096
        // Run exec in separate thread.
×
2097
        // Note that lastIndex is not preserved when a RegExp is passed to a
×
2098
        // Web Worker.  Thus it needs to be passed back and forth separately.
×
2099
        var execWorker = thisInterpreter.createWorker();
×
2100
        var pid = thisInterpreter.regExpTimeout(regexp, execWorker, callback);
×
2101
        var thisPseudoRegExp = this;
×
2102
        execWorker.onmessage = function(e) {
×
2103
          clearTimeout(pid);
×
2104
          // Return tuple: [result, lastIndex]
×
2105
          thisInterpreter.setProperty(thisPseudoRegExp, 'lastIndex', e.data[1]);
×
2106
          callback(thisInterpreter.matchToPseudo_(e.data[0]));
×
2107
        };
×
2108
        execWorker.postMessage(['exec', regexp, regexp.lastIndex, string]);
×
2109
      }
×
2110
      return;
×
2111
    }
×
2112
    // Run exec natively.
×
2113
    var match = regexp.exec(string);
×
2114
    thisInterpreter.setProperty(this, 'lastIndex', regexp.lastIndex);
×
2115
    callback(thisInterpreter.matchToPseudo_(match));
×
2116
  };
47✔
2117
  this.setAsyncFunctionPrototype(this.REGEXP, 'exec', wrapper);
47✔
2118
};
47✔
2119

1✔
2120
/**
1✔
2121
 * Regexp.prototype.exec and String.prototype.match both return an array
1✔
2122
 * of matches.  This array has two extra properties, 'input' and 'index'.
1✔
2123
 * Convert this native JavaScript data structure into an JS-Interpreter object.
1✔
2124
 * @param {!Array} match The native JavaScript match array to be converted.
1✔
2125
 * @returns {Interpreter.Value} The equivalent JS-Interpreter array or null.
1✔
2126
 * @private
1✔
2127
 */
1✔
2128
Interpreter.prototype.matchToPseudo_ = function(match) {
1✔
2129
  if (match) {
×
2130
    // ES9 adds a 'groups' property.  This isn't part of ES5.  Delete it.
×
2131
    // ES13 adds an 'indices' property though Acorn should forbid the
×
2132
    // regex 'd' flag that creates it.
×
2133
    // Future ES versions may add more properties.
×
2134
    // Delete all properties not compatible with ES5.
×
2135
    var props = /** @type {!Array<?>} */(Object.getOwnPropertyNames(match));
×
2136
    for (var i = 0; i < props.length; i++) {
×
2137
      var prop = props[i];
×
2138
      if (isNaN(Number(prop)) && prop !== 'length' &&
×
2139
          prop !== 'input' && prop !== 'index') {
×
2140
        delete match[prop];
×
2141
      }
×
2142
    }
×
2143
    // Convert from a native data structure to JS-Iterpreter objects.
×
2144
    return this.nativeToPseudo(match);
×
2145
  }
×
2146
  return null;
×
2147
};
×
2148

1✔
2149
/**
1✔
2150
 * Initialize the Error class.
1✔
2151
 * @param {!Interpreter.Object} globalObject Global object.
1✔
2152
 */
1✔
2153
Interpreter.prototype.initError = function(globalObject) {
1✔
2154
  var thisInterpreter = this;
47✔
2155
  // Error constructor.
47✔
2156
  this.ERROR = this.createNativeFunction(function Error(opt_message) {
47✔
2157
    if (thisInterpreter.calledWithNew()) {
×
2158
      // Called as `new Error()`.
×
2159
      var newError = this;
×
2160
    } else {
×
2161
      // Called as `Error()`.
×
2162
      var newError = thisInterpreter.createObject(thisInterpreter.ERROR);
×
2163
    }
×
2164
    thisInterpreter.populateError(newError, opt_message);
×
2165
    return newError;
×
2166
  }, true);
47✔
2167
  this.setProperty(globalObject, 'Error', this.ERROR,
47✔
2168
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
2169
  this.setProperty(this.ERROR.properties['prototype'], 'message', '',
47✔
2170
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
2171
  this.setProperty(this.ERROR.properties['prototype'], 'name', 'Error',
47✔
2172
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
2173

47✔
2174
  var createErrorSubclass = function(name) {
47✔
2175
    var constructor = thisInterpreter.createNativeFunction(
282✔
2176
        function(opt_message) {
282✔
2177
          if (thisInterpreter.calledWithNew()) {
×
2178
            // Called as `new XyzError()`.
×
2179
            var newError = this;
×
2180
          } else {
×
2181
            // Called as `XyzError()`.
×
2182
            var newError = thisInterpreter.createObject(constructor);
×
2183
          }
×
2184
          thisInterpreter.populateError(newError, opt_message);
×
2185
          return newError;
×
2186
        }, true);
282✔
2187
    thisInterpreter.setProperty(constructor, 'prototype',
282✔
2188
        thisInterpreter.createObject(thisInterpreter.ERROR),
282✔
2189
        Interpreter.NONENUMERABLE_DESCRIPTOR);
282✔
2190
    thisInterpreter.setProperty(constructor.properties['prototype'], 'name',
282✔
2191
        name, Interpreter.NONENUMERABLE_DESCRIPTOR);
282✔
2192
    thisInterpreter.setProperty(globalObject, name, constructor,
282✔
2193
        Interpreter.NONENUMERABLE_DESCRIPTOR);
282✔
2194

282✔
2195
    return constructor;
282✔
2196
  };
47✔
2197

47✔
2198
  this.EVAL_ERROR = createErrorSubclass('EvalError');
47✔
2199
  this.RANGE_ERROR = createErrorSubclass('RangeError');
47✔
2200
  this.REFERENCE_ERROR = createErrorSubclass('ReferenceError');
47✔
2201
  this.SYNTAX_ERROR = createErrorSubclass('SyntaxError');
47✔
2202
  this.TYPE_ERROR = createErrorSubclass('TypeError');
47✔
2203
  this.URI_ERROR = createErrorSubclass('URIError');
47✔
2204
};
47✔
2205

1✔
2206
/**
1✔
2207
 * Initialize Math object.
1✔
2208
 * @param {!Interpreter.Object} globalObject Global object.
1✔
2209
 */
1✔
2210
Interpreter.prototype.initMath = function(globalObject) {
1✔
2211
  var myMath = this.createObjectProto(this.OBJECT_PROTO);
47✔
2212
  this.setProperty(globalObject, 'Math', myMath,
47✔
2213
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
2214
  var mathConsts = ['E', 'LN2', 'LN10', 'LOG2E', 'LOG10E', 'PI',
47✔
2215
                    'SQRT1_2', 'SQRT2'];
47✔
2216
  for (var i = 0; i < mathConsts.length; i++) {
47✔
2217
    this.setProperty(myMath, mathConsts[i], Math[mathConsts[i]],
376✔
2218
        Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
376✔
2219
  }
376✔
2220
  var numFunctions = ['abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos',
47✔
2221
                      'exp', 'floor', 'log', 'max', 'min', 'pow', 'random',
47✔
2222
                      'round', 'sin', 'sqrt', 'tan'];
47✔
2223
  for (var i = 0; i < numFunctions.length; i++) {
47✔
2224
    this.setProperty(myMath, numFunctions[i],
846✔
2225
        this.createNativeFunction(Math[numFunctions[i]], false),
846✔
2226
        Interpreter.NONENUMERABLE_DESCRIPTOR);
846✔
2227
  }
846✔
2228
};
47✔
2229

1✔
2230
/**
1✔
2231
 * Initialize JSON object.
1✔
2232
 * @param {!Interpreter.Object} globalObject Global object.
1✔
2233
 */
1✔
2234
Interpreter.prototype.initJSON = function(globalObject) {
1✔
2235
  var wrapper;
47✔
2236
  var thisInterpreter = this;
47✔
2237
  var myJSON = thisInterpreter.createObjectProto(this.OBJECT_PROTO);
47✔
2238
  this.setProperty(globalObject, 'JSON', myJSON,
47✔
2239
      Interpreter.NONENUMERABLE_DESCRIPTOR);
47✔
2240

47✔
2241
  wrapper = function parse(text) {
47✔
2242
    try {
×
2243
      var nativeObj = JSON.parse(String(text));
×
2244
    } catch (e) {
×
2245
      thisInterpreter.throwException(thisInterpreter.SYNTAX_ERROR, e.message);
×
2246
    }
×
2247
    return thisInterpreter.nativeToPseudo(nativeObj);
×
2248
  };
47✔
2249
  this.setProperty(myJSON, 'parse', this.createNativeFunction(wrapper, false));
47✔
2250

47✔
2251
  wrapper = function stringify(value, replacer, space) {
47✔
2252
    if (replacer && replacer.class === 'Function') {
×
2253
      thisInterpreter.throwException(thisInterpreter.TYPE_ERROR,
×
2254
          'Function replacer on JSON.stringify not supported');
×
2255
    } else if (replacer && replacer.class === 'Array') {
×
2256
      replacer = thisInterpreter.pseudoToNative(replacer);
×
2257
      replacer = replacer.filter(function(word) {
×
2258
        // Spec says we should also support boxed primitives here.
×
2259
        return typeof word === 'string' || typeof word === 'number';
×
2260
      });
×
2261
    } else {
×
2262
      replacer = null;
×
2263
    }
×
2264
    // Spec says we should also support boxed primitives here.
×
2265
    if (typeof space !== 'string' && typeof space !== 'number') {
×
2266
      space = undefined;
×
2267
    }
×
2268

×
2269
    var nativeObj = thisInterpreter.pseudoToNative(value);
×
2270
    try {
×
2271
      var str = JSON.stringify(nativeObj, replacer, space);
×
2272
    } catch (e) {
×
2273
      thisInterpreter.throwException(thisInterpreter.TYPE_ERROR, e.message);
×
2274
    }
×
2275
    return str;
×
2276
  };
47✔
2277
  this.setProperty(myJSON, 'stringify',
47✔
2278
      this.createNativeFunction(wrapper, false));
47✔
2279
};
47✔
2280

1✔
2281
/**
1✔
2282
 * Is an object of a certain class?
1✔
2283
 * @param {Interpreter.Value} child Object to check.
1✔
2284
 * @param {Interpreter.Object} constructor Constructor of object.
1✔
2285
 * @returns {boolean} True if object is the class or inherits from it.
1✔
2286
 *     False otherwise.
1✔
2287
 */
1✔
2288
Interpreter.prototype.isa = function(child, constructor) {
1✔
2289
  if (child === null || child === undefined || !constructor) {
107,250✔
2290
    return false;
9,964✔
2291
  }
9,964✔
2292
  var proto = constructor.properties['prototype'];
97,286✔
2293
  if (child === proto) {
107,250✔
2294
    return true;
893✔
2295
  }
893✔
2296
  // The first step up the prototype chain is harder since the child might be
96,393✔
2297
  // a primitive value.  Subsequent steps can just follow the .proto property.
96,393✔
2298
  child = this.getPrototype(child);
96,393✔
2299
  while (child) {
107,250✔
2300
    if (child === proto) {
112,551✔
2301
      return true;
305✔
2302
    }
305✔
2303
    child = child.proto;
112,246✔
2304
  }
112,246✔
2305
  return false;
96,088✔
2306
};
96,088✔
2307

1✔
2308
/**
1✔
2309
 * Initialize a pseudo regular expression object based on a native regular
1✔
2310
 * expression object.
1✔
2311
 * @param {!Interpreter.Object} pseudoRegexp The existing object to set.
1✔
2312
 * @param {!RegExp} nativeRegexp The native regular expression.
1✔
2313
 */
1✔
2314
Interpreter.prototype.populateRegExp = function(pseudoRegexp, nativeRegexp) {
1✔
2315
  pseudoRegexp.data = new RegExp(nativeRegexp.source, nativeRegexp.flags);
4✔
2316
  // lastIndex is settable, all others are read-only attributes
4✔
2317
  this.setProperty(pseudoRegexp, 'lastIndex', nativeRegexp.lastIndex,
4✔
2318
      Interpreter.NONENUMERABLE_DESCRIPTOR);
4✔
2319
  this.setProperty(pseudoRegexp, 'source', nativeRegexp.source,
4✔
2320
      Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
4✔
2321
  this.setProperty(pseudoRegexp, 'global', nativeRegexp.global,
4✔
2322
      Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
4✔
2323
  this.setProperty(pseudoRegexp, 'ignoreCase', nativeRegexp.ignoreCase,
4✔
2324
      Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
4✔
2325
  this.setProperty(pseudoRegexp, 'multiline', nativeRegexp.multiline,
4✔
2326
      Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
4✔
2327
};
4✔
2328

1✔
2329
/**
1✔
2330
 * Initialize a pseudo error object.
1✔
2331
 * @param {!Interpreter.Object} pseudoError The existing object to set.
1✔
2332
 * @param {string=} opt_message Error's message.
1✔
2333
 */
1✔
2334
Interpreter.prototype.populateError = function(pseudoError, opt_message) {
1✔
2335
  if (opt_message) {
7✔
2336
    this.setProperty(pseudoError, 'message', String(opt_message),
7✔
2337
        Interpreter.NONENUMERABLE_DESCRIPTOR);
7✔
2338
  }
7✔
2339
  var tracebackData = [];
7✔
2340
  for (var i = this.stateStack.length - 1; i >= 0; i--) {
7✔
2341
    var state = this.stateStack[i];
28✔
2342
    var node = state.node;
28✔
2343
    if (node.type === 'CallExpression') {
28✔
2344
      var func = state.func_;
10✔
2345
      if (func && tracebackData.length) {
10✔
2346
        tracebackData[tracebackData.length - 1].datumName =
3✔
2347
            this.getProperty(func, 'name');
3✔
2348
      }
3✔
2349
    }
10✔
2350
    if (node.loc &&
28✔
2351
        (!tracebackData.length || node.type === 'CallExpression')) {
28✔
2352
      tracebackData.push({datumLoc: node.loc});
10✔
2353
    }
10✔
2354
  }
28✔
2355
  var errorName = String(this.getProperty(pseudoError, 'name'));
7✔
2356
  var errorMessage = String(this.getProperty(pseudoError, 'message'));
7✔
2357
  var stackString = errorName + ': ' + errorMessage + '\n';
7✔
2358
  for (var i = 0; i < tracebackData.length; i++) {
7✔
2359
    var loc = tracebackData[i].datumLoc;
10✔
2360
    var name = tracebackData[i].datumName;
10✔
2361
    var locString = loc.source + ':' +
10✔
2362
        loc.start.line + ':' + loc.start.column;
10✔
2363
    if (name) {
10✔
2364
      stackString += '  at ' + name + ' (' + locString + ')\n';
3✔
2365
    } else {
10✔
2366
      stackString += '  at ' + locString + '\n';
7✔
2367
    }
7✔
2368
  }
10✔
2369
  this.setProperty(pseudoError, 'stack', stackString.trim(),
7✔
2370
      Interpreter.NONENUMERABLE_DESCRIPTOR);
7✔
2371
};
7✔
2372

1✔
2373
/**
1✔
2374
 * Create a Web Worker to execute regular expressions.
1✔
2375
 * Using a separate file fails in Chrome when run locally on a file:// URI.
1✔
2376
 * Using a data encoded URI fails in IE and Edge.
1✔
2377
 * Using a blob works in IE11 and all other browsers.
1✔
2378
 * @returns {!Worker} Web Worker with regexp execution code loaded.
1✔
2379
 */
1✔
2380
Interpreter.prototype.createWorker = function() {
1✔
2381
  var blob = this.createWorker.blob_;
×
2382
  if (!blob) {
×
2383
    blob = new Blob([Interpreter.WORKER_CODE.join('\n')],
×
2384
        {type: 'application/javascript'});
×
2385
    // Cache the blob, so it doesn't need to be created next time.
×
2386
    this.createWorker.blob_ = blob;
×
2387
  }
×
2388
  return new Worker(URL.createObjectURL(blob));
×
2389
};
×
2390

1✔
2391
/**
1✔
2392
 * Execute regular expressions in a node vm.
1✔
2393
 * @param {string} code Code to execute.
1✔
2394
 * @param {!Object} sandbox Global variables for new vm.
1✔
2395
 * @param {!RegExp} nativeRegExp Regular expression.
1✔
2396
 * @param {!Function} callback Asynchronous callback function.
1✔
2397
 */
1✔
2398
Interpreter.prototype.vmCall = function(code, sandbox, nativeRegExp, callback) {
1✔
2399
  var options = {'timeout': this['REGEXP_THREAD_TIMEOUT']};
2✔
2400
  try {
2✔
2401
    return Interpreter.vm['runInNewContext'](code, sandbox, options);
2✔
2402
  } catch (_e) {
2✔
2403
    callback(null);
1✔
2404
    this.throwException(this.ERROR, 'RegExp Timeout: ' + nativeRegExp);
1✔
2405
  }
1✔
2406
};
2✔
2407

1✔
2408
/**
1✔
2409
 * If REGEXP_MODE is 0, then throw an error.
1✔
2410
 * Also throw if REGEXP_MODE is 2 and JS doesn't support Web Workers or vm.
1✔
2411
 * @param {!RegExp} nativeRegExp Regular expression.
1✔
2412
 * @param {!Function} callback Asynchronous callback function.
1✔
2413
 */
1✔
2414
Interpreter.prototype.maybeThrowRegExp = function(nativeRegExp, callback) {
1✔
2415
  var ok;
2✔
2416
  if (this['REGEXP_MODE'] === 0) {
2!
2417
    // Fail: No RegExp support.
×
2418
    ok = false;
×
2419
  } else if (this['REGEXP_MODE'] === 1) {
2!
2420
    // Ok: Native RegExp support.
×
2421
    ok = true;
×
2422
  } else {
2✔
2423
    // Sandboxed RegExp handling.
2✔
2424
    if (Interpreter.vm) {
2✔
2425
      // Ok: Node's vm module already loaded.
1✔
2426
      ok = true;
1✔
2427
    } else if (typeof Worker === 'function' && typeof URL === 'function') {
1!
2428
      // Ok: Web Workers available.
×
2429
      ok = true;
×
2430
    } else if (typeof require === 'function') {
1✔
2431
      // Try to load Node's vm module.
1✔
2432
      try {
1✔
2433
        Interpreter.vm = require('vm');
1✔
2434
      } catch (_e) {}
1!
2435
      ok = !!Interpreter.vm;
1✔
2436
    } else {
1!
2437
      // Fail: Neither Web Workers nor vm available.
×
2438
      ok = false;
×
2439
    }
×
2440
  }
2✔
2441
  if (!ok) {
2!
2442
    callback(null);
×
2443
    this.throwException(this.ERROR, 'Regular expressions not supported: ' +
×
2444
        nativeRegExp);
×
2445
  }
×
2446
};
2✔
2447

1✔
2448
/**
1✔
2449
 * Set a timeout for regular expression threads.  Unless cancelled, this will
1✔
2450
 * terminate the thread and throw an error.
1✔
2451
 * @param {!RegExp} nativeRegExp Regular expression (used for error message).
1✔
2452
 * @param {!Worker} worker Thread to terminate.
1✔
2453
 * @param {!Function} callback Async callback function to continue execution.
1✔
2454
 * @returns {number} PID of timeout.  Used to cancel if thread completes.
1✔
2455
 */
1✔
2456
Interpreter.prototype.regExpTimeout = function(nativeRegExp, worker, callback) {
1✔
2457
  var thisInterpreter = this;
×
2458
  return setTimeout(function() {
×
2459
      worker.terminate();
×
2460
      callback(null);
×
2461
      try {
×
2462
        thisInterpreter.throwException(thisInterpreter.ERROR,
×
2463
            'RegExp Timeout: ' + nativeRegExp);
×
2464
      } catch (_e) {
×
2465
        // Eat the expected Interpreter.STEP_ERROR.
×
2466
      }
×
2467
  }, this['REGEXP_THREAD_TIMEOUT']);
×
2468
};
×
2469

1✔
2470
/**
1✔
2471
 * Create a new data object based on a constructor's prototype.
1✔
2472
 * @param {Interpreter.Object} constructor Parent constructor function,
1✔
2473
 *     or null if scope object.
1✔
2474
 * @returns {!Interpreter.Object} New data object.
1✔
2475
 */
1✔
2476
Interpreter.prototype.createObject = function(constructor) {
1✔
2477
  return this.createObjectProto(constructor &&
289✔
2478
                                constructor.properties['prototype']);
289✔
2479
};
289✔
2480

1✔
2481
/**
1✔
2482
 * Create a new data object based on a prototype.
1✔
2483
 * @param {Interpreter.Object} proto Prototype object.
1✔
2484
 * @returns {!Interpreter.Object} New data object.
1✔
2485
 */
1✔
2486
Interpreter.prototype.createObjectProto = function(proto) {
1✔
2487
  if (typeof proto !== 'object') {
22,044!
2488
    throw Error('Non object prototype');
×
2489
  }
×
2490
  var obj = new Interpreter.Object(proto);
22,044✔
2491
  if (this.isa(obj, this.ERROR)) {
22,044✔
2492
    // Record this object as being an error so that its toString function can
289✔
2493
    // process it correctly (toString has no access to the interpreter and could
289✔
2494
    // not otherwise determine that the object is an error).
289✔
2495
    obj.class = 'Error';
289✔
2496
  }
289✔
2497
  return obj;
22,044✔
2498
};
22,044✔
2499

1✔
2500
/**
1✔
2501
 * Create a new array.
1✔
2502
 * @returns {!Interpreter.Object} New array.
1✔
2503
 */
1✔
2504
Interpreter.prototype.createArray = function() {
1✔
2505
  var array = this.createObjectProto(this.ARRAY_PROTO);
3,183✔
2506
  // Arrays have length.
3,183✔
2507
  this.setProperty(array, 'length', 0,
3,183✔
2508
      {'configurable': false, 'enumerable': false, 'writable': true});
3,183✔
2509
  array.class = 'Array';
3,183✔
2510
  return array;
3,183✔
2511
};
3,183✔
2512

1✔
2513
/**
1✔
2514
 * Create a new function object (could become interpreted or native or async).
1✔
2515
 * @param {number} argumentLength Number of arguments.
1✔
2516
 * @param {boolean} isConstructor True if function can be used with 'new'.
1✔
2517
 * @returns {!Interpreter.Object} New function.
1✔
2518
 * @private
1✔
2519
 */
1✔
2520
Interpreter.prototype.createFunctionBase_ = function(argumentLength,
1✔
2521
                                                     isConstructor) {
8,356✔
2522
  var func = this.createObjectProto(this.FUNCTION_PROTO);
8,356✔
2523
  if (isConstructor) {
8,356✔
2524
    var proto = this.createObjectProto(this.OBJECT_PROTO);
2,544✔
2525
    this.setProperty(func, 'prototype', proto,
2,544✔
2526
                     Interpreter.NONENUMERABLE_DESCRIPTOR);
2,544✔
2527
    this.setProperty(proto, 'constructor', func,
2,544✔
2528
                     Interpreter.NONENUMERABLE_DESCRIPTOR);
2,544✔
2529
  } else {
8,356✔
2530
    func.illegalConstructor = true;
5,812✔
2531
  }
5,812✔
2532
  this.setProperty(func, 'length', argumentLength,
8,356✔
2533
      Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
8,356✔
2534
  func.class = 'Function';
8,356✔
2535
  // When making changes to this function, check to see if those changes also
8,356✔
2536
  // need to be made to the creation of FUNCTION_PROTO in initFunction.
8,356✔
2537
  return func;
8,356✔
2538
};
8,356✔
2539

1✔
2540
/**
1✔
2541
 * Create a new interpreted function.
1✔
2542
 * @param {!Object} node AST node defining the function.
1✔
2543
 * @param {!Interpreter.Scope} scope Parent scope.
1✔
2544
 * @param {string=} opt_name Optional name for function.
1✔
2545
 * @returns {!Interpreter.Object} New function.
1✔
2546
 */
1✔
2547
Interpreter.prototype.createFunction = function(node, scope, opt_name) {
1✔
2548
  var func = this.createFunctionBase_(node.params.length, true);
1,604✔
2549
  func.parentScope = scope;
1,604✔
2550
  func.node = node;
1,604✔
2551
  // Choose a name for this function.
1,604✔
2552
  // function foo() {}             -> 'foo'
1,604✔
2553
  // var bar = function() {};      -> 'bar'
1,604✔
2554
  // var bar = function foo() {};  -> 'foo'
1,604✔
2555
  // foo.bar = function() {};      -> ''
1,604✔
2556
  // var bar = new Function('');   -> 'anonymous'
1,604✔
2557
  var name = node.id ? String(node.id.name) : (opt_name || '');
1,604✔
2558
  this.setProperty(func, 'name', name,
1,604✔
2559
      Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
1,604✔
2560
  return func;
1,604✔
2561
};
1,604✔
2562

1✔
2563
/**
1✔
2564
 * Create a new native function.
1✔
2565
 * @param {!Function} nativeFunc JavaScript function.
1✔
2566
 * @param {boolean} isConstructor True if function can be used with 'new'.
1✔
2567
 * @returns {!Interpreter.Object} New function.
1✔
2568
 */
1✔
2569
Interpreter.prototype.createNativeFunction = function(nativeFunc,
1✔
2570
                                                      isConstructor) {
6,517✔
2571
  var func = this.createFunctionBase_(nativeFunc.length, isConstructor);
6,517✔
2572
  func.nativeFunc = nativeFunc;
6,517✔
2573
  nativeFunc.id = this.functionCounter_++;
6,517✔
2574
  this.setProperty(func, 'name', nativeFunc.name,
6,517✔
2575
      Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
6,517✔
2576
  return func;
6,517✔
2577
};
6,517✔
2578

1✔
2579
/**
1✔
2580
 * Create a new native asynchronous function.
1✔
2581
 * @param {!Function} asyncFunc JavaScript function.
1✔
2582
 * @returns {!Interpreter.Object} New function.
1✔
2583
 */
1✔
2584
Interpreter.prototype.createAsyncFunction = function(asyncFunc) {
1✔
2585
  var func = this.createFunctionBase_(asyncFunc.length, true);
235✔
2586
  func.asyncFunc = asyncFunc;
235✔
2587
  asyncFunc.id = this.functionCounter_++;
235✔
2588
  this.setProperty(func, 'name', asyncFunc.name,
235✔
2589
      Interpreter.READONLY_NONENUMERABLE_DESCRIPTOR);
235✔
2590
  return func;
235✔
2591
};
235✔
2592

1✔
2593
/**
1✔
2594
 * Converts from a native JavaScript object or value to a JS-Interpreter object.
1✔
2595
 * Can handle JSON-style values, regular expressions, dates and functions.
1✔
2596
 * Does NOT handle cycles.
1✔
2597
 * @param {*} nativeObj The native JavaScript object to be converted.
1✔
2598
 * @param {Object=} opt_cycles Cycle detection object (used by recursive calls).
1✔
2599
 * @returns {Interpreter.Value} The equivalent JS-Interpreter object.
1✔
2600
 */
1✔
2601
Interpreter.prototype.nativeToPseudo = function(nativeObj, opt_cycles) {
1✔
2602
  if (nativeObj === null || nativeObj === undefined ||
8✔
2603
      nativeObj === true || nativeObj === false ||
8✔
2604
      typeof nativeObj === 'string' || typeof nativeObj === 'number') {
8✔
2605
    // Primitive value.
4✔
2606
    return nativeObj;
4✔
2607
  }
4✔
2608
  if (nativeObj instanceof Interpreter.Object) {
8!
2609
    throw Error('Object is already pseudo');
×
2610
  }
×
2611

4✔
2612
  // Look up if this object has already been converted.
4✔
2613
  var cycles = opt_cycles || {
4✔
2614
    pseudo: [],
4✔
2615
    native: [],
4✔
2616
  };
8✔
2617
  var index = cycles.native.indexOf(nativeObj);
8✔
2618
  if (index !== -1) {
8!
2619
    return cycles.pseudo[index];
×
2620
  }
×
2621
  cycles.native.push(nativeObj);
4✔
2622

4✔
2623
  if (nativeObj instanceof RegExp) {
8!
2624
    var pseudoRegexp = this.createObjectProto(this.REGEXP_PROTO);
×
2625
    this.populateRegExp(pseudoRegexp, nativeObj);
×
2626
    cycles.pseudo.push(pseudoRegexp);
×
2627
    return pseudoRegexp;
×
2628
  }
×
2629

4✔
2630
  if (nativeObj instanceof Date) {
8!
2631
    var pseudoDate = this.createObjectProto(this.DATE_PROTO);
×
2632
    pseudoDate.data = new Date(nativeObj.valueOf());
×
2633
    cycles.pseudo.push(pseudoDate);
×
2634
    return pseudoDate;
×
2635
  }
×
2636

4✔
2637
  if (typeof nativeObj === 'function') {
8!
2638
    var thisInterpreter = this;
×
2639
    var wrapper = function() {
×
2640
      var args = Array.prototype.slice.call(arguments).map(function(i) {
×
2641
          return thisInterpreter.pseudoToNative(i);
×
2642
      });
×
2643
      var value = nativeObj.apply(thisInterpreter, args);
×
2644
      return thisInterpreter.nativeToPseudo(value);
×
2645
    };
×
2646
    var prototype = Object.getOwnPropertyDescriptor(nativeObj, 'prototype');
×
2647
    var pseudoFunction = this.createNativeFunction(wrapper, !!prototype);
×
2648
    cycles.pseudo.push(pseudoFunction);
×
2649
    return pseudoFunction;
×
2650
  }
×
2651

4✔
2652
  var pseudoObj;
4✔
2653
  if (Array.isArray(nativeObj)) {  // Array.
8✔
2654
    pseudoObj = this.createArray();
3✔
2655
  } else {  // Object.
8✔
2656
    pseudoObj = this.createObjectProto(this.OBJECT_PROTO);
1✔
2657
  }
1✔
2658
  cycles.pseudo.push(pseudoObj);
4✔
2659
  for (var key in nativeObj) {
4✔
2660
    this.setProperty(pseudoObj, key,
4✔
2661
        this.nativeToPseudo(nativeObj[key], cycles));
4✔
2662
  }
4✔
2663
  return pseudoObj;
4✔
2664
};
4✔
2665

1✔
2666
/**
1✔
2667
 * Converts from a JS-Interpreter object to native JavaScript object.
1✔
2668
 * Can handle JSON-style values, regular expressions, and dates.
1✔
2669
 * Does handle cycles.
1✔
2670
 * @param {Interpreter.Value} pseudoObj The JS-Interpreter object to be
1✔
2671
 * converted.
1✔
2672
 * @param {Object=} opt_cycles Cycle detection object (used by recursive calls).
1✔
2673
 * @returns {*} The equivalent native JavaScript object or value.
1✔
2674
 */
1✔
2675
Interpreter.prototype.pseudoToNative = function(pseudoObj, opt_cycles) {
1✔
2676
  if (pseudoObj === null || pseudoObj === undefined ||
29✔
2677
      pseudoObj === true || pseudoObj === false ||
29✔
2678
      typeof pseudoObj === 'string' || typeof pseudoObj === 'number') {
29✔
2679
    // Primitive value.
14✔
2680
    return pseudoObj;
14✔
2681
  }
14✔
2682
  if (!(pseudoObj instanceof Interpreter.Object)) {
29✔
2683
    throw Error('Object is not pseudo');
1✔
2684
  }
1✔
2685

14✔
2686
  // Look up if this object has already been converted.
14✔
2687
  var cycles = opt_cycles || {
29✔
2688
    pseudo: [],
10✔
2689
    native: [],
10✔
2690
  };
29✔
2691
  var index = cycles.pseudo.indexOf(pseudoObj);
29✔
2692
  if (index !== -1) {
29✔
2693
    return cycles.native[index];
2✔
2694
  }
2✔
2695
  cycles.pseudo.push(pseudoObj);
12✔
2696

12✔
2697
  if ((pseudoObj.proto === this.NUMBER.properties['prototype'] ||
29✔
2698
       pseudoObj.proto === this.BOOLEAN.properties['prototype'] ||
29✔
2699
       pseudoObj.proto === this.STRING.properties['prototype']) &&
29✔
2700
      (pseudoObj.data === true || pseudoObj.data === false ||
2✔
2701
       typeof pseudoObj.data === 'string' || typeof pseudoObj.data === 'number')) {
29✔
2702
      var nativeBox = Object(pseudoObj.data);
2✔
2703
      cycles.native.push(nativeBox);
2✔
2704
      return nativeBox;
2✔
2705
  }
2✔
2706

10✔
2707
  if (this.isa(pseudoObj, this.REGEXP)) {  // Regular expression.
29✔
2708
    var nativeRegExp = new RegExp(pseudoObj.data.source, pseudoObj.data.flags);
2✔
2709
    nativeRegExp.lastIndex = pseudoObj.data.lastIndex;
2✔
2710
    cycles.native.push(nativeRegExp);
2✔
2711
    return nativeRegExp;
2✔
2712
  }
2✔
2713

8✔
2714
  if (this.isa(pseudoObj, this.DATE)) {  // Date.
29✔
2715
    var nativeDate = new Date(pseudoObj.data.valueOf());
1✔
2716
    cycles.native.push(nativeDate);
1✔
2717
    return nativeDate;
1✔
2718
  }
1✔
2719

7✔
2720
  // Functions are not supported for security reasons.  Probably not a great
7✔
2721
  // idea for the JS-Interpreter to be able to create native JS functions.
7✔
2722
  // Still, if that floats your boat, this is where you'd add it.  Good luck.
7✔
2723

7✔
2724
  var nativeObj = this.isa(pseudoObj, this.ARRAY) ? [] : {};
29✔
2725
  cycles.native.push(nativeObj);
29✔
2726
  var val;
29✔
2727
  for (var key in pseudoObj.properties) {
29✔
2728
    val = this.pseudoToNative(pseudoObj.properties[key], cycles);
13✔
2729
    // Use defineProperty to avoid side effects if setting '__proto__'.
13✔
2730
    Object.defineProperty(nativeObj, key,
13✔
2731
        {'value': val, 'writable': true, 'enumerable': true,
13✔
2732
         'configurable': true});
13✔
2733
  }
13✔
2734
  return nativeObj;
7✔
2735
};
7✔
2736

1✔
2737
/**
1✔
2738
 * Look up the prototype for this value.
1✔
2739
 * @param {Interpreter.Value} value Data object.
1✔
2740
 * @returns {Interpreter.Object} Prototype object, null if none.
1✔
2741
 */
1✔
2742
Interpreter.prototype.getPrototype = function(value) {
1✔
2743
  switch (typeof value) {
163,503✔
2744
    case 'number':
163,503✔
2745
      return this.NUMBER.properties['prototype'];
3✔
2746
    case 'boolean':
163,503!
2747
      return this.BOOLEAN.properties['prototype'];
×
2748
    case 'string':
163,503✔
2749
      return this.STRING.properties['prototype'];
4✔
2750
  }
163,503✔
2751
  if (value) {
163,496✔
2752
    return value.proto;
163,496✔
2753
  }
163,496✔
2754
  return null;
×
2755
};
×
2756

1✔
2757
/**
1✔
2758
 * Fetch a property value from a data object.
1✔
2759
 * @param {Interpreter.Value} obj Data object.
1✔
2760
 * @param {Interpreter.Value} name Name of property.
1✔
2761
 * @returns {Interpreter.Value} Property value (may be undefined).
1✔
2762
 */
1✔
2763
Interpreter.prototype.getProperty = function(obj, name) {
1✔
2764
  if (this.getterStep_) {
11,336!
2765
    throw Error('Getter not supported in that context');
×
2766
  }
×
2767
  name = String(name);
11,336✔
2768
  if (obj === undefined || obj === null) {
11,336!
2769
    this.throwException(this.TYPE_ERROR,
×
2770
                        "Cannot read property '" + name + "' of " + obj);
×
2771
  }
×
2772
  if (typeof obj === 'object' && !(obj instanceof Interpreter.Object)) {
11,336!
2773
    throw TypeError('Expecting native value or pseudo object');
×
2774
  }
×
2775
  if (name === 'length') {
11,336✔
2776
    // Special cases for magic length property.
6✔
2777
    if (this.isa(obj, this.STRING)) {
6!
2778
      return String(obj).length;
×
2779
    }
×
2780
  } else if (name.charCodeAt(0) < 0x40) {
11,336✔
2781
    // Might have numbers in there?
12✔
2782
    // Special cases for string array indexing
12✔
2783
    if (this.isa(obj, this.STRING)) {
12!
2784
      var n = Interpreter.legalArrayIndex(name);
×
2785
      if (!isNaN(n) && n < String(obj).length) {
×
2786
        return String(obj)[n];
×
2787
      }
×
2788
    }
×
2789
  }
12✔
2790
  do {
11,336✔
2791
    if (obj.properties && name in obj.properties) {
11,357✔
2792
      var getter = obj.getter[name];
11,335✔
2793
      if (getter) {
11,335!
2794
        // Flag this function as being a getter and thus needing immediate
×
2795
        // execution (rather than being the value of the property).
×
2796
        this.getterStep_ = true;
×
2797
        return getter;
×
2798
      }
×
2799
      return obj.properties[name];
11,335✔
2800
    }
11,335✔
2801
  } while ((obj = this.getPrototype(obj)));
11,336✔
2802
  return undefined;
1✔
2803
};
1✔
2804

1✔
2805
/**
1✔
2806
 * Does the named property exist on a data object.
1✔
2807
 * @param {Interpreter.Object} obj Data object.
1✔
2808
 * @param {Interpreter.Value} name Name of property.
1✔
2809
 * @returns {boolean} True if property exists.
1✔
2810
 */
1✔
2811
Interpreter.prototype.hasProperty = function(obj, name) {
1✔
2812
  if (!(obj instanceof Interpreter.Object)) {
15,654!
2813
    throw TypeError('Primitive data type has no properties');
×
2814
  }
×
2815
  name = String(name);
15,654✔
2816
  if (name === 'length' && this.isa(obj, this.STRING)) {
15,654!
2817
    return true;
×
2818
  }
×
2819
  if (this.isa(obj, this.STRING)) {
15,654!
2820
    var n = Interpreter.legalArrayIndex(name);
×
2821
    if (!isNaN(n) && n < String(obj).length) {
×
2822
      return true;
×
2823
    }
×
2824
  }
×
2825
  do {
15,654✔
2826
    if (obj.properties && name in obj.properties) {
23,497✔
2827
      return true;
7,811✔
2828
    }
7,811✔
2829
  } while ((obj = this.getPrototype(obj)));
15,654✔
2830
  return false;
7,843✔
2831
};
7,843✔
2832

1✔
2833
/**
1✔
2834
 * Set a property value on a data object.
1✔
2835
 * @param {Interpreter.Value} obj Data object.
1✔
2836
 * @param {Interpreter.Value} name Name of property.
1✔
2837
 * @param {Interpreter.Value} value New property value.
1✔
2838
 *     Use Interpreter.VALUE_IN_DESCRIPTOR if value is handled by
1✔
2839
 *     descriptor instead.
1✔
2840
 * @param {Object=} opt_descriptor Optional descriptor object.
1✔
2841
 * @returns {!Interpreter.Object|undefined} Returns a setter function if one
1✔
2842
 *     needs to be called, otherwise undefined.
1✔
2843
 */
1✔
2844
Interpreter.prototype.setProperty = function(obj, name, value, opt_descriptor) {
1✔
2845
  if (this.setterStep_) {
69,500!
2846
    // Getter from previous call to setProperty was not handled.
×
2847
    throw Error('Setter not supported in that context');
×
2848
  }
×
2849
  name = String(name);
69,500✔
2850
  if (obj === undefined || obj === null) {
69,500!
2851
    this.throwException(this.TYPE_ERROR,
×
2852
                        "Cannot set property '" + name + "' of " + obj);
×
2853
  }
×
2854
  if (typeof obj === 'object' && !(obj instanceof Interpreter.Object)) {
69,500!
2855
    throw TypeError('Expecting native value or pseudo object');
×
2856
  }
×
2857
  if (opt_descriptor && ('get' in opt_descriptor || 'set' in opt_descriptor) &&
69,500!
2858
      ('value' in opt_descriptor || 'writable' in opt_descriptor)) {
69,500!
2859
    this.throwException(this.TYPE_ERROR, 'Invalid property descriptor. ' +
×
2860
        'Cannot both specify accessors and a value or writable attribute');
×
2861
  }
×
2862
  var strict = !this.stateStack || this.getScope().strict;
69,500✔
2863
  if (!(obj instanceof Interpreter.Object)) {
69,500!
2864
    if (strict) {
×
2865
      this.throwException(this.TYPE_ERROR, "Can't create property '" + name +
×
2866
                          "' on '" + obj + "'");
×
2867
    }
×
2868
    return;
×
2869
  }
×
2870
  if (this.isa(obj, this.STRING)) {
69,500✔
2871
    var n = Interpreter.legalArrayIndex(name);
893✔
2872
    if (name === 'length' || (!isNaN(n) && n < String(obj).length)) {
893!
2873
      // Can't set length or letters on String objects.
×
2874
      if (strict) {
×
2875
        this.throwException(this.TYPE_ERROR, "Cannot assign to read only " +
×
2876
            "property '" + name + "' of String '" + obj.data + "'");
×
2877
      }
×
2878
      return;
×
2879
    }
×
2880
  }
893✔
2881
  if (obj.class === 'Array') {
69,500✔
2882
    // Arrays have a magic length variable that is bound to the elements.
8,796✔
2883
    var len = obj.properties.length;
8,796✔
2884
    var i;
8,796✔
2885
    if (name === 'length') {
8,796!
2886
      // Delete elements if length is smaller.
×
2887
      if (opt_descriptor) {
×
2888
        if (!('value' in opt_descriptor)) {
×
2889
          return;
×
2890
        }
×
2891
        value = opt_descriptor['value'];
×
2892
      }
×
2893
      value = Interpreter.legalArrayLength(value);
×
2894
      if (isNaN(value)) {
×
2895
        this.throwException(this.RANGE_ERROR, 'Invalid array length');
×
2896
      }
×
2897
      if (value < len) {
×
2898
        for (i in obj.properties) {
×
2899
          i = Interpreter.legalArrayIndex(i);
×
2900
          if (!isNaN(i) && value <= i) {
×
2901
            delete obj.properties[i];
×
2902
          }
×
2903
        }
×
2904
      }
×
2905
    } else if (!isNaN(i = Interpreter.legalArrayIndex(name))) {
8,796✔
2906
      // Increase length if this index is larger.
7,856✔
2907
      obj.properties.length = Math.max(len, i + 1);
7,856✔
2908
    }
7,856✔
2909
  }
8,796✔
2910
  if (obj.preventExtensions && !(name in obj.properties)) {
69,500!
2911
    if (strict) {
×
2912
      this.throwException(this.TYPE_ERROR, "Can't add property '" + name +
×
2913
                          "', object is not extensible");
×
2914
    }
×
2915
    return;
×
2916
  }
×
2917
  if (opt_descriptor) {
69,500✔
2918
    // Define the property.
41,911✔
2919
    var descriptor = {};
41,911✔
2920
    if ('get' in opt_descriptor && opt_descriptor['get']) {
41,911!
2921
      obj.getter[name] = opt_descriptor['get'];
×
2922
      descriptor['get'] = this.setProperty.placeholderGet_;
×
2923
    }
×
2924
    if ('set' in opt_descriptor && opt_descriptor['set']) {
41,911!
2925
      obj.setter[name] = opt_descriptor['set'];
×
2926
      descriptor['set'] = this.setProperty.placeholderSet_;
×
2927
    }
×
2928
    if ('configurable' in opt_descriptor) {
41,911✔
2929
      descriptor['configurable'] = opt_descriptor['configurable'];
40,963✔
2930
    }
40,963✔
2931
    if ('enumerable' in opt_descriptor) {
41,911✔
2932
      descriptor['enumerable'] = opt_descriptor['enumerable'];
39,883✔
2933
    }
39,883✔
2934
    if ('writable' in opt_descriptor) {
41,911✔
2935
      descriptor['writable'] = opt_descriptor['writable'];
40,963✔
2936
      delete obj.getter[name];
40,963✔
2937
      delete obj.setter[name];
40,963✔
2938
    }
40,963✔
2939
    if ('value' in opt_descriptor) {
41,911✔
2940
      descriptor['value'] = opt_descriptor['value'];
2,030✔
2941
      delete obj.getter[name];
2,030✔
2942
      delete obj.setter[name];
2,030✔
2943
    } else if (value !== Interpreter.VALUE_IN_DESCRIPTOR) {
41,911✔
2944
      descriptor['value'] = value;
39,881✔
2945
      delete obj.getter[name];
39,881✔
2946
      delete obj.setter[name];
39,881✔
2947
    }
39,881✔
2948
    try {
41,911✔
2949
      Object.defineProperty(obj.properties, name, descriptor);
41,911✔
2950
    } catch (e) {
41,911✔
2951
      this.throwException(this.TYPE_ERROR, 'Cannot redefine property: ' + name);
1✔
2952
    }
1✔
2953
    // Now that the definition has suceeded, clean up any obsolete get/set funcs.
41,910✔
2954
    if ('get' in opt_descriptor && !opt_descriptor['get']) {
41,911!
2955
      delete obj.getter[name];
×
2956
    }
×
2957
    if ('set' in opt_descriptor && !opt_descriptor['set']) {
41,911!
2958
      delete obj.setter[name];
×
2959
    }
×
2960
  } else {
69,500✔
2961
    // Set the property.
27,589✔
2962
    if (value === Interpreter.VALUE_IN_DESCRIPTOR) {
27,589!
2963
      throw ReferenceError('Value not specified');
×
2964
    }
×
2965
    // Determine the parent (possibly self) where the property is defined.
27,589✔
2966
    var defObj = obj;
27,589✔
2967
    while (!(name in defObj.properties)) {
27,589✔
2968
      defObj = this.getPrototype(defObj);
51,388✔
2969
      if (!defObj) {
51,388✔
2970
        // This is a new property.
27,292✔
2971
        defObj = obj;
27,292✔
2972
        break;
27,292✔
2973
      }
27,292✔
2974
    }
51,388✔
2975
    if (defObj.setter && defObj.setter[name]) {
27,589!
2976
      this.setterStep_ = true;
×
2977
      return defObj.setter[name];
×
2978
    }
×
2979
    if (defObj.getter && defObj.getter[name]) {
27,589!
2980
      if (strict) {
×
2981
        this.throwException(this.TYPE_ERROR, "Cannot set property '" + name +
×
2982
            "' of object '" + obj + "' which only has a getter");
×
2983
      }
×
2984
    } else {
27,589✔
2985
      // No setter, simple assignment.
27,589✔
2986
      try {
27,589✔
2987
        obj.properties[name] = value;
27,589✔
2988
      } catch (_e) {
27,589✔
2989
        if (strict) {
1!
2990
          this.throwException(this.TYPE_ERROR, "Cannot assign to read only " +
×
2991
              "property '" + name + "' of object '" + obj + "'");
×
2992
        }
×
2993
      }
1✔
2994
    }
27,589✔
2995
  }
27,589✔
2996
};
69,500✔
2997

1✔
2998
Interpreter.prototype.setProperty.placeholderGet_ =
1✔
2999
    function() {throw Error('Placeholder getter');};
1✔
3000
Interpreter.prototype.setProperty.placeholderSet_ =
1✔
3001
    function() {throw Error('Placeholder setter');};
1✔
3002

1✔
3003
/**
1✔
3004
 * Convenience method for adding a native function as a non-enumerable property
1✔
3005
 * onto an object's prototype.
1✔
3006
 * @param {!Interpreter.Object} obj Data object.
1✔
3007
 * @param {Interpreter.Value} name Name of property.
1✔
3008
 * @param {!Function} wrapper Function object.
1✔
3009
 */
1✔
3010
Interpreter.prototype.setNativeFunctionPrototype =
1✔
3011
    function(obj, name, wrapper) {
1✔
3012
  this.setProperty(obj.properties['prototype'], name,
3,431✔
3013
      this.createNativeFunction(wrapper, false),
3,431✔
3014
      Interpreter.NONENUMERABLE_DESCRIPTOR);
3,431✔
3015
};
3,431✔
3016

1✔
3017
/**
1✔
3018
 * Convenience method for adding an async function as a non-enumerable property
1✔
3019
 * onto an object's prototype.
1✔
3020
 * @param {!Interpreter.Object} obj Data object.
1✔
3021
 * @param {Interpreter.Value} name Name of property.
1✔
3022
 * @param {!Function} wrapper Function object.
1✔
3023
 */
1✔
3024
Interpreter.prototype.setAsyncFunctionPrototype =
1✔
3025
    function(obj, name, wrapper) {
1✔
3026
  this.setProperty(obj.properties['prototype'], name,
235✔
3027
      this.createAsyncFunction(wrapper),
235✔
3028
      Interpreter.NONENUMERABLE_DESCRIPTOR);
235✔
3029
};
235✔
3030

1✔
3031
/**
1✔
3032
 * Returns the current scope from the stateStack.
1✔
3033
 * @returns {!Interpreter.Scope} Current scope.
1✔
3034
 */
1✔
3035
Interpreter.prototype.getScope = function() {
1✔
3036
  var scope = this.stateStack[this.stateStack.length - 1].scope;
83,827✔
3037
  if (!scope) {
83,827!
3038
    throw Error('No scope found');
×
3039
  }
×
3040
  return scope;
83,827✔
3041
};
83,827✔
3042

1✔
3043
/**
1✔
3044
 * Create a new scope dictionary.
1✔
3045
 * @param {!Object} node AST node defining the scope container
1✔
3046
 *     (e.g. a function).
1✔
3047
 * @param {Interpreter.Scope} parentScope Scope to link to.
1✔
3048
 * @returns {!Interpreter.Scope} New scope.
1✔
3049
 */
1✔
3050
Interpreter.prototype.createScope = function(node, parentScope) {
1✔
3051
  // Determine if this scope starts with `use strict`.
3,213✔
3052
  var strict = false;
3,213✔
3053
  if (parentScope && parentScope.strict) {
3,213!
3054
    strict = true;
×
3055
  } else {
3,213✔
3056
    var firstNode = node.body && node.body[0];
3,213✔
3057
    if (firstNode && firstNode.expression &&
3,213✔
3058
        firstNode.expression.type === 'Literal' &&
3,213!
3059
        firstNode.expression.value === 'use strict') {
3,213!
3060
      strict = true;
×
3061
    }
×
3062
  }
3,213✔
3063
  var object = this.createObjectProto(null);
3,213✔
3064
  var scope = new Interpreter.Scope(parentScope, strict, object);
3,213✔
3065
  if (!parentScope) {
3,213✔
3066
    this.initGlobal(scope.object);
47✔
3067
  }
47✔
3068
  this.populateScope_(node, scope);
3,213✔
3069
  return scope;
3,213✔
3070
};
3,213✔
3071

1✔
3072
/**
1✔
3073
 * Create a new special scope dictionary. Similar to createScope(), but
1✔
3074
 * doesn't assume that the scope is for a function body.
1✔
3075
 * This is used for 'catch' clauses, 'with' statements,
1✔
3076
 * and named function expressions.
1✔
3077
 * @param {!Interpreter.Scope} parentScope Scope to link to.
1✔
3078
 * @param {Interpreter.Object=} opt_object Optional object to transform into
1✔
3079
 *     scope.
1✔
3080
 * @returns {!Interpreter.Scope} New scope.
1✔
3081
 */
1✔
3082
Interpreter.prototype.createSpecialScope = function(parentScope, opt_object) {
1✔
3083
  if (!parentScope) {
329!
3084
    throw Error('parentScope required');
×
3085
  }
×
3086
  var object = opt_object || this.createObjectProto(null);
329✔
3087
  return new Interpreter.Scope(parentScope, parentScope.strict, object);
329✔
3088
};
329✔
3089

1✔
3090
/**
1✔
3091
 * Retrieves a value from the scope chain.
1✔
3092
 * @param {string} name Name of variable.
1✔
3093
 * @returns {Interpreter.Value} Any value.
1✔
3094
 *   May be flagged as being a getter and thus needing immediate execution
1✔
3095
 *   (rather than being the value of the property).
1✔
3096
 */
1✔
3097
Interpreter.prototype.getValueFromScope = function(name) {
1✔
3098
  var scope = this.getScope();
36,451✔
3099
  while (scope && scope !== this.globalScope) {
36,451✔
3100
    if (name in scope.object.properties) {
42,692✔
3101
      return scope.object.properties[name];
32,696✔
3102
    }
32,696✔
3103
    scope = scope.parentScope;
9,996✔
3104
  }
9,996✔
3105
  // The root scope is also an object which has inherited properties and
3,755✔
3106
  // could also have getters.
3,755✔
3107
  if (scope === this.globalScope && this.hasProperty(scope.object, name)) {
36,451✔
3108
    return this.getProperty(scope.object, name);
3,755✔
3109
  }
3,755✔
3110
  // Typeof operator is unique: it can safely look at non-defined variables.
×
3111
  var prevNode = this.stateStack[this.stateStack.length - 1].node;
×
3112
  if (prevNode.type === 'UnaryExpression' &&
×
3113
      prevNode.operator === 'typeof') {
36,451!
3114
    return undefined;
×
3115
  }
×
3116
  this.throwException(this.REFERENCE_ERROR, name + ' is not defined');
×
3117
};
×
3118

1✔
3119
/**
1✔
3120
 * Sets a value to the current scope.
1✔
3121
 * @param {string} name Name of variable.
1✔
3122
 * @param {Interpreter.Value} value Value.
1✔
3123
 * @returns {!Interpreter.Object|undefined} Returns a setter function if one
1✔
3124
 *     needs to be called, otherwise undefined.
1✔
3125
 */
1✔
3126
Interpreter.prototype.setValueToScope = function(name, value) {
1✔
3127
  var scope = this.getScope();
2,289✔
3128
  var strict = scope.strict;
2,289✔
3129
  while (scope && scope !== this.globalScope) {
2,289✔
3130
    if (name in scope.object.properties) {
2,182✔
3131
      try {
2,182✔
3132
        scope.object.properties[name] = value;
2,182✔
3133
      } catch (_e) {
2,182!
3134
        if (strict) {
×
3135
          this.throwException(this.TYPE_ERROR,
×
3136
              "Cannot assign to read only variable '" + name + "'");
×
3137
        }
×
3138
      }
×
3139
      return undefined;
2,182✔
3140
    }
2,182✔
3141
    scope = scope.parentScope;
×
3142
  }
×
3143
  // The root scope is also an object which has readonly properties and
107✔
3144
  // could also have setters.
107✔
3145
  if (scope === this.globalScope &&
107✔
3146
      (!strict || this.hasProperty(scope.object, name))) {
2,289!
3147
    return this.setProperty(scope.object, name, value);
107✔
3148
  }
107✔
3149
  this.throwException(this.REFERENCE_ERROR, name + ' is not defined');
×
3150
};
×
3151

1✔
3152
/**
1✔
3153
 * Create a new scope for the given node and populate it with all variables
1✔
3154
 * and named functions.
1✔
3155
 * @param {!Object} node AST node (usually a program or function when initally
1✔
3156
 *   calling this function, though it recurses to scan many child nodes).
1✔
3157
 * @param {!Interpreter.Scope} scope Scope dictionary to populate.
1✔
3158
 * @returns {!Object} Map of all variable and function names.
1✔
3159
 * @private
1✔
3160
 */
1✔
3161
Interpreter.prototype.populateScope_ = function(node, scope) {
1✔
3162
  var variableCache;
5,783✔
3163
  if (!node.variableCache_) {
5,783✔
3164
    variableCache = Object.create(null);
2,954✔
3165
    switch (node.type) {
2,954✔
3166
      case 'VariableDeclaration':
2,954✔
3167
        for (var i = 0; i < node.declarations.length; i++) {
292✔
3168
          variableCache[node.declarations[i].id.name] = true;
294✔
3169
        }
294✔
3170
        break;
292✔
3171
      case 'FunctionDeclaration':
2,954✔
3172
        variableCache[node.id.name] = node;
48✔
3173
        break;
48✔
3174
      case 'BlockStatement':
2,954✔
3175
      case 'CatchClause':
2,954✔
3176
      case 'DoWhileStatement':
2,954✔
3177
      case 'ForInStatement':
2,954✔
3178
      case 'ForStatement':
2,954✔
3179
      case 'IfStatement':
2,954✔
3180
      case 'LabeledStatement':
2,954✔
3181
      case 'Program':
2,954✔
3182
      case 'SwitchCase':
2,954✔
3183
      case 'SwitchStatement':
2,954✔
3184
      case 'TryStatement':
2,954✔
3185
      case 'WithStatement':
2,954✔
3186
      case 'WhileStatement':
2,954✔
3187
        // All the structures within which a variable or function could hide.
686✔
3188
        var nodeClass = node.constructor;
686✔
3189
        for (var name in node) {
686✔
3190
          if (name === 'loc') continue;
2,821✔
3191
          var prop = node[name];
2,135✔
3192
          if (prop && typeof prop === 'object') {
2,821✔
3193
            var childCache;
990✔
3194
            if (Array.isArray(prop)) {
990✔
3195
              for (var i = 0; i < prop.length; i++) {
393✔
3196
                if (prop[i] && prop[i].constructor === nodeClass) {
1,972✔
3197
                  childCache = this.populateScope_(prop[i], scope);
1,972✔
3198
                  for (var name in childCache) {
1,972✔
3199
                    variableCache[name] = childCache[name];
342✔
3200
                  }
342✔
3201
                }
1,972✔
3202
              }
1,972✔
3203
            } else {
990✔
3204
              if (prop.constructor === nodeClass) {
597✔
3205
                childCache = this.populateScope_(prop, scope);
597✔
3206
                for (var name in childCache) {
597✔
3207
                  variableCache[name] = childCache[name];
7✔
3208
                }
7✔
3209
              }
597✔
3210
            }
597✔
3211
          }
990✔
3212
        }
2,821✔
3213
    }
2,954✔
3214
    node.variableCache_ = variableCache;
2,954✔
3215
  } else {
5,783✔
3216
    variableCache = node.variableCache_;
2,829✔
3217
  }
2,829✔
3218
  for (var name in variableCache) {
5,783✔
3219
    if (variableCache[name] === true) {
2,627✔
3220
      this.setProperty(scope.object, name, undefined,
2,531✔
3221
          Interpreter.VARIABLE_DESCRIPTOR);
2,531✔
3222
    } else {
2,627✔
3223
      this.setProperty(scope.object, name,
96✔
3224
          this.createFunction(variableCache[name], scope),
96✔
3225
          Interpreter.VARIABLE_DESCRIPTOR);
96✔
3226
    }
96✔
3227
  }
2,627✔
3228
  return variableCache;
5,783✔
3229
};
5,783✔
3230

1✔
3231
/**
1✔
3232
 * Is the current state directly being called with as a construction with 'new'.
1✔
3233
 * @returns {boolean} True if 'new foo()', false if 'foo()'.
1✔
3234
 */
1✔
3235
Interpreter.prototype.calledWithNew = function() {
1✔
3236
  return this.stateStack[this.stateStack.length - 1].isConstructor;
13✔
3237
};
13✔
3238

1✔
3239
/**
1✔
3240
 * Gets a value from the scope chain or from an object property.
1✔
3241
 * @param {!Array} ref Name of variable or object/propname tuple.
1✔
3242
 * @returns {Interpreter.Value} Any value.
1✔
3243
 *   May be flagged as being a getter and thus needing immediate execution
1✔
3244
 *   (rather than being the value of the property).
1✔
3245
 */
1✔
3246
Interpreter.prototype.getValue = function(ref) {
1✔
3247
  if (ref[0] === Interpreter.SCOPE_REFERENCE) {
5,092✔
3248
    // A null/varname variable lookup.
3,038✔
3249
    return this.getValueFromScope(ref[1]);
3,038✔
3250
  } else {
5,092✔
3251
    // An obj/prop components tuple (foo.bar).
2,054✔
3252
    return this.getProperty(ref[0], ref[1]);
2,054✔
3253
  }
2,054✔
3254
};
5,092✔
3255

1✔
3256
/**
1✔
3257
 * Sets a value to the scope chain or to an object property.
1✔
3258
 * @param {!Array} ref Name of variable or object/propname tuple.
1✔
3259
 * @param {Interpreter.Value} value Value.
1✔
3260
 * @returns {!Interpreter.Object|undefined} Returns a setter function if one
1✔
3261
 *     needs to be called, otherwise undefined.
1✔
3262
 */
1✔
3263
Interpreter.prototype.setValue = function(ref, value) {
1✔
3264
  if (ref[0] === Interpreter.SCOPE_REFERENCE) {
4,319✔
3265
    // A null/varname variable lookup.
66✔
3266
    return this.setValueToScope(ref[1], value);
66✔
3267
  }
66✔
3268
  // An obj/prop components tuple (foo.bar).
4,253✔
3269
  return this.setProperty(ref[0], ref[1], value);
4,253✔
3270
};
4,253✔
3271

1✔
3272
/**
1✔
3273
 * Throw an exception in the interpreter that can be handled by an
1✔
3274
 * interpreter try/catch statement.  If unhandled, a real exception will
1✔
3275
 * be thrown.  Can be called with either an error class and a message, or
1✔
3276
 * with an actual object to be thrown.
1✔
3277
 * @param {!Interpreter.Object|Interpreter.Value} errorClass Type of error
1✔
3278
 *   (if message is provided) or the value to throw (if no message).
1✔
3279
 * @param {string=} opt_message Message being thrown.
1✔
3280
 */
1✔
3281
Interpreter.prototype.throwException = function(errorClass, opt_message) {
1✔
3282
  if (!this.globalScope) {
7!
3283
    // This is an error being thrown in the initialization, throw a real error.
×
3284
    throw (opt_message === undefined) ? errorClass : opt_message;
×
3285
  }
×
3286
  if (opt_message === undefined ||
7✔
3287
      !(errorClass instanceof Interpreter.Object)) {
7!
3288
    var error = errorClass;  // This is a value to throw, not an error class.
×
3289
  } else {
7✔
3290
    var error = this.createObject(errorClass);
7✔
3291
    this.populateError(error, opt_message);
7✔
3292
  }
7✔
3293
  this.unwind(Interpreter.Completion.THROW, error, undefined);
7✔
3294
  // Abort anything related to the current step.
7✔
3295
  throw Interpreter.STEP_ERROR;
7✔
3296
};
7✔
3297

1✔
3298
/**
1✔
3299
 * Unwind the stack to the innermost relevant enclosing TryStatement,
1✔
3300
 * For/ForIn/WhileStatement or Call/NewExpression.  If this results in
1✔
3301
 * the stack being completely unwound the thread will be terminated
1✔
3302
 * and the appropriate error being thrown.
1✔
3303
 * @param {Interpreter.Completion} type Completion type.
1✔
3304
 * @param {Interpreter.Value} value Value computed, returned or thrown.
1✔
3305
 * @param {string|undefined} label Target label for break or return.
1✔
3306
 */
1✔
3307
Interpreter.prototype.unwind = function(type, value, label) {
1✔
3308
  if (type === Interpreter.Completion.NORMAL) {
1,994!
3309
    throw TypeError('Should not unwind for NORMAL completions');
×
3310
  }
×
3311

1,994✔
3312
  loop: for (var stack = this.stateStack; stack.length > 0; stack.pop()) {
1,994✔
3313
    var state = stack[stack.length - 1];
5,989✔
3314
    switch (state.node.type) {
5,989✔
3315
      case 'TryStatement':
5,989!
3316
        state.cv = {type: type, value: value, label: label};
×
3317
        return;
×
3318
      case 'CallExpression':
5,989✔
3319
      case 'NewExpression':
5,989✔
3320
        if (type === Interpreter.Completion.RETURN) {
1,997✔
3321
          state.value = value;
1,987✔
3322
          return;
1,987✔
3323
        } else if (type === Interpreter.Completion.BREAK ||
1,997✔
3324
            type === Interpreter.Completion.CONTINUE) {
10!
3325
          throw Error('Unsyntactic break/continue not rejected by Acorn');
×
3326
        }
×
3327
        break;
10✔
3328
      case 'Program':
5,989✔
3329
        if (type === Interpreter.Completion.RETURN) {
7!
3330
          // While a return outside of a function call isn't normally possible,
×
3331
          // this can happen if a setTimeout/setInterval task returns.
×
3332
          return;
×
3333
        }
×
3334
        // Don't pop the stateStack.
7✔
3335
        // Leave the root scope on the tree in case the program is appended to.
7✔
3336
        state.done = true;
7✔
3337
        break loop;
7✔
3338
    }
5,989✔
3339
    if (type === Interpreter.Completion.BREAK) {
5,989!
3340
      if (label ? (state.labels && state.labels.indexOf(label) !== -1) :
×
3341
          (state.isLoop || state.isSwitch)) {
×
3342
        stack.pop();
×
3343
        return;
×
3344
      }
×
3345
    } else if (type === Interpreter.Completion.CONTINUE) {
5,989!
3346
      if (label ? (state.labels && state.labels.indexOf(label) !== -1) :
×
3347
          state.isLoop) {
×
3348
        return;
×
3349
      }
×
3350
    }
×
3351
  }
5,989✔
3352

7✔
3353
  // Unhandled completion.  Throw a real error.
7✔
3354
  var realError;
7✔
3355
  if (this.isa(value, this.ERROR)) {
7✔
3356
    var errorTable = {
7✔
3357
      'EvalError': EvalError,
7✔
3358
      'RangeError': RangeError,
7✔
3359
      'ReferenceError': ReferenceError,
7✔
3360
      'SyntaxError': SyntaxError,
7✔
3361
      'TypeError': TypeError,
7✔
3362
      'URIError': URIError,
7✔
3363
    };
7✔
3364
    var name = String(this.getProperty(value, 'name'));
7✔
3365
    var message = this.getProperty(value, 'message').valueOf();
7✔
3366
    var errorConstructor = errorTable[name] || Error;
7✔
3367
    realError = errorConstructor(message);
7✔
3368
    realError.stack = String(this.getProperty(value, 'stack'));
7✔
3369
  } else {
1,994!
3370
    realError = String(value);
×
3371
  }
×
3372
  // Overwrite the previous (more or less random) interpreter return value.
7✔
3373
  // Replace it with the error.
7✔
3374
  this.value = realError;
7✔
3375
  throw realError;
7✔
3376
};
7✔
3377

1✔
3378
/**
1✔
3379
 * AST to code.  Summarizes the expression at the given node.  Currently
1✔
3380
 * not guaranteed to be correct or complete.  Used for error messages.
1✔
3381
 * E.g. `escape('hello') + 42` -> 'escape(...) + 42'
1✔
3382
 * @param {!Object} node AST node.
1✔
3383
 * @returns {string} Code string.
1✔
3384
 */
1✔
3385
Interpreter.prototype.nodeSummary = function(node) {
1✔
3386
  switch (node.type) {
3✔
3387
    case 'ArrayExpression':
3!
3388
      return '[...]';
×
3389
    case 'BinaryExpression':
3!
3390
    case 'LogicalExpression':
3!
3391
      return this.nodeSummary(node.left) + ' ' + node.operator + ' ' +
×
3392
          this.nodeSummary(node.right);
×
3393
    case 'CallExpression':
3!
3394
      return this.nodeSummary(node.callee) + '(...)';
×
3395
    case 'ConditionalExpression':
3!
3396
      return this.nodeSummary(node.test) + ' ? ' +
×
3397
          this.nodeSummary(node.consequent) + ' : ' +
×
3398
          this.nodeSummary(node.alternate);
×
3399
    case 'Identifier':
3✔
3400
      return node.name;
2✔
3401
    case 'Literal':
3!
3402
      return node.raw;
×
3403
    case 'MemberExpression':
3✔
3404
      var obj = this.nodeSummary(node.object);
1✔
3405
      var prop = this.nodeSummary(node.property);
1✔
3406
      return node.computed ? (obj + '[' + prop + ']') : (obj + '.' + prop);
1!
3407
    case 'NewExpression':
3!
3408
      return 'new ' + this.nodeSummary(node.callee) + '(...)';
×
3409
    case 'ObjectExpression':
3!
3410
      return '{...}';
×
3411
    case 'ThisExpression':
3!
3412
      return 'this';
×
3413
    case 'UnaryExpression':
3!
3414
      return node.operator + ' ' + this.nodeSummary(node.argument);
×
3415
    case 'UpdateExpression':
3!
3416
      var argument = this.nodeSummary(node.argument);
×
3417
      return node.prefix ? node.operator + argument : argument + node.operator;
×
3418
  }
3✔
3419
  return '???';
×
3420
};
×
3421

1✔
3422
/**
1✔
3423
 * Create a new queued task.
1✔
3424
 * @param {boolean} isInterval True if setInterval, false if setTimeout.
1✔
3425
 * @param {!Arguments} args Arguments from setInterval and setTimeout.
1✔
3426
 *     [code, delay]
1✔
3427
 *     [functionRef, delay, param1, param2, param3, ...]
1✔
3428
 * @returns {number} PID of new task.
1✔
3429
 * @private
1✔
3430
 */
1✔
3431
Interpreter.prototype.createTask_ = function(isInterval, args) {
1✔
3432
  var parentState = this.stateStack[this.stateStack.length - 1];
1✔
3433
  var argsArray = Array.from(args);
1✔
3434
  var exec = argsArray.shift();
1✔
3435
  var delay = Math.max(Number(argsArray.shift() || 0), 0);
1✔
3436
  var node = this.newNode();
1✔
3437
  var scope, functionRef, ast;
1✔
3438

1✔
3439
  if ((exec instanceof Interpreter.Object) && exec.class === 'Function') {
1✔
3440
    // setTimeout/setInterval with a function reference.
1✔
3441
    functionRef = exec;
1✔
3442
    node.type = 'CallExpression';
1✔
3443
    scope = parentState.scope;
1✔
3444
  } else {
1!
3445
    // setTimeout/setInterval with code string.
×
3446
    try {
×
3447
      ast = this.parse_(String(exec), 'taskCode' + (this.taskCodeNumber_++));
×
3448
    } catch (e) {
×
3449
      // Acorn threw a SyntaxError.  Rethrow as a trappable error.
×
3450
      this.throwException(this.SYNTAX_ERROR, 'Invalid code: ' + e.message);
×
3451
    }
×
3452
    node.type = 'EvalProgram_';
×
3453
    node.body = ast.body;
×
3454
    // Change highlighting to encompas the string.
×
3455
    var execNode = parentState.node.arguments[0];
×
3456
    var execStart = execNode ? execNode.start : undefined;
×
3457
    var execEnd = execNode ? execNode.end : undefined;
×
3458
    Interpreter.stripLocations_(node, execStart, execEnd);
×
3459
    scope = this.globalScope;
×
3460
    argsArray.length = 0;
×
3461
  }
×
3462

1✔
3463
  var task = new Interpreter.Task(functionRef, argsArray, scope, node,
1✔
3464
      isInterval ? delay : -1);
1!
3465
  this.scheduleTask_(task, delay);
1✔
3466
  return task.pid;
1✔
3467
};
1✔
3468

1✔
3469
/**
1✔
3470
 * Schedule a task to execute at some time in the future.
1✔
3471
 * @param {!Interpreter.Task} task Task to schedule.
1✔
3472
 * @param {number} delay Number of ms before the task should execute.
1✔
3473
 * @private
1✔
3474
 */
1✔
3475
Interpreter.prototype.scheduleTask_ = function(task, delay) {
1✔
3476
  task.time = Date.now() + delay;
1✔
3477
  // For optimum efficiency we could do a binary search and inject the task
1✔
3478
  // at the right spot.  But 'push' & 'sort' is just two lines of code.
1✔
3479
  this.tasks.push(task);
1✔
3480
  this.tasks.sort(function(a, b) {return a.time - b.time});
1✔
3481
};
1✔
3482

1✔
3483
/**
1✔
3484
 * Delete a queued task.
1✔
3485
 * @param {number} pid PID of task to delete.
1✔
3486
 * @private
1✔
3487
 */
1✔
3488
Interpreter.prototype.deleteTask_ = function(pid) {
1✔
3489
  for (var i = 0; i < this.tasks.length; i++) {
×
3490
    if (this.tasks[i].pid == pid) {
×
3491
      this.tasks.splice(i, 1);
×
3492
      break;
×
3493
    }
×
3494
  }
×
3495
};
×
3496

1✔
3497
/**
1✔
3498
 * Find the next queued task that's due to run.
1✔
3499
 * @returns {Interpreter.State} Starting state of next task.  Null if no task.
1✔
3500
 * @private
1✔
3501
 */
1✔
3502
Interpreter.prototype.nextTask_ = function() {
1✔
3503
  var task = this.tasks[0];
1✔
3504
  if (!task || task.time > Date.now()) {
1!
3505
    return null;
×
3506
  }
×
3507
  // Found a task that's due to run.
1✔
3508
  this.tasks.shift();
1✔
3509
  if (task.interval >= 0) {
1!
3510
    this.scheduleTask_(task, task.interval);
×
3511
  }
×
3512
  var state = new Interpreter.State(task.node, task.scope);
1✔
3513
  if (task.functionRef) {
1✔
3514
    // setTimeout/setInterval with a function reference.
1✔
3515
    state.doneCallee_ = 2;
1✔
3516
    state.funcThis_ = this.globalObject;
1✔
3517
    state.func_ = task.functionRef;
1✔
3518
    state.doneArgs_ = true;
1✔
3519
    state.arguments_ = task.argsArray;
1✔
3520
  }
1✔
3521
  return state;
1✔
3522
};
1✔
3523

1✔
3524
/**
1✔
3525
 * Create a call to a getter function.
1✔
3526
 * @param {!Interpreter.Object} func Function to execute.
1✔
3527
 * @param {!Interpreter.Object|!Array} left
1✔
3528
 *     Name of variable or object/propname tuple.
1✔
3529
 * @private
1✔
3530
 */
1✔
3531
Interpreter.prototype.createGetter_ = function(func, left) {
1✔
3532
  if (!this.getterStep_) {
×
3533
    throw Error('Unexpected call to createGetter');
×
3534
  }
×
3535
  // Clear the getter flag.
×
3536
  this.getterStep_ = false;
×
3537
  // Normally `this` will be specified as the object component (o.x).
×
3538
  // Sometimes `this` is explicitly provided (o).
×
3539
  var funcThis = Array.isArray(left) ? left[0] : left;
×
3540
  var node = this.newNode();
×
3541
  node.type = 'CallExpression';
×
3542
  var state = new Interpreter.State(node,
×
3543
      this.stateStack[this.stateStack.length - 1].scope);
×
3544
  state.doneCallee_ = 2;
×
3545
  state.funcThis_ = funcThis;
×
3546
  state.func_ = func;
×
3547
  state.doneArgs_ = true;
×
3548
  state.arguments_ = [];
×
3549
  return state;
×
3550
};
×
3551

1✔
3552
/**
1✔
3553
 * Create a call to a setter function.
1✔
3554
 * @param {!Interpreter.Object} func Function to execute.
1✔
3555
 * @param {!Interpreter.Object|!Array} left
1✔
3556
 *     Name of variable or object/propname tuple.
1✔
3557
 * @param {Interpreter.Value} value Value to set.
1✔
3558
 * @private
1✔
3559
 */
1✔
3560
Interpreter.prototype.createSetter_ = function(func, left, value) {
1✔
3561
  if (!this.setterStep_) {
×
3562
    throw Error('Unexpected call to createSetter');
×
3563
  }
×
3564
  // Clear the setter flag.
×
3565
  this.setterStep_ = false;
×
3566
  // Normally `this` will be specified as the object component (o.x).
×
3567
  // Sometimes `this` is implicitly the global object (x).
×
3568
  var funcThis = Array.isArray(left) ? left[0] : this.globalObject;
×
3569
  var node = this.newNode();
×
3570
  node.type = 'CallExpression';
×
3571
  var state = new Interpreter.State(node,
×
3572
      this.stateStack[this.stateStack.length - 1].scope);
×
3573
  state.doneCallee_ = 2;
×
3574
  state.funcThis_ = funcThis;
×
3575
  state.func_ = func;
×
3576
  state.doneArgs_ = true;
×
3577
  state.arguments_ = [value];
×
3578
  return state;
×
3579
};
×
3580

1✔
3581
/**
1✔
3582
 * In non-strict mode `this` must be an object.
1✔
3583
 * Must not be called in strict mode.
1✔
3584
 * @param {Interpreter.Value} value Proposed value for `this`.
1✔
3585
 * @returns {!Interpreter.Object} Final value for `this`.
1✔
3586
 * @private
1✔
3587
 */
1✔
3588
Interpreter.prototype.boxThis_ = function(value) {
1✔
3589
  if (value === undefined || value === null) {
5,271✔
3590
    // `Undefined` and `null` are changed to the global object.
3,209✔
3591
    return this.globalObject;
3,209✔
3592
  }
3,209✔
3593
  if (!(value instanceof Interpreter.Object)) {
5,271✔
3594
    // Primitives must be boxed.
2✔
3595
    var box = this.createObjectProto(this.getPrototype(value));
2✔
3596
    box.data = value;
2✔
3597
    return box;
2✔
3598
  }
2✔
3599
  return value;
2,060✔
3600
};
2,060✔
3601

1✔
3602
/**
1✔
3603
 * Return the global scope object.
1✔
3604
 * @returns {!Interpreter.Scope} Scope object.
1✔
3605
 */
1✔
3606
Interpreter.prototype.getGlobalScope = function() {
1✔
3607
  return this.globalScope;
×
3608
};
×
3609

1✔
3610
/**
1✔
3611
 * Sets the global scope object.
1✔
3612
 * @param {!Interpreter.Scope} newScope Scope object.
1✔
3613
 */
1✔
3614
Interpreter.prototype.setGlobalScope = function(newScope) {
1✔
3615
  this.globalScope = newScope;
×
3616
  this.stateStack[0].scope = newScope;
×
3617
};
×
3618

1✔
3619
/**
1✔
3620
 * Return the state stack.
1✔
3621
 * @returns {!Array<!Interpreter.State>} State stack.
1✔
3622
 */
1✔
3623
Interpreter.prototype.getStateStack = function() {
1✔
3624
  return this.stateStack;
×
3625
};
×
3626

1✔
3627
/**
1✔
3628
 * Replace the state stack with a new one.
1✔
3629
 * @param {!Array<!Interpreter.State>} newStack New state stack.
1✔
3630
 */
1✔
3631
Interpreter.prototype.setStateStack = function(newStack) {
1✔
3632
  this.stateStack = newStack;
×
3633
};
×
3634

1✔
3635
/**
1✔
3636
 * Typedef for JS values.
1✔
3637
 * @typedef {!Interpreter.Object|boolean|number|string|undefined|null}
1✔
3638
 */
1✔
3639
Interpreter.Value;
1✔
3640

1✔
3641
/**
1✔
3642
 * Class for a state.
1✔
3643
 * @param {!Object} node AST node for the state.
1✔
3644
 * @param {!Interpreter.Scope} scope Scope object for the state.
1✔
3645
 * @constructor
1✔
3646
 */
1✔
3647
Interpreter.State = function(node, scope) {
1✔
3648
  this.node = node;
118,518✔
3649
  this.scope = scope;
118,518✔
3650
};
118,518✔
3651

1✔
3652
/**
1✔
3653
 * Class for a scope.
1✔
3654
 * @param {Interpreter.Scope} parentScope Parent scope.
1✔
3655
 * @param {boolean} strict True if "use strict".
1✔
3656
 * @param {!Interpreter.Object} object Object containing scope's variables.
1✔
3657
 * @struct
1✔
3658
 * @constructor
1✔
3659
 */
1✔
3660
Interpreter.Scope = function(parentScope, strict, object) {
1✔
3661
  this.parentScope = parentScope;
3,542✔
3662
  this.strict = strict;
3,542✔
3663
  this.object = object;
3,542✔
3664
};
3,542✔
3665

1✔
3666
/**
1✔
3667
 * Class for an object.
1✔
3668
 * @param {Interpreter.Object} proto Prototype object or null.
1✔
3669
 * @constructor
1✔
3670
 */
1✔
3671
Interpreter.Object = function(proto) {
1✔
3672
  this.getter = Object.create(null);
22,138✔
3673
  this.setter = Object.create(null);
22,138✔
3674
  this.properties = Object.create(null);
22,138✔
3675
  this.proto = proto;
22,138✔
3676
};
22,138✔
3677

1✔
3678
/** @type {Interpreter.Object} */
1✔
3679
Interpreter.Object.prototype.proto = null;
1✔
3680

1✔
3681
/** @type {string} */
1✔
3682
Interpreter.Object.prototype.class = 'Object';
1✔
3683

1✔
3684
/** @type {Date|RegExp|boolean|number|string|null} */
1✔
3685
Interpreter.Object.prototype.data = null;
1✔
3686

1✔
3687
/**
1✔
3688
 * Convert this object into a string.
1✔
3689
 * @returns {string} String value.
1✔
3690
 * @override
1✔
3691
 */
1✔
3692
Interpreter.Object.prototype.toString = function() {
1✔
3693
  if (!Interpreter.currentInterpreter_) {
3!
3694
    // Called from outside an interpreter.
×
3695
    return '[object Interpreter.Object]';
×
3696
  }
×
3697
  if (!(this instanceof Interpreter.Object)) {
3!
3698
    // Primitive value.
×
3699
    return String(this);
×
3700
  }
×
3701

3✔
3702
  if (this.class === 'Array') {
3!
3703
    // Array contents must not have cycles.
×
3704
    var cycles = Interpreter.toStringCycles_;
×
3705
    cycles.push(this);
×
3706
    try {
×
3707
      var strs = [];
×
3708
      // Truncate very long strings.  This is not part of the spec,
×
3709
      // but it prevents hanging the interpreter for gigantic arrays.
×
3710
      var maxLength = this.properties.length;
×
3711
      var truncated = false;
×
3712
      if (maxLength > 1024) {
×
3713
        maxLength = 1000;
×
3714
        truncated = true;
×
3715
      }
×
3716
      for (var i = 0; i < maxLength; i++) {
×
3717
        var value = this.properties[i];
×
3718
        strs[i] = ((value instanceof Interpreter.Object) &&
×
3719
            cycles.indexOf(value) !== -1) ? '...' : value;
×
3720
      }
×
3721
      if (truncated) {
×
3722
        strs.push('...');
×
3723
      }
×
3724
    } finally {
×
3725
      cycles.pop();
×
3726
    }
×
3727
    return strs.join(',');
×
3728
  }
×
3729

3✔
3730
  if (this.class === 'Error') {
3!
3731
    // Error name and message properties must not have cycles.
×
3732
    var cycles = Interpreter.toStringCycles_;
×
3733
    if (cycles.indexOf(this) !== -1) {
×
3734
      return '[object Error]';
×
3735
    }
×
3736
    var name, message;
×
3737
    // Bug: Does not support getters and setters for name or message.
×
3738
    var obj = this;
×
3739
    do {
×
3740
      if ('name' in obj.properties) {
×
3741
        name = obj.properties.name;
×
3742
        break;
×
3743
      }
×
3744
    } while ((obj = obj.proto));
×
3745
    obj = this;
×
3746
    do {
×
3747
      if ('message' in obj.properties) {
×
3748
        message = obj.properties.message;
×
3749
        break;
×
3750
      }
×
3751
    } while ((obj = obj.proto));
×
3752
    cycles.push(this);
×
3753
    try {
×
3754
      name = name && String(name);
×
3755
      message = message && String(message);
×
3756
    } finally {
×
3757
      cycles.pop();
×
3758
    }
×
3759
    return message ? name + ': ' + message : String(name);
×
3760
  }
×
3761

3✔
3762
  if (this.data !== null) {
3✔
3763
    // RegExp, Date, and boxed primitives.
2✔
3764
    return String(this.data);
2✔
3765
  }
2✔
3766

1✔
3767
  return '[object ' + this.class + ']';
1✔
3768
};
1✔
3769

1✔
3770
/**
1✔
3771
 * Return the object's value.
1✔
3772
 * @returns {Interpreter.Value} Value.
1✔
3773
 * @override
1✔
3774
 */
1✔
3775
Interpreter.Object.prototype.valueOf = function() {
1✔
3776
  if (!Interpreter.currentInterpreter_) {
1!
3777
    // Called from outside an interpreter.
×
3778
    return this;
×
3779
  }
×
3780
  if (this.data === undefined || this.data === null ||
1✔
3781
      this.data instanceof RegExp) {
1!
3782
    return this;  // An Object, RegExp, or primitive.
×
3783
  }
×
3784
  if (this.data instanceof Date) {
1!
3785
    return this.data.valueOf();  // Milliseconds.
×
3786
  }
×
3787
  return /** @type {(boolean|number|string)} */ (this.data);  // Boxed primitive.
1✔
3788
};
1✔
3789

1✔
3790
/**
1✔
3791
 * Class for a task.
1✔
3792
 * @param {!Interpreter.Object|undefined} functionRef Function to call.
1✔
3793
 * @param {!Array<Interpreter.Value>} argsArray Array of arguments.
1✔
3794
 * @param {!Interpreter.Scope} scope Scope for this task.
1✔
3795
 * @param {!Object} node AST node to execute.
1✔
3796
 * @param {number} interval Number of ms this task repeats.  -1 for no repeats.
1✔
3797
 * @struct
1✔
3798
 * @constructor
1✔
3799
 */
1✔
3800
Interpreter.Task = function(functionRef, argsArray, scope, node, interval) {
1✔
3801
  this.functionRef = functionRef;
1✔
3802
  this.argsArray = argsArray;
1✔
3803
  this.scope = scope;
1✔
3804
  this.node = node;
1✔
3805

1✔
3806
  this.interval = interval;
1✔
3807
  this.pid = ++Interpreter.Task.pid;
1✔
3808
  this.time = 0;
1✔
3809
};
1✔
3810

1✔
3811
Interpreter.Task.pid = 0;
1✔
3812

1✔
3813
///////////////////////////////////////////////////////////////////////////////
1✔
3814
// Functions to handle each node type.
1✔
3815
///////////////////////////////////////////////////////////////////////////////
1✔
3816

1✔
3817
Interpreter.prototype['stepArrayExpression'] = function(stack, state, node) {
1✔
3818
  var elements = node.elements;
16✔
3819
  var n = state.n_ || 0;
16✔
3820
  if (!state.array_) {
16✔
3821
    state.array_ = this.createArray();
5✔
3822
    state.array_.properties.length = elements.length;
5✔
3823
  } else {
16✔
3824
    this.setProperty(state.array_, n, state.value);
11✔
3825
    n++;
11✔
3826
  }
11✔
3827
  while (n < elements.length) {
16✔
3828
    // Skip missing elements - they're not defined, not undefined.
11✔
3829
    if (elements[n]) {
11✔
3830
      state.n_ = n;
11✔
3831
      return new Interpreter.State(elements[n], state.scope);
11✔
3832
    }
11✔
3833
    n++;
×
3834
  }
×
3835
  stack.pop();
5✔
3836
  stack[stack.length - 1].value = state.array_;
5✔
3837
};
5✔
3838

1✔
3839
Interpreter.prototype['stepAssignmentExpression'] =
1✔
3840
    function(stack, state, node) {
1✔
3841
  if (!state.doneLeft_) {
12,840✔
3842
    state.doneLeft_ = true;
4,280✔
3843
    var nextState = new Interpreter.State(node.left, state.scope);
4,280✔
3844
    nextState.components = true;
4,280✔
3845
    return nextState;
4,280✔
3846
  }
4,280✔
3847
  if (!state.doneRight_) {
12,840✔
3848
    if (!state.leftReference_) {
4,280✔
3849
      state.leftReference_ = state.value;
4,280✔
3850
    }
4,280✔
3851
    if (state.doneGetter_) {
4,280!
3852
      state.leftValue_ = state.value;
×
3853
    }
×
3854
    if (!state.doneGetter_ && node.operator !== '=') {
4,280✔
3855
      var leftValue = this.getValue(state.leftReference_);
27✔
3856
      state.leftValue_ = leftValue;
27✔
3857
      if (this.getterStep_) {
27!
3858
        // Call the getter function.
×
3859
        state.doneGetter_ = true;
×
3860
        var func = /** @type {!Interpreter.Object} */ (leftValue);
×
3861
        return this.createGetter_(func, state.leftReference_);
×
3862
      }
×
3863
    }
27✔
3864
    state.doneRight_ = true;
4,280✔
3865
    // When assigning an unnamed function to a variable, the function's name
4,280✔
3866
    // is set to the variable name.  Record the variable name in case the
4,280✔
3867
    // right side is a functionExpression.
4,280✔
3868
    // E.g. foo = function() {};
4,280✔
3869
    if (node.operator === '=' && node.left.type === 'Identifier') {
4,280!
3870
      state.destinationName = node.left.name;
×
3871
    }
×
3872
    return new Interpreter.State(node.right, state.scope);
4,280✔
3873
  }
4,280✔
3874
  if (state.doneSetter_) {
12,840!
3875
    // Return if setter function.
×
3876
    // Setter method on property has completed.
×
3877
    // Ignore its return value, and use the original set value instead.
×
3878
    stack.pop();
×
3879
    stack[stack.length - 1].value = state.setterValue_;
×
3880
    return;
×
3881
  }
×
3882
  var value = state.leftValue_;
4,280✔
3883
  var rightValue = state.value;
4,280✔
3884
  switch (node.operator) {
4,280✔
3885
    case '=':    value =    rightValue; break;
12,840✔
3886
    case '+=':   value +=   rightValue; break;
12,840!
3887
    case '-=':   value -=   rightValue; break;
12,840!
3888
    case '*=':   value *=   rightValue; break;
12,840✔
3889
    case '/=':   value /=   rightValue; break;
12,840!
3890
    case '%=':   value %=   rightValue; break;
12,840!
3891
    case '<<=':  value <<=  rightValue; break;
12,840!
3892
    case '>>=':  value >>=  rightValue; break;
12,840!
3893
    case '>>>=': value >>>= rightValue; break;
12,840!
3894
    case '&=':   value &=   rightValue; break;
12,840!
3895
    case '^=':   value ^=   rightValue; break;
12,840!
3896
    case '|=':   value |=   rightValue; break;
12,840!
3897
    default:
12,840!
3898
      throw SyntaxError('Unknown assignment expression: ' + node.operator);
×
3899
  }
12,840✔
3900
  var setter = this.setValue(state.leftReference_, value);
4,280✔
3901
  if (setter) {
12,840!
3902
    state.doneSetter_ = true;
×
3903
    state.setterValue_ = value;
×
3904
    return this.createSetter_(setter, state.leftReference_, value);
×
3905
  }
×
3906
  // Return if no setter function.
4,280✔
3907
  stack.pop();
4,280✔
3908
  stack[stack.length - 1].value = value;
4,280✔
3909
};
4,280✔
3910

1✔
3911
Interpreter.prototype['stepBinaryExpression'] = function(stack, state, node) {
1✔
3912
  if (!state.doneLeft_) {
35,814✔
3913
    state.doneLeft_ = true;
11,938✔
3914
    return new Interpreter.State(node.left, state.scope);
11,938✔
3915
  }
11,938✔
3916
  if (!state.doneRight_) {
35,814✔
3917
    state.doneRight_ = true;
11,938✔
3918
    state.leftValue_ = state.value;
11,938✔
3919
    return new Interpreter.State(node.right, state.scope);
11,938✔
3920
  }
11,938✔
3921
  stack.pop();
11,938✔
3922
  var leftValue = state.leftValue_;
11,938✔
3923
  var rightValue = state.value;
11,938✔
3924
  var value;
11,938✔
3925
  switch (node.operator) {
11,938✔
3926
    case '==':  value = leftValue ==  rightValue; break;
35,814!
3927
    case '!=':  value = leftValue !=  rightValue; break;
35,814!
3928
    case '===': value = leftValue === rightValue; break;
35,814!
3929
    case '!==': value = leftValue !== rightValue; break;
35,814✔
3930
    case '>':   value = leftValue >   rightValue; break;
35,814✔
3931
    case '>=':  value = leftValue >=  rightValue; break;
35,814!
3932
    case '<':   value = leftValue <   rightValue; break;
35,814✔
3933
    case '<=':  value = leftValue <=  rightValue; break;
35,814!
3934
    case '+':   value = leftValue +   rightValue; break;
35,814✔
3935
    case '-':   value = leftValue -   rightValue; break;
35,814!
3936
    case '*':   value = leftValue *   rightValue; break;
35,814!
3937
    case '/':   value = leftValue /   rightValue; break;
35,814!
3938
    case '%':   value = leftValue %   rightValue; break;
35,814!
3939
    case '&':   value = leftValue &   rightValue; break;
35,814!
3940
    case '|':   value = leftValue |   rightValue; break;
35,814!
3941
    case '^':   value = leftValue ^   rightValue; break;
35,814!
3942
    case '<<':  value = leftValue <<  rightValue; break;
35,814!
3943
    case '>>':  value = leftValue >>  rightValue; break;
35,814!
3944
    case '>>>': value = leftValue >>> rightValue; break;
35,814✔
3945
    case 'in':
35,814✔
3946
      if (!(rightValue instanceof Interpreter.Object)) {
11,899!
3947
        this.throwException(this.TYPE_ERROR,
×
3948
            "'in' expects an object, not '" + rightValue + "'");
×
3949
      }
×
3950
      value = this.hasProperty(rightValue, leftValue);
11,899✔
3951
      break;
11,899✔
3952
    case 'instanceof':
35,814!
3953
      if (!this.isa(rightValue, this.FUNCTION)) {
×
3954
        this.throwException(this.TYPE_ERROR,
×
3955
            "'instanceof' expects an object, not '" + rightValue + "'");
×
3956
      }
×
3957
      value = (leftValue instanceof Interpreter.Object) ?
×
3958
          this.isa(leftValue, rightValue) : false;
×
3959
      break;
×
3960
    default:
35,814!
3961
      throw SyntaxError('Unknown binary operator: ' + node.operator);
×
3962
  }
35,814✔
3963
  stack[stack.length - 1].value = value;
11,938✔
3964
};
11,938✔
3965

1✔
3966
Interpreter.prototype['stepBlockStatement'] = function(stack, state, node) {
1✔
3967
  var n = state.n_ || 0;
20,384✔
3968
  var expression = node.body[n];
20,384✔
3969
  if (expression) {
20,384✔
3970
    state.n_ = n + 1;
19,168✔
3971
    return new Interpreter.State(expression, state.scope);
19,168✔
3972
  }
19,168✔
3973
  stack.pop();
1,216✔
3974
};
1,216✔
3975

1✔
3976
Interpreter.prototype['stepBreakStatement'] = function(stack, state, node) {
1✔
3977
  var label = node.label && node.label.name;
×
3978
  this.unwind(Interpreter.Completion.BREAK, undefined, label);
×
3979
};
×
3980

1✔
3981
/**
1✔
3982
 * Number of evals called by the interpreter.
1✔
3983
 * @private
1✔
3984
 */
1✔
3985
Interpreter.prototype.evalCodeNumber_ = 0;
1✔
3986

1✔
3987
Interpreter.prototype['stepCallExpression'] = function(stack, state, node) {
1✔
3988
  // Handles both CallExpression and NewExpression.
29,826✔
3989
  if (!state.doneCallee_) {
29,826✔
3990
    state.doneCallee_ = 1;
5,270✔
3991
    // Components needed to determine value of `this`.
5,270✔
3992
    var nextState = new Interpreter.State(node.callee, state.scope);
5,270✔
3993
    nextState.components = true;
5,270✔
3994
    return nextState;
5,270✔
3995
  }
5,270✔
3996
  if (state.doneCallee_ === 1) {
29,826✔
3997
    // Determine value of the function.
5,270✔
3998
    state.doneCallee_ = 2;
5,270✔
3999
    var func = state.value;
5,270✔
4000
    if (Array.isArray(func)) {
5,270✔
4001
      state.func_ = this.getValue(func);
5,035✔
4002
      if (func[0] === Interpreter.SCOPE_REFERENCE) {
5,035✔
4003
        // (Globally or locally) named function.  Is it named 'eval'?
2,981✔
4004
        state.directEval_ = (func[1] === 'eval');
2,981✔
4005
      } else {
5,035✔
4006
        // Method function, `this` is object (ignored if invoked as `new`).
2,054✔
4007
        state.funcThis_ = func[0];
2,054✔
4008
      }
2,054✔
4009
      func = state.func_;
5,035✔
4010
      if (this.getterStep_) {
5,035!
4011
        // Call the getter function.
×
4012
        state.doneCallee_ = 1;
×
4013
        return this.createGetter_(/** @type {!Interpreter.Object} */ (func),
×
4014
            state.value);
×
4015
      }
×
4016
    } else {
5,270✔
4017
      // Already evaluated function: (function(){...})();
235✔
4018
      state.func_ = func;
235✔
4019
    }
235✔
4020
    state.arguments_ = [];
5,270✔
4021
    state.n_ = 0;
5,270✔
4022
  }
5,270✔
4023
  var func = state.func_;
24,556✔
4024
  if (!state.doneArgs_) {
29,826✔
4025
    if (state.n_ !== 0) {
19,293✔
4026
      state.arguments_.push(state.value);
14,023✔
4027
    }
14,023✔
4028
    if (node.arguments[state.n_]) {
19,293✔
4029
      return new Interpreter.State(node.arguments[state.n_++], state.scope);
14,024✔
4030
    }
14,024✔
4031
    // Determine value of `this` in function.
5,269✔
4032
    if (node.type === 'NewExpression') {
19,293✔
4033
      if (!(func instanceof Interpreter.Object) || func.illegalConstructor) {
8!
4034
        // Illegal: new escape();
×
4035
        this.throwException(this.TYPE_ERROR,
×
4036
            this.nodeSummary(node.callee) + ' is not a constructor');
×
4037
      }
×
4038
      // Constructor, `this` is new object.
8✔
4039
      if (func === this.ARRAY) {
8✔
4040
        state.funcThis_ = this.createArray();
4✔
4041
      } else {
4✔
4042
        var proto = func.properties['prototype'];
4✔
4043
        if (typeof proto !== 'object' || proto === null) {
4!
4044
          // Non-object prototypes default to `Object.prototype`.
×
4045
          proto = this.OBJECT_PROTO;
×
4046
        }
×
4047
        state.funcThis_ = this.createObjectProto(proto);
4✔
4048
      }
4✔
4049
      state.isConstructor = true;
8✔
4050
    }
8✔
4051
    state.doneArgs_ = true;
5,269✔
4052
  }
5,269✔
4053
  if (!state.doneExec_) {
29,826✔
4054
    state.doneExec_ = true;
5,272✔
4055
    if (!(func instanceof Interpreter.Object)) {
5,272✔
4056
      this.throwException(this.TYPE_ERROR,
1✔
4057
          this.nodeSummary(node.callee) + ' is not a function');
1✔
4058
    }
1✔
4059
    var funcNode = func.node;
5,271✔
4060
    if (funcNode) {
5,272✔
4061
      var scope = this.createScope(funcNode.body, func.parentScope);
3,166✔
4062
      // Build arguments variable.
3,166✔
4063
      var argsList = this.createArray();
3,166✔
4064
      for (var i = 0; i < state.arguments_.length; i++) {
3,166✔
4065
        this.setProperty(argsList, i, state.arguments_[i]);
7,838✔
4066
      }
7,838✔
4067
      this.setProperty(scope.object, 'arguments', argsList);
3,166✔
4068
      // Add all arguments (may clobber 'arguments' if a param is named such).
3,166✔
4069
      for (var i = 0; i < funcNode.params.length; i++) {
3,166✔
4070
        var paramName = funcNode.params[i].name;
7,841✔
4071
        var paramValue = state.arguments_.length > i ? state.arguments_[i] :
7,841✔
4072
            undefined;
7,841✔
4073
        this.setProperty(scope.object, paramName, paramValue);
7,841✔
4074
      }
7,841✔
4075
      if (!scope.strict) {
3,166✔
4076
        state.funcThis_ = this.boxThis_(state.funcThis_);
3,166✔
4077
      }
3,166✔
4078
      this.setProperty(scope.object, 'this', state.funcThis_,
3,166✔
4079
                       Interpreter.READONLY_DESCRIPTOR);
3,166✔
4080
      state.value = undefined;  // Default value if no explicit return.
3,166✔
4081
      return new Interpreter.State(funcNode.body, scope);
3,166✔
4082
    } else if (func.eval) {
5,272!
4083
      var code = state.arguments_[0];
×
4084
      if (typeof code !== 'string') {
×
4085
        // JS does not parse String objects:
×
4086
        // eval(new String('1 + 1')) -> '1 + 1'
×
4087
        state.value = code;
×
4088
      } else {
×
4089
        try {
×
4090
          var ast = this.parse_(String(code),
×
4091
             'eval' + (this.evalCodeNumber_++));
×
4092
        } catch (e) {
×
4093
          // Acorn threw a SyntaxError.  Rethrow as a trappable error.
×
4094
          this.throwException(this.SYNTAX_ERROR, 'Invalid code: ' + e.message);
×
4095
        }
×
4096
        var evalNode = this.newNode();
×
4097
        evalNode.type = 'EvalProgram_';
×
4098
        evalNode.body = ast.body;
×
4099
        Interpreter.stripLocations_(evalNode, node.start, node.end);
×
4100
        // Create new scope and update it with definitions in eval().
×
4101
        var scope = state.directEval_ ? state.scope : this.globalScope;
×
4102
        if (scope.strict) {
×
4103
          // Strict mode get its own scope in eval.
×
4104
          scope = this.createScope(ast, scope);
×
4105
        } else {
×
4106
          // Non-strict mode pollutes the current scope.
×
4107
          this.populateScope_(ast, scope);
×
4108
        }
×
4109
        this.value = undefined;  // Default value if no code.
×
4110
        return new Interpreter.State(evalNode, scope);
×
4111
      }
×
4112
    } else if (func.nativeFunc) {
2,105✔
4113
      if (!state.scope.strict) {
2,103✔
4114
        state.funcThis_ = this.boxThis_(state.funcThis_);
2,103✔
4115
      }
2,103✔
4116
      state.value = func.nativeFunc.apply(state.funcThis_, state.arguments_);
2,103✔
4117
    } else if (func.asyncFunc) {
2,105✔
4118
      var thisInterpreter = this;
2✔
4119
      var callback = function(value) {
2✔
4120
        state.value = value;
2✔
4121
        thisInterpreter.paused_ = false;
2✔
4122
      };
2✔
4123
      // Force the argument lengths to match, then append the callback.
2✔
4124
      var argLength = func.asyncFunc.length - 1;
2✔
4125
      var argsWithCallback = state.arguments_.concat(
2✔
4126
          new Array(argLength)).slice(0, argLength);
2✔
4127
      argsWithCallback.push(callback);
2✔
4128
      this.paused_ = true;
2✔
4129
      if (!state.scope.strict) {
2✔
4130
        state.funcThis_ = this.boxThis_(state.funcThis_);
2✔
4131
      }
2✔
4132
      func.asyncFunc.apply(state.funcThis_, argsWithCallback);
2✔
4133
      return;
2✔
4134
    } else {
2!
4135
      /* A child of a function is a function but is not callable.  For example:
×
4136
      var F = function() {};
×
4137
      F.prototype = escape;
×
4138
      var f = new F();
×
4139
      f();
×
4140
      */
×
4141
      this.throwException(this.TYPE_ERROR,
×
4142
          this.nodeSummary(node.callee) + ' is not callable');
×
4143
    }
×
4144
  } else {
29,826✔
4145
    // Execution complete.  Put the return value on the stack.
5,260✔
4146
    stack.pop();
5,260✔
4147
    if (state.isConstructor && typeof state.value !== 'object') {
5,260✔
4148
      // Normal case for a constructor is to use the `this` value.
1✔
4149
      stack[stack.length - 1].value = state.funcThis_;
1✔
4150
    } else {
5,260✔
4151
      // Non-constructors or constructions explicitly returning objects use
5,259✔
4152
      // the return value.
5,259✔
4153
      stack[stack.length - 1].value = state.value;
5,259✔
4154
    }
5,259✔
4155
  }
5,260✔
4156
};
29,826✔
4157

1✔
4158
Interpreter.prototype['stepConditionalExpression'] =
1✔
4159
    function(stack, state, node) {
1✔
4160
  // Handles both ConditionalExpression and IfStatement.
27,858✔
4161
  var mode = state.mode_ || 0;
27,858✔
4162
  if (mode === 0) {
27,858✔
4163
    state.mode_ = 1;
11,901✔
4164
    return new Interpreter.State(node.test, state.scope);
11,901✔
4165
  }
11,901✔
4166
  if (mode === 1) {
27,858✔
4167
    state.mode_ = 2;
11,901✔
4168
    var value = Boolean(state.value);
11,901✔
4169
    if (value && node.consequent) {
11,901✔
4170
      // Execute `if` block.
4,056✔
4171
      return new Interpreter.State(node.consequent, state.scope);
4,056✔
4172
    } else if (!value && node.alternate) {
11,901!
4173
      // Execute `else` block.
×
4174
      return new Interpreter.State(node.alternate, state.scope);
×
4175
    }
×
4176
    // eval('1;if(false){2}') -> undefined
7,845✔
4177
    this.value = undefined;
7,845✔
4178
  }
7,845✔
4179
  stack.pop();
11,901✔
4180
  if (node.type === 'ConditionalExpression') {
27,858!
4181
    stack[stack.length - 1].value = state.value;
×
4182
  }
×
4183
};
27,858✔
4184

1✔
4185
Interpreter.prototype['stepContinueStatement'] = function(stack, state, node) {
1✔
4186
  var label = node.label && node.label.name;
×
4187
  this.unwind(Interpreter.Completion.CONTINUE, undefined, label);
×
4188
};
×
4189

1✔
4190
Interpreter.prototype['stepDebuggerStatement'] = function(stack, state, node) {
1✔
4191
  // Do nothing.  May be overridden by developers.
×
4192
  stack.pop();
×
4193
};
×
4194

1✔
4195
Interpreter.prototype['stepDoWhileStatement'] = function(stack, state, node) {
1✔
4196
  // Handles both DoWhileStatement and WhileStatement.
4✔
4197
  if (node.type === 'DoWhileStatement' && state.test_ === undefined) {
4!
4198
    // First iteration of do/while executes without checking test.
×
4199
    state.value = true;
×
4200
    state.test_ = true;
×
4201
  }
×
4202
  if (!state.test_) {
4✔
4203
    state.test_ = true;
2✔
4204
    return new Interpreter.State(node.test, state.scope);
2✔
4205
  }
2✔
4206
  if (!state.value) {  // Done, exit loop.
4✔
4207
    stack.pop();
1✔
4208
  } else if (node.body) {  // Execute the body.
1✔
4209
    state.test_ = false;
1✔
4210
    state.isLoop = true;
1✔
4211
    return new Interpreter.State(node.body, state.scope);
1✔
4212
  }
1✔
4213
};
4✔
4214

1✔
4215
Interpreter.prototype['stepEmptyStatement'] = function(stack, state, node) {
1✔
4216
  stack.pop();
×
4217
};
×
4218

1✔
4219
Interpreter.prototype['stepEvalProgram_'] = function(stack, state, node) {
1✔
4220
  var n = state.n_ || 0;
×
4221
  var expression = node.body[n];
×
4222
  if (expression) {
×
4223
    state.n_ = n + 1;
×
4224
    return new Interpreter.State(expression, state.scope);
×
4225
  }
×
4226
  stack.pop();
×
4227
  stack[stack.length - 1].value = this.value;
×
4228
};
×
4229

1✔
4230
Interpreter.prototype['stepExpressionStatement'] = function(stack, state, node) {
1✔
4231
  if (!state.done_) {
15,075✔
4232
    this.value = undefined;
7,540✔
4233
    state.done_ = true;
7,540✔
4234
    return new Interpreter.State(node.expression, state.scope);
7,540✔
4235
  }
7,540✔
4236
  stack.pop();
7,535✔
4237
  // Save this value to interpreter.value for use as a return value if
7,535✔
4238
  // this code is inside an eval function.
7,535✔
4239
  this.value = state.value;
7,535✔
4240
};
7,535✔
4241

1✔
4242
Interpreter.prototype['stepForInStatement'] = function(stack, state, node) {
1✔
4243
  // First, initialize a variable if exists.  Only do so once, ever.
15✔
4244
  if (!state.doneInit_) {
15✔
4245
    state.doneInit_ = true;
3✔
4246
    if (node.left.declarations &&
3✔
4247
        node.left.declarations[0].init) {
3!
4248
      if (state.scope.strict) {
×
4249
        this.throwException(this.SYNTAX_ERROR,
×
4250
            'for-in loop variable declaration may not have an initializer');
×
4251
      }
×
4252
      // Variable initialization: for (var x = 4 in y)
×
4253
      return new Interpreter.State(node.left, state.scope);
×
4254
    }
×
4255
  }
3✔
4256
  // Second, look up the object.  Only do so once, ever.
15✔
4257
  if (!state.doneObject_) {
15✔
4258
    state.doneObject_ = true;
3✔
4259
    if (!state.variable_) {
3✔
4260
      state.variable_ = state.value;
3✔
4261
    }
3✔
4262
    return new Interpreter.State(node.right, state.scope);
3✔
4263
  }
3✔
4264
  if (!state.isLoop) {
15✔
4265
    // First iteration.
3✔
4266
    state.isLoop = true;
3✔
4267
    state.object_ = state.value;
3✔
4268
    state.visited_ = Object.create(null);
3✔
4269
  }
3✔
4270
  // Third, find the property name for this iteration.
12✔
4271
  if (state.name_ === undefined) {
12✔
4272
    gotPropName: while (true) {
12✔
4273
      if (state.object_ instanceof Interpreter.Object) {
18✔
4274
        if (!state.props_) {
18✔
4275
          state.props_ = Object.getOwnPropertyNames(state.object_.properties);
9✔
4276
        }
9✔
4277
        while (true) {
18✔
4278
          var prop = state.props_.shift();
108✔
4279
          if (prop === undefined) {
108✔
4280
            break;  // Reached end of this object's properties.
9✔
4281
          }
9✔
4282
          if (!Object.prototype.hasOwnProperty.call(state.object_.properties,
99✔
4283
                prop)) {
108!
4284
            continue;  // Property has been deleted in the loop.
×
4285
          }
×
4286
          if (state.visited_[prop]) {
108✔
4287
            continue;  // Already seen this property on a child.
9✔
4288
          }
9✔
4289
          state.visited_[prop] = true;
90✔
4290
          if (!Object.prototype.propertyIsEnumerable.call(
90✔
4291
                state.object_.properties, prop)) {
108✔
4292
            continue;  // Skip non-enumerable property.
81✔
4293
          }
81✔
4294
          state.name_ = prop;
9✔
4295
          break gotPropName;
9✔
4296
        }
9✔
4297
      } else if (state.object_ !== null && state.object_ !== undefined) {
18!
4298
        // Primitive value (other than null or undefined).
×
4299
        if (!state.props_) {
×
4300
          state.props_ = Object.getOwnPropertyNames(state.object_);
×
4301
        }
×
4302
        while (true) {
×
4303
          var prop = state.props_.shift();
×
4304
          if (prop === undefined) {
×
4305
            break;  // Reached end of this value's properties.
×
4306
          }
×
4307
          state.visited_[prop] = true;
×
4308
          if (!Object.prototype.propertyIsEnumerable.call(
×
4309
                state.object_, prop)) {
×
4310
            continue;  // Skip non-enumerable property.
×
4311
          }
×
4312
          state.name_ = prop;
×
4313
          break gotPropName;
×
4314
        }
×
4315
      }
×
4316
      state.object_ = this.getPrototype(state.object_);
9✔
4317
      state.props_ = null;
9✔
4318
      if (state.object_ === null) {
18✔
4319
        // Done, exit loop.
3✔
4320
        stack.pop();
3✔
4321
        return;
3✔
4322
      }
3✔
4323
    }
18✔
4324
  }
12✔
4325
  // Fourth, find the variable
9✔
4326
  if (!state.doneVariable_) {
9✔
4327
    state.doneVariable_ = true;
9✔
4328
    var left = node.left;
9✔
4329
    if (left.type === 'VariableDeclaration') {
9✔
4330
      // Inline variable declaration: for (var x in y)
9✔
4331
      state.variable_ =
9✔
4332
          [Interpreter.SCOPE_REFERENCE, left.declarations[0].id.name];
9✔
4333
    } else {
9!
4334
      // Arbitrary left side: for (foo().bar in y)
×
4335
      state.variable_ = null;
×
4336
      var nextState = new Interpreter.State(left, state.scope);
×
4337
      nextState.components = true;
×
4338
      return nextState;
×
4339
    }
×
4340
  }
9✔
4341
  if (!state.variable_) {
15!
4342
    state.variable_ = state.value;
×
4343
  }
×
4344
  // Fifth, set the variable.
9✔
4345
  if (!state.doneSetter_) {
9✔
4346
    state.doneSetter_ = true;
9✔
4347
    var value = state.name_;
9✔
4348
    var setter = this.setValue(state.variable_, value);
9✔
4349
    if (setter) {
9!
4350
      return this.createSetter_(setter, state.variable_, value);
×
4351
    }
×
4352
  }
9✔
4353
  // Next step will be step three.
9✔
4354
  state.name_ = undefined;
9✔
4355
  // Reevaluate the variable since it could be a setter on the global object.
9✔
4356
  state.doneVariable_ = false;
9✔
4357
  state.doneSetter_ = false;
9✔
4358
  // Sixth and finally, execute the body if there was one.
9✔
4359
  if (node.body) {
9✔
4360
    return new Interpreter.State(node.body, state.scope);
9✔
4361
  }
9✔
4362
};
15✔
4363

1✔
4364
Interpreter.prototype['stepForStatement'] = function(stack, state, node) {
1✔
4365
  switch (state.mode_) {
99✔
4366
    default:
99✔
4367
      state.mode_ = 1;
4✔
4368
      if (node.init) {
4✔
4369
        return new Interpreter.State(node.init, state.scope);
4✔
4370
      }
4✔
4371
      break;
×
4372
    case 1:
99✔
4373
      state.mode_ = 2;
33✔
4374
      if (node.test) {
33✔
4375
        return new Interpreter.State(node.test, state.scope);
33✔
4376
      }
33✔
4377
      break;
×
4378
    case 2:
99✔
4379
      state.mode_ = 3;
33✔
4380
      if (node.test && !state.value) {
33✔
4381
        // Done, exit loop.
4✔
4382
        stack.pop();
4✔
4383
      } else {  // Execute the body.
33✔
4384
        state.isLoop = true;
29✔
4385
        return new Interpreter.State(node.body, state.scope);
29✔
4386
      }
29✔
4387
      break;
4✔
4388
    case 3:
99✔
4389
      state.mode_ = 1;
29✔
4390
      if (node.update) {
29✔
4391
        return new Interpreter.State(node.update, state.scope);
29✔
4392
      }
29✔
4393
      break;
×
4394
  }
99✔
4395
};
99✔
4396

1✔
4397
Interpreter.prototype['stepFunctionDeclaration'] =
1✔
4398
    function(stack, state, node) {
1✔
4399
  // This was found and handled when the scope was populated.
48✔
4400
  stack.pop();
48✔
4401
};
48✔
4402

1✔
4403
Interpreter.prototype['stepFunctionExpression'] = function(stack, state, node) {
1✔
4404
  stack.pop();
1,508✔
4405
  state = stack[stack.length - 1];
1,508✔
4406
  var parentScope = state.scope;
1,508✔
4407
  if (node.id) {
1,508✔
4408
    // Create a tiny scope to store the function name.
329✔
4409
    // E.g. var x = function foo(){};
329✔
4410
    parentScope = this.createSpecialScope(parentScope);
329✔
4411
  }
329✔
4412
  state.value = this.createFunction(node, parentScope, state.destinationName);
1,508✔
4413
  if (node.id) {
1,508✔
4414
    // Record the function name, read-only.
329✔
4415
    this.setProperty(parentScope.object, node.id.name, state.value,
329✔
4416
        Interpreter.READONLY_DESCRIPTOR);
329✔
4417
  }
329✔
4418
};
1,508✔
4419

1✔
4420
Interpreter.prototype['stepIdentifier'] = function(stack, state, node) {
1✔
4421
  stack.pop();
36,446✔
4422
  if (state.components) {
36,446✔
4423
    stack[stack.length - 1].value = [Interpreter.SCOPE_REFERENCE, node.name];
3,038✔
4424
    return;
3,038✔
4425
  }
3,038✔
4426
  var value = this.getValueFromScope(node.name);
33,408✔
4427
  // An identifier could be a getter if it's a property on the global object.
33,408✔
4428
  if (this.getterStep_) {
36,446!
4429
    // Call the getter function.
×
4430
    var func = /** @type {!Interpreter.Object} */ (value);
×
4431
    return this.createGetter_(func, this.globalObject);
×
4432
  }
×
4433
  stack[stack.length - 1].value = value;
33,408✔
4434
};
33,408✔
4435

1✔
4436
Interpreter.prototype['stepIfStatement'] =
1✔
4437
    Interpreter.prototype['stepConditionalExpression'];
1✔
4438

1✔
4439
Interpreter.prototype['stepLabeledStatement'] = function(stack, state, node) {
1✔
4440
  // No need to hit this node again on the way back up the stack.
×
4441
  stack.pop();
×
4442
  // Note that a statement might have multiple labels.
×
4443
  var labels = state.labels || [];
×
4444
  labels.push(node.label.name);
×
4445
  var nextState = new Interpreter.State(node.body, state.scope);
×
4446
  nextState.labels = labels;
×
4447
  return nextState;
×
4448
};
×
4449

1✔
4450
Interpreter.prototype['stepLiteral'] = function(stack, state, node) {
1✔
4451
  stack.pop();
16,204✔
4452
  var value = node.value;
16,204✔
4453
  if (value instanceof RegExp) {
16,204✔
4454
    var pseudoRegexp = this.createObjectProto(this.REGEXP_PROTO);
3✔
4455
    this.populateRegExp(pseudoRegexp, value);
3✔
4456
    value = pseudoRegexp;
3✔
4457
  }
3✔
4458
  stack[stack.length - 1].value = value;
16,204✔
4459
};
16,204✔
4460

1✔
4461
Interpreter.prototype['stepLogicalExpression'] = function(stack, state, node) {
1✔
4462
  if (node.operator !== '&&' && node.operator !== '||') {
7!
4463
    throw SyntaxError('Unknown logical operator: ' + node.operator);
×
4464
  }
×
4465
  if (!state.doneLeft_) {
7✔
4466
    state.doneLeft_ = true;
3✔
4467
    return new Interpreter.State(node.left, state.scope);
3✔
4468
  }
3✔
4469
  if (!state.doneRight_) {
7✔
4470
    if ((node.operator === '&&' && !state.value) ||
3✔
4471
        (node.operator === '||' && state.value)) {
3✔
4472
      // Shortcut evaluation.
2✔
4473
      stack.pop();
2✔
4474
      stack[stack.length - 1].value = state.value;
2✔
4475
    } else {
3✔
4476
      state.doneRight_ = true;
1✔
4477
      return new Interpreter.State(node.right, state.scope);
1✔
4478
    }
1✔
4479
  } else {
7✔
4480
    stack.pop();
1✔
4481
    stack[stack.length - 1].value = state.value;
1✔
4482
  }
1✔
4483
};
7✔
4484

1✔
4485
Interpreter.prototype['stepMemberExpression'] = function(stack, state, node) {
1✔
4486
  if (!state.doneObject_) {
23,608✔
4487
    state.doneObject_ = true;
11,796✔
4488
    return new Interpreter.State(node.object, state.scope);
11,796✔
4489
  }
11,796✔
4490
  var propName;
11,812✔
4491
  if (!node.computed) {
23,608✔
4492
    state.object_ = state.value;
11,780✔
4493
    // obj.foo -- Just access `foo` directly.
11,780✔
4494
    propName = node.property.name;
11,780✔
4495
  } else if (!state.doneProperty_) {
23,608✔
4496
    state.object_ = state.value;
16✔
4497
    // obj[foo] -- Compute value of `foo`.
16✔
4498
    state.doneProperty_ = true;
16✔
4499
    return new Interpreter.State(node.property, state.scope);
16✔
4500
  } else {
16✔
4501
    propName = state.value;
16✔
4502
  }
16✔
4503
  stack.pop();
11,796✔
4504
  if (state.components) {
23,608✔
4505
    stack[stack.length - 1].value = [state.object_, propName];
6,307✔
4506
  } else {
23,608✔
4507
    var value = this.getProperty(state.object_, propName);
5,489✔
4508
    if (this.getterStep_) {
5,489!
4509
      // Call the getter function.
×
4510
      var func = /** @type {!Interpreter.Object} */ (value);
×
4511
      return this.createGetter_(func, state.object_);
×
4512
    }
×
4513
    stack[stack.length - 1].value = value;
5,489✔
4514
  }
5,489✔
4515
};
23,608✔
4516

1✔
4517
Interpreter.prototype['stepNewExpression'] =
1✔
4518
    Interpreter.prototype['stepCallExpression'];
1✔
4519

1✔
4520
Interpreter.prototype['stepObjectExpression'] = function(stack, state, node) {
1✔
4521
  var n = state.n_ || 0;
8,216✔
4522
  var property = node.properties[n];
8,216✔
4523
  if (!state.object_) {
8,216✔
4524
    // First execution.
4,020✔
4525
    state.object_ = this.createObjectProto(this.OBJECT_PROTO);
4,020✔
4526
    state.properties_ = Object.create(null);
4,020✔
4527
  } else {
8,216✔
4528
    // Set the property computed in the previous execution.
4,196✔
4529
    var propName = state.destinationName;
4,196✔
4530
    if (!state.properties_[propName]) {
4,196✔
4531
      // Create temp object to collect value, getter, and/or setter.
4,196✔
4532
      state.properties_[propName] = {};
4,196✔
4533
    }
4,196✔
4534
    state.properties_[propName][property.kind] = state.value;
4,196✔
4535
    state.n_ = ++n;
4,196✔
4536
    property = node.properties[n];
4,196✔
4537
  }
4,196✔
4538
  if (property) {
8,216✔
4539
    // Determine property name.
4,196✔
4540
    var key = property.key;
4,196✔
4541
    if (key.type === 'Identifier') {
4,196✔
4542
      var propName = key.name;
4,196✔
4543
    } else if (key.type === 'Literal') {
4,196!
4544
      var propName = key.value;
×
4545
    } else {
×
4546
      throw SyntaxError('Unknown object structure: ' + key.type);
×
4547
    }
×
4548
    // When assigning an unnamed function to a property, the function's name
4,196✔
4549
    // is set to the property name.  Record the property name in case the
4,196✔
4550
    // value is a functionExpression.
4,196✔
4551
    // E.g. {foo: function() {}}
4,196✔
4552
    state.destinationName = propName;
4,196✔
4553
    return new Interpreter.State(property.value, state.scope);
4,196✔
4554
  }
4,196✔
4555
  for (var key in state.properties_) {
8,216✔
4556
    var kinds = state.properties_[key];
4,196✔
4557
    if ('get' in kinds || 'set' in kinds) {
4,196!
4558
      // Set a property with a getter or setter.
×
4559
      var descriptor = {
×
4560
        'configurable': true,
×
4561
        'enumerable': true,
×
4562
        'get': kinds['get'],
×
4563
        'set': kinds['set'],
×
4564
      };
×
4565
      this.setProperty(state.object_, key, Interpreter.VALUE_IN_DESCRIPTOR,
×
4566
                       descriptor);
×
4567
    } else {
4,196✔
4568
      // Set a normal property with a value.
4,196✔
4569
      this.setProperty(state.object_, key, kinds['init']);
4,196✔
4570
    }
4,196✔
4571
  }
4,196✔
4572
  stack.pop();
4,020✔
4573
  stack[stack.length - 1].value = state.object_;
4,020✔
4574
};
4,020✔
4575

1✔
4576
Interpreter.prototype['stepProgram'] = function(stack, state, node) {
1✔
4577
  var expression = node.body.shift();
568✔
4578
  if (expression) {
568✔
4579
    state.done = false;
482✔
4580
    return new Interpreter.State(expression, state.scope);
482✔
4581
  }
482✔
4582
  state.done = true;
86✔
4583
  // Don't pop the stateStack.
86✔
4584
  // Leave the root scope on the tree in case the program is appended to.
86✔
4585
};
86✔
4586

1✔
4587
Interpreter.prototype['stepReturnStatement'] = function(stack, state, node) {
1✔
4588
  if (node.argument && !state.done_) {
3,975✔
4589
    state.done_ = true;
1,988✔
4590
    return new Interpreter.State(node.argument, state.scope);
1,988✔
4591
  }
1,988✔
4592
  this.unwind(Interpreter.Completion.RETURN, state.value, undefined);
1,987✔
4593
};
1,987✔
4594

1✔
4595
Interpreter.prototype['stepSequenceExpression'] = function(stack, state, node) {
1✔
4596
  var n = state.n_ || 0;
×
4597
  var expression = node.expressions[n];
×
4598
  if (expression) {
×
4599
    state.n_ = n + 1;
×
4600
    return new Interpreter.State(expression, state.scope);
×
4601
  }
×
4602
  stack.pop();
×
4603
  stack[stack.length - 1].value = state.value;
×
4604
};
×
4605

1✔
4606
Interpreter.prototype['stepSwitchStatement'] = function(stack, state, node) {
1✔
4607
  if (!state.test_) {
×
4608
    state.test_ = 1;
×
4609
    return new Interpreter.State(node.discriminant, state.scope);
×
4610
  }
×
4611
  if (state.test_ === 1) {
×
4612
    state.test_ = 2;
×
4613
    // Preserve switch value between case tests.
×
4614
    state.switchValue_ = state.value;
×
4615
    state.defaultCase_ = -1;
×
4616
  }
×
4617

×
4618
  while (true) {
×
4619
    var index = state.index_ || 0;
×
4620
    var switchCase = node.cases[index];
×
4621
    if (!state.matched_ && switchCase && !switchCase.test) {
×
4622
      // Test on the default case is null.
×
4623
      // Bypass (but store) the default case, and get back to it later.
×
4624
      state.defaultCase_ = index;
×
4625
      state.index_ = index + 1;
×
4626
      continue;
×
4627
    }
×
4628
    if (!switchCase && !state.matched_ && state.defaultCase_ !== -1) {
×
4629
      // Ran through all cases, no match.  Jump to the default.
×
4630
      state.matched_ = true;
×
4631
      state.index_ = state.defaultCase_;
×
4632
      continue;
×
4633
    }
×
4634
    if (switchCase) {
×
4635
      if (!state.matched_ && !state.tested_ && switchCase.test) {
×
4636
        state.tested_ = true;
×
4637
        return new Interpreter.State(switchCase.test, state.scope);
×
4638
      }
×
4639
      if (state.matched_ || state.value === state.switchValue_) {
×
4640
        state.matched_ = true;
×
4641
        var n = state.n_ || 0;
×
4642
        if (switchCase.consequent[n]) {
×
4643
          state.isSwitch = true;
×
4644
          state.n_ = n + 1;
×
4645
          return new Interpreter.State(switchCase.consequent[n], state.scope);
×
4646
        }
×
4647
      }
×
4648
      // Move on to next case.
×
4649
      state.tested_ = false;
×
4650
      state.n_ = 0;
×
4651
      state.index_ = index + 1;
×
4652
    } else {
×
4653
      stack.pop();
×
4654
      return;
×
4655
    }
×
4656
  }
×
4657
};
×
4658

1✔
4659
Interpreter.prototype['stepThisExpression'] = function(stack, state, node) {
1✔
4660
  stack.pop();
5✔
4661
  stack[stack.length - 1].value = this.getValueFromScope('this');
5✔
4662
};
5✔
4663

1✔
4664
Interpreter.prototype['stepThrowStatement'] = function(stack, state, node) {
1✔
4665
  if (!state.done_) {
×
4666
    state.done_ = true;
×
4667
    return new Interpreter.State(node.argument, state.scope);
×
4668
  } else {
×
4669
    this.throwException(state.value);
×
4670
  }
×
4671
};
×
4672

1✔
4673
Interpreter.prototype['stepTryStatement'] = function(stack, state, node) {
1✔
4674
  // This step also handles all CatchClause nodes, since these nodes can
×
4675
  // only appear inside the `handler` property of a TryStatement node.
×
4676
  if (!state.doneBlock_) {
×
4677
    state.doneBlock_ = true;
×
4678
    return new Interpreter.State(node.block, state.scope);
×
4679
  }
×
4680
  if (state.cv && state.cv.type === Interpreter.Completion.THROW &&
×
4681
      !state.doneHandler_ && node.handler) {
×
4682
    state.doneHandler_ = true;
×
4683
    // Create an new scope and add the error variable.
×
4684
    var scope = this.createSpecialScope(state.scope);
×
4685
    this.setProperty(scope.object, node.handler.param.name, state.cv.value);
×
4686
    state.cv = undefined;  // This error has been handled, don't rethrow.
×
4687
    // Execute catch clause.
×
4688
    return new Interpreter.State(node.handler.body, scope);
×
4689
  }
×
4690
  if (!state.doneFinalizer_ && node.finalizer) {
×
4691
    state.doneFinalizer_ = true;
×
4692
    return new Interpreter.State(node.finalizer, state.scope);
×
4693
  }
×
4694
  stack.pop();
×
4695
  if (state.cv) {
×
4696
    // There was no catch handler, or the catch/finally threw an error.
×
4697
    // Throw the error up to a higher try.
×
4698
    this.unwind(state.cv.type, state.cv.value, state.cv.label);
×
4699
  }
×
4700
};
×
4701

1✔
4702
Interpreter.prototype['stepUnaryExpression'] = function(stack, state, node) {
1✔
4703
  if (!state.done_) {
6✔
4704
    state.done_ = true;
3✔
4705
    var nextState = new Interpreter.State(node.argument, state.scope);
3✔
4706
    nextState.components = node.operator === 'delete';
3✔
4707
    return nextState;
3✔
4708
  }
3✔
4709
  stack.pop();
3✔
4710
  var value = state.value;
3✔
4711
  switch (node.operator) {
3✔
4712
    case '-':
6✔
4713
      value = -value;
1✔
4714
      break;
1✔
4715
    case '+':
6!
4716
      value = +value;
×
4717
      break;
×
4718
    case '!':
6✔
4719
      value = !value;
1✔
4720
      break;
1✔
4721
    case '~':
6!
4722
      value = ~value;
×
4723
      break;
×
4724
    case 'delete':
6!
4725
      var result = true;
×
4726
      // If value is not an array, then it is a primitive, or some other value.
×
4727
      // If so, skip the delete and return true.
×
4728
      if (Array.isArray(value)) {
×
4729
        var obj = value[0];
×
4730
        if (obj === Interpreter.SCOPE_REFERENCE) {
×
4731
          // `delete foo;` is the same as `delete window.foo;`.
×
4732
          obj = state.scope;
×
4733
        }
×
4734
        var name = String(value[1]);
×
4735
        try {
×
4736
          delete obj.properties[name];
×
4737
        } catch (_e) {
×
4738
          if (state.scope.strict) {
×
4739
            this.throwException(this.TYPE_ERROR, "Cannot delete property '" +
×
4740
                                name + "' of '" + obj + "'");
×
4741
          } else {
×
4742
            result = false;
×
4743
          }
×
4744
        }
×
4745
      }
×
4746
      value = result;
×
4747
      break;
×
4748
    case 'typeof':
6✔
4749
      value = (value && value.class === 'Function') ? 'function' : typeof value;
1!
4750
      break;
1✔
4751
    case 'void':
6!
4752
      value = undefined;
×
4753
      break;
×
4754
    default:
6!
4755
      throw SyntaxError('Unknown unary operator: ' + node.operator);
×
4756
  }
6✔
4757
  stack[stack.length - 1].value = value;
3✔
4758
};
3✔
4759

1✔
4760
Interpreter.prototype['stepUpdateExpression'] = function(stack, state, node) {
1✔
4761
  if (!state.doneLeft_) {
60✔
4762
    state.doneLeft_ = true;
30✔
4763
    var nextState = new Interpreter.State(node.argument, state.scope);
30✔
4764
    nextState.components = true;
30✔
4765
    return nextState;
30✔
4766
  }
30✔
4767
  if (!state.leftSide_) {
30✔
4768
    state.leftSide_ = state.value;
30✔
4769
  }
30✔
4770
  if (state.doneGetter_) {
60!
4771
    state.leftValue_ = state.value;
×
4772
  }
×
4773
  if (!state.doneGetter_) {
30✔
4774
    var leftValue = this.getValue(state.leftSide_);
30✔
4775
    state.leftValue_ = leftValue;
30✔
4776
    if (this.getterStep_) {
30!
4777
      // Call the getter function.
×
4778
      state.doneGetter_ = true;
×
4779
      var func = /** @type {!Interpreter.Object} */ (leftValue);
×
4780
      return this.createGetter_(func, state.leftSide_);
×
4781
    }
×
4782
  }
30✔
4783
  if (state.doneSetter_) {
60!
4784
    // Return if setter function.
×
4785
    // Setter method on property has completed.
×
4786
    // Ignore its return value, and use the original set value instead.
×
4787
    stack.pop();
×
4788
    stack[stack.length - 1].value = state.setterValue_;
×
4789
    return;
×
4790
  }
×
4791
  var leftValue = Number(state.leftValue_);
30✔
4792
  var changeValue;
30✔
4793
  if (node.operator === '++') {
30✔
4794
    changeValue = leftValue + 1;
30✔
4795
  } else if (node.operator === '--') {
60!
4796
    changeValue = leftValue - 1;
×
4797
  } else {
×
4798
    throw SyntaxError('Unknown update expression: ' + node.operator);
×
4799
  }
×
4800
  var returnValue = node.prefix ? changeValue : leftValue;
60!
4801
  var setter = this.setValue(state.leftSide_, changeValue);
60✔
4802
  if (setter) {
60!
4803
    state.doneSetter_ = true;
×
4804
    state.setterValue_ = returnValue;
×
4805
    return this.createSetter_(setter, state.leftSide_, changeValue);
×
4806
  }
×
4807
  // Return if no setter function.
30✔
4808
  stack.pop();
30✔
4809
  stack[stack.length - 1].value = returnValue;
30✔
4810
};
30✔
4811

1✔
4812
Interpreter.prototype['stepVariableDeclaration'] = function(stack, state, node) {
1✔
4813
  // This step also handles all VariableDeclarator nodes, since these nodes can
4,448✔
4814
  // only appear inside the `declarations` array of a VariableDeclaration node.
4,448✔
4815
  var declarations = node.declarations;
4,448✔
4816
  var n = state.n_ || 0;
4,448✔
4817
  var declarationNode = declarations[n];
4,448✔
4818
  if (state.init_ && declarationNode) {
4,448✔
4819
    // This setValue call never needs to deal with calling a setter function.
2,223✔
4820
    // Note that this is setting the init value, not defining the variable.
2,223✔
4821
    // Variable definition is done when scope is populated.
2,223✔
4822
    this.setValueToScope(declarationNode.id.name, state.value);
2,223✔
4823
    state.init_ = false;
2,223✔
4824
    declarationNode = declarations[++n];
2,223✔
4825
  }
2,223✔
4826
  while (declarationNode) {
4,448✔
4827
    // Skip any declarations that are not initialized.  They have already
2,227✔
4828
    // been defined as undefined in populateScope_.
2,227✔
4829
    if (declarationNode.init) {
2,227✔
4830
      state.n_ = n;
2,226✔
4831
      state.init_ = true;
2,226✔
4832
      // When assigning an unnamed function to a variable, the function's name
2,226✔
4833
      // is set to the variable name.  Record the variable name in case the
2,226✔
4834
      // right side is a functionExpression.
2,226✔
4835
      // E.g. var foo = function() {};
2,226✔
4836
      state.destinationName = declarationNode.id.name;
2,226✔
4837
      return new Interpreter.State(declarationNode.init, state.scope);
2,226✔
4838
    }
2,226✔
4839
    declarationNode = declarations[++n];
1✔
4840
  }
1✔
4841
  stack.pop();
2,222✔
4842
};
2,222✔
4843

1✔
4844
Interpreter.prototype['stepWithStatement'] = function(stack, state, node) {
1✔
4845
  if (!state.doneObject_) {
×
4846
    state.doneObject_ = true;
×
4847
    return new Interpreter.State(node.object, state.scope);
×
4848
  }
×
4849
  stack.pop();
×
4850
  var scope = this.createSpecialScope(state.scope, state.value);
×
4851
  return new Interpreter.State(node.body, scope);
×
4852
};
×
4853

1✔
4854
Interpreter.prototype['stepWhileStatement'] =
1✔
4855
    Interpreter.prototype['stepDoWhileStatement'];
1✔
4856

1✔
4857
// Preserve top-level API functions from being pruned/renamed by JS compilers.
1✔
4858
// Add others as needed.
1✔
4859
Interpreter.nativeGlobal['Interpreter'] = Interpreter;
1✔
4860
Interpreter.prototype['step'] = Interpreter.prototype.step;
1✔
4861
Interpreter.prototype['run'] = Interpreter.prototype.run;
1✔
4862
Interpreter.prototype['appendCode'] = Interpreter.prototype.appendCode;
1✔
4863
Interpreter.prototype['createObject'] = Interpreter.prototype.createObject;
1✔
4864
Interpreter.prototype['createObjectProto'] =
1✔
4865
    Interpreter.prototype.createObjectProto;
1✔
4866
Interpreter.prototype['createAsyncFunction'] =
1✔
4867
    Interpreter.prototype.createAsyncFunction;
1✔
4868
Interpreter.prototype['createNativeFunction'] =
1✔
4869
    Interpreter.prototype.createNativeFunction;
1✔
4870
Interpreter.prototype['getProperty'] = Interpreter.prototype.getProperty;
1✔
4871
Interpreter.prototype['setProperty'] = Interpreter.prototype.setProperty;
1✔
4872
Interpreter.prototype['getStatus'] = Interpreter.prototype.getStatus;
1✔
4873
Interpreter.prototype['nativeToPseudo'] = Interpreter.prototype.nativeToPseudo;
1✔
4874
Interpreter.prototype['pseudoToNative'] = Interpreter.prototype.pseudoToNative;
1✔
4875
Interpreter.prototype['getGlobalScope'] = Interpreter.prototype.getGlobalScope;
1✔
4876
Interpreter.prototype['setGlobalScope'] = Interpreter.prototype.setGlobalScope;
1✔
4877
Interpreter.prototype['getStateStack'] = Interpreter.prototype.getStateStack;
1✔
4878
Interpreter.prototype['setStateStack'] = Interpreter.prototype.setStateStack;
1✔
4879
Interpreter['VALUE_IN_DESCRIPTOR'] = Interpreter.VALUE_IN_DESCRIPTOR;
1✔
4880
Interpreter['Status'] = Interpreter.Status;
1✔
4881

1✔
4882
export default Interpreter;
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc