Commit d48170db authored by jgruber's avatar jgruber Committed by Commit bot

Move NoSideEffectToString to C++

BUG=

Review-Url: https://codereview.chromium.org/2206573002
Cr-Commit-Position: refs/heads/master@{#38289}
parent fa1fd5a6
......@@ -3071,13 +3071,8 @@ MaybeLocal<String> Value::ToDetailString(Local<Context> context) const {
i::Handle<i::Object> obj = Utils::OpenHandle(this);
if (obj->IsString()) return ToApiHandle<String>(obj);
PREPARE_FOR_EXECUTION(context, Object, ToDetailString, String);
Local<String> result;
i::Handle<i::Object> args[] = {obj};
has_pending_exception = !ToLocal<String>(
i::Execution::TryCall(isolate, isolate->no_side_effects_to_string_fun(),
isolate->factory()->undefined_value(),
arraysize(args), args),
&result);
Local<String> result =
Utils::ToLocal(i::Object::NoSideEffectsToString(isolate, obj));
RETURN_ON_FAILED_EXECUTION(String);
RETURN_ESCAPED(result);
}
......
......@@ -1003,12 +1003,19 @@ static void InstallError(Isolate* isolate, Handle<JSObject> global,
JSObject::AddProperty(prototype, factory->constructor_string(), error_fun,
DONT_ENUM);
Handle<JSFunction> to_string_fun =
SimpleInstallFunction(prototype, factory->toString_string(),
Builtins::kErrorPrototypeToString, 0, true);
to_string_fun->shared()->set_native(true);
if (context_index == Context::ERROR_FUNCTION_INDEX) {
Handle<JSFunction> to_string_fun =
SimpleInstallFunction(prototype, factory->toString_string(),
Builtins::kErrorPrototypeToString, 0, true);
to_string_fun->shared()->set_native(true);
isolate->native_context()->set_error_to_string(*to_string_fun);
} else {
DCHECK(context_index != Context::ERROR_FUNCTION_INDEX);
DCHECK(isolate->native_context()->error_to_string()->IsJSFunction());
InstallFunction(prototype, isolate->error_to_string(),
factory->toString_string(), DONT_ENUM);
if (context_index != Context::ERROR_FUNCTION_INDEX) {
Handle<JSFunction> global_error = isolate->error_function();
CHECK(JSReceiver::SetPrototype(error_fun, global_error, false,
Object::THROW_ON_ERROR)
......@@ -2698,6 +2705,13 @@ void Bootstrapper::ExportFromRuntime(Isolate* isolate,
Accessors::FunctionSetPrototype(callsite_fun, proto).Assert();
}
}
{ // -- E r r o r
Handle<JSFunction> make_err_fun = InstallFunction(
container, "make_generic_error", JS_OBJECT_TYPE, JSObject::kHeaderSize,
isolate->initial_object_prototype(), Builtins::kMakeGenericError);
make_err_fun->shared()->DontAdaptArguments();
}
}
......
......@@ -15,11 +15,23 @@ namespace internal {
// ES6 section 19.5.1.1 Error ( message )
BUILTIN(ErrorConstructor) {
HandleScope scope(isolate);
FrameSkipMode mode = SKIP_FIRST;
Handle<Object> caller;
// When we're passed a JSFunction as new target, we can skip frames until that
// specific function is seen instead of unconditionally skipping the first
// frame.
if (args.new_target()->IsJSFunction()) {
mode = SKIP_UNTIL_SEEN;
caller = args.new_target();
}
RETURN_RESULT_OR_FAILURE(
isolate,
ErrorUtils::Construct(isolate, args.target<JSFunction>(),
Handle<Object>::cast(args.new_target()),
args.atOrUndefined(isolate, 1), SKIP_FIRST, false));
isolate, ErrorUtils::Construct(isolate, args.target<JSFunction>(),
Handle<Object>::cast(args.new_target()),
args.atOrUndefined(isolate, 1), mode,
caller, false));
}
// static
......@@ -69,5 +81,24 @@ BUILTIN(ErrorPrototypeToString) {
ErrorUtils::ToString(isolate, args.receiver()));
}
BUILTIN(MakeGenericError) {
HandleScope scope(isolate);
Handle<Object> constructor = args.atOrUndefined(isolate, 1);
Handle<Object> template_index = args.atOrUndefined(isolate, 2);
Handle<Object> arg0 = args.atOrUndefined(isolate, 3);
Handle<Object> arg1 = args.atOrUndefined(isolate, 4);
Handle<Object> arg2 = args.atOrUndefined(isolate, 5);
DCHECK(constructor->IsJSFunction());
DCHECK(template_index->IsSmi());
RETURN_RESULT_OR_FAILURE(
isolate,
ErrorUtils::MakeGenericError(
isolate, Handle<JSFunction>::cast(constructor),
Smi::cast(*template_index)->value(), arg0, arg1, arg2, SKIP_FIRST));
}
} // namespace internal
} // namespace v8
......@@ -312,6 +312,7 @@ namespace internal {
CPP(ErrorConstructor) \
CPP(ErrorCaptureStackTrace) \
CPP(ErrorPrototypeToString) \
CPP(MakeGenericError) \
\
/* Function */ \
CPP(FunctionConstructor) \
......
......@@ -68,9 +68,9 @@ enum ContextLookupFlags {
V(ASYNC_FUNCTION_AWAIT_INDEX, JSFunction, async_function_await) \
V(DERIVED_GET_TRAP_INDEX, JSFunction, derived_get_trap) \
V(ERROR_FUNCTION_INDEX, JSFunction, error_function) \
V(ERROR_TO_STRING, JSFunction, error_to_string) \
V(EVAL_ERROR_FUNCTION_INDEX, JSFunction, eval_error_function) \
V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \
V(MAKE_ERROR_FUNCTION_INDEX, JSFunction, make_error_function) \
V(MAP_DELETE_METHOD_INDEX, JSFunction, map_delete) \
V(MAP_GET_METHOD_INDEX, JSFunction, map_get) \
V(MAP_HAS_METHOD_INDEX, JSFunction, map_has) \
......@@ -78,8 +78,6 @@ enum ContextLookupFlags {
V(MESSAGE_GET_COLUMN_NUMBER_INDEX, JSFunction, message_get_column_number) \
V(MESSAGE_GET_LINE_NUMBER_INDEX, JSFunction, message_get_line_number) \
V(MESSAGE_GET_SOURCE_LINE_INDEX, JSFunction, message_get_source_line) \
V(NO_SIDE_EFFECTS_TO_STRING_FUN_INDEX, JSFunction, \
no_side_effects_to_string_fun) \
V(OBJECT_VALUE_OF, JSFunction, object_value_of) \
V(OBJECT_TO_STRING, JSFunction, object_to_string) \
V(PROMISE_CATCH_INDEX, JSFunction, promise_catch) \
......
......@@ -1154,27 +1154,21 @@ Handle<Object> Factory::NewError(Handle<JSFunction> constructor,
MessageTemplate::TemplateString(template_index)));
}
Handle<JSFunction> fun = isolate()->make_error_function();
Handle<Object> message_type(Smi::FromInt(template_index), isolate());
if (arg0.is_null()) arg0 = undefined_value();
if (arg1.is_null()) arg1 = undefined_value();
if (arg2.is_null()) arg2 = undefined_value();
Handle<Object> argv[] = {constructor, message_type, arg0, arg1, arg2};
// Invoke the JavaScript factory method. If an exception is thrown while
// running the factory method, use the exception as the result.
Handle<Object> result;
MaybeHandle<Object> exception;
if (!Execution::TryCall(isolate(), fun, undefined_value(), arraysize(argv),
argv, &exception)
if (!ErrorUtils::MakeGenericError(isolate(), constructor, template_index,
arg0, arg1, arg2, SKIP_NONE)
.ToHandle(&result)) {
Handle<Object> exception_obj;
if (exception.ToHandle(&exception_obj)) {
result = exception_obj;
} else {
result = undefined_value();
}
// If an exception is thrown while
// running the factory method, use the exception as the result.
DCHECK(isolate()->has_pending_exception());
result = handle(isolate()->pending_exception(), isolate());
isolate()->clear_pending_exception();
}
return scope.CloseAndEscape(result);
}
......
......@@ -178,7 +178,6 @@
V(frozen_symbol) \
V(hash_code_symbol) \
V(home_object_symbol) \
V(internal_error_symbol) \
V(intl_impl_object_symbol) \
V(intl_initialized_marker_symbol) \
V(intl_pattern_symbol) \
......
......@@ -975,10 +975,11 @@ Object* Isolate::StackOverflow() {
Handle<JSFunction> fun = range_error_function();
Handle<Object> msg = factory()->NewStringFromAsciiChecked(
MessageTemplate::TemplateString(MessageTemplate::kStackOverflow));
Handle<Object> no_caller;
Handle<Object> exception;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
this, exception,
ErrorUtils::Construct(this, fun, fun, msg, SKIP_NONE, true));
ErrorUtils::Construct(this, fun, fun, msg, SKIP_NONE, no_caller, true));
Throw(*exception, nullptr);
......
......@@ -925,17 +925,4 @@ utils.InstallFunctions(GlobalBool8x16, DONT_ENUM, [
'shuffle', Bool8x16ShuffleJS,
]);
utils.Export(function(to) {
to.Float32x4ToString = Float32x4ToString;
to.Int32x4ToString = Int32x4ToString;
to.Uint32x4ToString = Uint32x4ToString;
to.Bool32x4ToString = Bool32x4ToString;
to.Int16x8ToString = Int16x8ToString;
to.Uint16x8ToString = Uint16x8ToString;
to.Bool16x8ToString = Bool16x8ToString;
to.Int8x16ToString = Int8x16ToString;
to.Uint8x16ToString = Uint8x16ToString;
to.Bool8x16ToString = Bool8x16ToString;
});
})
......@@ -11,138 +11,16 @@
// -------------------------------------------------------------------
// Imports
var ArrayJoin;
var Bool16x8ToString;
var Bool32x4ToString;
var Bool8x16ToString;
var CallSite = utils.ImportNow("CallSite");
var Float32x4ToString;
var GlobalObject = global.Object;
var MakeGenericError = utils.ImportNow("make_generic_error");
var GlobalError = global.Error;
var GlobalEvalError = global.EvalError;
var GlobalRangeError = global.RangeError;
var GlobalReferenceError = global.ReferenceError;
var GlobalSyntaxError = global.SyntaxError;
var GlobalTypeError = global.TypeError;
var GlobalURIError = global.URIError;
var Int16x8ToString;
var Int32x4ToString;
var Int8x16ToString;
var InternalArray = utils.InternalArray;
var internalErrorSymbol = utils.ImportNow("internal_error_symbol");
var ObjectHasOwnProperty;
var ObjectToString = utils.ImportNow("object_to_string");
var Script = utils.ImportNow("Script");
var stackTraceSymbol = utils.ImportNow("stack_trace_symbol");
var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
var Uint16x8ToString;
var Uint32x4ToString;
var Uint8x16ToString;
utils.Import(function(from) {
ArrayJoin = from.ArrayJoin;
Bool16x8ToString = from.Bool16x8ToString;
Bool32x4ToString = from.Bool32x4ToString;
Bool8x16ToString = from.Bool8x16ToString;
Float32x4ToString = from.Float32x4ToString;
Int16x8ToString = from.Int16x8ToString;
Int32x4ToString = from.Int32x4ToString;
Int8x16ToString = from.Int8x16ToString;
ObjectHasOwnProperty = from.ObjectHasOwnProperty;
Uint16x8ToString = from.Uint16x8ToString;
Uint32x4ToString = from.Uint32x4ToString;
Uint8x16ToString = from.Uint8x16ToString;
});
// -------------------------------------------------------------------
function NoSideEffectsObjectToString() {
if (IS_UNDEFINED(this)) return "[object Undefined]";
if (IS_NULL(this)) return "[object Null]";
var O = TO_OBJECT(this);
var builtinTag = %_ClassOf(O);
var tag = %GetDataProperty(O, toStringTagSymbol);
if (!IS_STRING(tag)) {
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 NoSideEffectsToString(obj) {
if (IS_STRING(obj)) return obj;
if (IS_NUMBER(obj)) return %_NumberToString(obj);
if (IS_BOOLEAN(obj)) return obj ? 'true' : 'false';
if (IS_UNDEFINED(obj)) return 'undefined';
if (IS_NULL(obj)) return 'null';
if (IS_FUNCTION(obj)) {
var str = %FunctionToString(obj);
if (str.length > 128) {
str = %_SubString(str, 0, 111) + "...<omitted>..." +
%_SubString(str, str.length - 2, str.length);
}
return str;
}
if (IS_SYMBOL(obj)) return %SymbolDescriptiveString(obj);
if (IS_SIMD_VALUE(obj)) {
switch (typeof(obj)) {
case 'float32x4': return %_Call(Float32x4ToString, obj);
case 'int32x4': return %_Call(Int32x4ToString, obj);
case 'int16x8': return %_Call(Int16x8ToString, obj);
case 'int8x16': return %_Call(Int8x16ToString, obj);
case 'uint32x4': return %_Call(Uint32x4ToString, obj);
case 'uint16x8': return %_Call(Uint16x8ToString, obj);
case 'uint8x16': return %_Call(Uint8x16ToString, obj);
case 'bool32x4': return %_Call(Bool32x4ToString, obj);
case 'bool16x8': return %_Call(Bool16x8ToString, obj);
case 'bool8x16': return %_Call(Bool8x16ToString, obj);
}
}
if (IS_RECEIVER(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);
}
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 %_Call(NoSideEffectsObjectToString, obj);
}
function MakeGenericError(constructor, type, arg0, arg1, arg2) {
var error = new constructor(FormatMessage(type, arg0, arg1, arg2));
error[internalErrorSymbol] = true;
return error;
}
/**
* Set up the Script function and constructor.
*/
......@@ -154,20 +32,6 @@ function MakeGenericError(constructor, type, arg0, arg1, arg2) {
throw MakeError(kUnsupported);
});
// Helper functions; called from the runtime system.
function FormatMessage(type, arg0, arg1, arg2) {
var arg0 = NoSideEffectsToString(arg0);
var arg1 = NoSideEffectsToString(arg1);
var arg2 = NoSideEffectsToString(arg2);
try {
return %FormatMessageString(type, arg0, arg1, arg2);
} catch (e) {
return "<error>";
}
}
function GetLineNumber(message) {
var start_position = %MessageGetStartPosition(message);
if (start_position == -1) return kNoLineNumberInfo;
......@@ -288,13 +152,11 @@ function MakeURIError() {
}
%InstallToContext([
"make_error_function", MakeGenericError,
"make_range_error", MakeRangeError,
"make_type_error", MakeTypeError,
"message_get_column_number", GetColumnNumber,
"message_get_line_number", GetLineNumber,
"message_get_source_line", GetSourceLine,
"no_side_effects_to_string_fun", NoSideEffectsToString,
]);
utils.Export(function(to) {
......
......@@ -101,10 +101,7 @@ void MessageHandler::ReportMessage(Isolate* isolate, MessageLocation* loc,
Handle<Object> stringified;
// Make sure we don't leak uncaught internally generated Error objects.
if (argument->IsJSError()) {
Handle<Object> args[] = {argument};
maybe_stringified = Execution::TryCall(
isolate, isolate->no_side_effects_to_string_fun(),
isolate->factory()->undefined_value(), arraysize(args), args);
maybe_stringified = Object::NoSideEffectsToString(isolate, argument);
} else {
v8::TryCatch catcher(reinterpret_cast<v8::Isolate*>(isolate));
catcher.SetVerbose(false);
......@@ -670,20 +667,7 @@ Handle<String> MessageTemplate::FormatMessage(Isolate* isolate,
int template_index,
Handle<Object> arg) {
Factory* factory = isolate->factory();
Handle<String> result_string;
if (arg->IsString()) {
result_string = Handle<String>::cast(arg);
} else {
Handle<JSFunction> fun = isolate->no_side_effects_to_string_fun();
MaybeHandle<Object> maybe_result =
Execution::TryCall(isolate, fun, factory->undefined_value(), 1, &arg);
Handle<Object> result;
if (!maybe_result.ToHandle(&result) || !result->IsString()) {
return factory->InternalizeOneByteString(STATIC_CHAR_VECTOR("<error>"));
}
result_string = Handle<String>::cast(result);
}
Handle<String> result_string = Object::NoSideEffectsToString(isolate, arg);
MaybeHandle<String> maybe_result_string = MessageTemplate::FormatMessage(
template_index, result_string, factory->empty_string(),
factory->empty_string());
......@@ -749,7 +733,8 @@ MaybeHandle<String> MessageTemplate::FormatMessage(int template_index,
MaybeHandle<Object> ErrorUtils::Construct(
Isolate* isolate, Handle<JSFunction> target, Handle<Object> new_target,
Handle<Object> message, FrameSkipMode mode, bool suppress_detailed_trace) {
Handle<Object> message, FrameSkipMode mode, Handle<Object> caller,
bool suppress_detailed_trace) {
// 1. If NewTarget is undefined, let newTarget be the active function object,
// else let newTarget be NewTarget.
......@@ -786,15 +771,6 @@ MaybeHandle<Object> ErrorUtils::Construct(
Object);
}
// When we're passed a JSFunction as new target, we can skip frames until that
// specific function is seen instead of unconditionally skipping the first
// frame.
Handle<Object> caller;
if (mode == SKIP_FIRST && new_target->IsJSFunction()) {
mode = SKIP_UNTIL_SEEN;
caller = new_target;
}
// Capture a simple stack trace for the stack property.
RETURN_ON_EXCEPTION(isolate,
isolate->CaptureAndSetSimpleStackTrace(err, mode, caller),
......@@ -878,6 +854,49 @@ MaybeHandle<String> ErrorUtils::ToString(Isolate* isolate,
return result;
}
namespace {
Handle<String> FormatMessage(Isolate* isolate, int template_index,
Handle<Object> arg0, Handle<Object> arg1,
Handle<Object> arg2) {
Handle<String> arg0_str = Object::NoSideEffectsToString(isolate, arg0);
Handle<String> arg1_str = Object::NoSideEffectsToString(isolate, arg1);
Handle<String> arg2_str = Object::NoSideEffectsToString(isolate, arg2);
isolate->native_context()->IncrementErrorsThrown();
Handle<String> msg;
if (!MessageTemplate::FormatMessage(template_index, arg0_str, arg1_str,
arg2_str)
.ToHandle(&msg)) {
DCHECK(isolate->has_pending_exception());
isolate->clear_pending_exception();
return isolate->factory()->NewStringFromAsciiChecked("<error>");
}
return msg;
}
} // namespace
// static
MaybeHandle<Object> ErrorUtils::MakeGenericError(
Isolate* isolate, Handle<JSFunction> constructor, int template_index,
Handle<Object> arg0, Handle<Object> arg1, Handle<Object> arg2,
FrameSkipMode mode) {
// This function used to be implemented in JavaScript, and JSEntryStub clears
// any pending exceptions - so whenever we'd call this from C++, pending
// exceptions would be cleared. Preserve this behavior.
isolate->clear_pending_exception();
DCHECK(mode != SKIP_UNTIL_SEEN);
Handle<Object> no_caller;
Handle<String> msg = FormatMessage(isolate, template_index, arg0, arg1, arg2);
return ErrorUtils::Construct(isolate, constructor, constructor, msg, mode,
no_caller, false);
}
#define SET_CALLSITE_PROPERTY(target, key, value) \
RETURN_ON_EXCEPTION( \
isolate, JSObject::SetOwnPropertyIgnoreAttributes( \
......
......@@ -95,9 +95,15 @@ class ErrorUtils : public AllStatic {
public:
static MaybeHandle<Object> Construct(
Isolate* isolate, Handle<JSFunction> target, Handle<Object> new_target,
Handle<Object> message, FrameSkipMode mode, bool suppress_detailed_trace);
Handle<Object> message, FrameSkipMode mode, Handle<Object> caller,
bool suppress_detailed_trace);
static MaybeHandle<String> ToString(Isolate* isolate, Handle<Object> recv);
static MaybeHandle<Object> MakeGenericError(
Isolate* isolate, Handle<JSFunction> constructor, int template_index,
Handle<Object> arg0, Handle<Object> arg1, Handle<Object> arg2,
FrameSkipMode mode);
};
class CallSiteUtils : public AllStatic {
......
......@@ -219,6 +219,154 @@ MaybeHandle<String> Object::ToString(Isolate* isolate, Handle<Object> input) {
}
}
namespace {
bool IsErrorObject(Isolate* isolate, Handle<Object> object) {
if (!object->IsJSReceiver()) return false;
Handle<Symbol> symbol = isolate->factory()->stack_trace_symbol();
return JSReceiver::HasOwnProperty(Handle<JSReceiver>::cast(object), symbol)
.FromMaybe(false);
}
Handle<String> NoSideEffectsErrorToString(Isolate* isolate,
Handle<Object> input) {
Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(input);
Handle<Name> name_key = isolate->factory()->name_string();
Handle<Object> name = JSReceiver::GetDataProperty(receiver, name_key);
Handle<String> name_str = (name->IsUndefined(isolate))
? isolate->factory()->Error_string()
: Object::NoSideEffectsToString(isolate, name);
Handle<Name> msg_key = isolate->factory()->message_string();
Handle<Object> msg = JSReceiver::GetDataProperty(receiver, msg_key);
Handle<String> msg_str = (msg->IsUndefined(isolate))
? isolate->factory()->empty_string()
: Object::NoSideEffectsToString(isolate, msg);
if (name_str->length() == 0) return msg_str;
if (msg_str->length() == 0) return name_str;
IncrementalStringBuilder builder(isolate);
builder.AppendString(name_str);
builder.AppendCString(": ");
builder.AppendString(msg_str);
return builder.Finish().ToHandleChecked();
}
} // namespace
// static
Handle<String> Object::NoSideEffectsToString(Isolate* isolate,
Handle<Object> input) {
DisallowJavascriptExecution no_js(isolate);
if (input->IsString() || input->IsNumber() || input->IsOddball() ||
input->IsSimd128Value()) {
return Object::ToString(isolate, input).ToHandleChecked();
} else if (input->IsFunction()) {
// -- F u n c t i o n
Handle<String> fun_str;
if (input->IsJSBoundFunction()) {
fun_str = JSBoundFunction::ToString(Handle<JSBoundFunction>::cast(input));
} else {
DCHECK(input->IsJSFunction());
fun_str = JSFunction::ToString(Handle<JSFunction>::cast(input));
}
if (fun_str->length() > 128) {
IncrementalStringBuilder builder(isolate);
builder.AppendString(isolate->factory()->NewSubString(fun_str, 0, 111));
builder.AppendCString("...<omitted>...");
builder.AppendString(isolate->factory()->NewSubString(
fun_str, fun_str->length() - 2, fun_str->length()));
return builder.Finish().ToHandleChecked();
}
return fun_str;
} else if (input->IsSymbol()) {
// -- S y m b o l
Handle<Symbol> symbol = Handle<Symbol>::cast(input);
IncrementalStringBuilder builder(isolate);
builder.AppendCString("Symbol(");
if (symbol->name()->IsString()) {
builder.AppendString(handle(String::cast(symbol->name()), isolate));
}
builder.AppendCharacter(')');
return builder.Finish().ToHandleChecked();
} else if (input->IsJSReceiver()) {
// -- J S R e c e i v e r
Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(input);
Handle<Object> to_string = JSReceiver::GetDataProperty(
receiver, isolate->factory()->toString_string());
if (IsErrorObject(isolate, input) ||
*to_string == *isolate->error_to_string()) {
// When internally formatting error objects, use a side-effects-free
// version of Error.prototype.toString independent of the actually
// installed toString method.
return NoSideEffectsErrorToString(isolate, input);
} else if (*to_string == *isolate->object_to_string()) {
Handle<Object> ctor = JSReceiver::GetDataProperty(
receiver, isolate->factory()->constructor_string());
if (ctor->IsFunction()) {
Handle<String> ctor_name;
if (ctor->IsJSBoundFunction()) {
ctor_name = JSBoundFunction::GetName(
isolate, Handle<JSBoundFunction>::cast(ctor))
.ToHandleChecked();
} else if (ctor->IsJSFunction()) {
Handle<Object> ctor_name_obj =
JSFunction::GetName(isolate, Handle<JSFunction>::cast(ctor));
ctor_name = NoSideEffectsToString(isolate, ctor_name_obj);
}
if (ctor_name->length() != 0) {
IncrementalStringBuilder builder(isolate);
builder.AppendCString("#<");
builder.AppendString(ctor_name);
builder.AppendCString(">");
return builder.Finish().ToHandleChecked();
}
}
}
}
// At this point, input is either none of the above or a JSReceiver.
Handle<JSReceiver> receiver;
if (input->IsJSReceiver()) {
receiver = Handle<JSReceiver>::cast(input);
} else {
// This is the only case where Object::ToObject throws.
DCHECK(!input->IsSmi());
int constructor_function_index =
Handle<HeapObject>::cast(input)->map()->GetConstructorFunctionIndex();
if (constructor_function_index == Map::kNoConstructorFunctionIndex) {
return isolate->factory()->NewStringFromAsciiChecked("[object Unknown]");
}
receiver = Object::ToObject(isolate, input, isolate->native_context())
.ToHandleChecked();
}
Handle<String> builtin_tag = handle(receiver->class_name(), isolate);
Handle<Object> tag_obj = JSReceiver::GetDataProperty(
receiver, isolate->factory()->to_string_tag_symbol());
Handle<String> tag =
tag_obj->IsString() ? Handle<String>::cast(tag_obj) : builtin_tag;
IncrementalStringBuilder builder(isolate);
builder.AppendCString("[object ");
builder.AppendString(tag);
builder.AppendCString("]");
return builder.Finish().ToHandleChecked();
}
// static
MaybeHandle<Object> Object::ToLength(Isolate* isolate, Handle<Object> input) {
......
......@@ -1194,6 +1194,9 @@ class Object {
MUST_USE_RESULT static MaybeHandle<String> ToString(Isolate* isolate,
Handle<Object> input);
static Handle<String> NoSideEffectsToString(Isolate* isolate,
Handle<Object> input);
// ES6 section 7.1.14 ToPropertyKey
MUST_USE_RESULT static MaybeHandle<Object> ToPropertyKey(
Isolate* isolate, Handle<Object> value);
......
......@@ -384,19 +384,6 @@ RUNTIME_FUNCTION(Runtime_MessageGetScript) {
}
RUNTIME_FUNCTION(Runtime_FormatMessageString) {
HandleScope scope(isolate);
DCHECK(args.length() == 4);
CONVERT_INT32_ARG_CHECKED(template_index, 0);
CONVERT_ARG_HANDLE_CHECKED(String, arg0, 1);
CONVERT_ARG_HANDLE_CHECKED(String, arg1, 2);
CONVERT_ARG_HANDLE_CHECKED(String, arg2, 3);
isolate->native_context()->IncrementErrorsThrown();
RETURN_RESULT_OR_FAILURE(isolate, MessageTemplate::FormatMessage(
template_index, arg0, arg1, arg2));
}
RUNTIME_FUNCTION(Runtime_IS_VAR) {
UNREACHABLE(); // implemented as macro in the parser
return NULL;
......
......@@ -312,7 +312,6 @@ namespace internal {
F(AllocateSeqTwoByteString, 1, 1) \
F(MessageGetStartPosition, 1, 1) \
F(MessageGetScript, 1, 1) \
F(FormatMessageString, 4, 1) \
F(IS_VAR, 1, 1) \
F(ThrowConstructedNonConstructable, 1, 1) \
F(ThrowDerivedConstructorReturnedNonObject, 0, 1) \
......
......@@ -155,6 +155,7 @@
'test-lockers.cc',
'test-log.cc',
'test-mementos.cc',
'test-object.cc',
'test-parsing.cc',
'test-platform.cc',
'test-profile-generator.cc',
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/v8.h"
#include "test/cctest/cctest.h"
using namespace v8::internal;
static void CheckObject(Isolate* isolate, Handle<Object> obj,
const char* string) {
Object* print_string = *Object::NoSideEffectsToString(isolate, obj);
CHECK(String::cast(print_string)->IsUtf8EqualTo(CStrVector(string)));
}
static void CheckSmi(Isolate* isolate, int value, const char* string) {
Handle<Object> handle(Smi::FromInt(value), isolate);
CheckObject(isolate, handle, string);
}
static void CheckString(Isolate* isolate, const char* value,
const char* string) {
Handle<String> handle(isolate->factory()->NewStringFromAsciiChecked(value));
CheckObject(isolate, handle, string);
}
static void CheckNumber(Isolate* isolate, double value, const char* string) {
Handle<Object> number = isolate->factory()->NewNumber(value);
CHECK(number->IsNumber());
CheckObject(isolate, number, string);
}
static void CheckBoolean(Isolate* isolate, bool value, const char* string) {
CheckObject(isolate, value ? isolate->factory()->true_value()
: isolate->factory()->false_value(),
string);
}
TEST(NoSideEffectsToString) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
Factory* factory = isolate->factory();
HandleScope scope(isolate);
CheckString(isolate, "fisk hest", "fisk hest");
CheckNumber(isolate, 42.3, "42.3");
CheckSmi(isolate, 42, "42");
CheckBoolean(isolate, true, "true");
CheckBoolean(isolate, false, "false");
CheckBoolean(isolate, false, "false");
CheckObject(isolate, factory->undefined_value(), "undefined");
CheckObject(isolate, factory->null_value(), "null");
int lanes[] = {0, 1, 2, 3};
CheckObject(isolate, factory->NewInt32x4(lanes), "SIMD.Int32x4(0, 1, 2, 3)");
CheckObject(isolate, factory->error_to_string(), "[object Error]");
CheckObject(isolate, factory->stack_trace_symbol(),
"Symbol(stack_trace_symbol)");
CheckObject(isolate, factory->NewError(isolate->error_function(),
factory->empty_string()),
"Error");
CheckObject(isolate, factory->NewError(
isolate->error_function(),
factory->NewStringFromAsciiChecked("fisk hest")),
"Error: fisk hest");
CheckObject(isolate, factory->NewJSObject(isolate->object_function()),
"#<Object>");
}
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
assertThrows(function() { %FormatMessageString(-1, "", "", ""); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment