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

// -------------------------------------------------------------------

7 8 9 10
var kMessages = {
  // Error
  cyclic_proto:                  ["Cyclic __proto__ value"],
  code_gen_from_strings:         ["%0"],
11 12
  generator_running:             ["Generator is already running"],
  generator_finished:            ["Generator has already finished"],
13 14 15 16 17 18 19 20 21 22 23 24 25 26
  // TypeError
  unexpected_token:              ["Unexpected token ", "%0"],
  unexpected_token_number:       ["Unexpected number"],
  unexpected_token_string:       ["Unexpected string"],
  unexpected_token_identifier:   ["Unexpected identifier"],
  unexpected_reserved:           ["Unexpected reserved word"],
  unexpected_strict_reserved:    ["Unexpected strict mode reserved word"],
  unexpected_eos:                ["Unexpected end of input"],
  malformed_regexp:              ["Invalid regular expression: /", "%0", "/: ", "%1"],
  unterminated_regexp:           ["Invalid regular expression: missing /"],
  regexp_flags:                  ["Cannot supply flags when constructing one RegExp from another"],
  incompatible_method_receiver:  ["Method ", "%0", " called on incompatible receiver ", "%1"],
  multiple_defaults_in_switch:   ["More than one default clause in switch statement"],
  newline_after_throw:           ["Illegal newline after throw"],
27 28
  label_redeclaration:           ["Label '", "%0", "' has already been declared"],
  var_redeclaration:             ["Identifier '", "%0", "' has already been declared"],
29
  duplicate_template_property:   ["Object template has duplicate property '", "%0", "'"],
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
  no_catch_or_finally:           ["Missing catch or finally after try"],
  unknown_label:                 ["Undefined label '", "%0", "'"],
  uncaught_exception:            ["Uncaught ", "%0"],
  stack_trace:                   ["Stack Trace:\n", "%0"],
  called_non_callable:           ["%0", " is not a function"],
  undefined_method:              ["Object ", "%1", " has no method '", "%0", "'"],
  property_not_function:         ["Property '", "%0", "' of object ", "%1", " is not a function"],
  cannot_convert_to_primitive:   ["Cannot convert object to primitive value"],
  not_constructor:               ["%0", " is not a constructor"],
  not_defined:                   ["%0", " is not defined"],
  non_object_property_load:      ["Cannot read property '", "%0", "' of ", "%1"],
  non_object_property_store:     ["Cannot set property '", "%0", "' of ", "%1"],
  with_expression:               ["%0", " has no properties"],
  illegal_invocation:            ["Illegal invocation"],
  no_setter_in_callback:         ["Cannot set property ", "%0", " of ", "%1", " which has only a getter"],
  apply_non_function:            ["Function.prototype.apply was called on ", "%0", ", which is a ", "%1", " and not a function"],
  apply_wrong_args:              ["Function.prototype.apply: Arguments list has wrong type"],
  invalid_in_operator_use:       ["Cannot use 'in' operator to search for '", "%0", "' in ", "%1"],
  instanceof_function_expected:  ["Expecting a function in instanceof check, but got ", "%0"],
  instanceof_nonobject_proto:    ["Function has non-object prototype '", "%0", "' in instanceof check"],
50
  undefined_or_null_to_object:   ["Cannot convert undefined or null to object"],
51 52 53 54
  reduce_no_initial:             ["Reduce of empty array with no initial value"],
  getter_must_be_callable:       ["Getter must be a function: ", "%0"],
  setter_must_be_callable:       ["Setter must be a function: ", "%0"],
  value_and_accessor:            ["Invalid property.  A property cannot both have accessors and be writable or have a value, ", "%0"],
55
  proto_object_or_null:          ["Object prototype may only be an Object or null: ", "%0"],
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
  property_desc_object:          ["Property description must be an object: ", "%0"],
  redefine_disallowed:           ["Cannot redefine property: ", "%0"],
  define_disallowed:             ["Cannot define property:", "%0", ", object is not extensible."],
  non_extensible_proto:          ["%0", " is not extensible"],
  handler_non_object:            ["Proxy.", "%0", " called with non-object as handler"],
  proto_non_object:              ["Proxy.", "%0", " called with non-object as prototype"],
  trap_function_expected:        ["Proxy.", "%0", " called with non-function for '", "%1", "' trap"],
  handler_trap_missing:          ["Proxy handler ", "%0", " has no '", "%1", "' trap"],
  handler_trap_must_be_callable: ["Proxy handler ", "%0", " has non-callable '", "%1", "' trap"],
  handler_returned_false:        ["Proxy handler ", "%0", " returned false from '", "%1", "' trap"],
  handler_returned_undefined:    ["Proxy handler ", "%0", " returned undefined from '", "%1", "' trap"],
  proxy_prop_not_configurable:   ["Proxy handler ", "%0", " returned non-configurable descriptor for property '", "%2", "' from '", "%1", "' trap"],
  proxy_non_object_prop_names:   ["Trap '", "%1", "' returned non-object ", "%0"],
  proxy_repeated_prop_name:      ["Trap '", "%1", "' returned repeated property name '", "%2", "'"],
  invalid_weakmap_key:           ["Invalid value used as weak map key"],
71
  invalid_weakset_value:         ["Invalid value used in weak set"],
72 73 74 75
  not_date_object:               ["this is not a Date object."],
  observe_non_object:            ["Object.", "%0", " cannot ", "%0", " non-object"],
  observe_non_function:          ["Object.", "%0", " cannot deliver to non-function"],
  observe_callback_frozen:       ["Object.observe cannot deliver to a frozen function object"],
76
  observe_invalid_accept:        ["Object.observe accept must be an array of strings."],
77
  observe_type_non_string:       ["Invalid changeRecord with non-string 'type' property"],
78 79
  observe_perform_non_string:    ["Invalid non-string changeType"],
  observe_perform_non_function:  ["Cannot perform non-function"],
80
  observe_notify_non_notifier:   ["notify called on non-notifier object"],
81
  observe_global_proxy:          ["%0", " cannot be called on the global proxy object"],
82
  not_typed_array:               ["this is not a typed array."],
83
  invalid_argument:              ["invalid_argument"],
84
  data_view_not_array_buffer:    ["First argument to DataView constructor must be an ArrayBuffer"],
85
  constructor_not_function:      ["Constructor ", "%0", " requires 'new'"],
86
  not_a_symbol:                  ["%0", " is not a symbol"],
87 88 89
  not_a_promise:                 ["%0", " is not a promise"],
  resolver_not_a_function:       ["Promise resolver ", "%0", " is not a function"],
  promise_cyclic:                ["Chaining cycle detected for promise ", "%0"],
90 91
  array_functions_on_frozen:     ["Cannot modify frozen array elements"],
  array_functions_change_sealed: ["Cannot add/remove sealed array elements"],
92
  first_argument_not_regexp:     ["First argument to ", "%0", " must not be a regular expression"],
93 94 95 96
  not_iterable:                  ["%0", " is not iterable"],
  not_an_iterator:               ["%0", " is not an iterator"],
  iterator_result_not_an_object: ["Iterator result ", "%0", " is not an object"],
  iterator_value_not_an_object:  ["Iterator value ", "%0", " is not an entry object"],
97 98
  // RangeError
  invalid_array_length:          ["Invalid array length"],
99
  invalid_array_buffer_length:   ["Invalid array buffer length"],
100
  invalid_string_length:         ["Invalid string length"],
101 102
  invalid_typed_array_offset:    ["Start offset is too large:"],
  invalid_typed_array_length:    ["Invalid typed array length"],
103
  invalid_typed_array_alignment: ["%0", " of ", "%1", " should be a multiple of ", "%2"],
104 105
  typed_array_set_source_too_large:
                                 ["Source is too large"],
106 107
  typed_array_set_negative_offset:
                                 ["Start offset is negative"],
108
  invalid_data_view_offset:      ["Start offset is outside the bounds of the buffer"],
109
  invalid_data_view_length:      ["Invalid data view length"],
110 111 112
  invalid_data_view_accessor_offset:
                                 ["Offset is outside the bounds of the DataView"],

113 114
  stack_overflow:                ["Maximum call stack size exceeded"],
  invalid_time_value:            ["Invalid time value"],
115
  invalid_count_value:           ["Invalid count value"],
116 117 118 119 120
  // ReferenceError
  invalid_lhs_in_assignment:     ["Invalid left-hand side in assignment"],
  invalid_lhs_in_for:            ["Invalid left-hand side in for-loop"],
  invalid_lhs_in_postfix_op:     ["Invalid left-hand side expression in postfix operation"],
  invalid_lhs_in_prefix_op:      ["Invalid left-hand side expression in prefix operation"],
121
  // SyntaxError
122 123 124
  paren_in_arg_string:           ["Function arg string contains parenthesis"],
  not_isvar:                     ["builtin %IS_VAR: not a variable"],
  single_function_literal:       ["Single function literal required"],
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
  invalid_regexp_flags:          ["Invalid flags supplied to RegExp constructor '", "%0", "'"],
  invalid_regexp:                ["Invalid RegExp pattern /", "%0", "/"],
  illegal_break:                 ["Illegal break statement"],
  illegal_continue:              ["Illegal continue statement"],
  illegal_return:                ["Illegal return statement"],
  error_loading_debugger:        ["Error loading debugger"],
  no_input_to_regexp:            ["No input to ", "%0"],
  invalid_json:                  ["String '", "%0", "' is not valid JSON"],
  circular_structure:            ["Converting circular structure to JSON"],
  called_on_non_object:          ["%0", " called on non-object"],
  called_on_null_or_undefined:   ["%0", " called on null or undefined"],
  array_indexof_not_defined:     ["Array.getIndexOf: Argument undefined"],
  object_not_extensible:         ["Can't add property ", "%0", ", object is not extensible"],
  illegal_access:                ["Illegal access"],
  strict_mode_with:              ["Strict mode code may not include a with statement"],
140
  strict_eval_arguments:         ["Unexpected eval or arguments in strict mode"],
141 142
  too_many_arguments:            ["Too many arguments in function call (only 65535 allowed)"],
  too_many_parameters:           ["Too many parameters in function definition (only 65535 allowed)"],
143
  too_many_variables:            ["Too many variables declared (only 4194303 allowed)"],
144 145 146 147 148 149 150 151 152 153 154 155 156
  strict_param_dupe:             ["Strict mode function may not have duplicate parameter names"],
  strict_octal_literal:          ["Octal literals are not allowed in strict mode."],
  strict_duplicate_property:     ["Duplicate data property in object literal not allowed in strict mode"],
  accessor_data_property:        ["Object literal may not have data and accessor property with the same name"],
  accessor_get_set:              ["Object literal may not have multiple get/set accessors with the same name"],
  strict_delete:                 ["Delete of an unqualified identifier in strict mode."],
  strict_delete_property:        ["Cannot delete property '", "%0", "' of ", "%1"],
  strict_const:                  ["Use of const in strict mode."],
  strict_function:               ["In strict mode code, functions can only be declared at top level or immediately within another function." ],
  strict_read_only_property:     ["Cannot assign to read only property '", "%0", "' of ", "%1"],
  strict_cannot_assign:          ["Cannot assign to read only '", "%0", "' in strict mode"],
  strict_poison_pill:            ["'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them"],
  strict_caller:                 ["Illegal access to a strict mode caller function."],
157
  malformed_arrow_function_parameter_list: ["Malformed arrow function parameter list"],
158
  generator_poison_pill:         ["'caller' and 'arguments' properties may not be accessed on generator functions."],
159 160 161 162 163
  unprotected_let:               ["Illegal let declaration in unprotected statement context."],
  unprotected_const:             ["Illegal const declaration in unprotected statement context."],
  cant_prevent_ext_external_array_elements: ["Cannot prevent extension of an object with external array elements"],
  redef_external_array_element:  ["Cannot redefine a property of an object with external array elements"],
  harmony_const_assign:          ["Assignment to constant variable."],
164 165
  symbol_to_string:              ["Cannot convert a Symbol value to a string"],
  symbol_to_primitive:           ["Cannot convert a Symbol wrapper object to a primitive value"],
166 167
  invalid_module_path:           ["Module does not export '", "%0", "', or export is not itself a module"],
  module_type_error:             ["Module '", "%0", "' used improperly"],
168
  module_export_undefined:       ["Export '", "%0", "' is not defined in module"]
169 170 171 172
};


