messages.js 39.2 KB
Newer Older
1
// Copyright 2006-2008 the V8 project authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


// -------------------------------------------------------------------
30 31 32 33 34 35 36 37 38 39
//
// Matches Script::Type from objects.h
var TYPE_NATIVE = 0;
var TYPE_EXTENSION = 1;
var TYPE_NORMAL = 2;

// Matches Script::CompilationType from objects.h
var COMPILATION_TYPE_HOST = 0;
var COMPILATION_TYPE_EVAL = 1;
var COMPILATION_TYPE_JSON = 2;
40

41 42 43
// Matches Messages::kNoLineNumberInfo from v8.h
var kNoLineNumberInfo = 0;

44 45 46 47 48
// If this object gets passed to an error constructor the error will
// get an accessor for .message that constructs a descriptive error
// message on access.
var kAddMessageAccessorsMarker = { };

49
var kMessages = 0;
50

51
var kReplacementMarkers = [ "%0", "%1", "%2", "%3" ];
52

53 54
function FormatString(format, message) {
  var args = %MessageGetArguments(message);
55 56 57 58 59 60 61 62 63 64 65
  var result = "";
  var arg_num = 0;
  for (var i = 0; i < format.length; i++) {
    var str = format[i];
    for (arg_num = 0; arg_num < kReplacementMarkers.length; arg_num++) {
      if (format[i] !== kReplacementMarkers[arg_num]) continue;
      try {
        str = ToDetailString(args[arg_num]);
      } catch (e) {
        str = "#<error>";
      }
66
    }
67
    result += str;
68 69
  }
  return result;
70
}
71 72


73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
// To check if something is a native error we need to check the
// concrete native error types. It is not enough to check "obj
// instanceof $Error" because user code can replace
// NativeError.prototype.__proto__. User code cannot replace
// NativeError.prototype though and therefore this is a safe test.
function IsNativeErrorObject(obj) {
  return (obj instanceof $Error) ||
      (obj instanceof $EvalError) ||
      (obj instanceof $RangeError) ||
      (obj instanceof $ReferenceError) ||
      (obj instanceof $SyntaxError) ||
      (obj instanceof $TypeError) ||
      (obj instanceof $URIError);
}


89 90 91 92 93
// 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) {
94
  if (IsNativeErrorObject(obj)) {
95 96 97 98 99 100 101
    return %_CallFunction(obj, errorToString);
  } else {
    return ToString(obj);
  }
}


102 103 104
function ToDetailString(obj) {
  if (obj != null && IS_OBJECT(obj) && obj.toString === $Object.prototype.toString) {
    var constructor = obj.constructor;
105
    if (!constructor) return ToStringCheckErrorObject(obj);
106
    var constructorName = constructor.name;
107 108 109
    if (!constructorName || !IS_STRING(constructorName)) {
      return ToStringCheckErrorObject(obj);
    }
110
    return "#<" + constructorName + ">";
111
  } else {
112
    return ToStringCheckErrorObject(obj);
113
  }
114
}
115 116 117


function MakeGenericError(constructor, type, args) {
118
  if (IS_UNDEFINED(args)) {
119
    args = [];
120
  }
121
  var e = new constructor(kAddMessageAccessorsMarker);
122 123 124
  e.type = type;
  e.arguments = args;
  return e;
125
}
126 127 128 129 130 131


/**
 * Setup the Script function and constructor.
 */
