1 // Copyright 2012 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 var kMessages = { 31 // Error 32 cyclic_proto: ["Cyclic __proto__ value"], 33 code_gen_from_strings: ["%0"], 34 generator_running: ["Generator is already running"], 35 generator_finished: ["Generator has already finished"], 36 // TypeError 37 unexpected_token: ["Unexpected token ", "%0"], 38 unexpected_token_number: ["Unexpected number"], 39 unexpected_token_string: ["Unexpected string"], 40 unexpected_token_identifier: ["Unexpected identifier"], 41 unexpected_reserved: ["Unexpected reserved word"], 42 unexpected_strict_reserved: ["Unexpected strict mode reserved word"], 43 unexpected_eos: ["Unexpected end of input"], 44 malformed_regexp: ["Invalid regular expression: /", "%0", "/: ", "%1"], 45 unterminated_regexp: ["Invalid regular expression: missing /"], 46 regexp_flags: ["Cannot supply flags when constructing one RegExp from another"], 47 incompatible_method_receiver: ["Method ", "%0", " called on incompatible receiver ", "%1"], 48 invalid_lhs_in_assignment: ["Invalid left-hand side in assignment"], 49 invalid_lhs_in_for_in: ["Invalid left-hand side in for-in"], 50 invalid_lhs_in_postfix_op: ["Invalid left-hand side expression in postfix operation"], 51 invalid_lhs_in_prefix_op: ["Invalid left-hand side expression in prefix operation"], 52 multiple_defaults_in_switch: ["More than one default clause in switch statement"], 53 newline_after_throw: ["Illegal newline after throw"], 54 redeclaration: ["%0", " '", "%1", "' has already been declared"], 55 no_catch_or_finally: ["Missing catch or finally after try"], 56 unknown_label: ["Undefined label '", "%0", "'"], 57 uncaught_exception: ["Uncaught ", "%0"], 58 stack_trace: ["Stack Trace:\n", "%0"], 59 called_non_callable: ["%0", " is not a function"], 60 undefined_method: ["Object ", "%1", " has no method '", "%0", "'"], 61 property_not_function: ["Property '", "%0", "' of object ", "%1", " is not a function"], 62 cannot_convert_to_primitive: ["Cannot convert object to primitive value"], 63 not_constructor: ["%0", " is not a constructor"], 64 not_defined: ["%0", " is not defined"], 65 non_object_property_load: ["Cannot read property '", "%0", "' of ", "%1"], 66 non_object_property_store: ["Cannot set property '", "%0", "' of ", "%1"], 67 non_object_property_call: ["Cannot call method '", "%0", "' of ", "%1"], 68 with_expression: ["%0", " has no properties"], 69 illegal_invocation: ["Illegal invocation"], 70 no_setter_in_callback: ["Cannot set property ", "%0", " of ", "%1", " which has only a getter"], 71 apply_non_function: ["Function.prototype.apply was called on ", "%0", ", which is a ", "%1", " and not a function"], 72 apply_wrong_args: ["Function.prototype.apply: Arguments list has wrong type"], 73 invalid_in_operator_use: ["Cannot use 'in' operator to search for '", "%0", "' in ", "%1"], 74 instanceof_function_expected: ["Expecting a function in instanceof check, but got ", "%0"], 75 instanceof_nonobject_proto: ["Function has non-object prototype '", "%0", "' in instanceof check"], 76 undefined_or_null_to_object: ["Cannot convert undefined or null to object"], 77 reduce_no_initial: ["Reduce of empty array with no initial value"], 78 getter_must_be_callable: ["Getter must be a function: ", "%0"], 79 setter_must_be_callable: ["Setter must be a function: ", "%0"], 80 value_and_accessor: ["Invalid property. A property cannot both have accessors and be writable or have a value, ", "%0"], 81 proto_object_or_null: ["Object prototype may only be an Object or null"], 82 property_desc_object: ["Property description must be an object: ", "%0"], 83 redefine_disallowed: ["Cannot redefine property: ", "%0"], 84 define_disallowed: ["Cannot define property:", "%0", ", object is not extensible."], 85 non_extensible_proto: ["%0", " is not extensible"], 86 handler_non_object: ["Proxy.", "%0", " called with non-object as handler"], 87 proto_non_object: ["Proxy.", "%0", " called with non-object as prototype"], 88 trap_function_expected: ["Proxy.", "%0", " called with non-function for '", "%1", "' trap"], 89 handler_trap_missing: ["Proxy handler ", "%0", " has no '", "%1", "' trap"], 90 handler_trap_must_be_callable: ["Proxy handler ", "%0", " has non-callable '", "%1", "' trap"], 91 handler_returned_false: ["Proxy handler ", "%0", " returned false from '", "%1", "' trap"], 92 handler_returned_undefined: ["Proxy handler ", "%0", " returned undefined from '", "%1", "' trap"], 93 proxy_prop_not_configurable: ["Proxy handler ", "%0", " returned non-configurable descriptor for property '", "%2", "' from '", "%1", "' trap"], 94 proxy_non_object_prop_names: ["Trap '", "%1", "' returned non-object ", "%0"], 95 proxy_repeated_prop_name: ["Trap '", "%1", "' returned repeated property name '", "%2", "'"], 96 invalid_weakmap_key: ["Invalid value used as weak map key"], 97 invalid_weakset_value: ["Invalid value used in weak set"], 98 not_date_object: ["this is not a Date object."], 99 observe_non_object: ["Object.", "%0", " cannot ", "%0", " non-object"], 100 observe_non_function: ["Object.", "%0", " cannot deliver to non-function"], 101 observe_callback_frozen: ["Object.observe cannot deliver to a frozen function object"], 102 observe_invalid_accept: ["Object.observe accept must be an array of strings."], 103 observe_type_non_string: ["Invalid changeRecord with non-string 'type' property"], 104 observe_perform_non_string: ["Invalid non-string changeType"], 105 observe_perform_non_function: ["Cannot perform non-function"], 106 observe_notify_non_notifier: ["notify called on non-notifier object"], 107 proto_poison_pill: ["Generic use of __proto__ accessor not allowed"], 108 not_typed_array: ["this is not a typed array."], 109 invalid_argument: ["invalid_argument"], 110 data_view_not_array_buffer: ["First argument to DataView constructor must be an ArrayBuffer"], 111 constructor_not_function: ["Constructor ", "%0", " requires 'new'"], 112 // RangeError 113 invalid_array_length: ["Invalid array length"], 114 invalid_array_buffer_length: ["Invalid array buffer length"], 115 invalid_typed_array_offset: ["Start offset is too large:"], 116 invalid_typed_array_length: ["Invalid typed array length"], 117 invalid_typed_array_alignment: ["%0", "of", "%1", "should be a multiple of", "%3"], 118 typed_array_set_source_too_large: 119 ["Source is too large"], 120 typed_array_set_negative_offset: 121 ["Start offset is negative"], 122 invalid_data_view_offset: ["Start offset is outside the bounds of the buffer"], 123 invalid_data_view_length: ["Invalid data view length"], 124 invalid_data_view_accessor_offset: 125 ["Offset is outside the bounds of the DataView"], 126 127 stack_overflow: ["Maximum call stack size exceeded"], 128 invalid_time_value: ["Invalid time value"], 129 invalid_count_value: ["Invalid count value"], 130 // SyntaxError 131 paren_in_arg_string: ["Function arg string contains parenthesis"], 132 not_isvar: ["builtin %IS_VAR: not a variable"], 133 single_function_literal: ["Single function literal required"], 134 invalid_regexp_flags: ["Invalid flags supplied to RegExp constructor '", "%0", "'"], 135 invalid_regexp: ["Invalid RegExp pattern /", "%0", "/"], 136 illegal_break: ["Illegal break statement"], 137 illegal_continue: ["Illegal continue statement"], 138 illegal_return: ["Illegal return statement"], 139 illegal_let: ["Illegal let declaration outside extended mode"], 140 error_loading_debugger: ["Error loading debugger"], 141 no_input_to_regexp: ["No input to ", "%0"], 142 invalid_json: ["String '", "%0", "' is not valid JSON"], 143 circular_structure: ["Converting circular structure to JSON"], 144 called_on_non_object: ["%0", " called on non-object"], 145 called_on_null_or_undefined: ["%0", " called on null or undefined"], 146 array_indexof_not_defined: ["Array.getIndexOf: Argument undefined"], 147 object_not_extensible: ["Can't add property ", "%0", ", object is not extensible"], 148 illegal_access: ["Illegal access"], 149 invalid_preparser_data: ["Invalid preparser data for function ", "%0"], 150 strict_mode_with: ["Strict mode code may not include a with statement"], 151 strict_catch_variable: ["Catch variable may not be eval or arguments in strict mode"], 152 too_many_arguments: ["Too many arguments in function call (only 32766 allowed)"], 153 too_many_parameters: ["Too many parameters in function definition (only 32766 allowed)"], 154 too_many_variables: ["Too many variables declared (only 131071 allowed)"], 155 strict_param_name: ["Parameter name eval or arguments is not allowed in strict mode"], 156 strict_param_dupe: ["Strict mode function may not have duplicate parameter names"], 157 strict_var_name: ["Variable name may not be eval or arguments in strict mode"], 158 strict_function_name: ["Function name may not be eval or arguments in strict mode"], 159 strict_octal_literal: ["Octal literals are not allowed in strict mode."], 160 strict_duplicate_property: ["Duplicate data property in object literal not allowed in strict mode"], 161 accessor_data_property: ["Object literal may not have data and accessor property with the same name"], 162 accessor_get_set: ["Object literal may not have multiple get/set accessors with the same name"], 163 strict_lhs_assignment: ["Assignment to eval or arguments is not allowed in strict mode"], 164 strict_lhs_postfix: ["Postfix increment/decrement may not have eval or arguments operand in strict mode"], 165 strict_lhs_prefix: ["Prefix increment/decrement may not have eval or arguments operand in strict mode"], 166 strict_reserved_word: ["Use of future reserved word in strict mode"], 167 strict_delete: ["Delete of an unqualified identifier in strict mode."], 168 strict_delete_property: ["Cannot delete property '", "%0", "' of ", "%1"], 169 strict_const: ["Use of const in strict mode."], 170 strict_function: ["In strict mode code, functions can only be declared at top level or immediately within another function." ], 171 strict_read_only_property: ["Cannot assign to read only property '", "%0", "' of ", "%1"], 172 strict_cannot_assign: ["Cannot assign to read only '", "%0", "' in strict mode"], 173 strict_poison_pill: ["'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them"], 174 strict_caller: ["Illegal access to a strict mode caller function."], 175 unprotected_let: ["Illegal let declaration in unprotected statement context."], 176 unprotected_const: ["Illegal const declaration in unprotected statement context."], 177 cant_prevent_ext_external_array_elements: ["Cannot prevent extension of an object with external array elements"], 178 redef_external_array_element: ["Cannot redefine a property of an object with external array elements"], 179 harmony_const_assign: ["Assignment to constant variable."], 180 symbol_to_string: ["Conversion from symbol to string"], 181 invalid_module_path: ["Module does not export '", "%0", "', or export is not itself a module"], 182 module_type_error: ["Module '", "%0", "' used improperly"], 183 module_export_undefined: ["Export '", "%0", "' is not defined in module"] 184 }; 185 186 187 function FormatString(format, args) { 188 var result = ""; 189 var arg_num = 0; 190 for (var i = 0; i < format.length; i++) { 191 var str = format[i]; 192 if (str.length == 2 && %_StringCharCodeAt(str, 0) == 0x25) { 193 // Two-char string starts with "%". 194 var arg_num = (%_StringCharCodeAt(str, 1) - 0x30) >>> 0; 195 if (arg_num < 4) { 196 // str is one of %0, %1, %2 or %3. 197 try { 198 str = NoSideEffectToString(args[arg_num]); 199 } catch (e) { 200 if (%IsJSModule(args[arg_num])) 201 str = "module"; 202 else if (IS_SPEC_OBJECT(args[arg_num])) 203 str = "object"; 204 else 205 str = "#<error>"; 206 } 207 } 208 } 209 result += str; 210 } 211 return result; 212 } 213 214 215 function NoSideEffectToString(obj) { 216 if (IS_STRING(obj)) return obj; 217 if (IS_NUMBER(obj)) return %_NumberToString(obj); 218 if (IS_BOOLEAN(obj)) return x ? 'true' : 'false'; 219 if (IS_UNDEFINED(obj)) return 'undefined'; 220 if (IS_NULL(obj)) return 'null'; 221 if (IS_FUNCTION(obj)) return %_CallFunction(obj, FunctionToString); 222 if (IS_OBJECT(obj) && %GetDataProperty(obj, "toString") === ObjectToString) { 223 var constructor = %GetDataProperty(obj, "constructor"); 224 if (typeof constructor == "function") { 225 var constructorName = constructor.name; 226 if (IS_STRING(constructorName) && constructorName !== "") { 227 return "#<" + constructorName + ">"; 228 } 229 } 230 } 231 if (CanBeSafelyTreatedAsAnErrorObject(obj)) { 232 return %_CallFunction(obj, ErrorToString); 233 } 234 return %_CallFunction(obj, ObjectToString); 235 } 236 237 // To determine whether we can safely stringify an object using ErrorToString 238 // without the risk of side-effects, we need to check whether the object is 239 // either an instance of a native error type (via '%_ClassOf'), or has $Error 240 // in its prototype chain and hasn't overwritten 'toString' with something 241 // strange and unusual. 242 function CanBeSafelyTreatedAsAnErrorObject(obj) { 243 switch (%_ClassOf(obj)) { 244 case 'Error': 245 case 'EvalError': 246 case 'RangeError': 247 case 'ReferenceError': 248 case 'SyntaxError': 249 case 'TypeError': 250 case 'URIError': 251 return true; 252 } 253 254 var objToString = %GetDataProperty(obj, "toString"); 255 return obj instanceof $Error && objToString === ErrorToString; 256 } 257 258 259 // When formatting internally created error messages, do not 260 // invoke overwritten error toString methods but explicitly use 261 // the error to string method. This is to avoid leaking error 262 // objects between script tags in a browser setting. 263 function ToStringCheckErrorObject(obj) { 264 if (CanBeSafelyTreatedAsAnErrorObject(obj)) { 265 return %_CallFunction(obj, ErrorToString); 266 } else { 267 return ToString(obj); 268 } 269 } 270 271 272 function ToDetailString(obj) { 273 if (obj != null && IS_OBJECT(obj) && obj.toString === ObjectToString) { 274 var constructor = obj.constructor; 275 if (typeof constructor == "function") { 276 var constructorName = constructor.name; 277 if (IS_STRING(constructorName) && constructorName !== "") { 278 return "#<" + constructorName + ">"; 279 } 280 } 281 } 282 return ToStringCheckErrorObject(obj); 283 } 284 285 286 function MakeGenericError(constructor, type, args) { 287 if (IS_UNDEFINED(args)) args = []; 288 return new constructor(FormatMessage(type, args)); 289 } 290 291 292 /** 293 * Set up the Script function and constructor. 294 */ 295 %FunctionSetInstanceClassName(Script, 'Script'); 296 %SetProperty(Script.prototype, 'constructor', Script, 297 DONT_ENUM | DONT_DELETE | READ_ONLY); 298 %SetCode(Script, function(x) { 299 // Script objects can only be created by the VM. 300 throw new $Error("Not supported"); 301 }); 302 303 304 // Helper functions; called from the runtime system. 305 function FormatMessage(type, args) { 306 var format = kMessages[type]; 307 if (!format) return "<unknown message " + type + ">"; 308 return FormatString(format, args); 309 } 310 311 312 function GetLineNumber(message) { 313 var start_position = %MessageGetStartPosition(message); 314 if (start_position == -1) return kNoLineNumberInfo; 315 var script = %MessageGetScript(message); 316 var location = script.locationFromPosition(start_position, true); 317 if (location == null) return kNoLineNumberInfo; 318 return location.line + 1; 319 } 320 321 322 // Returns the source code line containing the given source 323 // position, or the empty string if the position is invalid. 324 function GetSourceLine(message) { 325 var script = %MessageGetScript(message); 326 var start_position = %MessageGetStartPosition(message); 327 var location = script.locationFromPosition(start_position, true); 328 if (location == null) return ""; 329 location.restrict(); 330 return location.sourceText(); 331 } 332 333 334 function MakeTypeError(type, args) { 335 return MakeGenericError($TypeError, type, args); 336 } 337 338 339 function MakeRangeError(type, args) { 340 return MakeGenericError($RangeError, type, args); 341 } 342 343 344 function MakeSyntaxError(type, args) { 345 return MakeGenericError($SyntaxError, type, args); 346 } 347 348 349 function MakeReferenceError(type, args) { 350 return MakeGenericError($ReferenceError, type, args); 351 } 352 353 354 function MakeEvalError(type, args) { 355 return MakeGenericError($EvalError, type, args); 356 } 357 358 359 function MakeError(type, args) { 360 return MakeGenericError($Error, type, args); 361 } 362 363 /** 364 * Find a line number given a specific source position. 365 * @param {number} position The source position. 366 * @return {number} 0 if input too small, -1 if input too large, 367 else the line number. 368 */ 369 function ScriptLineFromPosition(position) { 370 var lower = 0; 371 var upper = this.lineCount() - 1; 372 var line_ends = this.line_ends; 373 374 // We'll never find invalid positions so bail right away. 375 if (position > line_ends[upper]) { 376 return -1; 377 } 378 379 // This means we don't have to safe-guard indexing line_ends[i - 1]. 380 if (position <= line_ends[0]) { 381 return 0; 382 } 383 384 // Binary search to find line # from position range. 385 while (upper >= 1) { 386 var i = (lower + upper) >> 1; 387 388 if (position > line_ends[i]) { 389 lower = i + 1; 390 } else if (position <= line_ends[i - 1]) { 391 upper = i - 1; 392 } else { 393 return i; 394 } 395 } 396 397 return -1; 398 } 399 400 /** 401 * Get information on a specific source position. 402 * @param {number} position The source position 403 * @param {boolean} include_resource_offset Set to true to have the resource 404 * offset added to the location 405 * @return {SourceLocation} 406 * If line is negative or not in the source null is returned. 407 */ 408 function ScriptLocationFromPosition(position, 409 include_resource_offset) { 410 var line = this.lineFromPosition(position); 411 if (line == -1) return null; 412 413 // Determine start, end and column. 414 var line_ends = this.line_ends; 415 var start = line == 0 ? 0 : line_ends[line - 1] + 1; 416 var end = line_ends[line]; 417 if (end > 0 && %_CallFunction(this.source, end - 1, StringCharAt) == '\r') { 418 end--; 419 } 420 var column = position - start; 421 422 // Adjust according to the offset within the resource. 423 if (include_resource_offset) { 424 line += this.line_offset; 425 if (line == this.line_offset) { 426 column += this.column_offset; 427 } 428 } 429 430 return new SourceLocation(this, position, line, column, start, end); 431 } 432 433 434 /** 435 * Get information on a specific source line and column possibly offset by a 436 * fixed source position. This function is used to find a source position from 437 * a line and column position. The fixed source position offset is typically 438 * used to find a source position in a function based on a line and column in 439 * the source for the function alone. The offset passed will then be the 440 * start position of the source for the function within the full script source. 441 * @param {number} opt_line The line within the source. Default value is 0 442 * @param {number} opt_column The column in within the line. Default value is 0 443 * @param {number} opt_offset_position The offset from the begining of the 444 * source from where the line and column calculation starts. 445 * Default value is 0 446 * @return {SourceLocation} 447 * If line is negative or not in the source null is returned. 448 */ 449 function ScriptLocationFromLine(opt_line, opt_column, opt_offset_position) { 450 // Default is the first line in the script. Lines in the script is relative 451 // to the offset within the resource. 452 var line = 0; 453 if (!IS_UNDEFINED(opt_line)) { 454 line = opt_line - this.line_offset; 455 } 456 457 // Default is first column. If on the first line add the offset within the 458 // resource. 459 var column = opt_column || 0; 460 if (line == 0) { 461 column -= this.column_offset; 462 } 463 464 var offset_position = opt_offset_position || 0; 465 if (line < 0 || column < 0 || offset_position < 0) return null; 466 if (line == 0) { 467 return this.locationFromPosition(offset_position + column, false); 468 } else { 469 // Find the line where the offset position is located. 470 var offset_line = this.lineFromPosition(offset_position); 471 472 if (offset_line == -1 || offset_line + line >= this.lineCount()) { 473 return null; 474 } 475 476 return this.locationFromPosition( 477 this.line_ends[offset_line + line - 1] + 1 + column); // line > 0 here. 478 } 479 } 480 481 482 /** 483 * Get a slice of source code from the script. The boundaries for the slice is 484 * specified in lines. 485 * @param {number} opt_from_line The first line (zero bound) in the slice. 486 * Default is 0 487 * @param {number} opt_to_column The last line (zero bound) in the slice (non 488 * inclusive). Default is the number of lines in the script 489 * @return {SourceSlice} The source slice or null of the parameters where 490 * invalid 491 */ 492 function ScriptSourceSlice(opt_from_line, opt_to_line) { 493 var from_line = IS_UNDEFINED(opt_from_line) ? this.line_offset 494 : opt_from_line; 495 var to_line = IS_UNDEFINED(opt_to_line) ? this.line_offset + this.lineCount() 496 : opt_to_line; 497 498 // Adjust according to the offset within the resource. 499 from_line -= this.line_offset; 500 to_line -= this.line_offset; 501 if (from_line < 0) from_line = 0; 502 if (to_line > this.lineCount()) to_line = this.lineCount(); 503 504 // Check parameters. 505 if (from_line >= this.lineCount() || 506 to_line < 0 || 507 from_line > to_line) { 508 return null; 509 } 510 511 var line_ends = this.line_ends; 512 var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1; 513 var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1; 514 515 // Return a source slice with line numbers re-adjusted to the resource. 516 return new SourceSlice(this, 517 from_line + this.line_offset, 518 to_line + this.line_offset, 519 from_position, to_position); 520 } 521 522 523 function ScriptSourceLine(opt_line) { 524 // Default is the first line in the script. Lines in the script are relative 525 // to the offset within the resource. 526 var line = 0; 527 if (!IS_UNDEFINED(opt_line)) { 528 line = opt_line - this.line_offset; 529 } 530 531 // Check parameter. 532 if (line < 0 || this.lineCount() <= line) { 533 return null; 534 } 535 536 // Return the source line. 537 var line_ends = this.line_ends; 538 var start = line == 0 ? 0 : line_ends[line - 1] + 1; 539 var end = line_ends[line]; 540 return %_CallFunction(this.source, start, end, StringSubstring); 541 } 542 543 544 /** 545 * Returns the number of source lines. 546 * @return {number} 547 * Number of source lines. 548 */ 549 function ScriptLineCount() { 550 // Return number of source lines. 551 return this.line_ends.length; 552 } 553 554 555 /** 556 * If sourceURL comment is available and script starts at zero returns sourceURL 557 * comment contents. Otherwise, script name is returned. See 558 * http://fbug.googlecode.com/svn/branches/firebug1.1/docs/ReleaseNotes_1.1.txt 559 * and Source Map Revision 3 proposal for details on using //# sourceURL and 560 * deprecated //@ sourceURL comment to identify scripts that don't have name. 561 * 562 * @return {?string} script name if present, value for //# sourceURL or 563 * deprecated //@ sourceURL comment otherwise. 564 */ 565 function ScriptNameOrSourceURL() { 566 if (this.line_offset > 0 || this.column_offset > 0) { 567 return this.name; 568 } 569 570 // The result is cached as on long scripts it takes noticable time to search 571 // for the sourceURL. 572 if (this.hasCachedNameOrSourceURL) { 573 return this.cachedNameOrSourceURL; 574 } 575 this.hasCachedNameOrSourceURL = true; 576 577 // TODO(608): the spaces in a regexp below had to be escaped as \040 578 // because this file is being processed by js2c whose handling of spaces 579 // in regexps is broken. Also, ['"] are excluded from allowed URLs to 580 // avoid matches against sources that invoke evals with sourceURL. 581 // A better solution would be to detect these special comments in 582 // the scanner/parser. 583 var source = ToString(this.source); 584 var sourceUrlPos = %StringIndexOf(source, "sourceURL=", 0); 585 this.cachedNameOrSourceURL = this.name; 586 if (sourceUrlPos > 4) { 587 var sourceUrlPattern = 588 /\/\/[#@][\040\t]sourceURL=[\040\t]*([^\s\'\"]*)[\040\t]*$/gm; 589 // Don't reuse lastMatchInfo here, so we create a new array with room 590 // for four captures (array with length one longer than the index 591 // of the fourth capture, where the numbering is zero-based). 592 var matchInfo = new InternalArray(CAPTURE(3) + 1); 593 var match = 594 %_RegExpExec(sourceUrlPattern, source, sourceUrlPos - 4, matchInfo); 595 if (match) { 596 this.cachedNameOrSourceURL = 597 %_SubString(source, matchInfo[CAPTURE(2)], matchInfo[CAPTURE(3)]); 598 } 599 } 600 return this.cachedNameOrSourceURL; 601 } 602 603 604 SetUpLockedPrototype(Script, 605 $Array("source", "name", "line_ends", "line_offset", "column_offset", 606 "cachedNameOrSourceURL", "hasCachedNameOrSourceURL" ), 607 $Array( 608 "lineFromPosition", ScriptLineFromPosition, 609 "locationFromPosition", ScriptLocationFromPosition, 610 "locationFromLine", ScriptLocationFromLine, 611 "sourceSlice", ScriptSourceSlice, 612 "sourceLine", ScriptSourceLine, 613 "lineCount", ScriptLineCount, 614 "nameOrSourceURL", ScriptNameOrSourceURL 615 ) 616 ); 617 618 619 /** 620 * Class for source location. A source location is a position within some 621 * source with the following properties: 622 * script : script object for the source 623 * line : source line number 624 * column : source column within the line 625 * position : position within the source 626 * start : position of start of source context (inclusive) 627 * end : position of end of source context (not inclusive) 628 * Source text for the source context is the character interval 629 * [start, end[. In most cases end will point to a newline character. 630 * It might point just past the final position of the source if the last 631 * source line does not end with a newline character. 632 * @param {Script} script The Script object for which this is a location 633 * @param {number} position Source position for the location 634 * @param {number} line The line number for the location 635 * @param {number} column The column within the line for the location 636 * @param {number} start Source position for start of source context 637 * @param {number} end Source position for end of source context 638 * @constructor 639 */ 640 function SourceLocation(script, position, line, column, start, end) { 641 this.script = script; 642 this.position = position; 643 this.line = line; 644 this.column = column; 645 this.start = start; 646 this.end = end; 647 } 648 649 var kLineLengthLimit = 78; 650 651 /** 652 * Restrict source location start and end positions to make the source slice 653 * no more that a certain number of characters wide. 654 * @param {number} opt_limit The with limit of the source text with a default 655 * of 78 656 * @param {number} opt_before The number of characters to prefer before the 657 * position with a default value of 10 less that the limit 658 */ 659 function SourceLocationRestrict(opt_limit, opt_before) { 660 // Find the actual limit to use. 661 var limit; 662 var before; 663 if (!IS_UNDEFINED(opt_limit)) { 664 limit = opt_limit; 665 } else { 666 limit = kLineLengthLimit; 667 } 668 if (!IS_UNDEFINED(opt_before)) { 669 before = opt_before; 670 } else { 671 // If no before is specified center for small limits and perfer more source 672 // before the the position that after for longer limits. 673 if (limit <= 20) { 674 before = $floor(limit / 2); 675 } else { 676 before = limit - 10; 677 } 678 } 679 if (before >= limit) { 680 before = limit - 1; 681 } 682 683 // If the [start, end[ interval is too big we restrict 684 // it in one or both ends. We make sure to always produce 685 // restricted intervals of maximum allowed size. 686 if (this.end - this.start > limit) { 687 var start_limit = this.position - before; 688 var end_limit = this.position + limit - before; 689 if (this.start < start_limit && end_limit < this.end) { 690 this.start = start_limit; 691 this.end = end_limit; 692 } else if (this.start < start_limit) { 693 this.start = this.end - limit; 694 } else { 695 this.end = this.start + limit; 696 } 697 } 698 } 699 700 701 /** 702 * Get the source text for a SourceLocation 703 * @return {String} 704 * Source text for this location. 705 */ 706 function SourceLocationSourceText() { 707 return %_CallFunction(this.script.source, 708 this.start, 709 this.end, 710 StringSubstring); 711 } 712 713 714 SetUpLockedPrototype(SourceLocation, 715 $Array("script", "position", "line", "column", "start", "end"), 716 $Array( 717 "restrict", SourceLocationRestrict, 718 "sourceText", SourceLocationSourceText 719 ) 720 ); 721 722 723 /** 724 * Class for a source slice. A source slice is a part of a script source with 725 * the following properties: 726 * script : script object for the source 727 * from_line : line number for the first line in the slice 728 * to_line : source line number for the last line in the slice 729 * from_position : position of the first character in the slice 730 * to_position : position of the last character in the slice 731 * The to_line and to_position are not included in the slice, that is the lines 732 * in the slice are [from_line, to_line[. Likewise the characters in the slice 733 * are [from_position, to_position[. 734 * @param {Script} script The Script object for the source slice 735 * @param {number} from_line 736 * @param {number} to_line 737 * @param {number} from_position 738 * @param {number} to_position 739 * @constructor 740 */ 741 function SourceSlice(script, from_line, to_line, from_position, to_position) { 742 this.script = script; 743 this.from_line = from_line; 744 this.to_line = to_line; 745 this.from_position = from_position; 746 this.to_position = to_position; 747 } 748 749 /** 750 * Get the source text for a SourceSlice 751 * @return {String} Source text for this slice. The last line will include 752 * the line terminating characters (if any) 753 */ 754 function SourceSliceSourceText() { 755 return %_CallFunction(this.script.source, 756 this.from_position, 757 this.to_position, 758 StringSubstring); 759 } 760 761 SetUpLockedPrototype(SourceSlice, 762 $Array("script", "from_line", "to_line", "from_position", "to_position"), 763 $Array("sourceText", SourceSliceSourceText) 764 ); 765 766 767 // Returns the offset of the given position within the containing 768 // line. 769 function GetPositionInLine(message) { 770 var script = %MessageGetScript(message); 771 var start_position = %MessageGetStartPosition(message); 772 var location = script.locationFromPosition(start_position, false); 773 if (location == null) return -1; 774 location.restrict(); 775 return start_position - location.start; 776 } 777 778 779 function GetStackTraceLine(recv, fun, pos, isGlobal) { 780 return new CallSite(recv, fun, pos, false).toString(); 781 } 782 783 // ---------------------------------------------------------------------------- 784 // Error implementation 785 786 var CallSiteReceiverKey = %CreateSymbol("receiver"); 787 var CallSiteFunctionKey = %CreateSymbol("function"); 788 var CallSitePositionKey = %CreateSymbol("position"); 789 var CallSiteStrictModeKey = %CreateSymbol("strict mode"); 790 791 function CallSite(receiver, fun, pos, strict_mode) { 792 this[CallSiteReceiverKey] = receiver; 793 this[CallSiteFunctionKey] = fun; 794 this[CallSitePositionKey] = pos; 795 this[CallSiteStrictModeKey] = strict_mode; 796 } 797 798 function CallSiteGetThis() { 799 return this[CallSiteStrictModeKey] ? void 0 : this[CallSiteReceiverKey]; 800 } 801 802 function CallSiteGetTypeName() { 803 return GetTypeName(this[CallSiteReceiverKey], false); 804 } 805 806 function CallSiteIsToplevel() { 807 if (this[CallSiteReceiverKey] == null) { 808 return true; 809 } 810 return IS_GLOBAL(this[CallSiteReceiverKey]); 811 } 812 813 function CallSiteIsEval() { 814 var script = %FunctionGetScript(this[CallSiteFunctionKey]); 815 return script && script.compilation_type == COMPILATION_TYPE_EVAL; 816 } 817 818 function CallSiteGetEvalOrigin() { 819 var script = %FunctionGetScript(this[CallSiteFunctionKey]); 820 return FormatEvalOrigin(script); 821 } 822 823 function CallSiteGetScriptNameOrSourceURL() { 824 var script = %FunctionGetScript(this[CallSiteFunctionKey]); 825 return script ? script.nameOrSourceURL() : null; 826 } 827 828 function CallSiteGetFunction() { 829 return this[CallSiteStrictModeKey] ? void 0 : this[CallSiteFunctionKey]; 830 } 831 832 function CallSiteGetFunctionName() { 833 // See if the function knows its own name 834 var name = this[CallSiteFunctionKey].name; 835 if (name) { 836 return name; 837 } 838 name = %FunctionGetInferredName(this[CallSiteFunctionKey]); 839 if (name) { 840 return name; 841 } 842 // Maybe this is an evaluation? 843 var script = %FunctionGetScript(this[CallSiteFunctionKey]); 844 if (script && script.compilation_type == COMPILATION_TYPE_EVAL) { 845 return "eval"; 846 } 847 return null; 848 } 849 850 function CallSiteGetMethodName() { 851 // See if we can find a unique property on the receiver that holds 852 // this function. 853 var receiver = this[CallSiteReceiverKey]; 854 var fun = this[CallSiteFunctionKey]; 855 var ownName = fun.name; 856 if (ownName && receiver && 857 (%_CallFunction(receiver, ownName, ObjectLookupGetter) === fun || 858 %_CallFunction(receiver, ownName, ObjectLookupSetter) === fun || 859 (IS_OBJECT(receiver) && %GetDataProperty(receiver, ownName) === fun))) { 860 // To handle DontEnum properties we guess that the method has 861 // the same name as the function. 862 return ownName; 863 } 864 var name = null; 865 for (var prop in receiver) { 866 if (%_CallFunction(receiver, prop, ObjectLookupGetter) === fun || 867 %_CallFunction(receiver, prop, ObjectLookupSetter) === fun || 868 (IS_OBJECT(receiver) && %GetDataProperty(receiver, prop) === fun)) { 869 // If we find more than one match bail out to avoid confusion. 870 if (name) { 871 return null; 872 } 873 name = prop; 874 } 875 } 876 if (name) { 877 return name; 878 } 879 return null; 880 } 881 882 function CallSiteGetFileName() { 883 var script = %FunctionGetScript(this[CallSiteFunctionKey]); 884 return script ? script.name : null; 885 } 886 887 function CallSiteGetLineNumber() { 888 if (this[CallSitePositionKey] == -1) { 889 return null; 890 } 891 var script = %FunctionGetScript(this[CallSiteFunctionKey]); 892 var location = null; 893 if (script) { 894 location = script.locationFromPosition(this[CallSitePositionKey], true); 895 } 896 return location ? location.line + 1 : null; 897 } 898 899 function CallSiteGetColumnNumber() { 900 if (this[CallSitePositionKey] == -1) { 901 return null; 902 } 903 var script = %FunctionGetScript(this[CallSiteFunctionKey]); 904 var location = null; 905 if (script) { 906 location = script.locationFromPosition(this[CallSitePositionKey], true); 907 } 908 return location ? location.column + 1: null; 909 } 910 911 function CallSiteIsNative() { 912 var script = %FunctionGetScript(this[CallSiteFunctionKey]); 913 return script ? (script.type == TYPE_NATIVE) : false; 914 } 915 916 function CallSiteGetPosition() { 917 return this[CallSitePositionKey]; 918 } 919 920 function CallSiteIsConstructor() { 921 var receiver = this[CallSiteReceiverKey]; 922 var constructor = (receiver != null && IS_OBJECT(receiver)) 923 ? %GetDataProperty(receiver, "constructor") : null; 924 if (!constructor) return false; 925 return this[CallSiteFunctionKey] === constructor; 926 } 927 928 function CallSiteToString() { 929 var fileName; 930 var fileLocation = ""; 931 if (this.isNative()) { 932 fileLocation = "native"; 933 } else { 934 if (this.isEval()) { 935 fileName = this.getScriptNameOrSourceURL(); 936 if (!fileName) { 937 fileLocation = this.getEvalOrigin(); 938 fileLocation += ", "; // Expecting source position to follow. 939 } 940 } else { 941 fileName = this.getFileName(); 942 } 943 944 if (fileName) { 945 fileLocation += fileName; 946 } else { 947 // Source code does not originate from a file and is not native, but we 948 // can still get the source position inside the source string, e.g. in 949 // an eval string. 950 fileLocation += "<anonymous>"; 951 } 952 var lineNumber = this.getLineNumber(); 953 if (lineNumber != null) { 954 fileLocation += ":" + lineNumber; 955 var columnNumber = this.getColumnNumber(); 956 if (columnNumber) { 957 fileLocation += ":" + columnNumber; 958 } 959 } 960 } 961 962 var line = ""; 963 var functionName = this.getFunctionName(); 964 var addSuffix = true; 965 var isConstructor = this.isConstructor(); 966 var isMethodCall = !(this.isToplevel() || isConstructor); 967 if (isMethodCall) { 968 var typeName = GetTypeName(this[CallSiteReceiverKey], true); 969 var methodName = this.getMethodName(); 970 if (functionName) { 971 if (typeName && 972 %_CallFunction(functionName, typeName, StringIndexOf) != 0) { 973 line += typeName + "."; 974 } 975 line += functionName; 976 if (methodName && 977 (%_CallFunction(functionName, "." + methodName, StringIndexOf) != 978 functionName.length - methodName.length - 1)) { 979 line += " [as " + methodName + "]"; 980 } 981 } else { 982 line += typeName + "." + (methodName || "<anonymous>"); 983 } 984 } else if (isConstructor) { 985 line += "new " + (functionName || "<anonymous>"); 986 } else if (functionName) { 987 line += functionName; 988 } else { 989 line += fileLocation; 990 addSuffix = false; 991 } 992 if (addSuffix) { 993 line += " (" + fileLocation + ")"; 994 } 995 return line; 996 } 997 998 SetUpLockedPrototype(CallSite, $Array("receiver", "fun", "pos"), $Array( 999 "getThis", CallSiteGetThis, 1000 "getTypeName", CallSiteGetTypeName, 1001 "isToplevel", CallSiteIsToplevel, 1002 "isEval", CallSiteIsEval, 1003 "getEvalOrigin", CallSiteGetEvalOrigin, 1004 "getScriptNameOrSourceURL", CallSiteGetScriptNameOrSourceURL, 1005 "getFunction", CallSiteGetFunction, 1006 "getFunctionName", CallSiteGetFunctionName, 1007 "getMethodName", CallSiteGetMethodName, 1008 "getFileName", CallSiteGetFileName, 1009 "getLineNumber", CallSiteGetLineNumber, 1010 "getColumnNumber", CallSiteGetColumnNumber, 1011 "isNative", CallSiteIsNative, 1012 "getPosition", CallSiteGetPosition, 1013 "isConstructor", CallSiteIsConstructor, 1014 "toString", CallSiteToString 1015 )); 1016 1017 1018 function FormatEvalOrigin(script) { 1019 var sourceURL = script.nameOrSourceURL(); 1020 if (sourceURL) { 1021 return sourceURL; 1022 } 1023 1024 var eval_origin = "eval at "; 1025 if (script.eval_from_function_name) { 1026 eval_origin += script.eval_from_function_name; 1027 } else { 1028 eval_origin += "<anonymous>"; 1029 } 1030 1031 var eval_from_script = script.eval_from_script; 1032 if (eval_from_script) { 1033 if (eval_from_script.compilation_type == COMPILATION_TYPE_EVAL) { 1034 // eval script originated from another eval. 1035 eval_origin += " (" + FormatEvalOrigin(eval_from_script) + ")"; 1036 } else { 1037 // eval script originated from "real" source. 1038 if (eval_from_script.name) { 1039 eval_origin += " (" + eval_from_script.name; 1040 var location = eval_from_script.locationFromPosition( 1041 script.eval_from_script_position, true); 1042 if (location) { 1043 eval_origin += ":" + (location.line + 1); 1044 eval_origin += ":" + (location.column + 1); 1045 } 1046 eval_origin += ")"; 1047 } else { 1048 eval_origin += " (unknown source)"; 1049 } 1050 } 1051 } 1052 1053 return eval_origin; 1054 } 1055 1056 1057 function FormatErrorString(error) { 1058 try { 1059 return %_CallFunction(error, ErrorToString); 1060 } catch (e) { 1061 try { 1062 return "<error: " + e + ">"; 1063 } catch (ee) { 1064 return "<error>"; 1065 } 1066 } 1067 } 1068 1069 1070 function GetStackFrames(raw_stack) { 1071 var frames = new InternalArray(); 1072 var non_strict_frames = raw_stack[0]; 1073 for (var i = 1; i < raw_stack.length; i += 4) { 1074 var recv = raw_stack[i]; 1075 var fun = raw_stack[i + 1]; 1076 var code = raw_stack[i + 2]; 1077 var pc = raw_stack[i + 3]; 1078 var pos = %FunctionGetPositionForOffset(code, pc); 1079 non_strict_frames--; 1080 frames.push(new CallSite(recv, fun, pos, (non_strict_frames < 0))); 1081 } 1082 return frames; 1083 } 1084 1085 1086 // Flag to prevent recursive call of Error.prepareStackTrace. 1087 var formatting_custom_stack_trace = false; 1088 1089 1090 function FormatStackTrace(obj, error_string, frames) { 1091 if (IS_FUNCTION($Error.prepareStackTrace) && !formatting_custom_stack_trace) { 1092 var array = []; 1093 %MoveArrayContents(frames, array); 1094 formatting_custom_stack_trace = true; 1095 var stack_trace = void 0; 1096 try { 1097 stack_trace = $Error.prepareStackTrace(obj, array); 1098 } catch (e) { 1099 throw e; // The custom formatting function threw. Rethrow. 1100 } finally { 1101 formatting_custom_stack_trace = false; 1102 } 1103 return stack_trace; 1104 } 1105 1106 var lines = new InternalArray(); 1107 lines.push(error_string); 1108 for (var i = 0; i < frames.length; i++) { 1109 var frame = frames[i]; 1110 var line; 1111 try { 1112 line = frame.toString(); 1113 } catch (e) { 1114 try { 1115 line = "<error: " + e + ">"; 1116 } catch (ee) { 1117 // Any code that reaches this point is seriously nasty! 1118 line = "<error>"; 1119 } 1120 } 1121 lines.push(" at " + line); 1122 } 1123 return %_CallFunction(lines, "\n", ArrayJoin); 1124 } 1125 1126 1127 function GetTypeName(receiver, requireConstructor) { 1128 var constructor = receiver.constructor; 1129 if (!constructor) { 1130 return requireConstructor ? null : 1131 %_CallFunction(receiver, ObjectToString); 1132 } 1133 var constructorName = constructor.name; 1134 if (!constructorName) { 1135 return requireConstructor ? null : 1136 %_CallFunction(receiver, ObjectToString); 1137 } 1138 return constructorName; 1139 } 1140 1141 1142 function captureStackTrace(obj, cons_opt) { 1143 var stackTraceLimit = $Error.stackTraceLimit; 1144 if (!stackTraceLimit || !IS_NUMBER(stackTraceLimit)) return; 1145 if (stackTraceLimit < 0 || stackTraceLimit > 10000) { 1146 stackTraceLimit = 10000; 1147 } 1148 var stack = %CollectStackTrace(obj, 1149 cons_opt ? cons_opt : captureStackTrace, 1150 stackTraceLimit); 1151 1152 var error_string = FormatErrorString(obj); 1153 // The holder of this getter ('obj') may not be the receiver ('this'). 1154 // When this getter is called the first time, we use the context values to 1155 // format a stack trace string and turn this accessor pair into a data 1156 // property (on the holder). 1157 var getter = function() { 1158 // Stack is still a raw array awaiting to be formatted. 1159 var result = FormatStackTrace(obj, error_string, GetStackFrames(stack)); 1160 // Turn this accessor into a data property. 1161 %DefineOrRedefineDataProperty(obj, 'stack', result, NONE); 1162 // Release context values. 1163 stack = error_string = void 0; 1164 return result; 1165 }; 1166 1167 // Set the 'stack' property on the receiver. If the receiver is the same as 1168 // holder of this setter, the accessor pair is turned into a data property. 1169 var setter = function(v) { 1170 // Set data property on the receiver (not necessarily holder). 1171 %DefineOrRedefineDataProperty(this, 'stack', v, NONE); 1172 if (this === obj) { 1173 // Release context values if holder is the same as the receiver. 1174 stack = error_string = void 0; 1175 } 1176 }; 1177 1178 %DefineOrRedefineAccessorProperty(obj, 'stack', getter, setter, DONT_ENUM); 1179 } 1180 1181 1182 function SetUpError() { 1183 // Define special error type constructors. 1184 1185 var DefineError = function(f) { 1186 // Store the error function in both the global object 1187 // and the runtime object. The function is fetched 1188 // from the runtime object when throwing errors from 1189 // within the runtime system to avoid strange side 1190 // effects when overwriting the error functions from 1191 // user code. 1192 var name = f.name; 1193 %SetProperty(global, name, f, DONT_ENUM); 1194 %SetProperty(builtins, '$' + name, f, DONT_ENUM | DONT_DELETE | READ_ONLY); 1195 // Configure the error function. 1196 if (name == 'Error') { 1197 // The prototype of the Error object must itself be an error. 1198 // However, it can't be an instance of the Error object because 1199 // it hasn't been properly configured yet. Instead we create a 1200 // special not-a-true-error-but-close-enough object. 1201 var ErrorPrototype = function() {}; 1202 %FunctionSetPrototype(ErrorPrototype, $Object.prototype); 1203 %FunctionSetInstanceClassName(ErrorPrototype, 'Error'); 1204 %FunctionSetPrototype(f, new ErrorPrototype()); 1205 } else { 1206 %FunctionSetPrototype(f, new $Error()); 1207 } 1208 %FunctionSetInstanceClassName(f, 'Error'); 1209 %SetProperty(f.prototype, 'constructor', f, DONT_ENUM); 1210 %SetProperty(f.prototype, "name", name, DONT_ENUM); 1211 %SetCode(f, function(m) { 1212 if (%_IsConstructCall()) { 1213 // Define all the expected properties directly on the error 1214 // object. This avoids going through getters and setters defined 1215 // on prototype objects. 1216 %IgnoreAttributesAndSetProperty(this, 'stack', void 0, DONT_ENUM); 1217 if (!IS_UNDEFINED(m)) { 1218 %IgnoreAttributesAndSetProperty( 1219 this, 'message', ToString(m), DONT_ENUM); 1220 } 1221 captureStackTrace(this, f); 1222 } else { 1223 return new f(m); 1224 } 1225 }); 1226 %SetNativeFlag(f); 1227 }; 1228 1229 DefineError(function Error() { }); 1230 DefineError(function TypeError() { }); 1231 DefineError(function RangeError() { }); 1232 DefineError(function SyntaxError() { }); 1233 DefineError(function ReferenceError() { }); 1234 DefineError(function EvalError() { }); 1235 DefineError(function URIError() { }); 1236 } 1237 1238 SetUpError(); 1239 1240 $Error.captureStackTrace = captureStackTrace; 1241 1242 %SetProperty($Error.prototype, 'message', '', DONT_ENUM); 1243 1244 // Global list of error objects visited during ErrorToString. This is 1245 // used to detect cycles in error toString formatting. 1246 var visited_errors = new InternalArray(); 1247 var cyclic_error_marker = new $Object(); 1248 1249 function GetPropertyWithoutInvokingMonkeyGetters(error, name) { 1250 // Climb the prototype chain until we find the holder. 1251 while (error && !%HasLocalProperty(error, name)) { 1252 error = %GetPrototype(error); 1253 } 1254 if (error === null) return void 0; 1255 if (!IS_OBJECT(error)) return error[name]; 1256 // If the property is an accessor on one of the predefined errors that can be 1257 // generated statically by the compiler, don't touch it. This is to address 1258 // http://code.google.com/p/chromium/issues/detail?id=69187 1259 var desc = %GetOwnProperty(error, name); 1260 if (desc && desc[IS_ACCESSOR_INDEX]) { 1261 var isName = name === "name"; 1262 if (error === $ReferenceError.prototype) 1263 return isName ? "ReferenceError" : void 0; 1264 if (error === $SyntaxError.prototype) 1265 return isName ? "SyntaxError" : void 0; 1266 if (error === $TypeError.prototype) 1267 return isName ? "TypeError" : void 0; 1268 } 1269 // Otherwise, read normally. 1270 return error[name]; 1271 } 1272 1273 function ErrorToStringDetectCycle(error) { 1274 if (!%PushIfAbsent(visited_errors, error)) throw cyclic_error_marker; 1275 try { 1276 var name = GetPropertyWithoutInvokingMonkeyGetters(error, "name"); 1277 name = IS_UNDEFINED(name) ? "Error" : TO_STRING_INLINE(name); 1278 var message = GetPropertyWithoutInvokingMonkeyGetters(error, "message"); 1279 message = IS_UNDEFINED(message) ? "" : TO_STRING_INLINE(message); 1280 if (name === "") return message; 1281 if (message === "") return name; 1282 return name + ": " + message; 1283 } finally { 1284 visited_errors.length = visited_errors.length - 1; 1285 } 1286 } 1287 1288 function ErrorToString() { 1289 if (!IS_SPEC_OBJECT(this)) { 1290 throw MakeTypeError("called_on_non_object", ["Error.prototype.toString"]); 1291 } 1292 1293 try { 1294 return ErrorToStringDetectCycle(this); 1295 } catch(e) { 1296 // If this error message was encountered already return the empty 1297 // string for it instead of recursively formatting it. 1298 if (e === cyclic_error_marker) { 1299 return ''; 1300 } 1301 throw e; 1302 } 1303 } 1304 1305 1306 InstallFunctions($Error.prototype, DONT_ENUM, ['toString', ErrorToString]); 1307 1308 // Boilerplate for exceptions for stack overflows. Used from 1309 // Isolate::StackOverflow(). 1310 function SetUpStackOverflowBoilerplate() { 1311 var boilerplate = MakeRangeError('stack_overflow', []); 1312 1313 var error_string = boilerplate.name + ": " + boilerplate.message; 1314 1315 // The raw stack trace is stored as a hidden property on the holder of this 1316 // getter, which may not be the same as the receiver. Find the holder to 1317 // retrieve the raw stack trace and then turn this accessor pair into a 1318 // data property. 1319 var getter = function() { 1320 var holder = this; 1321 while (!IS_ERROR(holder)) { 1322 holder = %GetPrototype(holder); 1323 if (IS_NULL(holder)) return MakeSyntaxError('illegal_access', []); 1324 } 1325 var stack = %GetAndClearOverflowedStackTrace(holder); 1326 // We may not have captured any stack trace. 1327 if (IS_UNDEFINED(stack)) return stack; 1328 1329 var result = FormatStackTrace(holder, error_string, GetStackFrames(stack)); 1330 // Replace this accessor with a data property. 1331 %DefineOrRedefineDataProperty(holder, 'stack', result, NONE); 1332 return result; 1333 }; 1334 1335 // Set the 'stack' property on the receiver. If the receiver is the same as 1336 // holder of this setter, the accessor pair is turned into a data property. 1337 var setter = function(v) { 1338 %DefineOrRedefineDataProperty(this, 'stack', v, NONE); 1339 // Tentatively clear the hidden property. If the receiver is the same as 1340 // holder, we release the raw stack trace this way. 1341 %GetAndClearOverflowedStackTrace(this); 1342 }; 1343 1344 %DefineOrRedefineAccessorProperty( 1345 boilerplate, 'stack', getter, setter, DONT_ENUM); 1346 1347 return boilerplate; 1348 } 1349 1350 var kStackOverflowBoilerplate = SetUpStackOverflowBoilerplate(); 1351