function FormatString(format, args) {
173 174 175 176
  var result = "";
  var arg_num = 0;
  for (var i = 0; i < format.length; i++) {
    var str = format[i];
177 178 179 180 181
    if (str.length == 2 && %_StringCharCodeAt(str, 0) == 0x25) {
      // Two-char string starts with "%".
      var arg_num = (%_StringCharCodeAt(str, 1) - 0x30) >>> 0;
      if (arg_num < 4) {
        // str is one of %0, %1, %2 or %3.
182
        try {
183
          str = NoSideEffectToString(args[arg_num]);
184
        } catch (e) {
185 186 187 188 189 190
          if (%IsJSModule(args[arg_num]))
            str = "module";
          else if (IS_SPEC_OBJECT(args[arg_num]))
            str = "object";
          else
            str = "#<error>";
191
        }
192
      }
193
    }
194
    result += str;
195 196
  }
  return result;
197
}
198 199


200 201 202
function NoSideEffectToString(obj) {
  if (IS_STRING(obj)) return obj;
  if (IS_NUMBER(obj)) return %_NumberToString(obj);
203
  if (IS_BOOLEAN(obj)) return obj ? 'true' : 'false';
204 205
  if (IS_UNDEFINED(obj)) return 'undefined';
  if (IS_NULL(obj)) return 'null';
206 207 208 209 210 211 212 213
  if (IS_FUNCTION(obj)) {
    var str = %_CallFunction(obj, FunctionToString);
    if (str.length > 128) {
      str = %_SubString(str, 0, 111) + "...<omitted>..." +
            %_SubString(str, str.length - 2, str.length);
    }
    return str;
  }
214
  if (IS_SYMBOL(obj)) return %_CallFunction(obj, SymbolToString);
215
  if (IS_OBJECT(obj) && %GetDataProperty(obj, "toString") === ObjectToString) {
216
    var constructor = %GetDataProperty(obj, "constructor");
217 218 219 220 221 222 223
    if (typeof constructor == "function") {
      var constructorName = constructor.name;
      if (IS_STRING(constructorName) && constructorName !== "") {
        return "#<" + constructorName + ">";
      }
    }
  }
224 225 226
  if (CanBeSafelyTreatedAsAnErrorObject(obj)) {
    return %_CallFunction(obj, ErrorToString);
  }
227 228 229
  return %_CallFunction(obj, ObjectToString);
}