%FunctionSetInstanceClassName(Script, 'Script');
132
%SetProperty(Script.prototype, 'constructor', Script, DONT_ENUM);
133 134 135 136 137 138 139 140
%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.
function FormatMessage(message) {
141 142 143
  if (kMessages === 0) {
    kMessages = {
      // Error
144
      cyclic_proto:                 ["Cyclic __proto__ value"],
145
      code_gen_from_strings:        ["Code generation from strings disallowed for this context"],
146
      // TypeError
147 148 149 150
      unexpected_token:             ["Unexpected token ", "%0"],
      unexpected_token_number:      ["Unexpected number"],
      unexpected_token_string:      ["Unexpected string"],
      unexpected_token_identifier:  ["Unexpected identifier"],
151
      unexpected_strict_reserved:   ["Unexpected strict mode reserved word"],
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
      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"],
      invalid_lhs_in_assignment:    ["Invalid left-hand side in assignment"],
      invalid_lhs_in_for_in:        ["Invalid left-hand side in for-in"],
      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"],
      multiple_defaults_in_switch:  ["More than one default clause in switch statement"],
      newline_after_throw:          ["Illegal newline after throw"],
      redeclaration:                ["%0", " '", "%1", "' has already been declared"],
      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"],
      non_object_property_call:     ["Cannot call method '", "%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"],
      null_to_object:               ["Cannot convert null to object"],
      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"],
      proto_object_or_null:         ["Object prototype may only be an Object or null"],
      property_desc_object:         ["Property description must be an object: ", "%0"],
      redefine_disallowed:          ["Cannot redefine property: ", "%0"],
      define_disallowed:            ["Cannot define property, object is not extensible: ", "%0"],
peterhal@chromium.org's avatar
peterhal@chromium.org committed
194
      non_extensible_proto:         ["%0", " is not extensible"],
195
      // RangeError
196 197
      invalid_array_length:         ["Invalid array length"],
      stack_overflow:               ["Maximum call stack size exceeded"],
198
      // SyntaxError
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
      unable_to_parse:              ["Parse error"],
      duplicate_regexp_flag:        ["Duplicate RegExp flag ", "%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"],
      obj_ctor_property_non_object: ["Object.", "%0", " called on non-object"],
      array_indexof_not_defined:    ["Array.getIndexOf: Argument undefined"],
      object_not_extensible:        ["Can't add property ", "%0", ", object is not extensible"],
      illegal_access:               ["Illegal access"],
      invalid_preparser_data:       ["Invalid preparser data for function ", "%0"],
      strict_mode_with:             ["Strict mode code may not include a with statement"],
      strict_catch_variable:        ["Catch variable may not be eval or arguments in strict mode"],
216
      too_many_parameters:          ["Too many parameters in function definition"],
217 218 219 220 221 222 223 224
      strict_param_name:            ["Parameter name eval or arguments is not allowed in strict mode"],
      strict_param_dupe:            ["Strict mode function may not have duplicate parameter names"],
      strict_var_name:              ["Variable name may not be eval or arguments in strict mode"],
      strict_function_name:         ["Function name may not be eval or arguments in strict mode"],
      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"],
225
      strict_lhs_assignment:        ["Assignment to eval or arguments is not allowed in strict mode"],
226 227
      strict_lhs_postfix:           ["Postfix increment/decrement may not have eval or arguments operand in strict mode"],
      strict_lhs_prefix:            ["Prefix increment/decrement may not have eval or arguments operand in strict mode"],
228
      strict_reserved_word:         ["Use of future reserved word in strict mode"],
229
      strict_delete:                ["Delete of an unqualified identifier in strict mode."],
230
      strict_delete_property:       ["Cannot delete property '", "%0", "' of ", "%1"],
231
      strict_const:                 ["Use of const in strict mode."],
232
      strict_function:              ["In strict mode code, functions can only be declared at top level or immediately within another function." ],
233 234
      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"],
235 236
      strict_arguments_callee:      ["Cannot access property 'callee' of strict mode arguments"],
      strict_arguments_caller:      ["Cannot access property 'caller' of strict mode arguments"],
237 238
      strict_function_caller:       ["Cannot access property 'caller' of a strict mode function"],
      strict_function_arguments:    ["Cannot access property 'arguments' of a strict mode function"],
239
      strict_caller:                ["Illegal access to a strict mode caller function."],
240 241
    };
  }
242 243 244 245
  var message_type = %MessageGetType(message);
  var format = kMessages[message_type];
  if (!format) return "<unknown message " + message_type + ">";
  return FormatString(format, message);
246
}
247 248 249


