test-api.js 24.3 KB
Newer Older
1 2 3 4
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
"use strict";

// If true, prints all messages sent and received by inspector.
const printProtocolMessages = false;

// The active wrapper instance.
let activeWrapper = undefined;

// Receiver function called by inspector, delegating to active wrapper.
function receive(message) {
  activeWrapper.receiveMessage(message);
}

class DebugWrapper {
  constructor() {
    // Message dictionary storing {id, message} pairs.
21
    this.receivedMessages = new Map();
22 23 24 25 26 27

    // Each message dispatched by the Debug wrapper is assigned a unique number
    // using nextMessageId.
    this.nextMessageId = 0;

    // The listener method called on certain events.
28 29 30
    this.listener = undefined;

    // Debug events which can occur in the V8 JavaScript engine.
31 32
    this.DebugEvent = { Break: 1,
                        Exception: 2,
33 34
                        AfterCompile: 3,
                        CompileError: 4,
35
                        OOM: 5,
36
                      };
37

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
    // The different types of steps.
    this.StepAction = { StepOut: 0,
                        StepNext: 1,
                        StepIn: 2,
                      };

    // A copy of the scope types from runtime-debug.cc.
    // NOTE: these constants should be backward-compatible, so
    // add new ones to the end of this list.
    this.ScopeType = { Global:  0,
                       Local:   1,
                       With:    2,
                       Closure: 3,
                       Catch:   4,
                       Block:   5,
                       Script:  6,
                       Eval:    7,
                       Module:  8
                     };

58 59 60 61
    // Types of exceptions that can be broken upon.
    this.ExceptionBreak = { Caught : 0,
                            Uncaught: 1 };

62 63 64
    // Store the current script id so we can skip corresponding break events.
    this.thisScriptId = %FunctionGetScriptId(receive);

65 66 67
    // Stores all set breakpoints.
    this.breakpoints = new Set();

68 69 70 71 72
    // Register as the active wrapper.
    assertTrue(activeWrapper === undefined);
    activeWrapper = this;
  }

73 74 75 76 77 78 79 80 81
  enable() { this.sendMessageForMethodChecked("Debugger.enable"); }
  disable() { this.sendMessageForMethodChecked("Debugger.disable"); }

  setListener(listener) { this.listener = listener; }

  stepOver() { this.sendMessageForMethodChecked("Debugger.stepOver"); }
  stepInto() { this.sendMessageForMethodChecked("Debugger.stepInto"); }
  stepOut() { this.sendMessageForMethodChecked("Debugger.stepOut"); }

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
  setBreakOnException()  {
    this.sendMessageForMethodChecked(
        "Debugger.setPauseOnExceptions", { state : "all" });
  }

  clearBreakOnException()  {
    const newState = this.isBreakOnUncaughtException() ? "uncaught" : "none";
    this.sendMessageForMethodChecked(
        "Debugger.setPauseOnExceptions", { state : newState });
  }

  isBreakOnException() {
    return !!%IsBreakOnException(this.ExceptionBreak.Caught);
  };

  setBreakOnUncaughtException()  {
    const newState = this.isBreakOnException() ? "all" : "uncaught";
    this.sendMessageForMethodChecked(
        "Debugger.setPauseOnExceptions", { state : newState });
  }

  clearBreakOnUncaughtException()  {
    const newState = this.isBreakOnException() ? "all" : "none";
    this.sendMessageForMethodChecked(
        "Debugger.setPauseOnExceptions", { state : newState });
  }

  isBreakOnUncaughtException() {
    return !!%IsBreakOnException(this.ExceptionBreak.Uncaught);
  };

  clearStepping() { %ClearStepping(); };

115 116 117 118 119 120 121 122 123 124 125
  // Returns the resulting breakpoint id.
  setBreakPoint(func, opt_line, opt_column, opt_condition) {
    assertTrue(%IsFunction(func));
    assertFalse(%FunctionIsAPIFunction(func));

    const scriptid = %FunctionGetScriptId(func);
    assertTrue(scriptid != -1);

    const offset = %FunctionGetScriptSourcePosition(func);
    const loc =
      %ScriptLocationFromLine2(scriptid, opt_line, opt_column, offset);
126
    return this.setBreakPointAtLocation(scriptid, loc, opt_condition);
127 128
  }

129 130 131
  clearBreakPoint(breakpoint) {
    assertTrue(this.breakpoints.has(breakpoint));
    const breakid = breakpoint.id;
132 133
    const {msgid, msg} = this.createMessage(
        "Debugger.removeBreakpoint", { breakpointId : breakid });
134
    this.sendMessage(msg);
135
    this.takeReplyChecked(msgid);
136 137 138 139
    this.breakpoints.delete(breakid);
  }