230 231 232 233 234 235
// To determine whether we can safely stringify an object using ErrorToString
// without the risk of side-effects, we need to check whether the object is
// either an instance of a native error type (via '%_ClassOf'), or has $Error
// in its prototype chain and hasn't overwritten 'toString' with something
// strange and unusual.
function CanBeSafelyTreatedAsAnErrorObject(obj) {
236 237 238 239 240 241 242 243 244 245
  switch (%_ClassOf(obj)) {
    case 'Error':
    case 'EvalError':
    case 'RangeError':
    case 'ReferenceError':
    case 'SyntaxError':
    case 'TypeError':
    case 'URIError':
      return true;
  }
246 247 248

  var objToString = %GetDataProperty(obj, "toString");
  return obj instanceof $Error && objToString === ErrorToString;
249 250 251
}


252 253 254 255 256
// When formatting internally created error messages, do not
// invoke overwritten error toString methods but explicitly use
// the error to string method. This is to avoid leaking error
// objects between script tags in a browser setting.
function ToStringCheckErrorObject(obj) {
257
  if (CanBeSafelyTreatedAsAnErrorObject(obj)) {
258
    return %_CallFunction(obj, ErrorToString);
259 260 261 262 263 264
  } else {
    return ToString(obj);
  }
}


265
function ToDetailString(obj) {
266
  if (obj != null && IS_OBJECT(obj) && obj.toString === ObjectToString) {
267
    var constructor = obj.constructor;
268 269 270 271 272
    if (typeof constructor == "function") {
      var constructorName = constructor.name;
      if (IS_STRING(constructorName) && constructorName !== "") {
        return "#<" + constructorName + ">";
      }
273
    }
274
  }
275
  return ToStringCheckErrorObject(obj);
276
}
277 278 279


function MakeGenericError(constructor, type, args) {
280 281
  if (IS_UNDEFINED(args)) args = [];
  return new constructor(FormatMessage(type, args));
282
}
283 284 285


/**
286
 * Set up the Script function and constructor.
287 288
 */
%FunctionSetInstanceClassName(Script, 'Script');
289 290
%AddNamedProperty(Script.prototype, 'constructor', Script,
                  DONT_ENUM | DONT_DELETE | READ_ONLY);
291 292 293 294 295 296 297
%SetCode(Script, function(x) {
  // Script objects can only be created by the VM.
  throw new $Error("Not supported");
});


// Helper functions; called from the runtime system.
298 299 300 301
function FormatMessage(type, args) {
  var format = kMessages[type];
  if (!format) return "<unknown message " + type + ">";
  return FormatString(format, args);
302
}
303 304 305


function GetLineNumber(message) {
306 307 308 309
  var start_position = %MessageGetStartPosition(message);
  if (start_position == -1) return kNoLineNumberInfo;
  var script = %MessageGetScript(message);
  var location = script.locationFromPosition(start_position, true);
310
  if (location == null) return kNoLineNumberInfo;
311
  return location.line + 1;
312
}
313 314 315 316 317


// Returns the source code line containing the given source
// position, or the empty string if the position is invalid.
function GetSourceLine(message) {
318 319 320
  var script = %MessageGetScript(message);
  var start_position = %MessageGetStartPosition(message);
  var location = script.locationFromPosition(start_position, true);
321 322 323
  if (location == null) return "";
  location.restrict();
  return location.sourceText();
324
}
325 326 327 328


function MakeTypeError(type, args) {
  return MakeGenericError($TypeError, type, args);
329
}
330 331 332 333


function MakeRangeError(type, args) {
  return MakeGenericError($RangeError, type, args);
334
}
335 336 337 338


function MakeSyntaxError(type, args) {
  return MakeGenericError($SyntaxError, type, args);
339
}
340 341 342 343


function MakeReferenceError(type, args) {
  return MakeGenericError($ReferenceError, type, args);
344
}
345 346 347 348


function MakeEvalError(type, args) {
  return MakeGenericError($EvalError, type, args);
349
}
350 351 352 353


function MakeError(type, args) {
  return MakeGenericError($Error, type, args);
354
}
355

356 357 358 359 360 361
/**
 * Find a line number given a specific source position.
 * @param {number} position The source position.
 * @return {number} 0 if input too small, -1 if input too large,
       else the line number.
 */
362
function ScriptLineFromPosition(position) {
363 364
  var lower = 0;
  var upper = this.lineCount() - 1;
365
  var line_ends = this.line_ends;
366 367

  // We'll never find invalid positions so bail right away.
368
  if (position > line_ends[upper]) {
369 370 371 372
    return -1;
  }

  // This means we don't have to safe-guard indexing line_ends[i - 1].
373
  if (position <= line_ends[0]) {
374 375 376 377 378 379 380
    return 0;
  }

  // Binary search to find line # from position range.
  while (upper >= 1) {
    var i = (lower + upper) >> 1;

381
    if (position > line_ends[i]) {
382
      lower = i + 1;
383
    } else if (position <= line_ends[i - 1]) {
384 385 386 387 388
      upper = i - 1;
    } else {
      return i;
    }
  }
389

390 391
  return -1;
}
392 393 394 395

