1// Copyright 2006-2008 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 29// ------------------------------------------------------------------- 30// 31// Matches Script::Type from objects.h 32var TYPE_NATIVE = 0; 33var TYPE_EXTENSION = 1; 34var TYPE_NORMAL = 2; 35 36// Matches Script::CompilationType from objects.h 37var COMPILATION_TYPE_HOST = 0; 38var COMPILATION_TYPE_EVAL = 1; 39var COMPILATION_TYPE_JSON = 2; 40 41// Lazily initialized. 42var kVowelSounds = 0; 43var kCapitalVowelSounds = 0; 44 45// If this object gets passed to an error constructor the error will 46// get an accessor for .message that constructs a descriptive error 47// message on access. 48var kAddMessageAccessorsMarker = { }; 49 50 51function GetInstanceName(cons) { 52 if (cons.length == 0) { 53 return ""; 54 } 55 var first = %StringToLowerCase(StringCharAt.call(cons, 0)); 56 if (kVowelSounds === 0) { 57 kVowelSounds = {a: true, e: true, i: true, o: true, u: true, y: true}; 58 kCapitalVowelSounds = {a: true, e: true, i: true, o: true, u: true, h: true, 59 f: true, l: true, m: true, n: true, r: true, s: true, x: true, y: true}; 60 } 61 var vowel_mapping = kVowelSounds; 62 if (cons.length > 1 && (StringCharAt.call(cons, 0) != first)) { 63 // First char is upper case 64 var second = %StringToLowerCase(StringCharAt.call(cons, 1)); 65 // Second char is upper case 66 if (StringCharAt.call(cons, 1) != second) { 67 vowel_mapping = kCapitalVowelSounds; 68 } 69 } 70 var s = vowel_mapping[first] ? "an " : "a "; 71 return s + cons; 72} 73 74 75var kMessages = 0; 76 77 78function FormatString(format, args) { 79 var result = format; 80 for (var i = 0; i < args.length; i++) { 81 var str; 82 try { str = ToDetailString(args[i]); } 83 catch (e) { str = "#<error>"; } 84 result = ArrayJoin.call(StringSplit.call(result, "%" + i), str); 85 } 86 return result; 87} 88 89 90function ToDetailString(obj) { 91 if (obj != null && IS_OBJECT(obj) && obj.toString === $Object.prototype.toString) { 92 var constructor = obj.constructor; 93 if (!constructor) return ToString(obj); 94 var constructorName = constructor.name; 95 if (!constructorName) return ToString(obj); 96 return "#<" + GetInstanceName(constructorName) + ">"; 97 } else { 98 return ToString(obj); 99 } 100} 101 102 103function MakeGenericError(constructor, type, args) { 104 if (IS_UNDEFINED(args)) { 105 args = []; 106 } 107 var e = new constructor(kAddMessageAccessorsMarker); 108 e.type = type; 109 e.arguments = args; 110 return e; 111} 112 113 114/** 115 * Setup the Script function and constructor. 116 */ 117%FunctionSetInstanceClassName(Script, 'Script'); 118%SetProperty(Script.prototype, 'constructor', Script, DONT_ENUM); 119%SetCode(Script, function(x) { 120 // Script objects can only be created by the VM. 121 throw new $Error("Not supported"); 122}); 123 124 125// Helper functions; called from the runtime system. 126function FormatMessage(message) { 127 if (kMessages === 0) { 128 kMessages = { 129 // Error 130 cyclic_proto: "Cyclic __proto__ value", 131 // TypeError 132 unexpected_token: "Unexpected token %0", 133 unexpected_token_number: "Unexpected number", 134 unexpected_token_string: "Unexpected string", 135 unexpected_token_identifier: "Unexpected identifier", 136 unexpected_eos: "Unexpected end of input", 137 malformed_regexp: "Invalid regular expression: /%0/: %1", 138 unterminated_regexp: "Invalid regular expression: missing /", 139 regexp_flags: "Cannot supply flags when constructing one RegExp from another", 140 invalid_lhs_in_assignment: "Invalid left-hand side in assignment", 141 invalid_lhs_in_for_in: "Invalid left-hand side in for-in", 142 invalid_lhs_in_postfix_op: "Invalid left-hand side expression in postfix operation", 143 invalid_lhs_in_prefix_op: "Invalid left-hand side expression in prefix operation", 144 multiple_defaults_in_switch: "More than one default clause in switch statement", 145 newline_after_throw: "Illegal newline after throw", 146 redeclaration: "%0 '%1' has already been declared", 147 no_catch_or_finally: "Missing catch or finally after try", 148 unknown_label: "Undefined label '%0'", 149 uncaught_exception: "Uncaught %0", 150 stack_trace: "Stack Trace:\n%0", 151 called_non_callable: "%0 is not a function", 152 undefined_method: "Object %1 has no method '%0'", 153 property_not_function: "Property '%0' of object %1 is not a function", 154 cannot_convert_to_primitive: "Cannot convert object to primitive value", 155 not_constructor: "%0 is not a constructor", 156 not_defined: "%0 is not defined", 157 non_object_property_load: "Cannot read property '%0' of %1", 158 non_object_property_store: "Cannot set property '%0' of %1", 159 non_object_property_call: "Cannot call method '%0' of %1", 160 with_expression: "%0 has no properties", 161 illegal_invocation: "Illegal invocation", 162 no_setter_in_callback: "Cannot set property %0 of %1 which has only a getter", 163 apply_non_function: "Function.prototype.apply was called on %0, which is a %1 and not a function", 164 apply_wrong_args: "Function.prototype.apply: Arguments list has wrong type", 165 invalid_in_operator_use: "Cannot use 'in' operator to search for '%0' in %1", 166 instanceof_function_expected: "Expecting a function in instanceof check, but got %0", 167 instanceof_nonobject_proto: "Function has non-object prototype '%0' in instanceof check", 168 null_to_object: "Cannot convert null to object", 169 reduce_no_initial: "Reduce of empty array with no initial value", 170 getter_must_be_callable: "Getter must be a function: %0", 171 setter_must_be_callable: "Setter must be a function: %0", 172 value_and_accessor: "Invalid property. A property cannot both have accessors and be writable or have a value: %0", 173 proto_object_or_null: "Object prototype may only be an Object or null", 174 property_desc_object: "Property description must be an object: %0", 175 redefine_disallowed: "Cannot redefine property: %0", 176 define_disallowed: "Cannot define property, object is not extensible: %0", 177 // RangeError 178 invalid_array_length: "Invalid array length", 179 stack_overflow: "Maximum call stack size exceeded", 180 apply_overflow: "Function.prototype.apply cannot support %0 arguments", 181 // SyntaxError 182 unable_to_parse: "Parse error", 183 duplicate_regexp_flag: "Duplicate RegExp flag %0", 184 invalid_regexp: "Invalid RegExp pattern /%0/", 185 illegal_break: "Illegal break statement", 186 illegal_continue: "Illegal continue statement", 187 illegal_return: "Illegal return statement", 188 error_loading_debugger: "Error loading debugger", 189 no_input_to_regexp: "No input to %0", 190 result_not_primitive: "Result of %0 must be a primitive, was %1", 191 invalid_json: "String '%0' is not valid JSON", 192 circular_structure: "Converting circular structure to JSON", 193 obj_ctor_property_non_object: "Object.%0 called on non-object", 194 array_indexof_not_defined: "Array.getIndexOf: Argument undefined" 195 }; 196 } 197 var format = kMessages[message.type]; 198 if (!format) return "<unknown message " + message.type + ">"; 199 return FormatString(format, message.args); 200} 201 202 203function GetLineNumber(message) { 204 if (message.startPos == -1) return -1; 205 var location = message.script.locationFromPosition(message.startPos, true); 206 if (location == null) return -1; 207 return location.line + 1; 208} 209 210 211// Returns the source code line containing the given source 212// position, or the empty string if the position is invalid. 213function GetSourceLine(message) { 214 var location = message.script.locationFromPosition(message.startPos, true); 215 if (location == null) return ""; 216 location.restrict(); 217 return location.sourceText(); 218} 219 220 221function MakeTypeError(type, args) { 222 return MakeGenericError($TypeError, type, args); 223} 224 225 226function MakeRangeError(type, args) { 227 return MakeGenericError($RangeError, type, args); 228} 229 230 231function MakeSyntaxError(type, args) { 232 return MakeGenericError($SyntaxError, type, args); 233} 234 235 236function MakeReferenceError(type, args) { 237 return MakeGenericError($ReferenceError, type, args); 238} 239 240 241function MakeEvalError(type, args) { 242 return MakeGenericError($EvalError, type, args); 243} 244 245 246function MakeError(type, args) { 247 return MakeGenericError($Error, type, args); 248} 249 250/** 251 * Find a line number given a specific source position. 252 * @param {number} position The source position. 253 * @return {number} 0 if input too small, -1 if input too large, 254 else the line number. 255 */ 256Script.prototype.lineFromPosition = function(position) { 257 var lower = 0; 258 var upper = this.lineCount() - 1; 259 var line_ends = this.line_ends; 260 261 // We'll never find invalid positions so bail right away. 262 if (position > line_ends[upper]) { 263 return -1; 264 } 265 266 // This means we don't have to safe-guard indexing line_ends[i - 1]. 267 if (position <= line_ends[0]) { 268 return 0; 269 } 270 271 // Binary search to find line # from position range. 272 while (upper >= 1) { 273 var i = (lower + upper) >> 1; 274 275 if (position > line_ends[i]) { 276 lower = i + 1; 277 } else if (position <= line_ends[i - 1]) { 278 upper = i - 1; 279 } else { 280 return i; 281 } 282 } 283 return -1; 284} 285 286/** 287 * Get information on a specific source position. 288 * @param {number} position The source position 289 * @param {boolean} include_resource_offset Set to true to have the resource 290 * offset added to the location 291 * @return {SourceLocation} 292 * If line is negative or not in the source null is returned. 293 */ 294Script.prototype.locationFromPosition = function (position, 295 include_resource_offset) { 296 var line = this.lineFromPosition(position); 297 if (line == -1) return null; 298 299 // Determine start, end and column. 300 var line_ends = this.line_ends; 301 var start = line == 0 ? 0 : line_ends[line - 1] + 1; 302 var end = line_ends[line]; 303 if (end > 0 && StringCharAt.call(this.source, end - 1) == '\r') end--; 304 var column = position - start; 305 306 // Adjust according to the offset within the resource. 307 if (include_resource_offset) { 308 line += this.line_offset; 309 if (line == this.line_offset) { 310 column += this.column_offset; 311 } 312 } 313 314 return new SourceLocation(this, position, line, column, start, end); 315}; 316 317 318/** 319 * Get information on a specific source line and column possibly offset by a 320 * fixed source position. This function is used to find a source position from 321 * a line and column position. The fixed source position offset is typically 322 * used to find a source position in a function based on a line and column in 323 * the source for the function alone. The offset passed will then be the 324 * start position of the source for the function within the full script source. 325 * @param {number} opt_line The line within the source. Default value is 0 326 * @param {number} opt_column The column in within the line. Default value is 0 327 * @param {number} opt_offset_position The offset from the begining of the 328 * source from where the line and column calculation starts. Default value is 0 329 * @return {SourceLocation} 330 * If line is negative or not in the source null is returned. 331 */ 332Script.prototype.locationFromLine = function (opt_line, opt_column, opt_offset_position) { 333 // Default is the first line in the script. Lines in the script is relative 334 // to the offset within the resource. 335 var line = 0; 336 if (!IS_UNDEFINED(opt_line)) { 337 line = opt_line - this.line_offset; 338 } 339 340 // Default is first column. If on the first line add the offset within the 341 // resource. 342 var column = opt_column || 0; 343 if (line == 0) { 344 column -= this.column_offset 345 } 346 347 var offset_position = opt_offset_position || 0; 348 if (line < 0 || column < 0 || offset_position < 0) return null; 349 if (line == 0) { 350 return this.locationFromPosition(offset_position + column, false); 351 } else { 352 // Find the line where the offset position is located. 353 var offset_line = this.lineFromPosition(offset_position); 354 355 if (offset_line == -1 || offset_line + line >= this.lineCount()) { 356 return null; 357 } 358 359 return this.locationFromPosition(this.line_ends[offset_line + line - 1] + 1 + column); // line > 0 here. 360 } 361} 362 363 364/** 365 * Get a slice of source code from the script. The boundaries for the slice is 366 * specified in lines. 367 * @param {number} opt_from_line The first line (zero bound) in the slice. 368 * Default is 0 369 * @param {number} opt_to_column The last line (zero bound) in the slice (non 370 * inclusive). Default is the number of lines in the script 371 * @return {SourceSlice} The source slice or null of the parameters where 372 * invalid 373 */ 374Script.prototype.sourceSlice = function (opt_from_line, opt_to_line) { 375 var from_line = IS_UNDEFINED(opt_from_line) ? this.line_offset : opt_from_line; 376 var to_line = IS_UNDEFINED(opt_to_line) ? this.line_offset + this.lineCount() : opt_to_line 377 378 // Adjust according to the offset within the resource. 379 from_line -= this.line_offset; 380 to_line -= this.line_offset; 381 if (from_line < 0) from_line = 0; 382 if (to_line > this.lineCount()) to_line = this.lineCount(); 383 384 // Check parameters. 385 if (from_line >= this.lineCount() || 386 to_line < 0 || 387 from_line > to_line) { 388 return null; 389 } 390 391 var line_ends = this.line_ends; 392 var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1; 393 var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1; 394 395 // Return a source slice with line numbers re-adjusted to the resource. 396 return new SourceSlice(this, from_line + this.line_offset, to_line + this.line_offset, 397 from_position, to_position); 398} 399 400 401Script.prototype.sourceLine = function (opt_line) { 402 // Default is the first line in the script. Lines in the script are relative 403 // to the offset within the resource. 404 var line = 0; 405 if (!IS_UNDEFINED(opt_line)) { 406 line = opt_line - this.line_offset; 407 } 408 409 // Check parameter. 410 if (line < 0 || this.lineCount() <= line) { 411 return null; 412 } 413 414 // Return the source line. 415 var line_ends = this.line_ends; 416 var start = line == 0 ? 0 : line_ends[line - 1] + 1; 417 var end = line_ends[line]; 418 return StringSubstring.call(this.source, start, end); 419} 420 421 422/** 423 * Returns the number of source lines. 424 * @return {number} 425 * Number of source lines. 426 */ 427Script.prototype.lineCount = function() { 428 // Return number of source lines. 429 return this.line_ends.length; 430}; 431 432 433/** 434 * Class for source location. A source location is a position within some 435 * source with the following properties: 436 * script : script object for the source 437 * line : source line number 438 * column : source column within the line 439 * position : position within the source 440 * start : position of start of source context (inclusive) 441 * end : position of end of source context (not inclusive) 442 * Source text for the source context is the character interval [start, end[. In 443 * most cases end will point to a newline character. It might point just past 444 * the final position of the source if the last source line does not end with a 445 * newline character. 446 * @param {Script} script The Script object for which this is a location 447 * @param {number} position Source position for the location 448 * @param {number} line The line number for the location 449 * @param {number} column The column within the line for the location 450 * @param {number} start Source position for start of source context 451 * @param {number} end Source position for end of source context 452 * @constructor 453 */ 454function SourceLocation(script, position, line, column, start, end) { 455 this.script = script; 456 this.position = position; 457 this.line = line; 458 this.column = column; 459 this.start = start; 460 this.end = end; 461} 462 463 464const kLineLengthLimit = 78; 465 466/** 467 * Restrict source location start and end positions to make the source slice 468 * no more that a certain number of characters wide. 469 * @param {number} opt_limit The with limit of the source text with a default 470 * of 78 471 * @param {number} opt_before The number of characters to prefer before the 472 * position with a default value of 10 less that the limit 473 */ 474SourceLocation.prototype.restrict = function (opt_limit, opt_before) { 475 // Find the actual limit to use. 476 var limit; 477 var before; 478 if (!IS_UNDEFINED(opt_limit)) { 479 limit = opt_limit; 480 } else { 481 limit = kLineLengthLimit; 482 } 483 if (!IS_UNDEFINED(opt_before)) { 484 before = opt_before; 485 } else { 486 // If no before is specified center for small limits and perfer more source 487 // before the the position that after for longer limits. 488 if (limit <= 20) { 489 before = $floor(limit / 2); 490 } else { 491 before = limit - 10; 492 } 493 } 494 if (before >= limit) { 495 before = limit - 1; 496 } 497 498 // If the [start, end[ interval is too big we restrict 499 // it in one or both ends. We make sure to always produce 500 // restricted intervals of maximum allowed size. 501 if (this.end - this.start > limit) { 502 var start_limit = this.position - before; 503 var end_limit = this.position + limit - before; 504 if (this.start < start_limit && end_limit < this.end) { 505 this.start = start_limit; 506 this.end = end_limit; 507 } else if (this.start < start_limit) { 508 this.start = this.end - limit; 509 } else { 510 this.end = this.start + limit; 511 } 512 } 513}; 514 515 516/** 517 * Get the source text for a SourceLocation 518 * @return {String} 519 * Source text for this location. 520 */ 521SourceLocation.prototype.sourceText = function () { 522 return StringSubstring.call(this.script.source, this.start, this.end); 523}; 524 525 526/** 527 * Class for a source slice. A source slice is a part of a script source with 528 * the following properties: 529 * script : script object for the source 530 * from_line : line number for the first line in the slice 531 * to_line : source line number for the last line in the slice 532 * from_position : position of the first character in the slice 533 * to_position : position of the last character in the slice 534 * The to_line and to_position are not included in the slice, that is the lines 535 * in the slice are [from_line, to_line[. Likewise the characters in the slice 536 * are [from_position, to_position[. 537 * @param {Script} script The Script object for the source slice 538 * @param {number} from_line 539 * @param {number} to_line 540 * @param {number} from_position 541 * @param {number} to_position 542 * @constructor 543 */ 544function SourceSlice(script, from_line, to_line, from_position, to_position) { 545 this.script = script; 546 this.from_line = from_line; 547 this.to_line = to_line; 548 this.from_position = from_position; 549 this.to_position = to_position; 550} 551 552 553/** 554 * Get the source text for a SourceSlice 555 * @return {String} Source text for this slice. The last line will include 556 * the line terminating characters (if any) 557 */ 558SourceSlice.prototype.sourceText = function () { 559 return StringSubstring.call(this.script.source, this.from_position, this.to_position); 560}; 561 562 563// Returns the offset of the given position within the containing 564// line. 565function GetPositionInLine(message) { 566 var location = message.script.locationFromPosition(message.startPos, false); 567 if (location == null) return -1; 568 location.restrict(); 569 return message.startPos - location.start; 570} 571 572 573function ErrorMessage(type, args, startPos, endPos, script, stackTrace) { 574 this.startPos = startPos; 575 this.endPos = endPos; 576 this.type = type; 577 this.args = args; 578 this.script = script; 579 this.stackTrace = stackTrace; 580} 581 582 583function MakeMessage(type, args, startPos, endPos, script, stackTrace) { 584 return new ErrorMessage(type, args, startPos, endPos, script, stackTrace); 585} 586 587 588function GetStackTraceLine(recv, fun, pos, isGlobal) { 589 return FormatSourcePosition(new CallSite(recv, fun, pos)); 590} 591 592// ---------------------------------------------------------------------------- 593// Error implementation 594 595// Defines accessors for a property that is calculated the first time 596// the property is read. 597function DefineOneShotAccessor(obj, name, fun) { 598 // Note that the accessors consistently operate on 'obj', not 'this'. 599 // Since the object may occur in someone else's prototype chain we 600 // can't rely on 'this' being the same as 'obj'. 601 var hasBeenSet = false; 602 var value; 603 obj.__defineGetter__(name, function () { 604 if (hasBeenSet) { 605 return value; 606 } 607 hasBeenSet = true; 608 value = fun(obj); 609 return value; 610 }); 611 obj.__defineSetter__(name, function (v) { 612 hasBeenSet = true; 613 value = v; 614 }); 615} 616 617function CallSite(receiver, fun, pos) { 618 this.receiver = receiver; 619 this.fun = fun; 620 this.pos = pos; 621} 622 623CallSite.prototype.getThis = function () { 624 return this.receiver; 625}; 626 627CallSite.prototype.getTypeName = function () { 628 var constructor = this.receiver.constructor; 629 if (!constructor) 630 return $Object.prototype.toString.call(this.receiver); 631 var constructorName = constructor.name; 632 if (!constructorName) 633 return $Object.prototype.toString.call(this.receiver); 634 return constructorName; 635}; 636 637CallSite.prototype.isToplevel = function () { 638 if (this.receiver == null) 639 return true; 640 return IS_GLOBAL(this.receiver); 641}; 642 643CallSite.prototype.isEval = function () { 644 var script = %FunctionGetScript(this.fun); 645 return script && script.compilation_type == COMPILATION_TYPE_EVAL; 646}; 647 648CallSite.prototype.getEvalOrigin = function () { 649 var script = %FunctionGetScript(this.fun); 650 return FormatEvalOrigin(script); 651}; 652 653CallSite.prototype.getFunction = function () { 654 return this.fun; 655}; 656 657CallSite.prototype.getFunctionName = function () { 658 // See if the function knows its own name 659 var name = this.fun.name; 660 if (name) { 661 return name; 662 } else { 663 return %FunctionGetInferredName(this.fun); 664 } 665 // Maybe this is an evaluation? 666 var script = %FunctionGetScript(this.fun); 667 if (script && script.compilation_type == COMPILATION_TYPE_EVAL) 668 return "eval"; 669 return null; 670}; 671 672CallSite.prototype.getMethodName = function () { 673 // See if we can find a unique property on the receiver that holds 674 // this function. 675 var ownName = this.fun.name; 676 if (ownName && this.receiver && this.receiver[ownName] === this.fun) 677 // To handle DontEnum properties we guess that the method has 678 // the same name as the function. 679 return ownName; 680 var name = null; 681 for (var prop in this.receiver) { 682 if (this.receiver[prop] === this.fun) { 683 // If we find more than one match bail out to avoid confusion 684 if (name) 685 return null; 686 name = prop; 687 } 688 } 689 if (name) 690 return name; 691 return null; 692}; 693 694CallSite.prototype.getFileName = function () { 695 var script = %FunctionGetScript(this.fun); 696 return script ? script.name : null; 697}; 698 699CallSite.prototype.getLineNumber = function () { 700 if (this.pos == -1) 701 return null; 702 var script = %FunctionGetScript(this.fun); 703 var location = null; 704 if (script) { 705 location = script.locationFromPosition(this.pos, true); 706 } 707 return location ? location.line + 1 : null; 708}; 709 710CallSite.prototype.getColumnNumber = function () { 711 if (this.pos == -1) 712 return null; 713 var script = %FunctionGetScript(this.fun); 714 var location = null; 715 if (script) { 716 location = script.locationFromPosition(this.pos, true); 717 } 718 return location ? location.column + 1: null; 719}; 720 721CallSite.prototype.isNative = function () { 722 var script = %FunctionGetScript(this.fun); 723 return script ? (script.type == TYPE_NATIVE) : false; 724}; 725 726CallSite.prototype.getPosition = function () { 727 return this.pos; 728}; 729 730CallSite.prototype.isConstructor = function () { 731 var constructor = this.receiver ? this.receiver.constructor : null; 732 if (!constructor) 733 return false; 734 return this.fun === constructor; 735}; 736 737function FormatEvalOrigin(script) { 738 var eval_origin = ""; 739 if (script.eval_from_function_name) { 740 eval_origin += script.eval_from_function_name; 741 } else { 742 eval_origin += "<anonymous>"; 743 } 744 745 var eval_from_script = script.eval_from_script; 746 if (eval_from_script) { 747 if (eval_from_script.compilation_type == COMPILATION_TYPE_EVAL) { 748 // eval script originated from another eval. 749 eval_origin += " (eval at " + FormatEvalOrigin(eval_from_script) + ")"; 750 } else { 751 // eval script originated from "real" scource. 752 if (eval_from_script.name) { 753 eval_origin += " (" + eval_from_script.name; 754 var location = eval_from_script.locationFromPosition(script.eval_from_script_position, true); 755 if (location) { 756 eval_origin += ":" + (location.line + 1); 757 eval_origin += ":" + (location.column + 1); 758 } 759 eval_origin += ")" 760 } else { 761 eval_origin += " (unknown source)"; 762 } 763 } 764 } 765 766 return eval_origin; 767}; 768 769function FormatSourcePosition(frame) { 770 var fileLocation = ""; 771 if (frame.isNative()) { 772 fileLocation = "native"; 773 } else if (frame.isEval()) { 774 fileLocation = "eval at " + frame.getEvalOrigin(); 775 } else { 776 var fileName = frame.getFileName(); 777 if (fileName) { 778 fileLocation += fileName; 779 var lineNumber = frame.getLineNumber(); 780 if (lineNumber != null) { 781 fileLocation += ":" + lineNumber; 782 var columnNumber = frame.getColumnNumber(); 783 if (columnNumber) { 784 fileLocation += ":" + columnNumber; 785 } 786 } 787 } 788 } 789 if (!fileLocation) { 790 fileLocation = "unknown source"; 791 } 792 var line = ""; 793 var functionName = frame.getFunction().name; 794 var methodName = frame.getMethodName(); 795 var addPrefix = true; 796 var isConstructor = frame.isConstructor(); 797 var isMethodCall = !(frame.isToplevel() || isConstructor); 798 if (isMethodCall) { 799 line += frame.getTypeName() + "."; 800 if (functionName) { 801 line += functionName; 802 if (methodName && (methodName != functionName)) { 803 line += " [as " + methodName + "]"; 804 } 805 } else { 806 line += methodName || "<anonymous>"; 807 } 808 } else if (isConstructor) { 809 line += "new " + (functionName || "<anonymous>"); 810 } else if (functionName) { 811 line += functionName; 812 } else { 813 line += fileLocation; 814 addPrefix = false; 815 } 816 if (addPrefix) { 817 line += " (" + fileLocation + ")"; 818 } 819 return line; 820} 821 822function FormatStackTrace(error, frames) { 823 var lines = []; 824 try { 825 lines.push(error.toString()); 826 } catch (e) { 827 try { 828 lines.push("<error: " + e + ">"); 829 } catch (ee) { 830 lines.push("<error>"); 831 } 832 } 833 for (var i = 0; i < frames.length; i++) { 834 var frame = frames[i]; 835 var line; 836 try { 837 line = FormatSourcePosition(frame); 838 } catch (e) { 839 try { 840 line = "<error: " + e + ">"; 841 } catch (ee) { 842 // Any code that reaches this point is seriously nasty! 843 line = "<error>"; 844 } 845 } 846 lines.push(" at " + line); 847 } 848 return lines.join("\n"); 849} 850 851function FormatRawStackTrace(error, raw_stack) { 852 var frames = [ ]; 853 for (var i = 0; i < raw_stack.length; i += 3) { 854 var recv = raw_stack[i]; 855 var fun = raw_stack[i+1]; 856 var pc = raw_stack[i+2]; 857 var pos = %FunctionGetPositionForOffset(fun, pc); 858 frames.push(new CallSite(recv, fun, pos)); 859 } 860 if (IS_FUNCTION($Error.prepareStackTrace)) { 861 return $Error.prepareStackTrace(error, frames); 862 } else { 863 return FormatStackTrace(error, frames); 864 } 865} 866 867function DefineError(f) { 868 // Store the error function in both the global object 869 // and the runtime object. The function is fetched 870 // from the runtime object when throwing errors from 871 // within the runtime system to avoid strange side 872 // effects when overwriting the error functions from 873 // user code. 874 var name = f.name; 875 %SetProperty(global, name, f, DONT_ENUM); 876 this['$' + name] = f; 877 // Configure the error function. 878 if (name == 'Error') { 879 // The prototype of the Error object must itself be an error. 880 // However, it can't be an instance of the Error object because 881 // it hasn't been properly configured yet. Instead we create a 882 // special not-a-true-error-but-close-enough object. 883 function ErrorPrototype() {} 884 %FunctionSetPrototype(ErrorPrototype, $Object.prototype); 885 %FunctionSetInstanceClassName(ErrorPrototype, 'Error'); 886 %FunctionSetPrototype(f, new ErrorPrototype()); 887 } else { 888 %FunctionSetPrototype(f, new $Error()); 889 } 890 %FunctionSetInstanceClassName(f, 'Error'); 891 %SetProperty(f.prototype, 'constructor', f, DONT_ENUM); 892 f.prototype.name = name; 893 %SetCode(f, function(m) { 894 if (%_IsConstructCall()) { 895 if (m === kAddMessageAccessorsMarker) { 896 DefineOneShotAccessor(this, 'message', function (obj) { 897 return FormatMessage({type: obj.type, args: obj.arguments}); 898 }); 899 } else if (!IS_UNDEFINED(m)) { 900 this.message = ToString(m); 901 } 902 captureStackTrace(this, f); 903 } else { 904 return new f(m); 905 } 906 }); 907} 908 909function captureStackTrace(obj, cons_opt) { 910 var stackTraceLimit = $Error.stackTraceLimit; 911 if (!stackTraceLimit) return; 912 if (stackTraceLimit < 0 || stackTraceLimit > 10000) 913 stackTraceLimit = 10000; 914 var raw_stack = %CollectStackTrace(cons_opt ? cons_opt : captureStackTrace, 915 stackTraceLimit); 916 DefineOneShotAccessor(obj, 'stack', function (obj) { 917 return FormatRawStackTrace(obj, raw_stack); 918 }); 919}; 920 921$Math.__proto__ = global.Object.prototype; 922 923DefineError(function Error() { }); 924DefineError(function TypeError() { }); 925DefineError(function RangeError() { }); 926DefineError(function SyntaxError() { }); 927DefineError(function ReferenceError() { }); 928DefineError(function EvalError() { }); 929DefineError(function URIError() { }); 930 931$Error.captureStackTrace = captureStackTrace; 932 933// Setup extra properties of the Error.prototype object. 934$Error.prototype.message = ''; 935 936%SetProperty($Error.prototype, 'toString', function toString() { 937 var type = this.type; 938 if (type && !this.hasOwnProperty("message")) { 939 return this.name + ": " + FormatMessage({ type: type, args: this.arguments }); 940 } 941 var message = this.message; 942 return this.name + (message ? (": " + message) : ""); 943}, DONT_ENUM); 944 945 946// Boilerplate for exceptions for stack overflows. Used from 947// Top::StackOverflow(). 948const kStackOverflowBoilerplate = MakeRangeError('stack_overflow', []); 949