  clearAllBreakPoints() {
140 141
    for (let breakpoint of this.breakpoints) {
      this.clearBreakPoint(breakpoint);
142 143
    }
    this.breakpoints.clear();
144 145
  }

146
  showBreakPoints(f) {
147 148 149 150
    if (!%IsFunction(f)) throw new Error("Not passed a Function");

    const source = %FunctionGetSourceCode(f);
    const offset = %FunctionGetScriptSourcePosition(f);
151
    const locations = %GetBreakLocations(f);
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179

    if (!locations) return source;

    locations.sort(function(x, y) { return x - y; });

    let result = "";
    let prev_pos = 0;
    let pos;

    for (var i = 0; i < locations.length; i++) {
      pos = locations[i] - offset;
      result += source.slice(prev_pos, pos);
      result += "[B" + i + "]";
      prev_pos = pos;
    }

    pos = source.length;
    result += source.substring(prev_pos, pos);

    return result;
  }

  debuggerFlags() {
    return { breakPointsActive :
                { setValue : (enabled) => this.setBreakPointsActive(enabled) }
           };
  }

180 181 182 183
  // Returns the script source. The return value is the script source for the
  // script in which the function is defined.
  scriptSource(func) {
    return %FunctionGetScriptSource(func);
184 185
  };

186
  setBreakPointsActive(enabled) {
187
    const {msgid, msg} = this.createMessage(
188
        "Debugger.setBreakpointsActive", { active : enabled });
189
    this.sendMessage(msg);
190
    this.takeReplyChecked(msgid);
191 192
  }

193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
  generatorScopeCount(gen) {
    return %GetGeneratorScopeCount(gen);
  }

  generatorScope(gen, index) {
    // These indexes correspond definitions in debug-scopes.h.
    const kScopeDetailsTypeIndex = 0;
    const kScopeDetailsObjectIndex = 1;

    const details = %GetGeneratorScopeDetails(gen, index);

    function scopeObjectProperties() {
      const obj = details[kScopeDetailsObjectIndex];
      return Object.keys(obj).map((k, v) => v);
    }

    function setScopeVariableValue(name, value) {
210
      const res = %SetGeneratorScopeVariableValue(gen, index, name, value);
211
      if (!res) throw new Error("Failed to set variable '" + name + "' value");
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
    }

    const scopeObject =
        { value : () => details[kScopeDetailsObjectIndex],
          property : (prop) => details[kScopeDetailsObjectIndex][prop],
          properties : scopeObjectProperties,
          propertyNames : () => Object.keys(details[kScopeDetailsObjectIndex])
              .map((key, _) => key),
        };
    return { scopeType : () => details[kScopeDetailsTypeIndex],
             scopeIndex : () => index,
             scopeObject : () => scopeObject,
             setVariableValue : setScopeVariableValue,
           }
  }

  generatorScopes(gen) {
    const count = %GetGeneratorScopeCount(gen);
    const scopes = [];
    for (let i = 0; i < count; i++) {
      scopes.push(this.generatorScope(gen, i));
    }
    return scopes;
  }

237 238 239 240 241 242 243 244 245 246 247 248 249
  // --- Internal methods. -----------------------------------------------------

  getNextMessageId() {
    return this.nextMessageId++;
  }

  createMessage(method, params) {
    const id = this.getNextMessageId();
    const msg = JSON.stringify({
      id: id,
      method: method,
      params: params,
    });
250
    return { msgid : id, msg: msg };
251 252 253 254
  }

  receiveMessage(message) {
    const parsedMessage = JSON.parse(message);
255 256 257
    if (printProtocolMessages) {
      print(JSON.stringify(parsedMessage, undefined, 1));
    }
258
    if (parsedMessage.id !== undefined) {
259
      this.receivedMessages.set(parsedMessage.id, parsedMessage);
260 261 262 263 264 265 266 267 268 269
    }

    this.dispatchMessage(parsedMessage);
  }

  sendMessage(message) {
    if (printProtocolMessages) print(message);
    send(message);
  }

270 271
  sendMessageForMethodChecked(method, params) {
    const {msgid, msg} = this.createMessage(method, params);
272
    this.sendMessage(msg);
273 274 275 276 277 278 279 280 281 282
    this.takeReplyChecked(msgid);
  }