/**
 * Get information on a specific source position.
 * @param {number} position The source position
396 397
 * @param {boolean} include_resource_offset Set to true to have the resource
 *     offset added to the location
398 399 400
 * @return {SourceLocation}
 *     If line is negative or not in the source null is returned.
 */
401 402
function ScriptLocationFromPosition(position,
                                    include_resource_offset) {
403
  var line = this.lineFromPosition(position);
404
  if (line == -1) return null;
405

406
  // Determine start, end and column.
407 408 409
  var line_ends = this.line_ends;
  var start = line == 0 ? 0 : line_ends[line - 1] + 1;
  var end = line_ends[line];
410 411 412
  if (end > 0 && %_CallFunction(this.source, end - 1, StringCharAt) == '\r') {
    end--;
  }
413 414 415
  var column = position - start;

  // Adjust according to the offset within the resource.
416 417 418 419 420
  if (include_resource_offset) {
    line += this.line_offset;
    if (line == this.line_offset) {
      column += this.column_offset;
    }
421 422 423
  }

  return new SourceLocation(this, position, line, column, start, end);
424
}
425 426 427 428 429 430 431 432 433 434 435 436


/**
 * Get information on a specific source line and column possibly offset by a
 * fixed source position. This function is used to find a source position from
 * a line and column position. The fixed source position offset is typically
 * used to find a source position in a function based on a line and column in
 * the source for the function alone. The offset passed will then be the
 * start position of the source for the function within the full script source.
 * @param {number} opt_line The line within the source. Default value is 0
 * @param {number} opt_column The column in within the line. Default value is 0
 * @param {number} opt_offset_position The offset from the begining of the
437 438
 *     source from where the line and column calculation starts.
 *     Default value is 0
439 440 441
 * @return {SourceLocation}
 *     If line is negative or not in the source null is returned.
 */
442
function ScriptLocationFromLine(opt_line, opt_column, opt_offset_position) {
443 444 445 446 447 448
  // Default is the first line in the script. Lines in the script is relative
  // to the offset within the resource.
  var line = 0;
  if (!IS_UNDEFINED(opt_line)) {
    line = opt_line - this.line_offset;
  }
449

450 451 452 453
  // Default is first column. If on the first line add the offset within the
  // resource.
  var column = opt_column || 0;
  if (line == 0) {
454
    column -= this.column_offset;
455 456 457 458 459
  }

  var offset_position = opt_offset_position || 0;
  if (line < 0 || column < 0 || offset_position < 0) return null;
  if (line == 0) {
460
    return this.locationFromPosition(offset_position + column, false);
461
  } else {
462 463 464 465 466
    // Find the line where the offset position is located.
    var offset_line = this.lineFromPosition(offset_position);

    if (offset_line == -1 || offset_line + line >= this.lineCount()) {
      return null;
467
    }
468

469 470
    return this.locationFromPosition(
        this.line_ends[offset_line + line - 1] + 1 + column);  // line > 0 here.
471 472 473 474 475 476 477 478 479 480 481 482 483 484
  }
}


/**
 * Get a slice of source code from the script. The boundaries for the slice is
 * specified in lines.
 * @param {number} opt_from_line The first line (zero bound) in the slice.
 *     Default is 0
 * @param {number} opt_to_column The last line (zero bound) in the slice (non
 *     inclusive). Default is the number of lines in the script
 * @return {SourceSlice} The source slice or null of the parameters where
 *     invalid
 */
485
function ScriptSourceSlice(opt_from_line, opt_to_line) {
486 487 488 489
  var from_line = IS_UNDEFINED(opt_from_line) ? this.line_offset
                                              : opt_from_line;
  var to_line = IS_UNDEFINED(opt_to_line) ? this.line_offset + this.lineCount()
                                          : opt_to_line;
490 491 492 493 494 495 496

  // Adjust according to the offset within the resource.
  from_line -= this.line_offset;
  to_line -= this.line_offset;
  if (from_line < 0) from_line = 0;
  if (to_line > this.lineCount()) to_line = this.lineCount();

497
  // Check parameters.
498 499 500 501 502 503
  if (from_line >= this.lineCount() ||
      to_line < 0 ||
      from_line > to_line) {
    return null;
  }

504 505 506
  var line_ends = this.line_ends;
  var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1;
  var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1;
507 508

  // Return a source slice with line numbers re-adjusted to the resource.
509 510 511 512
  return new SourceSlice(this,
                         from_line + this.line_offset,
                         to_line + this.line_offset,
                          from_position, to_position);
513 514 515
}


516
function ScriptSourceLine(opt_line) {
517 518 519 520 521 522
  // Default is the first line in the script. Lines in the script are relative
  // to the offset within the resource.
  var line = 0;
  if (!IS_UNDEFINED(opt_line)) {
    line = opt_line - this.line_offset;
  }
523 524

  // Check parameter.
525 526 527 528
  if (line < 0 || this.lineCount() <= line) {
    return null;
  }

529
  // Return the source line.
530 531 532
  var line_ends = this.line_ends;
  var start = line == 0 ? 0 : line_ends[line - 1] + 1;
  var end = line_ends[line];
533
  return %_CallFunction(this.source, start, end, StringSubstring);
534 535 536 537 538 539 540 541
}


/**
 * Returns the number of source lines.
 * @return {number}
 *     Number of source lines.
 */
542
function ScriptLineCount() {
543 544
  // Return number of source lines.
  return this.line_ends.length;
545
}
546 547


548
/**
549 550
 * If sourceURL comment is available and script starts at zero returns sourceURL
 * comment contents. Otherwise, script name is returned. See
551
 * http://fbug.googlecode.com/svn/branches/firebug1.1/docs/ReleaseNotes_1.1.txt
552 553
 * and Source Map Revision 3 proposal for details on using //# sourceURL and
 * deprecated //@ sourceURL comment to identify scripts that don't have name.
554
 *
555 556
 * @return {?string} script name if present, value for //# sourceURL or
 * deprecated //@ sourceURL comment otherwise.
557
 */
558
function ScriptNameOrSourceURL() {
559
  if (this.line_offset > 0 || this.column_offset > 0) {
560
    return this.name;
561
  }
562 563
  if (this.source_url) {
    return this.source_url;
564
  }
565
  return this.name;
566 567 568
}


569
SetUpLockedPrototype(Script,
570 571
  $Array("source", "name", "source_url", "source_mapping_url", "line_ends",
         "line_offset", "column_offset"),
572 573 574 575 576 577 578 579 580 581 582 583
  $Array(
    "lineFromPosition", ScriptLineFromPosition,
    "locationFromPosition", ScriptLocationFromPosition,
    "locationFromLine", ScriptLocationFromLine,
    "sourceSlice", ScriptSourceSlice,
    "sourceLine", ScriptSourceLine,
    "lineCount", ScriptLineCount,
    "nameOrSourceURL", ScriptNameOrSourceURL
  )
);


