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 |