  takeReplyChecked(msgid) {
    const reply = this.receivedMessages.get(msgid);
    assertTrue(reply !== undefined);
    this.receivedMessages.delete(msgid);
    return reply;
  }

283 284 285 286 287 288 289 290 291 292 293 294 295
  setBreakPointAtLocation(scriptid, loc, opt_condition) {
    const params = { location :
                       { scriptId : scriptid.toString(),
                         lineNumber : loc.line,
                         columnNumber : loc.column,
                       },
                     condition : opt_condition,
                   };

    const {msgid, msg} = this.createMessage("Debugger.setBreakpoint", params);
    this.sendMessage(msg);

    const reply = this.takeReplyChecked(msgid);
296 297 298
    const result = reply.result;
    assertTrue(result !== undefined);
    const breakid = result.breakpointId;
299 300
    assertTrue(breakid !== undefined);

301
    const breakpoint = { id : result.breakpointId }
302 303 304

    this.breakpoints.add(breakpoint);
    return breakpoint;
305 306
  }

307 308 309 310 311 312 313 314 315
  execStatePrepareStep(action) {
    switch(action) {
      case this.StepAction.StepOut: this.stepOut(); break;
      case this.StepAction.StepNext: this.stepOver(); break;
      case this.StepAction.StepIn: this.stepInto(); break;
      default: %AbortJS("Unsupported StepAction"); break;
    }
  }

316 317 318 319 320 321 322 323 324
  execStateScopeType(type) {
    switch (type) {
      case "global": return this.ScopeType.Global;
      case "local": return this.ScopeType.Local;
      case "with": return this.ScopeType.With;
      case "closure": return this.ScopeType.Closure;
      case "catch": return this.ScopeType.Catch;
      case "block": return this.ScopeType.Block;
      case "script": return this.ScopeType.Script;
325
      case "eval": return this.ScopeType.Eval;
326
      case "module": return this.ScopeType.Module;
327 328 329 330
      default: %AbortJS("Unexpected scope type");
    }
  }

331 332 333 334 335 336 337 338 339 340
  execStateScopeObjectProperty(serialized_scope, prop) {
    let found = null;
    for (let i = 0; i < serialized_scope.length; i++) {
      const elem = serialized_scope[i];
      if (elem.name == prop) {
        found = elem;
        break;
      }
    }

341
    if (found == null) return { isUndefined : () => true };
342 343

    const val = { value : () => found.value.value };
344 345
    // Not undefined in the sense that we did find a property, even though
    // the value can be 'undefined'.
346
    return { value : () => val,
347
             isUndefined : () => false,
348 349 350
           };
  }

351 352 353 354
  // Returns an array of property descriptors of the scope object.
  // This is in contrast to the original API, which simply passed object
  // mirrors.
  execStateScopeObject(obj) {
355
    const serialized_scope = this.getProperties(obj.objectId);
356 357 358
    const scope = this.propertiesToObject(serialized_scope);
    return { value : () => scope,
             property : (prop) =>
359 360 361 362 363 364
                 this.execStateScopeObjectProperty(serialized_scope, prop),
             properties : () => serialized_scope.map(elem => elem.value),
             propertyNames : () => serialized_scope.map(elem => elem.name)
           };
  }

365 366 367 368 369 370 371 372 373 374
  setVariableValue(frame, scope_index, name, value) {
    const frameid = frame.callFrameId;
    const {msgid, msg} = this.createMessage(
        "Debugger.setVariableValue",
        { callFrameId : frameid,
          scopeNumber : scope_index,
          variableName : name,
          newValue : { value : value }
        });
    this.sendMessage(msg);
375 376
    const reply = this.takeReplyChecked(msgid);
    if (reply.error) {
377
      throw new Error("Failed to set variable '" + name + "' value");
378
    }
379 380 381 382 383
  }

  execStateScope(frame, scope_index) {
    const scope = frame.scopeChain[scope_index];
    return { scopeType : () => this.execStateScopeType(scope.type),
384 385
             scopeIndex : () => scope_index,
             frameIndex : () => frame.callFrameId,
386 387 388
             scopeObject : () => this.execStateScopeObject(scope.object),
             setVariableValue :
                (name, value) => this.setVariableValue(frame, scope_index,
389
                                                       name, value),
390 391 392 393 394 395 396 397
           };
  }

