Commit ed698f3d authored by adamk's avatar adamk Committed by Commit bot

Rewrite Object.prototype.toString in C++

The main impetus is to improve performance when --harmony-tostring
is enabled, thanks to using a generic property load instead of a
megamorphic IC.

This also reduces duplication, as the API function
v8::Object::ObjectProtoToString can share the runtime implementation.

The only functional change in this patch is to drop an accidental difference
between the JS and API implementations: the arguments object should toString
as "[object Arguments]". The JS side was corrected in
https://code.google.com/p/v8/source/detail?r=3279, but the API version was
missed in that patch.

BUG=chromium:555127, v8:3502
LOG=n
CQ_INCLUDE_TRYBOTS=tryserver.chromium.linux:linux_chromium_rel_ng;tryserver.blink:linux_blink_rel

Review URL: https://codereview.chromium.org/1509533003

Cr-Commit-Position: refs/heads/master@{#32777}
parent 8bd39309
......@@ -3781,63 +3781,13 @@ Local<Array> v8::Object::GetOwnPropertyNames() {
MaybeLocal<String> v8::Object::ObjectProtoToString(Local<Context> context) {
auto self = Utils::OpenHandle(this);
auto isolate = self->GetIsolate();
auto v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
i::Handle<i::Object> name(self->class_name(), isolate);
i::Handle<i::Object> tag;
// Native implementation of Object.prototype.toString (v8natives.js):
// var c = %_ClassOf(this);
// if (c === 'Arguments') c = 'Object';
// return "[object " + c + "]";
if (!name->IsString()) {
return v8::String::NewFromUtf8(v8_isolate, "[object ]",
NewStringType::kNormal);
}
auto class_name = i::Handle<i::String>::cast(name);
if (i::String::Equals(class_name, isolate->factory()->Arguments_string())) {
return v8::String::NewFromUtf8(v8_isolate, "[object Object]",
NewStringType::kNormal);
}
if (internal::FLAG_harmony_tostring) {
PREPARE_FOR_EXECUTION(context, "v8::Object::ObjectProtoToString()", String);
auto toStringTag = isolate->factory()->to_string_tag_symbol();
has_pending_exception = !i::Runtime::GetObjectProperty(
isolate, self, toStringTag).ToHandle(&tag);
RETURN_ON_FAILED_EXECUTION(String);
if (tag->IsString()) {
class_name = Utils::OpenHandle(*handle_scope.Escape(
Utils::ToLocal(i::Handle<i::String>::cast(tag))));
}
}
const char* prefix = "[object ";
Local<String> str = Utils::ToLocal(class_name);
const char* postfix = "]";
int prefix_len = i::StrLength(prefix);
int str_len = str->Utf8Length();
int postfix_len = i::StrLength(postfix);
int buf_len = prefix_len + str_len + postfix_len;
i::ScopedVector<char> buf(buf_len);
// Write prefix.
char* ptr = buf.start();
i::MemCopy(ptr, prefix, prefix_len * v8::internal::kCharSize);
ptr += prefix_len;
// Write real content.
str->WriteUtf8(ptr, str_len);
ptr += str_len;
// Write postfix.
i::MemCopy(ptr, postfix, postfix_len * v8::internal::kCharSize);
// Copy the buffer into a heap-allocated string and return it.
return v8::String::NewFromUtf8(v8_isolate, buf.start(),
NewStringType::kNormal, buf_len);
PREPARE_FOR_EXECUTION(context, "v8::Object::ObjectProtoToString", String);
auto obj = Utils::OpenHandle(this);
Local<String> result;
has_pending_exception =
!ToLocal<String>(i::JSObject::ObjectProtoToString(isolate, obj), &result);
RETURN_ON_FAILED_EXECUTION(String);
RETURN_ESCAPED(result);
}
......
......@@ -1852,6 +1852,15 @@ void Bootstrapper::ExportFromRuntime(Isolate* isolate,
native_context->set_reflect_construct(*construct);
}
{
Handle<JSFunction> to_string = InstallFunction(
container, "object_to_string", JS_OBJECT_TYPE, JSObject::kHeaderSize,
MaybeHandle<JSObject>(), Builtins::kObjectProtoToString);
to_string->shared()->DontAdaptArguments();
to_string->shared()->set_length(0);
native_context->set_object_to_string(*to_string);
}
Handle<JSObject> iterator_prototype;
{
......
......@@ -1754,6 +1754,17 @@ BUILTIN(SymbolConstructor_ConstructStub) {
}
// ES6 19.1.3.6 Object.prototype.toString
BUILTIN(ObjectProtoToString) {
HandleScope scope(isolate);
Handle<Object> object = args.at<Object>(0);
Handle<String> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, JSObject::ObjectProtoToString(isolate, object));
return *result;
}
namespace {
// ES6 section 9.5.15 ProxyCreate (target, handler)
......
......@@ -67,6 +67,8 @@ inline bool operator&(BuiltinExtraArguments lhs, BuiltinExtraArguments rhs) {
\
V(DateToPrimitive, kNone) \
\
V(ObjectProtoToString, kNone) \
\
V(ProxyConstructor, kNone) \
V(ProxyConstructor_ConstructStub, kTarget) \
\
......
......@@ -282,6 +282,7 @@ namespace internal {
V(nan_string, "NaN") \
V(next_string, "next") \
V(null_string, "null") \
V(null_to_string, "[object Null]") \
V(number_string, "number") \
V(Number_string, "Number") \
V(object_string, "object") \
......@@ -319,6 +320,7 @@ namespace internal {
V(uint8x16_string, "uint8x16") \
V(Uint8x16_string, "Uint8x16") \
V(undefined_string, "undefined") \
V(undefined_to_string, "[object Undefined]") \
V(valueOf_string, "valueOf") \
V(value_string, "value") \
V(WeakMap_string, "WeakMap") \
......
......@@ -25,7 +25,7 @@ var ObjectDefineProperty;
var ObjectHasOwnProperty;
var ObjectIsFrozen;
var ObjectIsSealed;
var ObjectToString;
var ObjectToString = utils.ImportNow("object_to_string");
var ObserveBeginPerformSplice;
var ObserveEndPerformSplice;
var ObserveEnqueueSpliceRecord;
......@@ -43,7 +43,6 @@ utils.Import(function(from) {
ObjectHasOwnProperty = from.ObjectHasOwnProperty;
ObjectIsFrozen = from.ObjectIsFrozen;
ObjectIsSealed = from.ObjectIsSealed;
ObjectToString = from.ObjectToString;
ObserveBeginPerformSplice = from.ObserveBeginPerformSplice;
ObserveEndPerformSplice = from.ObserveEndPerformSplice;
ObserveEnqueueSpliceRecord = from.ObserveEnqueueSpliceRecord;
......
......@@ -35,7 +35,7 @@ var Int8x16ToString;
var InternalArray = utils.InternalArray;
var internalErrorSymbol = utils.ImportNow("internal_error_symbol");
var ObjectDefineProperty;
var ObjectToString;
var ObjectToString = utils.ImportNow("object_to_string");
var Script = utils.ImportNow("Script");
var stackTraceSymbol = utils.ImportNow("stack_trace_symbol");
var StringCharAt;
......@@ -58,7 +58,6 @@ utils.Import(function(from) {
Int32x4ToString = from.Int32x4ToString;
Int8x16ToString = from.Int8x16ToString;
ObjectDefineProperty = from.ObjectDefineProperty;
ObjectToString = from.ObjectToString;
StringCharAt = from.StringCharAt;
StringIndexOf = from.StringIndexOf;
StringSubstring = from.StringSubstring;
......
......@@ -220,6 +220,7 @@ function PostNatives(utils) {
"reflect_construct",
"regexp_flags_symbol",
"to_string_tag_symbol",
"object_to_string",
];
var filtered_exports = {};
......
......@@ -9,7 +9,6 @@
// ----------------------------------------------------------------------------
// Imports
var FLAG_harmony_tostring;
var GlobalArray = global.Array;
var GlobalBoolean = global.Boolean;
var GlobalFunction = global.Function;
......@@ -22,6 +21,7 @@ var MakeSyntaxError;
var MakeTypeError;
var MathAbs;
var NaN = %GetRootNaN();
var ObjectToString = utils.ImportNow("object_to_string");
var ObserveBeginPerformSplice;
var ObserveEndPerformSplice;
var ObserveEnqueueSpliceRecord;
......@@ -40,10 +40,6 @@ utils.Import(function(from) {
StringIndexOf = from.StringIndexOf;
});
utils.ImportFromExperimental(function(from) {
FLAG_harmony_tostring = from.FLAG_harmony_tostring;
});
// ----------------------------------------------------------------------------
......@@ -144,28 +140,6 @@ utils.InstallFunctions(global, DONT_ENUM, [
// ----------------------------------------------------------------------------
// Object
// ES6 19.1.3.6 Object.prototype.toString()
function ObjectToString() {
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;
// TODO(caitp): cannot wait to get rid of this flag :>
if (FLAG_harmony_tostring) {
tag = O[toStringTagSymbol];
if (!IS_STRING(tag)) {
tag = builtinTag;
}
} else {
tag = builtinTag;
}
return `[object ${tag}]`;
}
// ES6 19.1.3.5 Object.prototype.toLocaleString([reserved1 [,reserved2]])
function ObjectToLocaleString() {
CHECK_OBJECT_COERCIBLE(this, "Object.prototype.toLocaleString");
......@@ -1515,13 +1489,11 @@ utils.Export(function(to) {
to.ObjectIsFrozen = ObjectIsFrozen;
to.ObjectIsSealed = ObjectIsSealed;
to.ObjectKeys = ObjectKeys;
to.ObjectToString = ObjectToString;
});
%InstallToContext([
"global_eval_fun", GlobalEval,
"object_value_of", ObjectValueOf,
"object_to_string", ObjectToString,
]);
})
......@@ -16184,6 +16184,40 @@ int JSObject::GetOwnElementKeys(FixedArray* storage, PropertyFilter filter) {
}
MaybeHandle<String> JSObject::ObjectProtoToString(Isolate* isolate,
Handle<Object> object) {
if (object->IsUndefined()) return isolate->factory()->undefined_to_string();
if (object->IsNull()) return isolate->factory()->null_to_string();
Handle<JSReceiver> receiver;
CHECK(Object::ToObject(isolate, object).ToHandle(&receiver));
Handle<String> tag;
if (FLAG_harmony_tostring) {
Handle<Object> to_string_tag;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, to_string_tag,
GetProperty(receiver, isolate->factory()->to_string_tag_symbol()),
String);
if (to_string_tag->IsString()) {
tag = Handle<String>::cast(to_string_tag);
}
}
if (tag.is_null()) {
// TODO(adamk): class_name() is expensive, replace with instance type
// checks where possible.
tag = handle(receiver->class_name(), isolate);
}
IncrementalStringBuilder builder(isolate);
builder.AppendCString("[object ");
builder.AppendString(tag);
builder.AppendCharacter(']');
return builder.Finish();
}
const char* Symbol::PrivateSymbolToName() const {
Heap* heap = GetIsolate()->heap();
#define SYMBOL_CHECK_AND_PRINT(name) \
......
......@@ -2471,6 +2471,10 @@ class JSObject: public JSReceiver {
static bool AllCanRead(LookupIterator* it);
static bool AllCanWrite(LookupIterator* it);
// ES6 19.1.3.6 Object.prototype.toString
MUST_USE_RESULT static MaybeHandle<String> ObjectProtoToString(
Isolate* isolate, Handle<Object> object);
private:
friend class JSReceiver;
friend class Object;
......
......@@ -368,12 +368,6 @@ RUNTIME_FUNCTION(Runtime_IncrementStatsCounter) {
}
RUNTIME_FUNCTION(Runtime_HarmonyToString) {
// TODO(caitp): Delete this runtime method when removing --harmony-tostring
return isolate->heap()->ToBoolean(FLAG_harmony_tostring);
}
namespace {
bool ComputeLocation(Isolate* isolate, MessageLocation* target) {
......
......@@ -348,7 +348,6 @@ namespace internal {
F(CallSiteIsConstructorRT, 1, 1) \
F(IS_VAR, 1, 1) \
F(IncrementStatsCounter, 1, 1) \
F(HarmonyToString, 0, 1) \
F(ThrowConstructedNonConstructable, 1, 1) \
F(ThrowCalledNonCallable, 1, 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