function GetLineNumber(message) {
250 251 252 253
  var start_position = %MessageGetStartPosition(message);
  if (start_position == -1) return kNoLineNumberInfo;
  var script = %MessageGetScript(message);
  var location = script.locationFromPosition(start_position, true);
254
  if (location == null) return kNoLineNumberInfo;
255
  return location.line + 1;
256
}
257 258 259 260 261


// Returns the source code line containing the given source
// position, or the empty string if the position is invalid.
function GetSourceLine(message) {
262 263 264
  var script = %MessageGetScript(message);
  var start_position = %MessageGetStartPosition(message);
  var location = script.locationFromPosition(start_position, true);
265 266 267
  if (location == null) return "";
  location.restrict();
  return location.sourceText();
268
}
269 270 271 272


function MakeTypeError(type, args) {
  return MakeGenericError($TypeError, type, args);
273
}
274 275 276 277


function MakeRangeError(type, args) {
  return MakeGenericError($RangeError, type, args);
278
}
279 280 281 282


function MakeSyntaxError(type, args) {
  return MakeGenericError($SyntaxError, type, args);
283
}
284 285 286 287


function MakeReferenceError(type, args) {
  return MakeGenericError($ReferenceError, type, args);
288
}
289 290 291 292


function MakeEvalError(type, args) {
  return MakeGenericError($EvalError, type, args);
293
}
294 295 296 297


function MakeError(type, args) {
  return MakeGenericError($Error, type, args);
298
}
299

300 301 302 303 304 305 306 307 308
/**
 * 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.
 */
Script.prototype.lineFromPosition = function(position) {
  var lower = 0;
  var upper = this.lineCount() - 1;
309
  var line_ends = this.line_ends;
310 311

  // We'll never find invalid positions so bail right away.
312
  if (position > line_ends[upper]) {
313 314 315 316
    return -1;
  }

  // This means we don't have to safe-guard indexing line_ends[i - 1].
317
  if (position <= line_ends[0]) {
318 319 320 321 322 323 324
    return 0;
  }

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

325
    if (position > line_ends[i]) {
326
      lower = i + 1;
327
    } else if (position <= line_ends[i - 1]) {
328 329 330 331 332
      upper = i - 1;
    } else {
      return i;
    }
  }
333

334 335
  return -1;
}
336 337 338 339

/**
 * Get information on a specific source position.
 * @param {number} position The source position
340 341
 * @param {boolean} include_resource_offset Set to true to have the resource
 *     offset added to the location
342 343 344
 * @return {SourceLocation}
 *     If line is negative or not in the source null is returned.
 */
345 346
Script.prototype.locationFromPosition = function (position,
                                                  include_resource_offset) {
347
  var line = this.lineFromPosition(position);
348
  if (line == -1) return null;
349

350
  // Determine start, end and column.
351 352 353
  var line_ends = this.line_ends;
  var start = line == 0 ? 0 : line_ends[line - 1] + 1;
  var end = line_ends[line];
354
  if (end > 0 && %_CallFunction(this.source, end - 1, StringCharAt) == '\r') end--;
355 356 357
  var column = position - start;

  // Adjust according to the offset within the resource.
358 359 360 361 362
  if (include_resource_offset) {
    line += this.line_offset;
    if (line == this.line_offset) {
      column += this.column_offset;
    }
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
  }

  return new SourceLocation(this, position, line, column, start, end);
};


/**
 * 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
 *     source from where the line and column calculation starts. Default value is 0
 * @return {SourceLocation}
 *     If line is negative or not in the source null is returned.
 */
Script.prototype.locationFromLine = function (opt_line, opt_column, opt_offset_position) {
  // 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;
  }
390