584 585 586 587 588 589 590 591 592
/**
 * Class for source location. A source location is a position within some
 * source with the following properties:
 *   script   : script object for the source
 *   line     : source line number
 *   column   : source column within the line
 *   position : position within the source
 *   start    : position of start of source context (inclusive)
 *   end      : position of end of source context (not inclusive)
593 594 595 596
 * Source text for the source context is the character interval
 * [start, end[. In most cases end will point to a newline character.
 * It might point just past the final position of the source if the last
 * source line does not end with a newline character.
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
 * @param {Script} script The Script object for which this is a location
 * @param {number} position Source position for the location
 * @param {number} line The line number for the location
 * @param {number} column The column within the line for the location
 * @param {number} start Source position for start of source context
 * @param {number} end Source position for end of source context
 * @constructor
 */
function SourceLocation(script, position, line, column, start, end) {
  this.script = script;
  this.position = position;
  this.line = line;
  this.column = column;
  this.start = start;
  this.end = end;
612
}
613

614
var kLineLengthLimit = 78;
615 616 617 618 619 620 621 622 623

/**
 * Restrict source location start and end positions to make the source slice
 * no more that a certain number of characters wide.
 * @param {number} opt_limit The with limit of the source text with a default
 *     of 78
 * @param {number} opt_before The number of characters to prefer before the
 *     position with a default value of 10 less that the limit
 */
624
function SourceLocationRestrict(opt_limit, opt_before) {
625 626 627 628 629 630 631 632 633 634 635 636 637 638
  // Find the actual limit to use.
  var limit;
  var before;
  if (!IS_UNDEFINED(opt_limit)) {
    limit = opt_limit;
  } else {
    limit = kLineLengthLimit;
  }
  if (!IS_UNDEFINED(opt_before)) {
    before = opt_before;
  } else {
    // If no before is specified center for small limits and perfer more source
    // before the the position that after for longer limits.
    if (limit <= 20) {
639
      before = $floor(limit / 2);
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
    } else {
      before = limit - 10;
    }
  }
  if (before >= limit) {
    before = limit - 1;
  }

  // If the [start, end[ interval is too big we restrict
  // it in one or both ends. We make sure to always produce
  // restricted intervals of maximum allowed size.
  if (this.end - this.start > limit) {
    var start_limit = this.position - before;
    var end_limit = this.position + limit - before;
    if (this.start < start_limit && end_limit < this.end) {
      this.start = start_limit;
      this.end = end_limit;
    } else if (this.start < start_limit) {
      this.start = this.end - limit;
    } else {
      this.end = this.start + limit;
    }
  }
663
}
664 665 666 667 668 669 670


/**
 * Get the source text for a SourceLocation
 * @return {String}
 *     Source text for this location.
 */
671
function SourceLocationSourceText() {
672 673 674 675 676
  return %_CallFunction(this.script.source,
                        this.start,
                        this.end,
                        StringSubstring);
}
677 678


679 680 681 682 683
SetUpLockedPrototype(SourceLocation,
  $Array("script", "position", "line", "column", "start", "end"),
  $Array(
    "restrict", SourceLocationRestrict,
    "sourceText", SourceLocationSourceText
684
 )
685 686 687
);


688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718
/**
 * Class for a source slice. A source slice is a part of a script source with
 * the following properties:
 *   script        : script object for the source
 *   from_line     : line number for the first line in the slice
 *   to_line       : source line number for the last line in the slice
 *   from_position : position of the first character in the slice
 *   to_position   : position of the last character in the slice
 * The to_line and to_position are not included in the slice, that is the lines
 * in the slice are [from_line, to_line[. Likewise the characters in the slice
 * are [from_position, to_position[.
 * @param {Script} script The Script object for the source slice
 * @param {number} from_line
 * @param {number} to_line
 * @param {number} from_position
 * @param {number} to_position
 * @constructor
 */
function SourceSlice(script, from_line, to_line, from_position, to_position) {
  this.script = script;
  this.from_line = from_line;
  this.to_line = to_line;
  this.from_position = from_position;
  this.to_position = to_position;
}

/**
 * Get the source text for a SourceSlice
 * @return {String} Source text for this slice. The last line will include
 *     the line terminating characters (if any)
 */
719
function SourceSliceSourceText() {
720 721 722 723
  return %_CallFunction(this.script.source,
                        this.from_position,
                        this.to_position,
                        StringSubstring);
724
}
725

726 727 728 729 730
SetUpLockedPrototype(SourceSlice,
  $Array("script", "from_line", "to_line", "from_position", "to_position"),
  $Array("sourceText", SourceSliceSourceText)
);

731 732 733 734

// Returns the offset of the given position within the containing
// line.
function GetPositionInLine(message) {
735 736 737
  var script = %MessageGetScript(message);
  var start_position = %MessageGetStartPosition(message);
  var location = script.locationFromPosition(start_position, false);
738 739
  if (location == null) return -1;
  location.restrict();
740
  return start_position - location.start;
741
}
742 743 744


function GetStackTraceLine(recv, fun, pos, isGlobal) {
745
  return new CallSite(recv, fun, pos, false).toString();
746
}
747 748 749 750

// ----------------------------------------------------------------------------
// Error implementation

751 752 753 754
var CallSiteReceiverKey = NEW_PRIVATE("CallSite#receiver");
var CallSiteFunctionKey = NEW_PRIVATE("CallSite#function");
var CallSitePositionKey = NEW_PRIVATE("CallSite#position");
var CallSiteStrictModeKey = NEW_PRIVATE("CallSite#strict_mode");
755 756

function CallSite(receiver, fun, pos, strict_mode) {
757 758 759 760
  SET_PRIVATE(this, CallSiteReceiverKey, receiver);
  SET_PRIVATE(this, CallSiteFunctionKey, fun);
  SET_PRIVATE(this, CallSitePositionKey, pos);
  SET_PRIVATE(this, CallSiteStrictModeKey, strict_mode);
761 762
}

763
function CallSiteGetThis() {
764 765
  return GET_PRIVATE(this, CallSiteStrictModeKey)
      ? UNDEFINED : GET_PRIVATE(this, CallSiteReceiverKey);
766
}
767

768
function CallSiteGetTypeName() {
769
  return GetTypeName(GET_PRIVATE(this, CallSiteReceiverKey), false);
770
}
771

772
function CallSiteIsToplevel() {
773
  if (GET_PRIVATE(this, CallSiteReceiverKey) == null) {
774
    return true;
775
  }
776
  return IS_GLOBAL(GET_PRIVATE(this, CallSiteReceiverKey));
777
}
778

