Index: src/js/messages.js |
diff --git a/src/js/messages.js b/src/js/messages.js |
index dc845ea97adc855fa7d0847374dad1af99e7551a..d0305412b74896a7971e4e435032b5f9a8ee3a05 100644 |
--- a/src/js/messages.js |
+++ b/src/js/messages.js |
@@ -23,6 +23,7 @@ var callSitePositionSymbol = |
utils.ImportNow("call_site_position_symbol"); |
var callSiteStrictSymbol = |
utils.ImportNow("call_site_strict_symbol"); |
+var FLAG_harmony_tostring; |
var Float32x4ToString; |
var formattedStackTraceSymbol = |
utils.ImportNow("formatted_stack_trace_symbol"); |
@@ -41,6 +42,7 @@ var StringCharAt; |
var StringIndexOf; |
var StringSubstring; |
var SymbolToString; |
+var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol"); |
var Uint16x8ToString; |
var Uint32x4ToString; |
var Uint8x16ToString; |
@@ -66,6 +68,10 @@ utils.Import(function(from) { |
Uint8x16ToString = from.Uint8x16ToString; |
}); |
+utils.ImportFromExperimental(function(from) { |
+ FLAG_harmony_tostring = from.FLAG_harmony_tostring; |
+}); |
+ |
// ------------------------------------------------------------------- |
var GlobalError; |
@@ -80,11 +86,35 @@ var GlobalEvalError; |
function NoSideEffectsObjectToString() { |
if (IS_UNDEFINED(this)) return "[object Undefined]"; |
if (IS_NULL(this)) return "[object Null]"; |
- return "[object " + %_ClassOf(TO_OBJECT(this)) + "]"; |
+ var O = TO_OBJECT(this); |
+ var builtinTag = %_ClassOf(O); |
+ var tag; |
+ if (FLAG_harmony_tostring) { |
+ tag = %GetDataProperty(O, toStringTagSymbol); |
+ if (!IS_STRING(tag)) { |
+ tag = builtinTag; |
+ } |
+ } else { |
+ tag = builtinTag; |
+ } |
+ return `[object ${tag}]`; |
+} |
+ |
+function IsErrorObject(obj) { |
+ return HAS_PRIVATE(obj, stackTraceSymbol); |
} |
+function NoSideEffectsErrorToString() { |
+ var name = %GetDataProperty(this, "name"); |
+ var message = %GetDataProperty(this, "message"); |
+ name = IS_UNDEFINED(name) ? "Error" : NoSideEffectsToString(name); |
+ message = IS_UNDEFINED(message) ? "" : NoSideEffectsToString(message); |
+ if (name == "") return message; |
+ if (message == "") return name; |
+ return `${name}: ${message}`; |
+} |
-function NoSideEffectToString(obj) { |
+function NoSideEffectsToString(obj) { |
if (IS_STRING(obj)) return obj; |
if (IS_NUMBER(obj)) return %_NumberToString(obj); |
if (IS_BOOLEAN(obj)) return obj ? 'true' : 'false'; |
@@ -113,69 +143,26 @@ function NoSideEffectToString(obj) { |
case 'bool8x16': return %_Call(Bool8x16ToString, obj); |
} |
} |
- if (IS_OBJECT(obj) |
- && %GetDataProperty(obj, "toString") === ObjectToString) { |
- var constructor = %GetDataProperty(obj, "constructor"); |
- if (typeof constructor == "function") { |
- var constructorName = constructor.name; |
- if (IS_STRING(constructorName) && constructorName !== "") { |
- return "#<" + constructorName + ">"; |
- } |
- } |
- } |
- if (CanBeSafelyTreatedAsAnErrorObject(obj)) { |
- return %_Call(ErrorToString, obj); |
- } |
- |
- return %_Call(NoSideEffectsObjectToString, obj); |
-} |
- |
-// To determine whether we can safely stringify an object using ErrorToString |
-// without the risk of side-effects, we need to check whether the object is |
-// either an instance of a native error type (via '%_ClassOf'), or has Error |
-// in its prototype chain and hasn't overwritten 'toString' with something |
-// strange and unusual. |
-function CanBeSafelyTreatedAsAnErrorObject(obj) { |
- switch (%_ClassOf(obj)) { |
- case 'Error': |
- case 'EvalError': |
- case 'RangeError': |
- case 'ReferenceError': |
- case 'SyntaxError': |
- case 'TypeError': |
- case 'URIError': |
- return true; |
- } |
- |
- var objToString = %GetDataProperty(obj, "toString"); |
- return obj instanceof GlobalError && objToString === ErrorToString; |
-} |
- |
- |
-// When formatting internally created error messages, do not |
-// invoke overwritten error toString methods but explicitly use |
-// the error to string method. This is to avoid leaking error |
-// objects between script tags in a browser setting. |
-function ToStringCheckErrorObject(obj) { |
- if (CanBeSafelyTreatedAsAnErrorObject(obj)) { |
- return %_Call(ErrorToString, obj); |
- } else { |
- return TO_STRING(obj); |
- } |
-} |
+ if (IS_SPEC_OBJECT(obj)) { |
+ // When internally formatting error objects, use a side-effects-free version |
+ // of Error.prototype.toString independent of the actually installed |
+ // toString method. |
+ if (IsErrorObject(obj) || |
+ %GetDataProperty(obj, "toString") === ErrorToString) { |
+ return %_Call(NoSideEffectsErrorToString, obj); |
+ } |
-function ToDetailString(obj) { |
- if (obj != null && IS_OBJECT(obj) && obj.toString === ObjectToString) { |
- var constructor = obj.constructor; |
- if (typeof constructor == "function") { |
- var constructorName = constructor.name; |
- if (IS_STRING(constructorName) && constructorName !== "") { |
- return "#<" + constructorName + ">"; |
+ if (%GetDataProperty(obj, "toString") === ObjectToString) { |
+ var constructor = %GetDataProperty(obj, "constructor"); |
+ if (IS_FUNCTION(constructor)) { |
+ var constructor_name = %FunctionGetName(constructor); |
+ if (constructor_name != "") return `#<${constructor_name}>`; |
} |
} |
} |
- return ToStringCheckErrorObject(obj); |
+ |
+ return %_Call(NoSideEffectsObjectToString, obj); |
} |
@@ -200,9 +187,9 @@ function MakeGenericError(constructor, type, arg0, arg1, arg2) { |
// Helper functions; called from the runtime system. |
function FormatMessage(type, arg0, arg1, arg2) { |
- var arg0 = NoSideEffectToString(arg0); |
- var arg1 = NoSideEffectToString(arg1); |
- var arg2 = NoSideEffectToString(arg2); |
+ var arg0 = NoSideEffectsToString(arg0); |
+ var arg1 = NoSideEffectsToString(arg1); |
+ var arg2 = NoSideEffectsToString(arg2); |
try { |
return %FormatMessageString(type, arg0, arg1, arg2); |
} catch (e) { |
@@ -849,20 +836,13 @@ function FormatStackTrace(obj, raw_stack) { |
function GetTypeName(receiver, requireConstructor) { |
if (IS_NULL_OR_UNDEFINED(receiver)) return null; |
- if (%_IsJSProxy(receiver)) { |
- return "Proxy"; |
- }; |
- var constructor = receiver.constructor; |
- if (!constructor) { |
- return requireConstructor ? null : |
- %_Call(NoSideEffectsObjectToString, receiver); |
- } |
- var constructorName = constructor.name; |
- if (!constructorName) { |
- return requireConstructor ? null : |
- %_Call(NoSideEffectsObjectToString, receiver); |
+ if (%_IsJSProxy(receiver)) return "Proxy"; |
+ |
+ var constructor = %GetDataProperty(TO_OBJECT(receiver), "constructor"); |
+ if (!IS_FUNCTION(constructor)) { |
+ return requireConstructor ? null : %_Call(NoSideEffectsToString, receiver); |
} |
- return constructorName; |
+ return %FunctionGetName(constructor); |
} |
@@ -896,7 +876,7 @@ var StackTraceGetter = function() { |
// If the receiver equals the holder, set the formatted stack trace that the |
// getter returns. |
var StackTraceSetter = function(v) { |
- if (HAS_PRIVATE(this, stackTraceSymbol)) { |
+ if (IsErrorObject(this)) { |
SET_PRIVATE(this, stackTraceSymbol, UNDEFINED); |
SET_PRIVATE(this, formattedStackTraceSymbol, v); |
} |
@@ -956,7 +936,15 @@ function ErrorToString() { |
throw MakeTypeError(kCalledOnNonObject, "Error.prototype.toString"); |
} |
- return %ErrorToStringRT(this); |
+ var name = this.name; |
+ name = IS_UNDEFINED(name) ? "Error" : TO_STRING(name); |
+ |
+ var message = this.message; |
+ message = IS_UNDEFINED(message) ? "" : TO_STRING(message); |
+ |
+ if (name == "") return message; |
+ if (message == "") return name; |
+ return `${name}: ${message}` |
} |
function MakeError(type, arg0, arg1, arg2) { |
@@ -1004,9 +992,8 @@ GlobalError.captureStackTrace = captureStackTrace; |
"message_get_column_number", GetColumnNumber, |
"message_get_line_number", GetLineNumber, |
"message_get_source_line", GetSourceLine, |
- "no_side_effect_to_string_fun", NoSideEffectToString, |
+ "no_side_effects_to_string_fun", NoSideEffectsToString, |
"stack_overflow_boilerplate", StackOverflowBoilerplate, |
- "to_detail_string_fun", ToDetailString, |
]); |
utils.Export(function(to) { |