391 392 393 394 395 396 397 398 399 400
  // Default is first column. If on the first line add the offset within the
  // resource.
  var column = opt_column || 0;
  if (line == 0) {
    column -= this.column_offset
  }

  var offset_position = opt_offset_position || 0;
  if (line < 0 || column < 0 || offset_position < 0) return null;
  if (line == 0) {
401
    return this.locationFromPosition(offset_position + column, false);
402
  } else {
403 404 405 406 407
    // 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;
408
    }
409

410
    return this.locationFromPosition(this.line_ends[offset_line + line - 1] + 1 + column);  // line > 0 here.
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
  }
}


/**
 * 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
 */
Script.prototype.sourceSlice = function (opt_from_line, opt_to_line) {
  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

  // 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();

435
  // Check parameters.
436 437 438 439 440 441
  if (from_line >= this.lineCount() ||
      to_line < 0 ||
      from_line > to_line) {
    return null;
  }

442 443 444
  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;
445 446 447 448 449 450 451 452 453 454 455 456 457 458

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


Script.prototype.sourceLine = function (opt_line) {
  // 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;
  }
459 460

  // Check parameter.
461 462 463 464
  if (line < 0 || this.lineCount() <= line) {
    return null;
  }

465
  // Return the source line.
466 467 468
  var line_ends = this.line_ends;
  var start = line == 0 ? 0 : line_ends[line - 1] + 1;
  var end = line_ends[line];
469
  return %_CallFunction(this.source, start, end, StringSubstring);
470 471 472 473 474 475 476 477 478
}


/**
 * Returns the number of source lines.
 * @return {number}
 *     Number of source lines.
 */
Script.prototype.lineCount = function() {
479 480
  // Return number of source lines.
  return this.line_ends.length;
481 482 483
};


484 485
/**
 * Returns the name of script if available, contents of sourceURL comment
486
 * otherwise. See
487 488 489
 * http://fbug.googlecode.com/svn/branches/firebug1.1/docs/ReleaseNotes_1.1.txt
 * for details on using //@ sourceURL comment to identify scritps that don't
 * have name.
490
 *
491 492 493 494 495 496
 * @return {?string} script name if present, value for //@ sourceURL comment
 * otherwise.
 */
Script.prototype.nameOrSourceURL = function() {
  if (this.name)
    return this.name;
497
  // TODO(608): the spaces in a regexp below had to be escaped as \040
498 499 500
  // because this file is being processed by js2c whose handling of spaces
  // in regexps is broken. Also, ['"] are excluded from allowed URLs to
  // avoid matches against sources that invoke evals with sourceURL.
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
  // A better solution would be to detect these special comments in
  // the scanner/parser.
  var source = ToString(this.source);
  var sourceUrlPos = %StringIndexOf(source, "sourceURL=", 0);
  if (sourceUrlPos > 4) {
    var sourceUrlPattern =
        /\/\/@[\040\t]sourceURL=[\040\t]*([^\s\'\"]*)[\040\t]*$/gm;
    // Don't reuse lastMatchInfo here, so we create a new array with room
    // for four captures (array with length one longer than the index
    // of the fourth capture, where the numbering is zero-based).
    var matchInfo = new InternalArray(CAPTURE(3) + 1);
    var match =
        %_RegExpExec(sourceUrlPattern, source, sourceUrlPos - 4, matchInfo);
    if (match) {
      return SubString(source, matchInfo[CAPTURE(2)], matchInfo[CAPTURE(3)]);
    }
  }
  return this.name;
519 520 521
}


522 523 524 525 526 527 528 529 530 531 532 533
/**
 * 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)
 * 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
534
 * newline character.
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
 * @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;
550
}
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577


const kLineLengthLimit = 78;

/**
 * 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
 */
SourceLocation.prototype.restrict = function (opt_limit, opt_before) {
  // 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) {
578
      before = $floor(limit / 2);
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
    } 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;
    }
  }
};


/**
 * Get the source text for a SourceLocation
 * @return {String}
 *     Source text for this location.
 */