779
function CallSiteIsEval() {
780
  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
781
  return script && script.compilation_type == COMPILATION_TYPE_EVAL;
782
}
783

784
function CallSiteGetEvalOrigin() {
785
  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
786
  return FormatEvalOrigin(script);
787
}
788

789
function CallSiteGetScriptNameOrSourceURL() {
790
  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
791
  return script ? script.nameOrSourceURL() : null;
792
}
793

794
function CallSiteGetFunction() {
795 796
  return GET_PRIVATE(this, CallSiteStrictModeKey)
      ? UNDEFINED : GET_PRIVATE(this, CallSiteFunctionKey);
797
}
798

799
function CallSiteGetFunctionName() {
800
  // See if the function knows its own name
801
  var name = GET_PRIVATE(this, CallSiteFunctionKey).name;
802
  if (name) {
803
    return name;
804
  }
805
  name = %FunctionGetInferredName(GET_PRIVATE(this, CallSiteFunctionKey));
806 807
  if (name) {
    return name;
808 809
  }
  // Maybe this is an evaluation?
810
  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
811
  if (script && script.compilation_type == COMPILATION_TYPE_EVAL) {
812
    return "eval";
813
  }
814
  return null;
815
}
816

817
function CallSiteGetMethodName() {
818 819
  // See if we can find a unique property on the receiver that holds
  // this function.
820 821
  var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
  var fun = GET_PRIVATE(this, CallSiteFunctionKey);
822 823 824 825 826
  var ownName = fun.name;
  if (ownName && receiver &&
      (%_CallFunction(receiver, ownName, ObjectLookupGetter) === fun ||
       %_CallFunction(receiver, ownName, ObjectLookupSetter) === fun ||
       (IS_OBJECT(receiver) && %GetDataProperty(receiver, ownName) === fun))) {
827 828 829
    // To handle DontEnum properties we guess that the method has
    // the same name as the function.
    return ownName;
830
  }
831
  var name = null;
832 833 834 835
  for (var prop in receiver) {
    if (%_CallFunction(receiver, prop, ObjectLookupGetter) === fun ||
        %_CallFunction(receiver, prop, ObjectLookupSetter) === fun ||
        (IS_OBJECT(receiver) && %GetDataProperty(receiver, prop) === fun)) {
836
      // If we find more than one match bail out to avoid confusion.
837
      if (name) {
838
        return null;
839
      }
840 841 842
      name = prop;
    }
  }
843
  if (name) {
844
    return name;
845
  }
846
  return null;
847
}
848

849
function CallSiteGetFileName() {
850
  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
851
  return script ? script.name : null;
852
}
853

854
function CallSiteGetLineNumber() {
855
  if (GET_PRIVATE(this, CallSitePositionKey) == -1) {
856
    return null;
857
  }
858
  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
859 860
  var location = null;
  if (script) {
861 862
    location = script.locationFromPosition(
        GET_PRIVATE(this, CallSitePositionKey), true);
863 864
  }
  return location ? location.line + 1 : null;
865
}
866

867
function CallSiteGetColumnNumber() {
868
  if (GET_PRIVATE(this, CallSitePositionKey) == -1) {
869
    return null;
870
  }
871
  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
872 873
  var location = null;
  if (script) {
874 875
    location = script.locationFromPosition(
      GET_PRIVATE(this, CallSitePositionKey), true);
876
  }
877
  return location ? location.column + 1: null;
878
}
879

880
function CallSiteIsNative() {
881
  var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
882
  return script ? (script.type == TYPE_NATIVE) : false;
883
}
884

885
function CallSiteGetPosition() {
886
  return GET_PRIVATE(this, CallSitePositionKey);
887
}
888

889
function CallSiteIsConstructor() {
890
  var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
891 892
  var constructor = (receiver != null && IS_OBJECT(receiver))
                        ? %GetDataProperty(receiver, "constructor") : null;
893
  if (!constructor) return false;
894
  return GET_PRIVATE(this, CallSiteFunctionKey) === constructor;
895
}
896

897 898 899 900 901 902
function CallSiteToString() {
  var fileName;
  var fileLocation = "";
  if (this.isNative()) {
    fileLocation = "native";
  } else {
903 904 905 906
    fileName = this.getScriptNameOrSourceURL();
    if (!fileName && this.isEval()) {
      fileLocation = this.getEvalOrigin();
      fileLocation += ", ";  // Expecting source position to follow.
907
    }
908

909 910 911 912 913 914 915 916
    if (fileName) {
      fileLocation += fileName;
    } else {
      // Source code does not originate from a file and is not native, but we
      // can still get the source position inside the source string, e.g. in
      // an eval string.
      fileLocation += "<anonymous>";
    }
917 918 919 920 921 922 923 924 925 926 927
    var lineNumber = this.getLineNumber();
    if (lineNumber != null) {
      fileLocation += ":" + lineNumber;
      var columnNumber = this.getColumnNumber();
      if (columnNumber) {
        fileLocation += ":" + columnNumber;
      }
    }
  }

  var line = "";
928 929
  var functionName = this.getFunctionName();
  var addSuffix = true;
930 931 932
  var isConstructor = this.isConstructor();
  var isMethodCall = !(this.isToplevel() || isConstructor);
  if (isMethodCall) {
933
    var typeName = GetTypeName(GET_PRIVATE(this, CallSiteReceiverKey), true);
934 935
    var methodName = this.getMethodName();
    if (functionName) {
936
      if (typeName &&
937
          %_CallFunction(functionName, typeName, StringIndexOfJS) != 0) {
938 939
        line += typeName + ".";
      }
940
      line += functionName;
941
      if (methodName &&
942
          (%_CallFunction(functionName, "." + methodName, StringIndexOfJS) !=
943
           functionName.length - methodName.length - 1)) {
944 945 946
        line += " [as " + methodName + "]";
      }
    } else {
947
      line += typeName + "." + (methodName || "<anonymous>");
948 949 950 951 952 953 954
    }
  } else if (isConstructor) {
    line += "new " + (functionName || "<anonymous>");
  } else if (functionName) {
    line += functionName;
  } else {
    line += fileLocation;
955
    addSuffix = false;
956
  }
957
  if (addSuffix) {
958 959 960 961 962
    line += " (" + fileLocation + ")";
  }
  return line;
}

963 964 965 966 967 968 969 970 971 972 973 974 975 976 977
SetUpLockedPrototype(CallSite, $Array("receiver", "fun", "pos"), $Array(
  "getThis", CallSiteGetThis,
  "getTypeName", CallSiteGetTypeName,
  "isToplevel", CallSiteIsToplevel,
  "isEval", CallSiteIsEval,
  "getEvalOrigin", CallSiteGetEvalOrigin,
  "getScriptNameOrSourceURL", CallSiteGetScriptNameOrSourceURL,
  "getFunction", CallSiteGetFunction,
  "getFunctionName", CallSiteGetFunctionName,
  "getMethodName", CallSiteGetMethodName,
  "getFileName", CallSiteGetFileName,
  "getLineNumber", CallSiteGetLineNumber,
  "getColumnNumber", CallSiteGetColumnNumber,
  "isNative", CallSiteIsNative,
  "getPosition", CallSiteGetPosition,
978 979
  "isConstructor", CallSiteIsConstructor,
  "toString", CallSiteToString
980 981 982
));


983
function FormatEvalOrigin(script) {
984
  var sourceURL = script.nameOrSourceURL();
985
  if (sourceURL) {
986
    return sourceURL;
987
  }
988 989

  var eval_origin = "eval at ";
990 991 992 993 994
  if (script.eval_from_function_name) {
    eval_origin += script.eval_from_function_name;
  } else {
    eval_origin +=  "<anonymous>";
  }
995

996 997
  var eval_from_script = script.eval_from_script;
  if (eval_from_script) {
998
    if (eval_from_script.compilation_type == COMPILATION_TYPE_EVAL) {
999
      // eval script originated from another eval.
1000
      eval_origin += " (" + FormatEvalOrigin(eval_from_script) + ")";
1001
    } else {
1002
      // eval script originated from "real" source.
1003 1004
      if (eval_from_script.name) {
        eval_origin += " (" + eval_from_script.name;
1005 1006
        var location = eval_from_script.locationFromPosition(
            script.eval_from_script_position, true);
1007 1008 1009 1010
        if (location) {
          eval_origin += ":" + (location.line + 1);
          eval_origin += ":" + (location.column + 1);
        }
1011
        eval_origin += ")";
1012 1013 1014 1015 1016
      } else {
        eval_origin += " (unknown source)";
      }
    }
  }
1017

1018
  return eval_origin;
1019
}
1020

1021 1022

function FormatErrorString(error) {
1023
  try {
1024
    return %_CallFunction(error, ErrorToString);
1025 1026
  } catch (e) {
    try {
1027
      return "<error: " + e + ">";
1028
    } catch (ee) {
1029
      return "<error>";
1030 1031
    }
  }
1032 1033 1034 1035
}


function GetStackFrames(raw_stack) {
1036
  var frames = new InternalArray();
1037
  var sloppy_frames = raw_stack[0];
1038
  for (var i = 1; i < raw_stack.length; i += 4) {
1039 1040 1041 1042 1043
    var recv = raw_stack[i];
    var fun = raw_stack[i + 1];
    var code = raw_stack[i + 2];
    var pc = raw_stack[i + 3];
    var pos = %FunctionGetPositionForOffset(code, pc);
1044 1045
    sloppy_frames--;
    frames.push(new CallSite(recv, fun, pos, (sloppy_frames < 0)));
1046 1047 1048 1049 1050
  }
  return frames;
}


1051 1052 1053 1054
// Flag to prevent recursive call of Error.prepareStackTrace.
var formatting_custom_stack_trace = false;


1055 1056
function FormatStackTrace(obj, raw_stack) {
  var frames = GetStackFrames(raw_stack);
1057 1058 1059 1060
  if (IS_FUNCTION($Error.prepareStackTrace) && !formatting_custom_stack_trace) {
    var array = [];
    %MoveArrayContents(frames, array);
    formatting_custom_stack_trace = true;
1061
    var stack_trace = UNDEFINED;
1062 1063 1064 1065 1066 1067 1068 1069 1070 1071
    try {
      stack_trace = $Error.prepareStackTrace(obj, array);
    } catch (e) {
      throw e;  // The custom formatting function threw.  Rethrow.
    } finally {
      formatting_custom_stack_trace = false;
    }
    return stack_trace;
  }

1072
  var lines = new InternalArray();
1073
  lines.push(FormatErrorString(obj));
1074 1075
  for (var i = 0; i < frames.length; i++) {
    var frame = frames[i];
1076
    var line;
1077
    try {
1078
      line = frame.toString();
1079 1080
    } catch (e) {
      try {
1081
        line = "<error: " + e + ">";
1082 1083
      } catch (ee) {
        // Any code that reaches this point is seriously nasty!
1084
        line = "<error>";
1085 1086 1087 1088
      }
    }
    lines.push("    at " + line);
  }
1089
  return %_CallFunction(lines, "\n", ArrayJoin);
1090 1091 1092
}


1093 1094
function GetTypeName(receiver, requireConstructor) {
  var constructor = receiver.constructor;
1095 1096
  if (!constructor) {
    return requireConstructor ? null :
1097
        %_CallFunction(receiver, ObjectToString);
1098 1099 1100 1101
  }
  var constructorName = constructor.name;
  if (!constructorName) {
    return requireConstructor ? null :
1102
        %_CallFunction(receiver, ObjectToString);
1103 1104 1105
  }
  return constructorName;
}
1106

1107

1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120
var stack_trace_symbol;  // Set during bootstrapping.
var formatted_stack_trace_symbol = NEW_PRIVATE("formatted stack trace");


// Format the stack trace if not yet done, and return it.
// Cache the formatted stack trace on the holder.
var StackTraceGetter = function() {
  var formatted_stack_trace = GET_PRIVATE(this, formatted_stack_trace_symbol);
  if (IS_UNDEFINED(formatted_stack_trace)) {
    var holder = this;
    while (!HAS_PRIVATE(holder, stack_trace_symbol)) {
      holder = %GetPrototype(holder);
      if (!holder) return UNDEFINED;
1121
    }
1122 1123 1124 1125 1126 1127 1128 1129
    var stack_trace = GET_PRIVATE(holder, stack_trace_symbol);
    if (IS_UNDEFINED(stack_trace)) return UNDEFINED;
    formatted_stack_trace = FormatStackTrace(holder, stack_trace);
    SET_PRIVATE(holder, stack_trace_symbol, UNDEFINED);
    SET_PRIVATE(holder, formatted_stack_trace_symbol, formatted_stack_trace);
  }
  return formatted_stack_trace;
};
1130 1131


1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146
// If the receiver equals the holder, set the formatted stack trace that the
// getter returns.
var StackTraceSetter = function(v) {
  if (HAS_PRIVATE(this, stack_trace_symbol)) {
    SET_PRIVATE(this, stack_trace_symbol, UNDEFINED);
    SET_PRIVATE(this, formatted_stack_trace_symbol, v);
  }
};


