| OLD | NEW |
| (Empty) |
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 // ------------------------------------------------------------------- | |
| 6 | |
| 7 var $errorToString; | |
| 8 var MakeError; | |
| 9 var MakeEvalError; | |
| 10 var MakeRangeError; | |
| 11 var MakeReferenceError; | |
| 12 var MakeSyntaxError; | |
| 13 var MakeTypeError; | |
| 14 var MakeURIError; | |
| 15 | |
| 16 (function(global, utils) { | |
| 17 | |
| 18 %CheckIsBootstrapping(); | |
| 19 | |
| 20 // ------------------------------------------------------------------- | |
| 21 // Imports | |
| 22 | |
| 23 var ArrayJoin; | |
| 24 var Bool16x8ToString; | |
| 25 var Bool32x4ToString; | |
| 26 var Bool8x16ToString; | |
| 27 var callSiteReceiverSymbol = | |
| 28 utils.ImportNow("call_site_receiver_symbol"); | |
| 29 var callSiteFunctionSymbol = | |
| 30 utils.ImportNow("call_site_function_symbol"); | |
| 31 var callSitePositionSymbol = | |
| 32 utils.ImportNow("call_site_position_symbol"); | |
| 33 var callSiteStrictSymbol = | |
| 34 utils.ImportNow("call_site_strict_symbol"); | |
| 35 var Float32x4ToString; | |
| 36 var formattedStackTraceSymbol = | |
| 37 utils.ImportNow("formatted_stack_trace_symbol"); | |
| 38 var FunctionSourceString | |
| 39 var GlobalObject = global.Object; | |
| 40 var Int16x8ToString; | |
| 41 var Int32x4ToString; | |
| 42 var Int8x16ToString; | |
| 43 var InternalArray = utils.InternalArray; | |
| 44 var internalErrorSymbol = utils.ImportNow("internal_error_symbol"); | |
| 45 var ObjectDefineProperty; | |
| 46 var ObjectToString; | |
| 47 var stackTraceSymbol = utils.ImportNow("stack_trace_symbol"); | |
| 48 var StringCharAt; | |
| 49 var StringIndexOf; | |
| 50 var StringSubstring; | |
| 51 var SymbolToString; | |
| 52 var Uint16x8ToString; | |
| 53 var Uint32x4ToString; | |
| 54 var Uint8x16ToString; | |
| 55 | |
| 56 utils.Import(function(from) { | |
| 57 ArrayJoin = from.ArrayJoin; | |
| 58 Bool16x8ToString = from.Bool16x8ToString; | |
| 59 Bool32x4ToString = from.Bool32x4ToString; | |
| 60 Bool8x16ToString = from.Bool8x16ToString; | |
| 61 Float32x4ToString = from.Float32x4ToString; | |
| 62 FunctionSourceString = from.FunctionSourceString; | |
| 63 Int16x8ToString = from.Int16x8ToString; | |
| 64 Int32x4ToString = from.Int32x4ToString; | |
| 65 Int8x16ToString = from.Int8x16ToString; | |
| 66 ObjectDefineProperty = from.ObjectDefineProperty; | |
| 67 ObjectToString = from.ObjectToString; | |
| 68 StringCharAt = from.StringCharAt; | |
| 69 StringIndexOf = from.StringIndexOf; | |
| 70 StringSubstring = from.StringSubstring; | |
| 71 SymbolToString = from.SymbolToString; | |
| 72 Uint16x8ToString = from.Uint16x8ToString; | |
| 73 Uint32x4ToString = from.Uint32x4ToString; | |
| 74 Uint8x16ToString = from.Uint8x16ToString; | |
| 75 }); | |
| 76 | |
| 77 // ------------------------------------------------------------------- | |
| 78 | |
| 79 var GlobalError; | |
| 80 var GlobalTypeError; | |
| 81 var GlobalRangeError; | |
| 82 var GlobalURIError; | |
| 83 var GlobalSyntaxError; | |
| 84 var GlobalReferenceError; | |
| 85 var GlobalEvalError; | |
| 86 | |
| 87 | |
| 88 function NoSideEffectsObjectToString() { | |
| 89 if (IS_UNDEFINED(this)) return "[object Undefined]"; | |
| 90 if (IS_NULL(this)) return "[object Null]"; | |
| 91 return "[object " + %_ClassOf(TO_OBJECT(this)) + "]"; | |
| 92 } | |
| 93 | |
| 94 | |
| 95 function NoSideEffectToString(obj) { | |
| 96 if (IS_STRING(obj)) return obj; | |
| 97 if (IS_NUMBER(obj)) return %_NumberToString(obj); | |
| 98 if (IS_BOOLEAN(obj)) return obj ? 'true' : 'false'; | |
| 99 if (IS_UNDEFINED(obj)) return 'undefined'; | |
| 100 if (IS_NULL(obj)) return 'null'; | |
| 101 if (IS_FUNCTION(obj)) { | |
| 102 var str = %_CallFunction(obj, obj, FunctionSourceString); | |
| 103 if (str.length > 128) { | |
| 104 str = %_SubString(str, 0, 111) + "...<omitted>..." + | |
| 105 %_SubString(str, str.length - 2, str.length); | |
| 106 } | |
| 107 return str; | |
| 108 } | |
| 109 if (IS_SYMBOL(obj)) return %_CallFunction(obj, SymbolToString); | |
| 110 if (IS_SIMD_VALUE(obj)) { | |
| 111 switch (typeof(obj)) { | |
| 112 case 'float32x4': return %_CallFunction(obj, Float32x4ToString); | |
| 113 case 'int32x4': return %_CallFunction(obj, Int32x4ToString); | |
| 114 case 'int16x8': return %_CallFunction(obj, Int16x8ToString); | |
| 115 case 'int8x16': return %_CallFunction(obj, Int8x16ToString); | |
| 116 case 'uint32x4': return %_CallFunction(obj, Uint32x4ToString); | |
| 117 case 'uint16x8': return %_CallFunction(obj, Uint16x8ToString); | |
| 118 case 'uint8x16': return %_CallFunction(obj, Uint8x16ToString); | |
| 119 case 'bool32x4': return %_CallFunction(obj, Bool32x4ToString); | |
| 120 case 'bool16x8': return %_CallFunction(obj, Bool16x8ToString); | |
| 121 case 'bool8x16': return %_CallFunction(obj, Bool8x16ToString); | |
| 122 } | |
| 123 } | |
| 124 if (IS_OBJECT(obj) | |
| 125 && %GetDataProperty(obj, "toString") === ObjectToString) { | |
| 126 var constructor = %GetDataProperty(obj, "constructor"); | |
| 127 if (typeof constructor == "function") { | |
| 128 var constructorName = constructor.name; | |
| 129 if (IS_STRING(constructorName) && constructorName !== "") { | |
| 130 return "#<" + constructorName + ">"; | |
| 131 } | |
| 132 } | |
| 133 } | |
| 134 if (CanBeSafelyTreatedAsAnErrorObject(obj)) { | |
| 135 return %_CallFunction(obj, ErrorToString); | |
| 136 } | |
| 137 | |
| 138 return %_CallFunction(obj, NoSideEffectsObjectToString); | |
| 139 } | |
| 140 | |
| 141 // To determine whether we can safely stringify an object using ErrorToString | |
| 142 // without the risk of side-effects, we need to check whether the object is | |
| 143 // either an instance of a native error type (via '%_ClassOf'), or has Error | |
| 144 // in its prototype chain and hasn't overwritten 'toString' with something | |
| 145 // strange and unusual. | |
| 146 function CanBeSafelyTreatedAsAnErrorObject(obj) { | |
| 147 switch (%_ClassOf(obj)) { | |
| 148 case 'Error': | |
| 149 case 'EvalError': | |
| 150 case 'RangeError': | |
| 151 case 'ReferenceError': | |
| 152 case 'SyntaxError': | |
| 153 case 'TypeError': | |
| 154 case 'URIError': | |
| 155 return true; | |
| 156 } | |
| 157 | |
| 158 var objToString = %GetDataProperty(obj, "toString"); | |
| 159 return obj instanceof GlobalError && objToString === ErrorToString; | |
| 160 } | |
| 161 | |
| 162 | |
| 163 // When formatting internally created error messages, do not | |
| 164 // invoke overwritten error toString methods but explicitly use | |
| 165 // the error to string method. This is to avoid leaking error | |
| 166 // objects between script tags in a browser setting. | |
| 167 function ToStringCheckErrorObject(obj) { | |
| 168 if (CanBeSafelyTreatedAsAnErrorObject(obj)) { | |
| 169 return %_CallFunction(obj, ErrorToString); | |
| 170 } else { | |
| 171 return TO_STRING(obj); | |
| 172 } | |
| 173 } | |
| 174 | |
| 175 | |
| 176 function ToDetailString(obj) { | |
| 177 if (obj != null && IS_OBJECT(obj) && obj.toString === ObjectToString) { | |
| 178 var constructor = obj.constructor; | |
| 179 if (typeof constructor == "function") { | |
| 180 var constructorName = constructor.name; | |
| 181 if (IS_STRING(constructorName) && constructorName !== "") { | |
| 182 return "#<" + constructorName + ">"; | |
| 183 } | |
| 184 } | |
| 185 } | |
| 186 return ToStringCheckErrorObject(obj); | |
| 187 } | |
| 188 | |
| 189 | |
| 190 function MakeGenericError(constructor, type, arg0, arg1, arg2) { | |
| 191 var error = new constructor(FormatMessage(type, arg0, arg1, arg2)); | |
| 192 error[internalErrorSymbol] = true; | |
| 193 return error; | |
| 194 } | |
| 195 | |
| 196 | |
| 197 /** | |
| 198 * Set up the Script function and constructor. | |
| 199 */ | |
| 200 %FunctionSetInstanceClassName(Script, 'Script'); | |
| 201 %AddNamedProperty(Script.prototype, 'constructor', Script, | |
| 202 DONT_ENUM | DONT_DELETE | READ_ONLY); | |
| 203 %SetCode(Script, function(x) { | |
| 204 // Script objects can only be created by the VM. | |
| 205 throw MakeError(kUnsupported); | |
| 206 }); | |
| 207 | |
| 208 | |
| 209 // Helper functions; called from the runtime system. | |
| 210 function FormatMessage(type, arg0, arg1, arg2) { | |
| 211 var arg0 = NoSideEffectToString(arg0); | |
| 212 var arg1 = NoSideEffectToString(arg1); | |
| 213 var arg2 = NoSideEffectToString(arg2); | |
| 214 try { | |
| 215 return %FormatMessageString(type, arg0, arg1, arg2); | |
| 216 } catch (e) { | |
| 217 return "<error>"; | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 | |
| 222 function GetLineNumber(message) { | |
| 223 var start_position = %MessageGetStartPosition(message); | |
| 224 if (start_position == -1) return kNoLineNumberInfo; | |
| 225 var script = %MessageGetScript(message); | |
| 226 var location = script.locationFromPosition(start_position, true); | |
| 227 if (location == null) return kNoLineNumberInfo; | |
| 228 return location.line + 1; | |
| 229 } | |
| 230 | |
| 231 | |
| 232 //Returns the offset of the given position within the containing line. | |
| 233 function GetColumnNumber(message) { | |
| 234 var script = %MessageGetScript(message); | |
| 235 var start_position = %MessageGetStartPosition(message); | |
| 236 var location = script.locationFromPosition(start_position, true); | |
| 237 if (location == null) return -1; | |
| 238 return location.column; | |
| 239 } | |
| 240 | |
| 241 | |
| 242 // Returns the source code line containing the given source | |
| 243 // position, or the empty string if the position is invalid. | |
| 244 function GetSourceLine(message) { | |
| 245 var script = %MessageGetScript(message); | |
| 246 var start_position = %MessageGetStartPosition(message); | |
| 247 var location = script.locationFromPosition(start_position, true); | |
| 248 if (location == null) return ""; | |
| 249 return location.sourceText(); | |
| 250 } | |
| 251 | |
| 252 | |
| 253 /** | |
| 254 * Find a line number given a specific source position. | |
| 255 * @param {number} position The source position. | |
| 256 * @return {number} 0 if input too small, -1 if input too large, | |
| 257 else the line number. | |
| 258 */ | |
| 259 function ScriptLineFromPosition(position) { | |
| 260 var lower = 0; | |
| 261 var upper = this.lineCount() - 1; | |
| 262 var line_ends = this.line_ends; | |
| 263 | |
| 264 // We'll never find invalid positions so bail right away. | |
| 265 if (position > line_ends[upper]) { | |
| 266 return -1; | |
| 267 } | |
| 268 | |
| 269 // This means we don't have to safe-guard indexing line_ends[i - 1]. | |
| 270 if (position <= line_ends[0]) { | |
| 271 return 0; | |
| 272 } | |
| 273 | |
| 274 // Binary search to find line # from position range. | |
| 275 while (upper >= 1) { | |
| 276 var i = (lower + upper) >> 1; | |
| 277 | |
| 278 if (position > line_ends[i]) { | |
| 279 lower = i + 1; | |
| 280 } else if (position <= line_ends[i - 1]) { | |
| 281 upper = i - 1; | |
| 282 } else { | |
| 283 return i; | |
| 284 } | |
| 285 } | |
| 286 | |
| 287 return -1; | |
| 288 } | |
| 289 | |
| 290 /** | |
| 291 * Get information on a specific source position. | |
| 292 * @param {number} position The source position | |
| 293 * @param {boolean} include_resource_offset Set to true to have the resource | |
| 294 * offset added to the location | |
| 295 * @return {SourceLocation} | |
| 296 * If line is negative or not in the source null is returned. | |
| 297 */ | |
| 298 function ScriptLocationFromPosition(position, | |
| 299 include_resource_offset) { | |
| 300 var line = this.lineFromPosition(position); | |
| 301 if (line == -1) return null; | |
| 302 | |
| 303 // Determine start, end and column. | |
| 304 var line_ends = this.line_ends; | |
| 305 var start = line == 0 ? 0 : line_ends[line - 1] + 1; | |
| 306 var end = line_ends[line]; | |
| 307 if (end > 0 && %_CallFunction(this.source, end - 1, StringCharAt) == '\r') { | |
| 308 end--; | |
| 309 } | |
| 310 var column = position - start; | |
| 311 | |
| 312 // Adjust according to the offset within the resource. | |
| 313 if (include_resource_offset) { | |
| 314 line += this.line_offset; | |
| 315 if (line == this.line_offset) { | |
| 316 column += this.column_offset; | |
| 317 } | |
| 318 } | |
| 319 | |
| 320 return new SourceLocation(this, position, line, column, start, end); | |
| 321 } | |
| 322 | |
| 323 | |
| 324 /** | |
| 325 * Get information on a specific source line and column possibly offset by a | |
| 326 * fixed source position. This function is used to find a source position from | |
| 327 * a line and column position. The fixed source position offset is typically | |
| 328 * used to find a source position in a function based on a line and column in | |
| 329 * the source for the function alone. The offset passed will then be the | |
| 330 * start position of the source for the function within the full script source. | |
| 331 * @param {number} opt_line The line within the source. Default value is 0 | |
| 332 * @param {number} opt_column The column in within the line. Default value is 0 | |
| 333 * @param {number} opt_offset_position The offset from the begining of the | |
| 334 * source from where the line and column calculation starts. | |
| 335 * Default value is 0 | |
| 336 * @return {SourceLocation} | |
| 337 * If line is negative or not in the source null is returned. | |
| 338 */ | |
| 339 function ScriptLocationFromLine(opt_line, opt_column, opt_offset_position) { | |
| 340 // Default is the first line in the script. Lines in the script is relative | |
| 341 // to the offset within the resource. | |
| 342 var line = 0; | |
| 343 if (!IS_UNDEFINED(opt_line)) { | |
| 344 line = opt_line - this.line_offset; | |
| 345 } | |
| 346 | |
| 347 // Default is first column. If on the first line add the offset within the | |
| 348 // resource. | |
| 349 var column = opt_column || 0; | |
| 350 if (line == 0) { | |
| 351 column -= this.column_offset; | |
| 352 } | |
| 353 | |
| 354 var offset_position = opt_offset_position || 0; | |
| 355 if (line < 0 || column < 0 || offset_position < 0) return null; | |
| 356 if (line == 0) { | |
| 357 return this.locationFromPosition(offset_position + column, false); | |
| 358 } else { | |
| 359 // Find the line where the offset position is located. | |
| 360 var offset_line = this.lineFromPosition(offset_position); | |
| 361 | |
| 362 if (offset_line == -1 || offset_line + line >= this.lineCount()) { | |
| 363 return null; | |
| 364 } | |
| 365 | |
| 366 return this.locationFromPosition( | |
| 367 this.line_ends[offset_line + line - 1] + 1 + column); // line > 0 here. | |
| 368 } | |
| 369 } | |
| 370 | |
| 371 | |
| 372 /** | |
| 373 * Get a slice of source code from the script. The boundaries for the slice is | |
| 374 * specified in lines. | |
| 375 * @param {number} opt_from_line The first line (zero bound) in the slice. | |
| 376 * Default is 0 | |
| 377 * @param {number} opt_to_column The last line (zero bound) in the slice (non | |
| 378 * inclusive). Default is the number of lines in the script | |
| 379 * @return {SourceSlice} The source slice or null of the parameters where | |
| 380 * invalid | |
| 381 */ | |
| 382 function ScriptSourceSlice(opt_from_line, opt_to_line) { | |
| 383 var from_line = IS_UNDEFINED(opt_from_line) ? this.line_offset | |
| 384 : opt_from_line; | |
| 385 var to_line = IS_UNDEFINED(opt_to_line) ? this.line_offset + this.lineCount() | |
| 386 : opt_to_line; | |
| 387 | |
| 388 // Adjust according to the offset within the resource. | |
| 389 from_line -= this.line_offset; | |
| 390 to_line -= this.line_offset; | |
| 391 if (from_line < 0) from_line = 0; | |
| 392 if (to_line > this.lineCount()) to_line = this.lineCount(); | |
| 393 | |
| 394 // Check parameters. | |
| 395 if (from_line >= this.lineCount() || | |
| 396 to_line < 0 || | |
| 397 from_line > to_line) { | |
| 398 return null; | |
| 399 } | |
| 400 | |
| 401 var line_ends = this.line_ends; | |
| 402 var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1; | |
| 403 var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1; | |
| 404 | |
| 405 // Return a source slice with line numbers re-adjusted to the resource. | |
| 406 return new SourceSlice(this, | |
| 407 from_line + this.line_offset, | |
| 408 to_line + this.line_offset, | |
| 409 from_position, to_position); | |
| 410 } | |
| 411 | |
| 412 | |
| 413 function ScriptSourceLine(opt_line) { | |
| 414 // Default is the first line in the script. Lines in the script are relative | |
| 415 // to the offset within the resource. | |
| 416 var line = 0; | |
| 417 if (!IS_UNDEFINED(opt_line)) { | |
| 418 line = opt_line - this.line_offset; | |
| 419 } | |
| 420 | |
| 421 // Check parameter. | |
| 422 if (line < 0 || this.lineCount() <= line) { | |
| 423 return null; | |
| 424 } | |
| 425 | |
| 426 // Return the source line. | |
| 427 var line_ends = this.line_ends; | |
| 428 var start = line == 0 ? 0 : line_ends[line - 1] + 1; | |
| 429 var end = line_ends[line]; | |
| 430 return %_CallFunction(this.source, start, end, StringSubstring); | |
| 431 } | |
| 432 | |
| 433 | |
| 434 /** | |
| 435 * Returns the number of source lines. | |
| 436 * @return {number} | |
| 437 * Number of source lines. | |
| 438 */ | |
| 439 function ScriptLineCount() { | |
| 440 // Return number of source lines. | |
| 441 return this.line_ends.length; | |
| 442 } | |
| 443 | |
| 444 | |
| 445 /** | |
| 446 * Returns the position of the nth line end. | |
| 447 * @return {number} | |
| 448 * Zero-based position of the nth line end in the script. | |
| 449 */ | |
| 450 function ScriptLineEnd(n) { | |
| 451 return this.line_ends[n]; | |
| 452 } | |
| 453 | |
| 454 | |
| 455 /** | |
| 456 * If sourceURL comment is available returns sourceURL comment contents. | |
| 457 * Otherwise, script name is returned. See | |
| 458 * http://fbug.googlecode.com/svn/branches/firebug1.1/docs/ReleaseNotes_1.1.txt | |
| 459 * and Source Map Revision 3 proposal for details on using //# sourceURL and | |
| 460 * deprecated //@ sourceURL comment to identify scripts that don't have name. | |
| 461 * | |
| 462 * @return {?string} script name if present, value for //# sourceURL or | |
| 463 * deprecated //@ sourceURL comment otherwise. | |
| 464 */ | |
| 465 function ScriptNameOrSourceURL() { | |
| 466 if (this.source_url) return this.source_url; | |
| 467 return this.name; | |
| 468 } | |
| 469 | |
| 470 | |
| 471 utils.SetUpLockedPrototype(Script, [ | |
| 472 "source", | |
| 473 "name", | |
| 474 "source_url", | |
| 475 "source_mapping_url", | |
| 476 "line_ends", | |
| 477 "line_offset", | |
| 478 "column_offset" | |
| 479 ], [ | |
| 480 "lineFromPosition", ScriptLineFromPosition, | |
| 481 "locationFromPosition", ScriptLocationFromPosition, | |
| 482 "locationFromLine", ScriptLocationFromLine, | |
| 483 "sourceSlice", ScriptSourceSlice, | |
| 484 "sourceLine", ScriptSourceLine, | |
| 485 "lineCount", ScriptLineCount, | |
| 486 "nameOrSourceURL", ScriptNameOrSourceURL, | |
| 487 "lineEnd", ScriptLineEnd | |
| 488 ] | |
| 489 ); | |
| 490 | |
| 491 | |
| 492 /** | |
| 493 * Class for source location. A source location is a position within some | |
| 494 * source with the following properties: | |
| 495 * script : script object for the source | |
| 496 * line : source line number | |
| 497 * column : source column within the line | |
| 498 * position : position within the source | |
| 499 * start : position of start of source context (inclusive) | |
| 500 * end : position of end of source context (not inclusive) | |
| 501 * Source text for the source context is the character interval | |
| 502 * [start, end[. In most cases end will point to a newline character. | |
| 503 * It might point just past the final position of the source if the last | |
| 504 * source line does not end with a newline character. | |
| 505 * @param {Script} script The Script object for which this is a location | |
| 506 * @param {number} position Source position for the location | |
| 507 * @param {number} line The line number for the location | |
| 508 * @param {number} column The column within the line for the location | |
| 509 * @param {number} start Source position for start of source context | |
| 510 * @param {number} end Source position for end of source context | |
| 511 * @constructor | |
| 512 */ | |
| 513 function SourceLocation(script, position, line, column, start, end) { | |
| 514 this.script = script; | |
| 515 this.position = position; | |
| 516 this.line = line; | |
| 517 this.column = column; | |
| 518 this.start = start; | |
| 519 this.end = end; | |
| 520 } | |
| 521 | |
| 522 | |
| 523 /** | |
| 524 * Get the source text for a SourceLocation | |
| 525 * @return {String} | |
| 526 * Source text for this location. | |
| 527 */ | |
| 528 function SourceLocationSourceText() { | |
| 529 return %_CallFunction(this.script.source, | |
| 530 this.start, | |
| 531 this.end, | |
| 532 StringSubstring); | |
| 533 } | |
| 534 | |
| 535 | |
| 536 utils.SetUpLockedPrototype(SourceLocation, | |
| 537 ["script", "position", "line", "column", "start", "end"], | |
| 538 ["sourceText", SourceLocationSourceText] | |
| 539 ); | |
| 540 | |
| 541 | |
| 542 /** | |
| 543 * Class for a source slice. A source slice is a part of a script source with | |
| 544 * the following properties: | |
| 545 * script : script object for the source | |
| 546 * from_line : line number for the first line in the slice | |
| 547 * to_line : source line number for the last line in the slice | |
| 548 * from_position : position of the first character in the slice | |
| 549 * to_position : position of the last character in the slice | |
| 550 * The to_line and to_position are not included in the slice, that is the lines | |
| 551 * in the slice are [from_line, to_line[. Likewise the characters in the slice | |
| 552 * are [from_position, to_position[. | |
| 553 * @param {Script} script The Script object for the source slice | |
| 554 * @param {number} from_line | |
| 555 * @param {number} to_line | |
| 556 * @param {number} from_position | |
| 557 * @param {number} to_position | |
| 558 * @constructor | |
| 559 */ | |
| 560 function SourceSlice(script, from_line, to_line, from_position, to_position) { | |
| 561 this.script = script; | |
| 562 this.from_line = from_line; | |
| 563 this.to_line = to_line; | |
| 564 this.from_position = from_position; | |
| 565 this.to_position = to_position; | |
| 566 } | |
| 567 | |
| 568 /** | |
| 569 * Get the source text for a SourceSlice | |
| 570 * @return {String} Source text for this slice. The last line will include | |
| 571 * the line terminating characters (if any) | |
| 572 */ | |
| 573 function SourceSliceSourceText() { | |
| 574 return %_CallFunction(this.script.source, | |
| 575 this.from_position, | |
| 576 this.to_position, | |
| 577 StringSubstring); | |
| 578 } | |
| 579 | |
| 580 utils.SetUpLockedPrototype(SourceSlice, | |
| 581 ["script", "from_line", "to_line", "from_position", "to_position"], | |
| 582 ["sourceText", SourceSliceSourceText] | |
| 583 ); | |
| 584 | |
| 585 | |
| 586 function GetStackTraceLine(recv, fun, pos, isGlobal) { | |
| 587 return new CallSite(recv, fun, pos, false).toString(); | |
| 588 } | |
| 589 | |
| 590 // ---------------------------------------------------------------------------- | |
| 591 // Error implementation | |
| 592 | |
| 593 function CallSite(receiver, fun, pos, strict_mode) { | |
| 594 SET_PRIVATE(this, callSiteReceiverSymbol, receiver); | |
| 595 SET_PRIVATE(this, callSiteFunctionSymbol, fun); | |
| 596 SET_PRIVATE(this, callSitePositionSymbol, pos); | |
| 597 SET_PRIVATE(this, callSiteStrictSymbol, strict_mode); | |
| 598 } | |
| 599 | |
| 600 function CallSiteGetThis() { | |
| 601 return GET_PRIVATE(this, callSiteStrictSymbol) | |
| 602 ? UNDEFINED : GET_PRIVATE(this, callSiteReceiverSymbol); | |
| 603 } | |
| 604 | |
| 605 function CallSiteGetFunction() { | |
| 606 return GET_PRIVATE(this, callSiteStrictSymbol) | |
| 607 ? UNDEFINED : GET_PRIVATE(this, callSiteFunctionSymbol); | |
| 608 } | |
| 609 | |
| 610 function CallSiteGetPosition() { | |
| 611 return GET_PRIVATE(this, callSitePositionSymbol); | |
| 612 } | |
| 613 | |
| 614 function CallSiteGetTypeName() { | |
| 615 return GetTypeName(GET_PRIVATE(this, callSiteReceiverSymbol), false); | |
| 616 } | |
| 617 | |
| 618 function CallSiteIsToplevel() { | |
| 619 return %CallSiteIsToplevelRT(this); | |
| 620 } | |
| 621 | |
| 622 function CallSiteIsEval() { | |
| 623 return %CallSiteIsEvalRT(this); | |
| 624 } | |
| 625 | |
| 626 function CallSiteGetEvalOrigin() { | |
| 627 var script = %FunctionGetScript(GET_PRIVATE(this, callSiteFunctionSymbol)); | |
| 628 return FormatEvalOrigin(script); | |
| 629 } | |
| 630 | |
| 631 function CallSiteGetScriptNameOrSourceURL() { | |
| 632 return %CallSiteGetScriptNameOrSourceUrlRT(this); | |
| 633 } | |
| 634 | |
| 635 function CallSiteGetFunctionName() { | |
| 636 // See if the function knows its own name | |
| 637 return %CallSiteGetFunctionNameRT(this); | |
| 638 } | |
| 639 | |
| 640 function CallSiteGetMethodName() { | |
| 641 // See if we can find a unique property on the receiver that holds | |
| 642 // this function. | |
| 643 return %CallSiteGetMethodNameRT(this); | |
| 644 } | |
| 645 | |
| 646 function CallSiteGetFileName() { | |
| 647 return %CallSiteGetFileNameRT(this); | |
| 648 } | |
| 649 | |
| 650 function CallSiteGetLineNumber() { | |
| 651 return %CallSiteGetLineNumberRT(this); | |
| 652 } | |
| 653 | |
| 654 function CallSiteGetColumnNumber() { | |
| 655 return %CallSiteGetColumnNumberRT(this); | |
| 656 } | |
| 657 | |
| 658 function CallSiteIsNative() { | |
| 659 return %CallSiteIsNativeRT(this); | |
| 660 } | |
| 661 | |
| 662 function CallSiteIsConstructor() { | |
| 663 return %CallSiteIsConstructorRT(this); | |
| 664 } | |
| 665 | |
| 666 function CallSiteToString() { | |
| 667 var fileName; | |
| 668 var fileLocation = ""; | |
| 669 if (this.isNative()) { | |
| 670 fileLocation = "native"; | |
| 671 } else { | |
| 672 fileName = this.getScriptNameOrSourceURL(); | |
| 673 if (!fileName && this.isEval()) { | |
| 674 fileLocation = this.getEvalOrigin(); | |
| 675 fileLocation += ", "; // Expecting source position to follow. | |
| 676 } | |
| 677 | |
| 678 if (fileName) { | |
| 679 fileLocation += fileName; | |
| 680 } else { | |
| 681 // Source code does not originate from a file and is not native, but we | |
| 682 // can still get the source position inside the source string, e.g. in | |
| 683 // an eval string. | |
| 684 fileLocation += "<anonymous>"; | |
| 685 } | |
| 686 var lineNumber = this.getLineNumber(); | |
| 687 if (lineNumber != null) { | |
| 688 fileLocation += ":" + lineNumber; | |
| 689 var columnNumber = this.getColumnNumber(); | |
| 690 if (columnNumber) { | |
| 691 fileLocation += ":" + columnNumber; | |
| 692 } | |
| 693 } | |
| 694 } | |
| 695 | |
| 696 var line = ""; | |
| 697 var functionName = this.getFunctionName(); | |
| 698 var addSuffix = true; | |
| 699 var isConstructor = this.isConstructor(); | |
| 700 var isMethodCall = !(this.isToplevel() || isConstructor); | |
| 701 if (isMethodCall) { | |
| 702 var typeName = GetTypeName(GET_PRIVATE(this, callSiteReceiverSymbol), true); | |
| 703 var methodName = this.getMethodName(); | |
| 704 if (functionName) { | |
| 705 if (typeName && | |
| 706 %_CallFunction(functionName, typeName, StringIndexOf) != 0) { | |
| 707 line += typeName + "."; | |
| 708 } | |
| 709 line += functionName; | |
| 710 if (methodName && | |
| 711 (%_CallFunction(functionName, "." + methodName, StringIndexOf) != | |
| 712 functionName.length - methodName.length - 1)) { | |
| 713 line += " [as " + methodName + "]"; | |
| 714 } | |
| 715 } else { | |
| 716 line += typeName + "." + (methodName || "<anonymous>"); | |
| 717 } | |
| 718 } else if (isConstructor) { | |
| 719 line += "new " + (functionName || "<anonymous>"); | |
| 720 } else if (functionName) { | |
| 721 line += functionName; | |
| 722 } else { | |
| 723 line += fileLocation; | |
| 724 addSuffix = false; | |
| 725 } | |
| 726 if (addSuffix) { | |
| 727 line += " (" + fileLocation + ")"; | |
| 728 } | |
| 729 return line; | |
| 730 } | |
| 731 | |
| 732 utils.SetUpLockedPrototype(CallSite, ["receiver", "fun", "pos"], [ | |
| 733 "getThis", CallSiteGetThis, | |
| 734 "getTypeName", CallSiteGetTypeName, | |
| 735 "isToplevel", CallSiteIsToplevel, | |
| 736 "isEval", CallSiteIsEval, | |
| 737 "getEvalOrigin", CallSiteGetEvalOrigin, | |
| 738 "getScriptNameOrSourceURL", CallSiteGetScriptNameOrSourceURL, | |
| 739 "getFunction", CallSiteGetFunction, | |
| 740 "getFunctionName", CallSiteGetFunctionName, | |
| 741 "getMethodName", CallSiteGetMethodName, | |
| 742 "getFileName", CallSiteGetFileName, | |
| 743 "getLineNumber", CallSiteGetLineNumber, | |
| 744 "getColumnNumber", CallSiteGetColumnNumber, | |
| 745 "isNative", CallSiteIsNative, | |
| 746 "getPosition", CallSiteGetPosition, | |
| 747 "isConstructor", CallSiteIsConstructor, | |
| 748 "toString", CallSiteToString | |
| 749 ]); | |
| 750 | |
| 751 | |
| 752 function FormatEvalOrigin(script) { | |
| 753 var sourceURL = script.nameOrSourceURL(); | |
| 754 if (sourceURL) { | |
| 755 return sourceURL; | |
| 756 } | |
| 757 | |
| 758 var eval_origin = "eval at "; | |
| 759 if (script.eval_from_function_name) { | |
| 760 eval_origin += script.eval_from_function_name; | |
| 761 } else { | |
| 762 eval_origin += "<anonymous>"; | |
| 763 } | |
| 764 | |
| 765 var eval_from_script = script.eval_from_script; | |
| 766 if (eval_from_script) { | |
| 767 if (eval_from_script.compilation_type == COMPILATION_TYPE_EVAL) { | |
| 768 // eval script originated from another eval. | |
| 769 eval_origin += " (" + FormatEvalOrigin(eval_from_script) + ")"; | |
| 770 } else { | |
| 771 // eval script originated from "real" source. | |
| 772 if (eval_from_script.name) { | |
| 773 eval_origin += " (" + eval_from_script.name; | |
| 774 var location = eval_from_script.locationFromPosition( | |
| 775 script.eval_from_script_position, true); | |
| 776 if (location) { | |
| 777 eval_origin += ":" + (location.line + 1); | |
| 778 eval_origin += ":" + (location.column + 1); | |
| 779 } | |
| 780 eval_origin += ")"; | |
| 781 } else { | |
| 782 eval_origin += " (unknown source)"; | |
| 783 } | |
| 784 } | |
| 785 } | |
| 786 | |
| 787 return eval_origin; | |
| 788 } | |
| 789 | |
| 790 | |
| 791 function FormatErrorString(error) { | |
| 792 try { | |
| 793 return %_CallFunction(error, ErrorToString); | |
| 794 } catch (e) { | |
| 795 try { | |
| 796 return "<error: " + e + ">"; | |
| 797 } catch (ee) { | |
| 798 return "<error>"; | |
| 799 } | |
| 800 } | |
| 801 } | |
| 802 | |
| 803 | |
| 804 function GetStackFrames(raw_stack) { | |
| 805 var frames = new InternalArray(); | |
| 806 var sloppy_frames = raw_stack[0]; | |
| 807 for (var i = 1; i < raw_stack.length; i += 4) { | |
| 808 var recv = raw_stack[i]; | |
| 809 var fun = raw_stack[i + 1]; | |
| 810 var code = raw_stack[i + 2]; | |
| 811 var pc = raw_stack[i + 3]; | |
| 812 var pos = %_IsSmi(code) ? code : %FunctionGetPositionForOffset(code, pc); | |
| 813 sloppy_frames--; | |
| 814 frames.push(new CallSite(recv, fun, pos, (sloppy_frames < 0))); | |
| 815 } | |
| 816 return frames; | |
| 817 } | |
| 818 | |
| 819 | |
| 820 // Flag to prevent recursive call of Error.prepareStackTrace. | |
| 821 var formatting_custom_stack_trace = false; | |
| 822 | |
| 823 | |
| 824 function FormatStackTrace(obj, raw_stack) { | |
| 825 var frames = GetStackFrames(raw_stack); | |
| 826 if (IS_FUNCTION(GlobalError.prepareStackTrace) && | |
| 827 !formatting_custom_stack_trace) { | |
| 828 var array = []; | |
| 829 %MoveArrayContents(frames, array); | |
| 830 formatting_custom_stack_trace = true; | |
| 831 var stack_trace = UNDEFINED; | |
| 832 try { | |
| 833 stack_trace = GlobalError.prepareStackTrace(obj, array); | |
| 834 } catch (e) { | |
| 835 throw e; // The custom formatting function threw. Rethrow. | |
| 836 } finally { | |
| 837 formatting_custom_stack_trace = false; | |
| 838 } | |
| 839 return stack_trace; | |
| 840 } | |
| 841 | |
| 842 var lines = new InternalArray(); | |
| 843 lines.push(FormatErrorString(obj)); | |
| 844 for (var i = 0; i < frames.length; i++) { | |
| 845 var frame = frames[i]; | |
| 846 var line; | |
| 847 try { | |
| 848 line = frame.toString(); | |
| 849 } catch (e) { | |
| 850 try { | |
| 851 line = "<error: " + e + ">"; | |
| 852 } catch (ee) { | |
| 853 // Any code that reaches this point is seriously nasty! | |
| 854 line = "<error>"; | |
| 855 } | |
| 856 } | |
| 857 lines.push(" at " + line); | |
| 858 } | |
| 859 return %_CallFunction(lines, "\n", ArrayJoin); | |
| 860 } | |
| 861 | |
| 862 | |
| 863 function GetTypeName(receiver, requireConstructor) { | |
| 864 if (IS_NULL_OR_UNDEFINED(receiver)) return null; | |
| 865 var constructor = receiver.constructor; | |
| 866 if (!constructor) { | |
| 867 return requireConstructor ? null : | |
| 868 %_CallFunction(receiver, NoSideEffectsObjectToString); | |
| 869 } | |
| 870 var constructorName = constructor.name; | |
| 871 if (!constructorName) { | |
| 872 return requireConstructor ? null : | |
| 873 %_CallFunction(receiver, NoSideEffectsObjectToString); | |
| 874 } | |
| 875 return constructorName; | |
| 876 } | |
| 877 | |
| 878 | |
| 879 // Format the stack trace if not yet done, and return it. | |
| 880 // Cache the formatted stack trace on the holder. | |
| 881 var StackTraceGetter = function() { | |
| 882 var formatted_stack_trace = UNDEFINED; | |
| 883 var holder = this; | |
| 884 while (holder) { | |
| 885 var formatted_stack_trace = | |
| 886 GET_PRIVATE(holder, formattedStackTraceSymbol); | |
| 887 if (IS_UNDEFINED(formatted_stack_trace)) { | |
| 888 // No formatted stack trace available. | |
| 889 var stack_trace = GET_PRIVATE(holder, stackTraceSymbol); | |
| 890 if (IS_UNDEFINED(stack_trace)) { | |
| 891 // Neither formatted nor structured stack trace available. | |
| 892 // Look further up the prototype chain. | |
| 893 holder = %_GetPrototype(holder); | |
| 894 continue; | |
| 895 } | |
| 896 formatted_stack_trace = FormatStackTrace(holder, stack_trace); | |
| 897 SET_PRIVATE(holder, stackTraceSymbol, UNDEFINED); | |
| 898 SET_PRIVATE(holder, formattedStackTraceSymbol, formatted_stack_trace); | |
| 899 } | |
| 900 return formatted_stack_trace; | |
| 901 } | |
| 902 return UNDEFINED; | |
| 903 }; | |
| 904 | |
| 905 | |
| 906 // If the receiver equals the holder, set the formatted stack trace that the | |
| 907 // getter returns. | |
| 908 var StackTraceSetter = function(v) { | |
| 909 if (HAS_PRIVATE(this, stackTraceSymbol)) { | |
| 910 SET_PRIVATE(this, stackTraceSymbol, UNDEFINED); | |
| 911 SET_PRIVATE(this, formattedStackTraceSymbol, v); | |
| 912 } | |
| 913 }; | |
| 914 | |
| 915 | |
| 916 // Use a dummy function since we do not actually want to capture a stack trace | |
| 917 // when constructing the initial Error prototytpes. | |
| 918 var captureStackTrace = function() {}; | |
| 919 | |
| 920 | |
| 921 // Define special error type constructors. | |
| 922 function DefineError(global, f) { | |
| 923 // Store the error function in both the global object | |
| 924 // and the runtime object. The function is fetched | |
| 925 // from the runtime object when throwing errors from | |
| 926 // within the runtime system to avoid strange side | |
| 927 // effects when overwriting the error functions from | |
| 928 // user code. | |
| 929 var name = f.name; | |
| 930 %AddNamedProperty(global, name, f, DONT_ENUM); | |
| 931 // Configure the error function. | |
| 932 if (name == 'Error') { | |
| 933 // The prototype of the Error object must itself be an error. | |
| 934 // However, it can't be an instance of the Error object because | |
| 935 // it hasn't been properly configured yet. Instead we create a | |
| 936 // special not-a-true-error-but-close-enough object. | |
| 937 var ErrorPrototype = function() {}; | |
| 938 %FunctionSetPrototype(ErrorPrototype, GlobalObject.prototype); | |
| 939 %FunctionSetInstanceClassName(ErrorPrototype, 'Error'); | |
| 940 %FunctionSetPrototype(f, new ErrorPrototype()); | |
| 941 } else { | |
| 942 %FunctionSetPrototype(f, new GlobalError()); | |
| 943 %InternalSetPrototype(f, GlobalError); | |
| 944 } | |
| 945 %FunctionSetInstanceClassName(f, 'Error'); | |
| 946 %AddNamedProperty(f.prototype, 'constructor', f, DONT_ENUM); | |
| 947 %AddNamedProperty(f.prototype, 'name', name, DONT_ENUM); | |
| 948 %SetCode(f, function(m) { | |
| 949 if (%_IsConstructCall()) { | |
| 950 try { captureStackTrace(this, f); } catch (e) { } | |
| 951 // Define all the expected properties directly on the error | |
| 952 // object. This avoids going through getters and setters defined | |
| 953 // on prototype objects. | |
| 954 if (!IS_UNDEFINED(m)) { | |
| 955 %AddNamedProperty(this, 'message', TO_STRING(m), DONT_ENUM); | |
| 956 } | |
| 957 } else { | |
| 958 return new f(m); | |
| 959 } | |
| 960 }); | |
| 961 %SetNativeFlag(f); | |
| 962 return f; | |
| 963 }; | |
| 964 | |
| 965 GlobalError = DefineError(global, function Error() { }); | |
| 966 GlobalEvalError = DefineError(global, function EvalError() { }); | |
| 967 GlobalRangeError = DefineError(global, function RangeError() { }); | |
| 968 GlobalReferenceError = DefineError(global, function ReferenceError() { }); | |
| 969 GlobalSyntaxError = DefineError(global, function SyntaxError() { }); | |
| 970 GlobalTypeError = DefineError(global, function TypeError() { }); | |
| 971 GlobalURIError = DefineError(global, function URIError() { }); | |
| 972 | |
| 973 %AddNamedProperty(GlobalError.prototype, 'message', '', DONT_ENUM); | |
| 974 | |
| 975 function ErrorToString() { | |
| 976 if (!IS_SPEC_OBJECT(this)) { | |
| 977 throw MakeTypeError(kCalledOnNonObject, "Error.prototype.toString"); | |
| 978 } | |
| 979 | |
| 980 return %ErrorToStringRT(this); | |
| 981 } | |
| 982 | |
| 983 utils.InstallFunctions(GlobalError.prototype, DONT_ENUM, | |
| 984 ['toString', ErrorToString]); | |
| 985 | |
| 986 $errorToString = ErrorToString; | |
| 987 | |
| 988 MakeError = function(type, arg0, arg1, arg2) { | |
| 989 return MakeGenericError(GlobalError, type, arg0, arg1, arg2); | |
| 990 } | |
| 991 | |
| 992 MakeRangeError = function(type, arg0, arg1, arg2) { | |
| 993 return MakeGenericError(GlobalRangeError, type, arg0, arg1, arg2); | |
| 994 } | |
| 995 | |
| 996 MakeSyntaxError = function(type, arg0, arg1, arg2) { | |
| 997 return MakeGenericError(GlobalSyntaxError, type, arg0, arg1, arg2); | |
| 998 } | |
| 999 | |
| 1000 MakeTypeError = function(type, arg0, arg1, arg2) { | |
| 1001 return MakeGenericError(GlobalTypeError, type, arg0, arg1, arg2); | |
| 1002 } | |
| 1003 | |
| 1004 MakeURIError = function() { | |
| 1005 return MakeGenericError(GlobalURIError, kURIMalformed); | |
| 1006 } | |
| 1007 | |
| 1008 // Boilerplate for exceptions for stack overflows. Used from | |
| 1009 // Isolate::StackOverflow(). | |
| 1010 var StackOverflowBoilerplate = MakeRangeError(kStackOverflow); | |
| 1011 %DefineAccessorPropertyUnchecked(StackOverflowBoilerplate, 'stack', | |
| 1012 StackTraceGetter, StackTraceSetter, | |
| 1013 DONT_ENUM); | |
| 1014 | |
| 1015 // Define actual captureStackTrace function after everything has been set up. | |
| 1016 captureStackTrace = function captureStackTrace(obj, cons_opt) { | |
| 1017 // Define accessors first, as this may fail and throw. | |
| 1018 ObjectDefineProperty(obj, 'stack', { get: StackTraceGetter, | |
| 1019 set: StackTraceSetter, | |
| 1020 configurable: true }); | |
| 1021 %CollectStackTrace(obj, cons_opt ? cons_opt : captureStackTrace); | |
| 1022 }; | |
| 1023 | |
| 1024 GlobalError.captureStackTrace = captureStackTrace; | |
| 1025 | |
| 1026 %InstallToContext([ | |
| 1027 "error_function", GlobalError, | |
| 1028 "eval_error_function", GlobalEvalError, | |
| 1029 "get_stack_trace_line_fun", GetStackTraceLine, | |
| 1030 "make_error_function", MakeGenericError, | |
| 1031 "make_range_error", MakeRangeError, | |
| 1032 "make_type_error", MakeTypeError, | |
| 1033 "message_get_column_number", GetColumnNumber, | |
| 1034 "message_get_line_number", GetLineNumber, | |
| 1035 "message_get_source_line", GetSourceLine, | |
| 1036 "no_side_effect_to_string_fun", NoSideEffectToString, | |
| 1037 "range_error_function", GlobalRangeError, | |
| 1038 "reference_error_function", GlobalReferenceError, | |
| 1039 "stack_overflow_boilerplate", StackOverflowBoilerplate, | |
| 1040 "syntax_error_function", GlobalSyntaxError, | |
| 1041 "to_detail_string_fun", ToDetailString, | |
| 1042 "type_error_function", GlobalTypeError, | |
| 1043 "uri_error_function", GlobalURIError, | |
| 1044 ]); | |
| 1045 | |
| 1046 }); | |
| OLD | NEW |