SourceLocation.prototype.sourceText = function () {
611
  return %_CallFunction(this.script.source, this.start, this.end, StringSubstring);
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
};


/**
 * 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)
 */
SourceSlice.prototype.sourceText = function () {
648 649 650 651
  return %_CallFunction(this.script.source,
                        this.from_position,
                        this.to_position,
                        StringSubstring);
652 653 654 655 656 657
};


// Returns the offset of the given position within the containing
// line.
function GetPositionInLine(message) {
658 659 660
  var script = %MessageGetScript(message);
  var start_position = %MessageGetStartPosition(message);
  var location = script.locationFromPosition(start_position, false);
661 662
  if (location == null) return -1;
  location.restrict();
663
  return start_position - location.start;
664
}
665 666 667


function GetStackTraceLine(recv, fun, pos, isGlobal) {
668
  return FormatSourcePosition(new CallSite(recv, fun, pos));
669
}
670 671 672 673

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

674
// Defines accessors for a property that is calculated the first time
675
// the property is read.
676
function DefineOneShotAccessor(obj, name, fun) {
677 678 679
  // Note that the accessors consistently operate on 'obj', not 'this'.
  // Since the object may occur in someone else's prototype chain we
  // can't rely on 'this' being the same as 'obj'.
680 681
  var hasBeenSet = false;
  var value;
682
  obj.__defineGetter__(name, function () {
683 684 685 686 687
    if (hasBeenSet) {
      return value;
    }
    hasBeenSet = true;
    value = fun(obj);
688 689
    return value;
  });
690
  obj.__defineSetter__(name, function (v) {
691 692
    hasBeenSet = true;
    value = v;
693
  });
694 695
}

696 697 698 699 700 701 702 703 704 705 706 707 708
function CallSite(receiver, fun, pos) {
  this.receiver = receiver;
  this.fun = fun;
  this.pos = pos;
}

CallSite.prototype.getThis = function () {
  return this.receiver;
};

CallSite.prototype.getTypeName = function () {
  var constructor = this.receiver.constructor;
  if (!constructor)
709
    return %_CallFunction(this.receiver, ObjectToString);
710 711
  var constructorName = constructor.name;
  if (!constructorName)
712
    return %_CallFunction(this.receiver, ObjectToString);
713 714 715 716 717 718 719 720 721 722 723
  return constructorName;
};

CallSite.prototype.isToplevel = function () {
  if (this.receiver == null)
    return true;
  return IS_GLOBAL(this.receiver);
};

CallSite.prototype.isEval = function () {
  var script = %FunctionGetScript(this.fun);
724
  return script && script.compilation_type == COMPILATION_TYPE_EVAL;
725 726 727 728
};

CallSite.prototype.getEvalOrigin = function () {
  var script = %FunctionGetScript(this.fun);
729
  return FormatEvalOrigin(script);
730 731
};

732 733 734 735 736
CallSite.prototype.getScriptNameOrSourceURL = function () {
  var script = %FunctionGetScript(this.fun);
  return script ? script.nameOrSourceURL() : null;
};

737 738 739 740
CallSite.prototype.getFunction = function () {
  return this.fun;
};

741 742 743
CallSite.prototype.getFunctionName = function () {
  // See if the function knows its own name
  var name = this.fun.name;
744
  if (name) {
745
    return name;
746 747 748 749 750
  } else {
    return %FunctionGetInferredName(this.fun);
  }
  // Maybe this is an evaluation?
  var script = %FunctionGetScript(this.fun);
751
  if (script && script.compilation_type == COMPILATION_TYPE_EVAL)
752 753 754 755 756
    return "eval";
  return null;
};

