diff --git a/src/builtins/builtins-object-gen.cc b/src/builtins/builtins-object-gen.cc index 0604549558..5a0174de38 100644 --- a/src/builtins/builtins-object-gen.cc +++ b/src/builtins/builtins-object-gen.cc @@ -709,12 +709,12 @@ TF_BUILTIN(ObjectPrototypeIsPrototypeOf, ObjectBuiltinsAssembler) { } TF_BUILTIN(ObjectToString, ObjectBuiltinsAssembler) { - Label checkstringtag(this), if_arguments(this), if_array(this), - if_boolean(this), if_date(this), if_error(this), if_function(this), - if_number(this, Label::kDeferred), if_object(this), if_primitive(this), - if_proxy(this, Label::kDeferred), if_regexp(this), if_string(this), - if_symbol(this, Label::kDeferred), if_value(this), - if_bigint(this, Label::kDeferred); + Label checkstringtag(this), if_apiobject(this, Label::kDeferred), + if_arguments(this), if_array(this), if_boolean(this), if_date(this), + if_error(this), if_function(this), if_number(this, Label::kDeferred), + if_object(this), if_primitive(this), if_proxy(this, Label::kDeferred), + if_regexp(this), if_string(this), if_symbol(this, Label::kDeferred), + if_value(this), if_bigint(this, Label::kDeferred); TNode receiver = CAST(Parameter(Descriptor::kReceiver)); TNode context = CAST(Parameter(Descriptor::kContext)); @@ -740,8 +740,8 @@ TF_BUILTIN(ObjectToString, ObjectBuiltinsAssembler) { {JS_ARGUMENTS_OBJECT_TYPE, &if_arguments}, {JS_DATE_TYPE, &if_date}, {JS_BOUND_FUNCTION_TYPE, &if_function}, - {JS_API_OBJECT_TYPE, &if_object}, - {JS_SPECIAL_API_OBJECT_TYPE, &if_object}, + {JS_API_OBJECT_TYPE, &if_apiobject}, + {JS_SPECIAL_API_OBJECT_TYPE, &if_apiobject}, {JS_PROXY_TYPE, &if_proxy}, {JS_ERROR_TYPE, &if_error}, {JS_PRIMITIVE_WRAPPER_TYPE, &if_value}}; @@ -755,6 +755,25 @@ TF_BUILTIN(ObjectToString, ObjectBuiltinsAssembler) { Switch(receiver_instance_type, &if_object, case_values, case_labels, arraysize(case_values)); + BIND(&if_apiobject); + { + // Lookup the @@toStringTag property on the {receiver}. + TVARIABLE(Object, var_tag, + GetProperty(context, receiver, + isolate()->factory()->to_string_tag_symbol())); + Label if_tagisnotstring(this), if_tagisstring(this); + GotoIf(TaggedIsSmi(var_tag.value()), &if_tagisnotstring); + Branch(IsString(CAST(var_tag.value())), &if_tagisstring, + &if_tagisnotstring); + BIND(&if_tagisnotstring); + { + var_tag = CallRuntime(Runtime::kClassOf, context, receiver); + Goto(&if_tagisstring); + } + BIND(&if_tagisstring); + ReturnToStringFormat(context, CAST(var_tag.value())); + } + BIND(&if_arguments); { var_default = ArgumentsToStringConstant(); diff --git a/src/debug/debug-evaluate.cc b/src/debug/debug-evaluate.cc index d44abe8e41..bd1ac5df83 100644 --- a/src/debug/debug-evaluate.cc +++ b/src/debug/debug-evaluate.cc @@ -313,6 +313,7 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) { V(ArrayIncludes_Slow) \ V(ArrayIndexOf) \ V(ArrayIsArray) \ + V(ClassOf) \ V(GetFunctionName) \ V(GetOwnPropertyDescriptor) \ V(GlobalPrint) \ diff --git a/src/objects/js-objects.cc b/src/objects/js-objects.cc index ec2b8aa513..80549b308f 100644 --- a/src/objects/js-objects.cc +++ b/src/objects/js-objects.cc @@ -405,6 +405,19 @@ String JSReceiver::class_name() { if (IsJSWeakSet()) return roots.WeakSet_string(); if (IsJSGlobalProxy()) return roots.global_string(); + Object maybe_constructor = map().GetConstructor(); + if (maybe_constructor.IsJSFunction()) { + JSFunction constructor = JSFunction::cast(maybe_constructor); + if (constructor.shared().IsApiFunction()) { + maybe_constructor = constructor.shared().get_api_func_data(); + } + } + + if (maybe_constructor.IsFunctionTemplateInfo()) { + FunctionTemplateInfo info = FunctionTemplateInfo::cast(maybe_constructor); + if (info.class_name().IsString()) return String::cast(info.class_name()); + } + return roots.Object_string(); } @@ -428,6 +441,12 @@ std::pair, Handle> GetConstructorHelper( return std::make_pair(handle(constructor, isolate), handle(name, isolate)); } + } else if (maybe_constructor.IsFunctionTemplateInfo()) { + FunctionTemplateInfo info = FunctionTemplateInfo::cast(maybe_constructor); + if (info.class_name().IsString()) { + return std::make_pair(MaybeHandle(), + handle(String::cast(info.class_name()), isolate)); + } } } @@ -4491,13 +4510,14 @@ Maybe JSObject::SetPrototype(Handle object, NewTypeError(MessageTemplate::kImmutablePrototypeSet, object)); } - // From 6.1.7.3 Invariants of the Essential Internal Methods - // - // [[SetPrototypeOf]] ( V ) - // * ... - // * If target is non-extensible, [[SetPrototypeOf]] must return false, - // unless V is the SameValue as the target's observed [[GetPrototypeOf]] - // value. + // From 8.6.2 Object Internal Methods + // ... + // In addition, if [[Extensible]] is false the value of the [[Class]] and + // [[Prototype]] internal properties of the object may not be modified. + // ... + // Implementation specific extensions that modify [[Class]], [[Prototype]] + // or [[Extensible]] must not violate the invariants defined in the preceding + // paragraph. if (!all_extensible) { RETURN_FAILURE(isolate, should_throw, NewTypeError(MessageTemplate::kNonExtensibleProto, object)); diff --git a/src/objects/js-objects.h b/src/objects/js-objects.h index 40881c9138..418c12ac50 100644 --- a/src/objects/js-objects.h +++ b/src/objects/js-objects.h @@ -206,17 +206,15 @@ class JSReceiver : public HeapObject { V8_WARN_UNUSED_RESULT static Maybe IsExtensible( Handle object); - // Returns the class name. + // Returns the class name ([[Class]] property in the specification). V8_EXPORT_PRIVATE String class_name(); // Returns the constructor (the function that was used to instantiate the // object). static MaybeHandle GetConstructor(Handle receiver); - // Returns the constructor name (the (possibly inferred) name of the function - // that was used to instantiate the object), if any. If a FunctionTemplate is - // used to instantiate the object, the class_name of the FunctionTemplate is - // returned instead. + // Returns the constructor name (the name (possibly, inferred name) of the + // function that was used to instantiate the object). static Handle GetConstructorName(Handle receiver); V8_EXPORT_PRIVATE Handle GetCreationContext(); diff --git a/src/runtime/runtime-object.cc b/src/runtime/runtime-object.cc index 30e6ad7cbe..71338b1d39 100644 --- a/src/runtime/runtime-object.cc +++ b/src/runtime/runtime-object.cc @@ -989,6 +989,14 @@ RUNTIME_FUNCTION(Runtime_IsJSReceiver) { return isolate->heap()->ToBoolean(obj.IsJSReceiver()); } +RUNTIME_FUNCTION(Runtime_ClassOf) { + SealHandleScope shs(isolate); + DCHECK_EQ(1, args.length()); + CONVERT_ARG_CHECKED(Object, obj, 0); + if (!obj.IsJSReceiver()) return ReadOnlyRoots(isolate).null_value(); + return JSReceiver::cast(obj).class_name(); +} + RUNTIME_FUNCTION(Runtime_GetFunctionName) { HandleScope scope(isolate); DCHECK_EQ(1, args.length()); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 67cec15c27..688143c4f5 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -285,6 +285,7 @@ namespace internal { F(AddPrivateField, 3, 1) \ F(AddPrivateBrand, 3, 1) \ F(AllocateHeapNumber, 0, 1) \ + F(ClassOf, 1, 1) \ F(CollectTypeProfile, 3, 1) \ F(CompleteInobjectSlackTrackingForMap, 1, 1) \ I(CopyDataProperties, 2, 1) \ diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index faa963cb8b..518dd232c7 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -1075,23 +1075,20 @@ template static void TestFunctionTemplateAccessor(Constructor constructor, Accessor accessor) { LocalContext env; - v8::Isolate* isolate = env->GetIsolate(); - v8::HandleScope scope(isolate); + v8::HandleScope scope(env->GetIsolate()); Local fun_templ = - v8::FunctionTemplate::New(isolate, constructor); - fun_templ->PrototypeTemplate()->Set( - v8::Symbol::GetToStringTag(isolate), v8_str("funky"), - static_cast(v8::ReadOnly | v8::DontEnum)); + v8::FunctionTemplate::New(env->GetIsolate(), constructor); + fun_templ->SetClassName(v8_str("funky")); fun_templ->InstanceTemplate()->SetAccessor(v8_str("m"), accessor); - Local fun = fun_templ->GetFunction(env.local()).ToLocalChecked(); CHECK(env->Global()->Set(env.local(), v8_str("obj"), fun).FromJust()); - Local result = CompileRun("(new obj()).toString()"); + Local result = + v8_compile("(new obj()).toString()")->Run(env.local()).ToLocalChecked(); CHECK(v8_str("[object funky]")->Equals(env.local(), result).FromJust()); CompileRun("var obj_instance = new obj();"); - - Local