// Use a dummy function since we do not actually want to capture a stack trace
// when constructing the initial Error prototytpes.
var captureStackTrace = function captureStackTrace(obj, cons_opt) {
  // Define accessors first, as this may fail and throw.
  ObjectDefineProperty(obj, 'stack', { get: StackTraceGetter,
1147 1148
                                       set: StackTraceSetter,
                                       configurable: true });
1149
  %CollectStackTrace(obj, cons_opt ? cons_opt : captureStackTrace);
1150
}
1151

1152

1153
function SetUpError() {
1154 1155
  // Define special error type constructors.

1156
  var DefineError = function(f) {
1157 1158 1159 1160 1161 1162 1163
    // Store the error function in both the global object
    // and the runtime object. The function is fetched
    // from the runtime object when throwing errors from
    // within the runtime system to avoid strange side
    // effects when overwriting the error functions from
    // user code.
    var name = f.name;
1164 1165 1166
    %AddNamedProperty(global, name, f, DONT_ENUM);
    %AddNamedProperty(builtins, '$' + name, f,
                      DONT_ENUM | DONT_DELETE | READ_ONLY);
1167 1168 1169 1170 1171 1172
    // Configure the error function.
    if (name == 'Error') {
      // The prototype of the Error object must itself be an error.
      // However, it can't be an instance of the Error object because
      // it hasn't been properly configured yet.  Instead we create a
      // special not-a-true-error-but-close-enough object.
1173
      var ErrorPrototype = function() {};
1174 1175 1176 1177 1178 1179 1180
      %FunctionSetPrototype(ErrorPrototype, $Object.prototype);
      %FunctionSetInstanceClassName(ErrorPrototype, 'Error');
      %FunctionSetPrototype(f, new ErrorPrototype());
    } else {
      %FunctionSetPrototype(f, new $Error());
    }
    %FunctionSetInstanceClassName(f, 'Error');
1181 1182
    %AddNamedProperty(f.prototype, 'constructor', f, DONT_ENUM);
    %AddNamedProperty(f.prototype, "name", name, DONT_ENUM);
1183 1184 1185 1186 1187
    %SetCode(f, function(m) {
      if (%_IsConstructCall()) {
        // Define all the expected properties directly on the error
        // object. This avoids going through getters and setters defined
        // on prototype objects.
1188
        %AddNamedProperty(this, 'stack', UNDEFINED, DONT_ENUM);
1189
        if (!IS_UNDEFINED(m)) {
1190
          %AddNamedProperty(this, 'message', ToString(m), DONT_ENUM);
1191
        }
1192
        try { captureStackTrace(this, f); } catch (e) { }
1193 1194 1195 1196
      } else {
        return new f(m);
      }
    });
1197
    %SetNativeFlag(f);
1198
  };
1199

1200 1201 1202 1203 1204 1205 1206
  DefineError(function Error() { });
  DefineError(function TypeError() { });
  DefineError(function RangeError() { });
  DefineError(function SyntaxError() { });
  DefineError(function ReferenceError() { });
  DefineError(function EvalError() { });
  DefineError(function URIError() { });
1207 1208 1209
}

SetUpError();
1210

1211
$Error.captureStackTrace = captureStackTrace;
1212

1213
%AddNamedProperty($Error.prototype, 'message', '', DONT_ENUM);
1214

1215
// Global list of error objects visited during ErrorToString. This is
1216
// used to detect cycles in error toString formatting.
1217 1218
var visited_errors = new InternalArray();
var cyclic_error_marker = new $Object();
1219

1220
function GetPropertyWithoutInvokingMonkeyGetters(error, name) {
1221
  var current = error;
1222
  // Climb the prototype chain until we find the holder.
1223
  while (current && !%HasOwnProperty(current, name)) {
1224
    current = %GetPrototype(current);
1225
  }
1226 1227
  if (IS_NULL(current)) return UNDEFINED;
  if (!IS_OBJECT(current)) return error[name];
1228 1229 1230
  // If the property is an accessor on one of the predefined errors that can be
  // generated statically by the compiler, don't touch it. This is to address
  // http://code.google.com/p/chromium/issues/detail?id=69187
1231
  var desc = %GetOwnProperty(current, name);
1232 1233
  if (desc && desc[IS_ACCESSOR_INDEX]) {
    var isName = name === "name";
1234
    if (current === $ReferenceError.prototype)
1235
      return isName ? "ReferenceError" : UNDEFINED;
1236
    if (current === $SyntaxError.prototype)
1237
      return isName ? "SyntaxError" : UNDEFINED;
1238
    if (current === $TypeError.prototype)
1239
      return isName ? "TypeError" : UNDEFINED;
1240 1241 1242 1243 1244
  }
  // Otherwise, read normally.
  return error[name];
}

1245
function ErrorToStringDetectCycle(error) {
1246
  if (!%PushIfAbsent(visited_errors, error)) throw cyclic_error_marker;
1247
  try {
1248
    var name = GetPropertyWithoutInvokingMonkeyGetters(error, "name");
1249
    name = IS_UNDEFINED(name) ? "Error" : TO_STRING_INLINE(name);
1250
    var message = GetPropertyWithoutInvokingMonkeyGetters(error, "message");
1251 1252 1253 1254
    message = IS_UNDEFINED(message) ? "" : TO_STRING_INLINE(message);
    if (name === "") return message;
    if (message === "") return name;
    return name + ": " + message;
1255
  } finally {
1256
    visited_errors.length = visited_errors.length - 1;
1257 1258 1259
  }
}

1260
function ErrorToString() {
1261 1262
  if (!IS_SPEC_OBJECT(this)) {
    throw MakeTypeError("called_on_non_object", ["Error.prototype.toString"]);
1263
  }
1264 1265

  try {
1266
    return ErrorToStringDetectCycle(this);
1267
  } catch(e) {
1268 1269
    // If this error message was encountered already return the empty
    // string for it instead of recursively formatting it.
1270
    if (e === cyclic_error_marker) {
1271 1272 1273
      return '';
    }
    throw e;
1274
  }
1275
}
1276

1277

1278
InstallFunctions($Error.prototype, DONT_ENUM, ['toString', ErrorToString]);
1279 1280

// Boilerplate for exceptions for stack overflows. Used from
1281
// Isolate::StackOverflow().
1282 1283 1284
function SetUpStackOverflowBoilerplate() {
  var boilerplate = MakeRangeError('stack_overflow', []);

1285
  %DefineAccessorPropertyUnchecked(
1286
      boilerplate, 'stack', StackTraceGetter, StackTraceSetter, DONT_ENUM);
1287 1288 1289 1290 1291

  return boilerplate;
}

var kStackOverflowBoilerplate = SetUpStackOverflowBoilerplate();