CallSite.prototype.getMethodName = function () {
757 758
  // See if we can find a unique property on the receiver that holds
  // this function.
759
  var ownName = this.fun.name;
760
  if (ownName && this.receiver &&
761 762
      (%_CallFunction(this.receiver, ownName, ObjectLookupGetter) === this.fun ||
       %_CallFunction(this.receiver, ownName, ObjectLookupSetter) === this.fun ||
763
       this.receiver[ownName] === this.fun)) {
764 765 766
    // To handle DontEnum properties we guess that the method has
    // the same name as the function.
    return ownName;
767
  }
768
  var name = null;
769
  for (var prop in this.receiver) {
770 771 772 773
    if (this.receiver.__lookupGetter__(prop) === this.fun ||
        this.receiver.__lookupSetter__(prop) === this.fun ||
        (!this.receiver.__lookupGetter__(prop) && this.receiver[prop] === this.fun)) {
      // If we find more than one match bail out to avoid confusion.
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
      if (name)
        return null;
      name = prop;
    }
  }
  if (name)
    return name;
  return null;
};

CallSite.prototype.getFileName = function () {
  var script = %FunctionGetScript(this.fun);
  return script ? script.name : null;
};

CallSite.prototype.getLineNumber = function () {
  if (this.pos == -1)
    return null;
  var script = %FunctionGetScript(this.fun);
  var location = null;
  if (script) {
    location = script.locationFromPosition(this.pos, true);
  }
  return location ? location.line + 1 : null;
};

CallSite.prototype.getColumnNumber = function () {
  if (this.pos == -1)
    return null;
  var script = %FunctionGetScript(this.fun);
  var location = null;
  if (script) {
    location = script.locationFromPosition(this.pos, true);
  }
808
  return location ? location.column + 1: null;
809 810 811 812
};

CallSite.prototype.isNative = function () {
  var script = %FunctionGetScript(this.fun);
813
  return script ? (script.type == TYPE_NATIVE) : false;
814 815 816 817 818 819 820 821 822 823 824 825 826
};

CallSite.prototype.getPosition = function () {
  return this.pos;
};

CallSite.prototype.isConstructor = function () {
  var constructor = this.receiver ? this.receiver.constructor : null;
  if (!constructor)
    return false;
  return this.fun === constructor;
};

827
function FormatEvalOrigin(script) {
828 829 830 831 832
  var sourceURL = script.nameOrSourceURL();
  if (sourceURL)
    return sourceURL;

  var eval_origin = "eval at ";
833 834 835 836 837
  if (script.eval_from_function_name) {
    eval_origin += script.eval_from_function_name;
  } else {
    eval_origin +=  "<anonymous>";
  }
838

839 840
  var eval_from_script = script.eval_from_script;
  if (eval_from_script) {
841
    if (eval_from_script.compilation_type == COMPILATION_TYPE_EVAL) {
842
      // eval script originated from another eval.
843
      eval_origin += " (" + FormatEvalOrigin(eval_from_script) + ")";
844
    } else {
845
      // eval script originated from "real" source.
846 847 848 849 850 851 852 853 854 855 856 857 858
      if (eval_from_script.name) {
        eval_origin += " (" + eval_from_script.name;
        var location = eval_from_script.locationFromPosition(script.eval_from_script_position, true);
        if (location) {
          eval_origin += ":" + (location.line + 1);
          eval_origin += ":" + (location.column + 1);
        }
        eval_origin += ")"
      } else {
        eval_origin += " (unknown source)";
      }
    }
  }
859

860 861 862
  return eval_origin;
};

863
function FormatSourcePosition(frame) {
864
  var fileName;
865 866 867 868
  var fileLocation = "";
  if (frame.isNative()) {
    fileLocation = "native";
  } else if (frame.isEval()) {
869 870 871
    fileName = frame.getScriptNameOrSourceURL();
    if (!fileName)
      fileLocation = frame.getEvalOrigin();
872
  } else {
873 874 875 876 877 878 879 880 881 882 883
    fileName = frame.getFileName();
  }

  if (fileName) {
    fileLocation += fileName;
    var lineNumber = frame.getLineNumber();
    if (lineNumber != null) {
      fileLocation += ":" + lineNumber;
      var columnNumber = frame.getColumnNumber();
      if (columnNumber) {
        fileLocation += ":" + columnNumber;
884 885 886
      }
    }
  }
887

888 889 890 891
  if (!fileLocation) {
    fileLocation = "unknown source";
  }
  var line = "";
892
  var functionName = frame.getFunction().name;
893
  var addPrefix = true;
894 895 896
  var isConstructor = frame.isConstructor();
  var isMethodCall = !(frame.isToplevel() || isConstructor);
  if (isMethodCall) {
897
    var methodName = frame.getMethodName();
898 899 900 901 902 903 904 905 906 907 908
    line += frame.getTypeName() + ".";
    if (functionName) {
      line += functionName;
      if (methodName && (methodName != functionName)) {
        line += " [as " + methodName + "]";
      }
    } else {
      line += methodName || "<anonymous>";
    }
  } else if (isConstructor) {
    line += "new " + (functionName || "<anonymous>");
909 910
  } else if (functionName) {
    line += functionName;
911 912
  } else {
    line += fileLocation;
913 914 915
    addPrefix = false;
  }
  if (addPrefix) {
916
    line += " (" + fileLocation + ")";
917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
  }
  return line;
}

function FormatStackTrace(error, frames) {
  var lines = [];
  try {
    lines.push(error.toString());
  } catch (e) {
    try {
      lines.push("<error: " + e + ">");
    } catch (ee) {
      lines.push("<error>");
    }
  }
  for (var i = 0; i < frames.length; i++) {
    var frame = frames[i];
934
    var line;
935
    try {
936
      line = FormatSourcePosition(frame);
937 938
    } catch (e) {
      try {
939
        line = "<error: " + e + ">";
940 941
      } catch (ee) {
        // Any code that reaches this point is seriously nasty!
942
        line = "<error>";
943 944 945 946 947 948 949 950 951
      }
    }
    lines.push("    at " + line);
  }
  return lines.join("\n");
}

function FormatRawStackTrace(error, raw_stack) {
  var frames = [ ];
952
  for (var i = 0; i < raw_stack.length; i += 4) {
953
    var recv = raw_stack[i];
954 955 956 957
    var fun = raw_stack[i + 1];
    var code = raw_stack[i + 2];
    var pc = raw_stack[i + 3];
    var pos = %FunctionGetPositionForOffset(code, pc);
958 959 960 961 962 963 964 965 966
    frames.push(new CallSite(recv, fun, pos));
  }
  if (IS_FUNCTION($Error.prepareStackTrace)) {
    return $Error.prepareStackTrace(error, frames);
  } else {
    return FormatStackTrace(error, frames);
  }
}

967
function DefineError(f) {
968 969 970 971 972 973
  // 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.
974
  var name = f.name;
975
  %SetProperty(global, name, f, DONT_ENUM);
976 977
  this['$' + name] = f;
  // Configure the error function.
978 979 980 981 982 983
  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.
    function ErrorPrototype() {}
984
    %FunctionSetPrototype(ErrorPrototype, $Object.prototype);
985 986 987 988 989
    %FunctionSetInstanceClassName(ErrorPrototype, 'Error');
    %FunctionSetPrototype(f, new ErrorPrototype());
  } else {
    %FunctionSetPrototype(f, new $Error());
  }
990
  %FunctionSetInstanceClassName(f, 'Error');
991
  %SetProperty(f.prototype, 'constructor', f, DONT_ENUM);
992 993 994 995 996 997
  // The name property on the prototype of error objects is not
  // specified as being read-one and dont-delete. However, allowing
  // overwriting allows leaks of error objects between script blocks
  // in the same context in a browser setting. Therefore we fix the
  // name.
  %SetProperty(f.prototype, "name", name, READ_ONLY | DONT_DELETE);