  // Takes a list of properties as produced by getProperties and turns them
  // into an object.
  propertiesToObject(props) {
    const obj = {}
    props.forEach((elem) => {
398 399 400 401 402 403 404 405 406 407 408
      const key = elem.name;

      let value;
      if (elem.value) {
        // Some properties (e.g. with getters/setters) don't have a value.
        switch (elem.value.type) {
          case "undefined": value = undefined; break;
          default: value = elem.value.value; break;
        }
      }

409
      obj[key] = value;
410 411
    })

412
    return obj;
413 414
  }

415 416
  getProperties(objectId) {
    const {msgid, msg} = this.createMessage(
417
        "Runtime.getProperties", { objectId : objectId, ownProperties: true });
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
    this.sendMessage(msg);
    const reply = this.takeReplyChecked(msgid);
    return reply.result.result;
  }

  getLocalScopeDetails(frame) {
    const scopes = frame.scopeChain;
    for (let i = 0; i < scopes.length; i++) {
      const scope = scopes[i]
      if (scope.type == "local") {
        return this.getProperties(scope.object.objectId);
      }
    }

    return undefined;
  }

  execStateFrameLocalCount(frame) {
    const scope_details = this.getLocalScopeDetails(frame);
    return scope_details ? scope_details.length : 0;
  }

  execStateFrameLocalName(frame, index) {
    const scope_details = this.getLocalScopeDetails(frame);
    if (index < 0 || index >= scope_details.length) return undefined;
    return scope_details[index].name;
  }

  execStateFrameLocalValue(frame, index) {
    const scope_details = this.getLocalScopeDetails(frame);
    if (index < 0 || index >= scope_details.length) return undefined;

    const local = scope_details[index];

    let localValue;
    switch (local.value.type) {
      case "undefined": localValue = undefined; break;
      default: localValue = local.value.value; break;
    }

    return { value : () => localValue };
  }

461 462 463 464 465 466 467 468
  reconstructValue(objectId) {
    const {msgid, msg} = this.createMessage(
        "Runtime.getProperties", { objectId : objectId, ownProperties: true });
    this.sendMessage(msg);
    const reply = this.takeReplyChecked(msgid);
    return Object(reply.result.internalProperties[0].value.value);
  }

469 470
  reconstructRemoteObject(obj) {
    let value = obj.value;
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
    let isUndefined = false;

    switch (obj.type) {
      case "object": {
        switch (obj.subtype) {
          case "error": {
            const desc = obj.description;
            switch (obj.className) {
              case "EvalError": throw new EvalError(desc);
              case "RangeError": throw new RangeError(desc);
              case "ReferenceError": throw new ReferenceError(desc);
              case "SyntaxError": throw new SyntaxError(desc);
              case "TypeError": throw new TypeError(desc);
              case "URIError": throw new URIError(desc);
              default: throw new Error(desc);
            }
            break;
          }
          case "array": {
            const array = [];
            const props = this.propertiesToObject(
                this.getProperties(obj.objectId));
            for (let i = 0; i < props.length; i++) {
              array[i] = props[i];
            }
            value = array;
            break;
          }
          case "null": {
            value = null;
            break;
          }
          default: {
504 505 506 507 508 509 510 511 512 513 514 515 516 517
            switch (obj.className) {
              case "global":
                value = Function('return this')();
                break;
              case "Number":
              case "String":
              case "Boolean":
                value = this.reconstructValue(obj.objectId);
                break;
              default:
                value = this.propertiesToObject(
                    this.getProperties(obj.objectId));
                break;
            }
518 519
            break;
          }
520
        }
521 522 523 524 525 526 527
        break;
      }
      case "undefined": {
        value = undefined;
        isUndefined = true;
        break;
      }
528
      case "number": {
529 530
        if (obj.description === "NaN") {
          value = NaN;
531 532 533
        }
        break;
      }
534 535 536 537 538 539
      case "bigint": {
        assertEquals("n", obj.unserializableValue.charAt(
            obj.unserializableValue.length - 1));
        value = eval(obj.unserializableValue);
        break;
      }
540 541 542 543
      case "string":
      case "boolean": {
        break;
      }
544 545 546
      case "function": {
        value = obj.description;
      }
547 548
      default: {
        break;
549 550 551 552
      }
    }

    return { value : () => value,
553 554 555
             isUndefined : () => isUndefined,
             type : () => obj.type,
             className : () => obj.className
556 557 558
           };
  }

559
  evaluateOnCallFrame(frame, expr, throw_on_side_effect = false) {
560 561 562 563
    const frameid = frame.callFrameId;
    const {msgid, msg} = this.createMessage(
        "Debugger.evaluateOnCallFrame",
        { callFrameId : frameid,
564 565
          expression : expr,
          throwOnSideEffect : throw_on_side_effect,
566 567 568 569 570
        });
    this.sendMessage(msg);
    const reply = this.takeReplyChecked(msgid);

    const result = reply.result.result;
571
    return this.reconstructRemoteObject(result);
572 573
  }

574 575 576 577 578 579 580 581
  frameReceiver(frame) {
    return this.reconstructRemoteObject(frame.this);
  }

