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.
5 // -------------------------------------------------------------------
8 var $getStackTraceLine;
9 var $messageGetPositionInLine;
10 var $messageGetLineNumber;
11 var $messageGetSourceLine;
12 var $noSideEffectToString;
13 var $stackOverflowBoilerplate;
14 var $stackTraceSymbol;
26 var MakeReferenceError;
31 (function(global, utils) {
33 %CheckIsBootstrapping();
35 // -------------------------------------------------------------------
38 var GlobalObject = global.Object;
39 var InternalArray = utils.InternalArray;
40 var ObjectDefineProperty = utils.ObjectDefineProperty;
48 utils.Import(function(from) {
49 ArrayJoin = from.ArrayJoin;
50 ObjectToString = from.ObjectToString;
51 StringCharAt = from.StringCharAt;
52 StringIndexOf = from.StringIndexOf;
53 StringSubstring = from.StringSubstring;
56 // -------------------------------------------------------------------
62 var GlobalSyntaxError;
63 var GlobalReferenceError;
67 function NoSideEffectsObjectToString() {
68 if (IS_UNDEFINED(this) && !IS_UNDETECTABLE(this)) return "[object Undefined]";
69 if (IS_NULL(this)) return "[object Null]";
70 return "[object " + %_ClassOf(TO_OBJECT_INLINE(this)) + "]";
74 function NoSideEffectToString(obj) {
75 if (IS_STRING(obj)) return obj;
76 if (IS_NUMBER(obj)) return %_NumberToString(obj);
77 if (IS_BOOLEAN(obj)) return obj ? 'true' : 'false';
78 if (IS_UNDEFINED(obj)) return 'undefined';
79 if (IS_NULL(obj)) return 'null';
80 if (IS_FUNCTION(obj)) {
81 var str = %_CallFunction(obj, obj, $functionSourceString);
82 if (str.length > 128) {
83 str = %_SubString(str, 0, 111) + "...<omitted>..." +
84 %_SubString(str, str.length - 2, str.length);
88 if (IS_SYMBOL(obj)) return %_CallFunction(obj, $symbolToString);
89 if (IS_FLOAT32X4(obj)) return %_CallFunction(obj, $float32x4ToString);
91 && %GetDataProperty(obj, "toString") === ObjectToString) {
92 var constructor = %GetDataProperty(obj, "constructor");
93 if (typeof constructor == "function") {
94 var constructorName = constructor.name;
95 if (IS_STRING(constructorName) && constructorName !== "") {
96 return "#<" + constructorName + ">";
100 if (CanBeSafelyTreatedAsAnErrorObject(obj)) {
101 return %_CallFunction(obj, ErrorToString);
104 return %_CallFunction(obj, NoSideEffectsObjectToString);
107 // To determine whether we can safely stringify an object using ErrorToString
108 // without the risk of side-effects, we need to check whether the object is
109 // either an instance of a native error type (via '%_ClassOf'), or has Error
110 // in its prototype chain and hasn't overwritten 'toString' with something
111 // strange and unusual.
112 function CanBeSafelyTreatedAsAnErrorObject(obj) {
113 switch (%_ClassOf(obj)) {
117 case 'ReferenceError':
124 var objToString = %GetDataProperty(obj, "toString");
125 return obj instanceof GlobalError && objToString === ErrorToString;
129 // When formatting internally created error messages, do not
130 // invoke overwritten error toString methods but explicitly use
131 // the error to string method. This is to avoid leaking error
132 // objects between script tags in a browser setting.
133 function ToStringCheckErrorObject(obj) {
134 if (CanBeSafelyTreatedAsAnErrorObject(obj)) {
135 return %_CallFunction(obj, ErrorToString);
137 return $toString(obj);
142 function ToDetailString(obj) {
143 if (obj != null && IS_OBJECT(obj) && obj.toString === ObjectToString) {
144 var constructor = obj.constructor;
145 if (typeof constructor == "function") {
146 var constructorName = constructor.name;
147 if (IS_STRING(constructorName) && constructorName !== "") {
148 return "#<" + constructorName + ">";
152 return ToStringCheckErrorObject(obj);
156 function MakeGenericError(constructor, type, arg0, arg1, arg2) {
157 if (IS_UNDEFINED(arg0) && IS_STRING(type)) arg0 = [];
158 return new constructor(FormatMessage(type, arg0, arg1, arg2));
163 * Set up the Script function and constructor.
165 %FunctionSetInstanceClassName(Script, 'Script');
166 %AddNamedProperty(Script.prototype, 'constructor', Script,
167 DONT_ENUM | DONT_DELETE | READ_ONLY);
168 %SetCode(Script, function(x) {
169 // Script objects can only be created by the VM.
170 throw MakeError(kUnsupported);
174 // Helper functions; called from the runtime system.
175 function FormatMessage(type, arg0, arg1, arg2) {
176 var arg0 = NoSideEffectToString(arg0);
177 var arg1 = NoSideEffectToString(arg1);
178 var arg2 = NoSideEffectToString(arg2);
180 return %FormatMessageString(type, arg0, arg1, arg2);
187 function GetLineNumber(message) {
188 var start_position = %MessageGetStartPosition(message);
189 if (start_position == -1) return kNoLineNumberInfo;
190 var script = %MessageGetScript(message);
191 var location = script.locationFromPosition(start_position, true);
192 if (location == null) return kNoLineNumberInfo;
193 return location.line + 1;
197 // Returns the source code line containing the given source
198 // position, or the empty string if the position is invalid.
199 function GetSourceLine(message) {
200 var script = %MessageGetScript(message);
201 var start_position = %MessageGetStartPosition(message);
202 var location = script.locationFromPosition(start_position, true);
203 if (location == null) return "";
204 return location.sourceText();
208 * Find a line number given a specific source position.
209 * @param {number} position The source position.
210 * @return {number} 0 if input too small, -1 if input too large,
211 else the line number.
213 function ScriptLineFromPosition(position) {
215 var upper = this.lineCount() - 1;
216 var line_ends = this.line_ends;
218 // We'll never find invalid positions so bail right away.
219 if (position > line_ends[upper]) {
223 // This means we don't have to safe-guard indexing line_ends[i - 1].
224 if (position <= line_ends[0]) {
228 // Binary search to find line # from position range.
230 var i = (lower + upper) >> 1;
232 if (position > line_ends[i]) {
234 } else if (position <= line_ends[i - 1]) {
245 * Get information on a specific source position.
246 * @param {number} position The source position
247 * @param {boolean} include_resource_offset Set to true to have the resource
248 * offset added to the location
249 * @return {SourceLocation}
250 * If line is negative or not in the source null is returned.
252 function ScriptLocationFromPosition(position,
253 include_resource_offset) {
254 var line = this.lineFromPosition(position);
255 if (line == -1) return null;
257 // Determine start, end and column.
258 var line_ends = this.line_ends;
259 var start = line == 0 ? 0 : line_ends[line - 1] + 1;
260 var end = line_ends[line];
261 if (end > 0 && %_CallFunction(this.source, end - 1, StringCharAt) == '\r') {
264 var column = position - start;
266 // Adjust according to the offset within the resource.
267 if (include_resource_offset) {
268 line += this.line_offset;
269 if (line == this.line_offset) {
270 column += this.column_offset;
274 return new SourceLocation(this, position, line, column, start, end);
279 * Get information on a specific source line and column possibly offset by a
280 * fixed source position. This function is used to find a source position from
281 * a line and column position. The fixed source position offset is typically
282 * used to find a source position in a function based on a line and column in
283 * the source for the function alone. The offset passed will then be the
284 * start position of the source for the function within the full script source.
285 * @param {number} opt_line The line within the source. Default value is 0
286 * @param {number} opt_column The column in within the line. Default value is 0
287 * @param {number} opt_offset_position The offset from the begining of the
288 * source from where the line and column calculation starts.
290 * @return {SourceLocation}
291 * If line is negative or not in the source null is returned.
293 function ScriptLocationFromLine(opt_line, opt_column, opt_offset_position) {
294 // Default is the first line in the script. Lines in the script is relative
295 // to the offset within the resource.
297 if (!IS_UNDEFINED(opt_line)) {
298 line = opt_line - this.line_offset;
301 // Default is first column. If on the first line add the offset within the
303 var column = opt_column || 0;
305 column -= this.column_offset;
308 var offset_position = opt_offset_position || 0;
309 if (line < 0 || column < 0 || offset_position < 0) return null;
311 return this.locationFromPosition(offset_position + column, false);
313 // Find the line where the offset position is located.
314 var offset_line = this.lineFromPosition(offset_position);
316 if (offset_line == -1 || offset_line + line >= this.lineCount()) {
320 return this.locationFromPosition(
321 this.line_ends[offset_line + line - 1] + 1 + column); // line > 0 here.
327 * Get a slice of source code from the script. The boundaries for the slice is
328 * specified in lines.
329 * @param {number} opt_from_line The first line (zero bound) in the slice.
331 * @param {number} opt_to_column The last line (zero bound) in the slice (non
332 * inclusive). Default is the number of lines in the script
333 * @return {SourceSlice} The source slice or null of the parameters where
336 function ScriptSourceSlice(opt_from_line, opt_to_line) {
337 var from_line = IS_UNDEFINED(opt_from_line) ? this.line_offset
339 var to_line = IS_UNDEFINED(opt_to_line) ? this.line_offset + this.lineCount()
342 // Adjust according to the offset within the resource.
343 from_line -= this.line_offset;
344 to_line -= this.line_offset;
345 if (from_line < 0) from_line = 0;
346 if (to_line > this.lineCount()) to_line = this.lineCount();
349 if (from_line >= this.lineCount() ||
351 from_line > to_line) {
355 var line_ends = this.line_ends;
356 var from_position = from_line == 0 ? 0 : line_ends[from_line - 1] + 1;
357 var to_position = to_line == 0 ? 0 : line_ends[to_line - 1] + 1;
359 // Return a source slice with line numbers re-adjusted to the resource.
360 return new SourceSlice(this,
361 from_line + this.line_offset,
362 to_line + this.line_offset,
363 from_position, to_position);
367 function ScriptSourceLine(opt_line) {
368 // Default is the first line in the script. Lines in the script are relative
369 // to the offset within the resource.
371 if (!IS_UNDEFINED(opt_line)) {
372 line = opt_line - this.line_offset;
376 if (line < 0 || this.lineCount() <= line) {
380 // Return the source line.
381 var line_ends = this.line_ends;
382 var start = line == 0 ? 0 : line_ends[line - 1] + 1;
383 var end = line_ends[line];
384 return %_CallFunction(this.source, start, end, StringSubstring);
389 * Returns the number of source lines.
391 * Number of source lines.
393 function ScriptLineCount() {
394 // Return number of source lines.
395 return this.line_ends.length;
400 * Returns the position of the nth line end.
402 * Zero-based position of the nth line end in the script.
404 function ScriptLineEnd(n) {
405 return this.line_ends[n];
410 * If sourceURL comment is available returns sourceURL comment contents.
411 * Otherwise, script name is returned. See
412 * http://fbug.googlecode.com/svn/branches/firebug1.1/docs/ReleaseNotes_1.1.txt
413 * and Source Map Revision 3 proposal for details on using //# sourceURL and
414 * deprecated //@ sourceURL comment to identify scripts that don't have name.
416 * @return {?string} script name if present, value for //# sourceURL or
417 * deprecated //@ sourceURL comment otherwise.
419 function ScriptNameOrSourceURL() {
420 if (this.source_url) return this.source_url;
425 utils.SetUpLockedPrototype(Script, [
429 "source_mapping_url",
434 "lineFromPosition", ScriptLineFromPosition,
435 "locationFromPosition", ScriptLocationFromPosition,
436 "locationFromLine", ScriptLocationFromLine,
437 "sourceSlice", ScriptSourceSlice,
438 "sourceLine", ScriptSourceLine,
439 "lineCount", ScriptLineCount,
440 "nameOrSourceURL", ScriptNameOrSourceURL,
441 "lineEnd", ScriptLineEnd
447 * Class for source location. A source location is a position within some
448 * source with the following properties:
449 * script : script object for the source
450 * line : source line number
451 * column : source column within the line
452 * position : position within the source
453 * start : position of start of source context (inclusive)
454 * end : position of end of source context (not inclusive)
455 * Source text for the source context is the character interval
456 * [start, end[. In most cases end will point to a newline character.
457 * It might point just past the final position of the source if the last
458 * source line does not end with a newline character.
459 * @param {Script} script The Script object for which this is a location
460 * @param {number} position Source position for the location
461 * @param {number} line The line number for the location
462 * @param {number} column The column within the line for the location
463 * @param {number} start Source position for start of source context
464 * @param {number} end Source position for end of source context
467 function SourceLocation(script, position, line, column, start, end) {
468 this.script = script;
469 this.position = position;
471 this.column = column;
478 * Get the source text for a SourceLocation
480 * Source text for this location.
482 function SourceLocationSourceText() {
483 return %_CallFunction(this.script.source,
490 utils.SetUpLockedPrototype(SourceLocation,
491 ["script", "position", "line", "column", "start", "end"],
492 ["sourceText", SourceLocationSourceText]
497 * Class for a source slice. A source slice is a part of a script source with
498 * the following properties:
499 * script : script object for the source
500 * from_line : line number for the first line in the slice
501 * to_line : source line number for the last line in the slice
502 * from_position : position of the first character in the slice
503 * to_position : position of the last character in the slice
504 * The to_line and to_position are not included in the slice, that is the lines
505 * in the slice are [from_line, to_line[. Likewise the characters in the slice
506 * are [from_position, to_position[.
507 * @param {Script} script The Script object for the source slice
508 * @param {number} from_line
509 * @param {number} to_line
510 * @param {number} from_position
511 * @param {number} to_position
514 function SourceSlice(script, from_line, to_line, from_position, to_position) {
515 this.script = script;
516 this.from_line = from_line;
517 this.to_line = to_line;
518 this.from_position = from_position;
519 this.to_position = to_position;
523 * Get the source text for a SourceSlice
524 * @return {String} Source text for this slice. The last line will include
525 * the line terminating characters (if any)
527 function SourceSliceSourceText() {
528 return %_CallFunction(this.script.source,
534 utils.SetUpLockedPrototype(SourceSlice,
535 ["script", "from_line", "to_line", "from_position", "to_position"],
536 ["sourceText", SourceSliceSourceText]
540 // Returns the offset of the given position within the containing
542 function GetPositionInLine(message) {
543 var script = %MessageGetScript(message);
544 var start_position = %MessageGetStartPosition(message);
545 var location = script.locationFromPosition(start_position, true);
546 if (location == null) return -1;
547 return location.column;
551 function GetStackTraceLine(recv, fun, pos, isGlobal) {
552 return new CallSite(recv, fun, pos, false).toString();
555 // ----------------------------------------------------------------------------
556 // Error implementation
558 var CallSiteReceiverKey = NEW_PRIVATE("CallSite#receiver");
559 var CallSiteFunctionKey = NEW_PRIVATE("CallSite#function");
560 var CallSitePositionKey = NEW_PRIVATE("CallSite#position");
561 var CallSiteStrictModeKey = NEW_PRIVATE("CallSite#strict_mode");
563 function CallSite(receiver, fun, pos, strict_mode) {
564 SET_PRIVATE(this, CallSiteReceiverKey, receiver);
565 SET_PRIVATE(this, CallSiteFunctionKey, fun);
566 SET_PRIVATE(this, CallSitePositionKey, pos);
567 SET_PRIVATE(this, CallSiteStrictModeKey, strict_mode);
570 function CallSiteGetThis() {
571 return GET_PRIVATE(this, CallSiteStrictModeKey)
572 ? UNDEFINED : GET_PRIVATE(this, CallSiteReceiverKey);
575 function CallSiteGetFunction() {
576 return GET_PRIVATE(this, CallSiteStrictModeKey)
577 ? UNDEFINED : GET_PRIVATE(this, CallSiteFunctionKey);
580 function CallSiteGetPosition() {
581 return GET_PRIVATE(this, CallSitePositionKey);
584 function CallSiteGetTypeName() {
585 return GetTypeName(GET_PRIVATE(this, CallSiteReceiverKey), false);
588 function CallSiteIsToplevel() {
589 var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
590 var fun = GET_PRIVATE(this, CallSiteFunctionKey);
591 var pos = GET_PRIVATE(this, CallSitePositionKey);
592 return %CallSiteIsToplevelRT(receiver, fun, pos);
595 function CallSiteIsEval() {
596 var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
597 var fun = GET_PRIVATE(this, CallSiteFunctionKey);
598 var pos = GET_PRIVATE(this, CallSitePositionKey);
599 return %CallSiteIsEvalRT(receiver, fun, pos);
602 function CallSiteGetEvalOrigin() {
603 var script = %FunctionGetScript(GET_PRIVATE(this, CallSiteFunctionKey));
604 return FormatEvalOrigin(script);
607 function CallSiteGetScriptNameOrSourceURL() {
608 var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
609 var fun = GET_PRIVATE(this, CallSiteFunctionKey);
610 var pos = GET_PRIVATE(this, CallSitePositionKey);
611 return %CallSiteGetScriptNameOrSourceUrlRT(receiver, fun, pos);
614 function CallSiteGetFunctionName() {
615 // See if the function knows its own name
616 var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
617 var fun = GET_PRIVATE(this, CallSiteFunctionKey);
618 var pos = GET_PRIVATE(this, CallSitePositionKey);
619 return %CallSiteGetFunctionNameRT(receiver, fun, pos);
622 function CallSiteGetMethodName() {
623 // See if we can find a unique property on the receiver that holds
625 var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
626 var fun = GET_PRIVATE(this, CallSiteFunctionKey);
627 var pos = GET_PRIVATE(this, CallSitePositionKey);
628 return %CallSiteGetMethodNameRT(receiver, fun, pos);
631 function CallSiteGetFileName() {
632 var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
633 var fun = GET_PRIVATE(this, CallSiteFunctionKey);
634 var pos = GET_PRIVATE(this, CallSitePositionKey);
635 return %CallSiteGetFileNameRT(receiver, fun, pos);
638 function CallSiteGetLineNumber() {
639 var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
640 var fun = GET_PRIVATE(this, CallSiteFunctionKey);
641 var pos = GET_PRIVATE(this, CallSitePositionKey);
642 return %CallSiteGetLineNumberRT(receiver, fun, pos);
645 function CallSiteGetColumnNumber() {
646 var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
647 var fun = GET_PRIVATE(this, CallSiteFunctionKey);
648 var pos = GET_PRIVATE(this, CallSitePositionKey);
649 return %CallSiteGetColumnNumberRT(receiver, fun, pos);
652 function CallSiteIsNative() {
653 var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
654 var fun = GET_PRIVATE(this, CallSiteFunctionKey);
655 var pos = GET_PRIVATE(this, CallSitePositionKey);
656 return %CallSiteIsNativeRT(receiver, fun, pos);
659 function CallSiteIsConstructor() {
660 var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
661 var fun = GET_PRIVATE(this, CallSiteFunctionKey);
662 var pos = GET_PRIVATE(this, CallSitePositionKey);
663 return %CallSiteIsConstructorRT(receiver, fun, pos);
666 function CallSiteToString() {
668 var fileLocation = "";
669 if (this.isNative()) {
670 fileLocation = "native";
672 fileName = this.getScriptNameOrSourceURL();
673 if (!fileName && this.isEval()) {
674 fileLocation = this.getEvalOrigin();
675 fileLocation += ", "; // Expecting source position to follow.
679 fileLocation += fileName;
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
684 fileLocation += "<anonymous>";
686 var lineNumber = this.getLineNumber();
687 if (lineNumber != null) {
688 fileLocation += ":" + lineNumber;
689 var columnNumber = this.getColumnNumber();
691 fileLocation += ":" + columnNumber;
697 var functionName = this.getFunctionName();
698 var addSuffix = true;
699 var isConstructor = this.isConstructor();
700 var isMethodCall = !(this.isToplevel() || isConstructor);
702 var typeName = GetTypeName(GET_PRIVATE(this, CallSiteReceiverKey), true);
703 var methodName = this.getMethodName();
706 %_CallFunction(functionName, typeName, StringIndexOf) != 0) {
707 line += typeName + ".";
709 line += functionName;
711 (%_CallFunction(functionName, "." + methodName, StringIndexOf) !=
712 functionName.length - methodName.length - 1)) {
713 line += " [as " + methodName + "]";
716 line += typeName + "." + (methodName || "<anonymous>");
718 } else if (isConstructor) {
719 line += "new " + (functionName || "<anonymous>");
720 } else if (functionName) {
721 line += functionName;
723 line += fileLocation;
727 line += " (" + fileLocation + ")";
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
752 function FormatEvalOrigin(script) {
753 var sourceURL = script.nameOrSourceURL();
758 var eval_origin = "eval at ";
759 if (script.eval_from_function_name) {
760 eval_origin += script.eval_from_function_name;
762 eval_origin += "<anonymous>";
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) + ")";
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);
777 eval_origin += ":" + (location.line + 1);
778 eval_origin += ":" + (location.column + 1);
782 eval_origin += " (unknown source)";
791 function FormatErrorString(error) {
793 return %_CallFunction(error, ErrorToString);
796 return "<error: " + e + ">";
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);
814 frames.push(new CallSite(recv, fun, pos, (sloppy_frames < 0)));
820 // Flag to prevent recursive call of Error.prepareStackTrace.
821 var formatting_custom_stack_trace = false;
824 function FormatStackTrace(obj, raw_stack) {
825 var frames = GetStackFrames(raw_stack);
826 if (IS_FUNCTION(GlobalError.prepareStackTrace) &&
827 !formatting_custom_stack_trace) {
829 %MoveArrayContents(frames, array);
830 formatting_custom_stack_trace = true;
831 var stack_trace = UNDEFINED;
833 stack_trace = GlobalError.prepareStackTrace(obj, array);
835 throw e; // The custom formatting function threw. Rethrow.
837 formatting_custom_stack_trace = false;
842 var lines = new InternalArray();
843 lines.push(FormatErrorString(obj));
844 for (var i = 0; i < frames.length; i++) {
845 var frame = frames[i];
848 line = frame.toString();
851 line = "<error: " + e + ">";
853 // Any code that reaches this point is seriously nasty!
857 lines.push(" at " + line);
859 return %_CallFunction(lines, "\n", ArrayJoin);
863 function GetTypeName(receiver, requireConstructor) {
864 if (IS_NULL_OR_UNDEFINED(receiver)) return null;
865 var constructor = receiver.constructor;
867 return requireConstructor ? null :
868 %_CallFunction(receiver, NoSideEffectsObjectToString);
870 var constructorName = constructor.name;
871 if (!constructorName) {
872 return requireConstructor ? null :
873 %_CallFunction(receiver, NoSideEffectsObjectToString);
875 return constructorName;
878 var formatted_stack_trace_symbol = NEW_PRIVATE("formatted stack trace");
881 // Format the stack trace if not yet done, and return it.
882 // Cache the formatted stack trace on the holder.
883 var StackTraceGetter = function() {
884 var formatted_stack_trace = UNDEFINED;
887 var formatted_stack_trace =
888 GET_PRIVATE(holder, formatted_stack_trace_symbol);
889 if (IS_UNDEFINED(formatted_stack_trace)) {
890 // No formatted stack trace available.
891 var stack_trace = GET_PRIVATE(holder, $stackTraceSymbol);
892 if (IS_UNDEFINED(stack_trace)) {
893 // Neither formatted nor structured stack trace available.
894 // Look further up the prototype chain.
895 holder = %_GetPrototype(holder);
898 formatted_stack_trace = FormatStackTrace(holder, stack_trace);
899 SET_PRIVATE(holder, $stackTraceSymbol, UNDEFINED);
900 SET_PRIVATE(holder, formatted_stack_trace_symbol, formatted_stack_trace);
902 return formatted_stack_trace;
908 // If the receiver equals the holder, set the formatted stack trace that the
910 var StackTraceSetter = function(v) {
911 if (HAS_PRIVATE(this, $stackTraceSymbol)) {
912 SET_PRIVATE(this, $stackTraceSymbol, UNDEFINED);
913 SET_PRIVATE(this, formatted_stack_trace_symbol, v);
918 // Use a dummy function since we do not actually want to capture a stack trace
919 // when constructing the initial Error prototytpes.
920 var captureStackTrace = function() {};
923 // Define special error type constructors.
924 function DefineError(global, f) {
925 // Store the error function in both the global object
926 // and the runtime object. The function is fetched
927 // from the runtime object when throwing errors from
928 // within the runtime system to avoid strange side
929 // effects when overwriting the error functions from
932 %AddNamedProperty(global, name, f, DONT_ENUM);
933 // Configure the error function.
934 if (name == 'Error') {
935 // The prototype of the Error object must itself be an error.
936 // However, it can't be an instance of the Error object because
937 // it hasn't been properly configured yet. Instead we create a
938 // special not-a-true-error-but-close-enough object.
939 var ErrorPrototype = function() {};
940 %FunctionSetPrototype(ErrorPrototype, GlobalObject.prototype);
941 %FunctionSetInstanceClassName(ErrorPrototype, 'Error');
942 %FunctionSetPrototype(f, new ErrorPrototype());
944 %FunctionSetPrototype(f, new GlobalError());
945 %InternalSetPrototype(f, GlobalError);
947 %FunctionSetInstanceClassName(f, 'Error');
948 %AddNamedProperty(f.prototype, 'constructor', f, DONT_ENUM);
949 %AddNamedProperty(f.prototype, 'name', name, DONT_ENUM);
950 %SetCode(f, function(m) {
951 if (%_IsConstructCall()) {
952 try { captureStackTrace(this, f); } catch (e) { }
953 // Define all the expected properties directly on the error
954 // object. This avoids going through getters and setters defined
955 // on prototype objects.
956 if (!IS_UNDEFINED(m)) {
957 %AddNamedProperty(this, 'message', $toString(m), DONT_ENUM);
967 GlobalError = DefineError(global, function Error() { });
968 GlobalEvalError = DefineError(global, function EvalError() { });
969 GlobalRangeError = DefineError(global, function RangeError() { });
970 GlobalReferenceError = DefineError(global, function ReferenceError() { });
971 GlobalSyntaxError = DefineError(global, function SyntaxError() { });
972 GlobalTypeError = DefineError(global, function TypeError() { });
973 GlobalURIError = DefineError(global, function URIError() { });
975 %AddNamedProperty(GlobalError.prototype, 'message', '', DONT_ENUM);
977 // Global list of error objects visited during ErrorToString. This is
978 // used to detect cycles in error toString formatting.
979 var visited_errors = new InternalArray();
980 var cyclic_error_marker = new GlobalObject();
982 function GetPropertyWithoutInvokingMonkeyGetters(error, name) {
984 // Climb the prototype chain until we find the holder.
985 while (current && !%HasOwnProperty(current, name)) {
986 current = %_GetPrototype(current);
988 if (IS_NULL(current)) return UNDEFINED;
989 if (!IS_OBJECT(current)) return error[name];
990 // If the property is an accessor on one of the predefined errors that can be
991 // generated statically by the compiler, don't touch it. This is to address
992 // http://code.google.com/p/chromium/issues/detail?id=69187
993 var desc = %GetOwnProperty(current, name);
994 if (desc && desc[IS_ACCESSOR_INDEX]) {
995 var isName = name === "name";
996 if (current === GlobalReferenceError.prototype)
997 return isName ? "ReferenceError" : UNDEFINED;
998 if (current === GlobalSyntaxError.prototype)
999 return isName ? "SyntaxError" : UNDEFINED;
1000 if (current === GlobalTypeError.prototype)
1001 return isName ? "TypeError" : UNDEFINED;
1003 // Otherwise, read normally.
1007 function ErrorToStringDetectCycle(error) {
1008 if (!%PushIfAbsent(visited_errors, error)) throw cyclic_error_marker;
1010 var name = GetPropertyWithoutInvokingMonkeyGetters(error, "name");
1011 name = IS_UNDEFINED(name) ? "Error" : TO_STRING_INLINE(name);
1012 var message = GetPropertyWithoutInvokingMonkeyGetters(error, "message");
1013 message = IS_UNDEFINED(message) ? "" : TO_STRING_INLINE(message);
1014 if (name === "") return message;
1015 if (message === "") return name;
1016 return name + ": " + message;
1018 visited_errors.length = visited_errors.length - 1;
1022 function ErrorToString() {
1023 if (!IS_SPEC_OBJECT(this)) {
1024 throw MakeTypeError(kCalledOnNonObject, "Error.prototype.toString");
1028 return ErrorToStringDetectCycle(this);
1030 // If this error message was encountered already return the empty
1031 // string for it instead of recursively formatting it.
1032 if (e === cyclic_error_marker) {
1039 utils.InstallFunctions(GlobalError.prototype, DONT_ENUM,
1040 ['toString', ErrorToString]);
1042 $errorToString = ErrorToString;
1043 $getStackTraceLine = GetStackTraceLine;
1044 $messageGetPositionInLine = GetPositionInLine;
1045 $messageGetLineNumber = GetLineNumber;
1046 $messageGetSourceLine = GetSourceLine;
1047 $noSideEffectToString = NoSideEffectToString;
1048 $toDetailString = ToDetailString;
1050 $Error = GlobalError;
1051 $EvalError = GlobalEvalError;
1052 $RangeError = GlobalRangeError;
1053 $ReferenceError = GlobalReferenceError;
1054 $SyntaxError = GlobalSyntaxError;
1055 $TypeError = GlobalTypeError;
1056 $URIError = GlobalURIError;
1058 MakeError = function(type, arg0, arg1, arg2) {
1059 return MakeGenericError(GlobalError, type, arg0, arg1, arg2);
1062 MakeEvalError = function(type, arg0, arg1, arg2) {
1063 return MakeGenericError(GlobalEvalError, type, arg0, arg1, arg2);
1066 MakeRangeError = function(type, arg0, arg1, arg2) {
1067 return MakeGenericError(GlobalRangeError, type, arg0, arg1, arg2);
1070 MakeReferenceError = function(type, arg0, arg1, arg2) {
1071 return MakeGenericError(GlobalReferenceError, type, arg0, arg1, arg2);
1074 MakeSyntaxError = function(type, arg0, arg1, arg2) {
1075 return MakeGenericError(GlobalSyntaxError, type, arg0, arg1, arg2);
1078 MakeTypeError = function(type, arg0, arg1, arg2) {
1079 return MakeGenericError(GlobalTypeError, type, arg0, arg1, arg2);
1082 MakeURIError = function() {
1083 return MakeGenericError(GlobalURIError, kURIMalformed);
1086 // Boilerplate for exceptions for stack overflows. Used from
1087 // Isolate::StackOverflow().
1088 $stackOverflowBoilerplate = MakeRangeError(kStackOverflow);
1089 %DefineAccessorPropertyUnchecked($stackOverflowBoilerplate, 'stack',
1090 StackTraceGetter, StackTraceSetter,
1093 // Define actual captureStackTrace function after everything has been set up.
1094 captureStackTrace = function captureStackTrace(obj, cons_opt) {
1095 // Define accessors first, as this may fail and throw.
1096 ObjectDefineProperty(obj, 'stack', { get: StackTraceGetter,
1097 set: StackTraceSetter,
1098 configurable: true });
1099 %CollectStackTrace(obj, cons_opt ? cons_opt : captureStackTrace);
1102 GlobalError.captureStackTrace = captureStackTrace;