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