  frameReturnValue(frame) {
    return this.reconstructRemoteObject(frame.returnValue);
  }

582 583 584 585 586 587 588 589
  execStateFrameRestart(frame) {
    const frameid = frame.callFrameId;
    const {msgid, msg} = this.createMessage(
        "Debugger.restartFrame", { callFrameId : frameid });
    this.sendMessage(msg);
    this.takeReplyChecked(msgid);
  }

590 591 592 593 594 595
  execStateFrame(frame) {
    const scriptid = parseInt(frame.location.scriptId);
    const line = frame.location.lineNumber;
    const column = frame.location.columnNumber;
    const loc = %ScriptLocationFromLine2(scriptid, line, column, 0);
    const func = { name : () => frame.functionName };
596
    const index = JSON.parse(frame.callFrameId).ordinal;
597 598 599 600 601 602 603

    function allScopes() {
      const scopes = [];
      for (let i = 0; i < frame.scopeChain.length; i++) {
        scopes.push(this.execStateScope(frame, i));
      }
      return scopes;
604
    }
605

606 607
    return { sourceColumn : () => column,
             sourceLine : () => line + 1,
608
             sourceLineText : () => loc.sourceText,
609
             sourcePosition : () => loc.position,
610 611
             evaluate : (expr, throw_on_side_effect) =>
                 this.evaluateOnCallFrame(frame, expr, throw_on_side_effect),
612 613
             functionName : () => frame.functionName,
             func : () => func,
614
             index : () => index,
615 616 617
             localCount : () => this.execStateFrameLocalCount(frame),
             localName : (ix) => this.execStateFrameLocalName(frame, ix),
             localValue: (ix) => this.execStateFrameLocalValue(frame, ix),
618
             receiver : () => this.frameReceiver(frame),
619
             restart : () => this.execStateFrameRestart(frame),
620
             returnValue : () => this.frameReturnValue(frame),
621
             scopeCount : () => frame.scopeChain.length,
622 623 624 625 626
             scope : (index) => this.execStateScope(frame, index),
             allScopes : allScopes.bind(this)
           };
  }

627
  evaluateGlobal(expr, throw_on_side_effect) {
628
    const {msgid, msg} = this.createMessage(
629
        "Runtime.evaluate", { expression : expr, throwOnSideEffect: throw_on_side_effect });
630 631 632 633 634 635 636
    this.sendMessage(msg);
    const reply = this.takeReplyChecked(msgid);

    const result = reply.result.result;
    return this.reconstructRemoteObject(result);
  }

Simon Zünd's avatar
Simon Zünd committed
637
  evaluateGlobalREPL(expr) {
638 639 640
    return %RuntimeEvaluateREPL(expr).then(value => {
      return value[".repl_result"];
    });
Simon Zünd's avatar
Simon Zünd committed
641 642
  }

643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
  eventDataException(params) {
    switch (params.data.type) {
      case "string": {
        return params.data.value;
      }
      case "object": {
        const props = this.getProperties(params.data.objectId);
        return this.propertiesToObject(props);
      }
      default: {
        return undefined;
      }
    }
  }

  eventDataScriptSource(id) {
    const {msgid, msg} = this.createMessage(
660
        "Debugger.getScriptSource", { scriptId : String(id) });
661 662 663 664 665 666 667 668 669 670 671 672 673
    this.sendMessage(msg);
    const reply = this.takeReplyChecked(msgid);
    return reply.result.scriptSource;
  }

  eventDataScriptSetSource(id, src) {
    const {msgid, msg} = this.createMessage(
        "Debugger.setScriptSource", { scriptId : id, scriptSource : src });
    this.sendMessage(msg);
    this.takeReplyChecked(msgid);
  }

  eventDataScript(params) {
674
    const id = parseInt(params.scriptId);
675 676 677 678
    const name = params.url ? params.url : undefined;

    return { id : () => id,
             name : () => name,
679
             source : () => this.eventDataScriptSource(params.scriptId),
680
             setSource : (src) => this.eventDataScriptSetSource(id, src)
681
           };
682 683
  }

684 685 686 687
  // --- Message handlers. -----------------------------------------------------

  dispatchMessage(message) {
    const method = message.method;
688 689 690
    if (method == "Debugger.paused") {
      this.handleDebuggerPaused(message);
    } else if (method == "Debugger.scriptParsed") {
691
      this.handleDebuggerScriptParsed(message);
692 693
    } else if (method == "Debugger.scriptFailedToParse") {
      this.handleDebuggerScriptFailedToParse(message);
694 695 696
    }
  }

697 698 699
  handleDebuggerPaused(message) {
    const params = message.params;

700 701 702 703 704 705
    var debugEvent;
    switch (params.reason) {
      case "exception":
      case "promiseRejection":
        debugEvent = this.DebugEvent.Exception;
        break;
706 707 708 709
      case "OOM":
        debugEvent = this.DebugEvent.OOM;
        break;
      case "other":
710 711
        debugEvent = this.DebugEvent.Break;
        break;
712 713 714 715 716 717 718 719 720
      case "ambiguous":
      case "XHR":
      case "DOM":
      case "EventListener":
      case "assert":
      case "debugCommand":
        assertUnreachable();
      default:
        assertUnreachable();
721 722
    }

723 724
    if (!params.callFrames[0]) return;

725 726 727
    // Skip break events in this file.
    if (params.callFrames[0].location.scriptId == this.thisScriptId) return;

728
    // TODO(jgruber): Arguments as needed.
729 730
    let execState = { frames : params.callFrames,
                      prepareStep : this.execStatePrepareStep.bind(this),
731
                      evaluateGlobal :
732
                        (expr) => this.evaluateGlobal(expr),
733 734 735 736 737
                      frame : (index) => this.execStateFrame(
                          index ? params.callFrames[index]
                                : params.callFrames[0]),
                      frameCount : () => params.callFrames.length
                    };
738

739
    let eventData = this.execStateFrame(params.callFrames[0]);
740 741
    if (debugEvent == this.DebugEvent.Exception) {
      eventData.uncaught = () => params.data.uncaught;
742
      eventData.exception = () => this.eventDataException(params);
743 744
    }

745
    this.invokeListener(debugEvent, execState, eventData);
746 747
  }

748 749
  handleDebuggerScriptParsed(message) {
    const params = message.params;
750
    let eventData = { scriptId : params.scriptId,
751
                      script : () => this.eventDataScript(params),
752
                      eventType : this.DebugEvent.AfterCompile
753 754 755 756
                    }

    // TODO(jgruber): Arguments as needed. Still completely missing exec_state,
    // and eventData used to contain the script mirror instead of its id.
757 758 759 760
    this.invokeListener(this.DebugEvent.AfterCompile, undefined, eventData,
                        undefined);
  }

761 762 763 764 765 766 767 768 769 770 771 772 773
  handleDebuggerScriptFailedToParse(message) {
    const params = message.params;
    let eventData = { scriptId : params.scriptId,
                      script : () => this.eventDataScript(params),
                      eventType : this.DebugEvent.CompileError
                    }

    // TODO(jgruber): Arguments as needed. Still completely missing exec_state,
    // and eventData used to contain the script mirror instead of its id.
    this.invokeListener(this.DebugEvent.CompileError, undefined, eventData,
                        undefined);
  }

774 775 776 777
  invokeListener(event, exec_state, event_data, data) {
    if (this.listener) {
      this.listener(event, exec_state, event_data, data);
    }
778 779
  }
}
780 781 782

// Simulate the debug object generated by --expose-debug-as debug.
var debug = { instance : undefined };
783

784 785 786 787 788 789 790
Object.defineProperty(debug, 'Debug', { get: function() {
  if (!debug.instance) {
    debug.instance = new DebugWrapper();
    debug.instance.enable();
  }
  return debug.instance;
}});
791 792 793 794 795

Object.defineProperty(debug, 'ScopeType', { get: function() {
  const instance = debug.Debug;
  return instance.ScopeType;
}});