998
  %SetCode(f, function(m) {
999
    if (%_IsConstructCall()) {
1000 1001 1002 1003 1004 1005
      // Define all the expected properties directly on the error
      // object. This avoids going through getters and setters defined
      // on prototype objects.
      %IgnoreAttributesAndSetProperty(this, 'stack', void 0);
      %IgnoreAttributesAndSetProperty(this, 'arguments', void 0);
      %IgnoreAttributesAndSetProperty(this, 'type', void 0);
1006
      if (m === kAddMessageAccessorsMarker) {
1007 1008
        // DefineOneShotAccessor always inserts a message property and
        // ignores setters.
1009
        DefineOneShotAccessor(this, 'message', function (obj) {
1010
            return FormatMessage(%NewMessageObject(obj.type, obj.arguments));
1011
        });
1012
      } else if (!IS_UNDEFINED(m)) {
1013
        %IgnoreAttributesAndSetProperty(this, 'message', ToString(m));
1014
      }
1015
      captureStackTrace(this, f);
1016 1017 1018 1019
    } else {
      return new f(m);
    }
  });
1020
}
1021

1022 1023
function captureStackTrace(obj, cons_opt) {
  var stackTraceLimit = $Error.stackTraceLimit;
1024
  if (!stackTraceLimit || !IS_NUMBER(stackTraceLimit)) return;
1025 1026
  if (stackTraceLimit < 0 || stackTraceLimit > 10000)
    stackTraceLimit = 10000;
1027 1028 1029
  var raw_stack = %CollectStackTrace(cons_opt
                                     ? cons_opt
                                     : captureStackTrace, stackTraceLimit);
1030 1031 1032 1033 1034
  DefineOneShotAccessor(obj, 'stack', function (obj) {
    return FormatRawStackTrace(obj, raw_stack);
  });
};

1035 1036
$Math.__proto__ = global.Object.prototype;

1037 1038 1039 1040 1041 1042 1043
DefineError(function Error() { });
DefineError(function TypeError() { });
DefineError(function RangeError() { });
DefineError(function SyntaxError() { });
DefineError(function ReferenceError() { });
DefineError(function EvalError() { });
DefineError(function URIError() { });
1044

1045 1046
$Error.captureStackTrace = captureStackTrace;

1047 1048 1049
// Setup extra properties of the Error.prototype object.
$Error.prototype.message = '';

1050 1051 1052 1053 1054 1055 1056 1057 1058
// Global list of error objects visited during errorToString. This is
// used to detect cycles in error toString formatting.
var visited_errors = new $Array();
var cyclic_error_marker = new $Object();

function errorToStringDetectCycle() {
  if (!%PushIfAbsent(visited_errors, this)) throw cyclic_error_marker;
  try {
    var type = this.type;
1059
    if (type && !%_CallFunction(this, "message", ObjectHasOwnProperty)) {
1060
      var formatted = FormatMessage(%NewMessageObject(type, this.arguments));
1061 1062
      return this.name + ": " + formatted;
    }
1063 1064 1065
    var message = %_CallFunction(this, "message", ObjectHasOwnProperty)
        ? (": " + this.message)
        : "";
1066 1067
    return this.name + message;
  } finally {
1068
    visited_errors.length = visited_errors.length - 1;
1069 1070 1071
  }
}

1072
function errorToString() {
1073
  // This helper function is needed because access to properties on
1074 1075 1076 1077 1078 1079
  // the builtins object do not work inside of a catch clause.
  function isCyclicErrorMarker(o) { return o === cyclic_error_marker; }

  try {
    return %_CallFunction(this, errorToStringDetectCycle);
  } catch(e) {
1080 1081 1082
    // If this error message was encountered already return the empty
    // string for it instead of recursively formatting it.
    if (isCyclicErrorMarker(e)) return '';
1083
    else throw e;
1084
  }
1085
}
1086

1087 1088

InstallFunctions($Error.prototype, DONT_ENUM, ['toString', errorToString]);
1089 1090

// Boilerplate for exceptions for stack overflows. Used from
1091
// Isolate::StackOverflow().
1092
const kStackOverflowBoilerplate = MakeRangeError('stack_overflow', []);