diff options
Diffstat (limited to 'deps/v8/src/runtime/runtime-classes.cc')
-rw-r--r-- | deps/v8/src/runtime/runtime-classes.cc | 565 |
1 files changed, 474 insertions, 91 deletions
diff --git a/deps/v8/src/runtime/runtime-classes.cc b/deps/v8/src/runtime/runtime-classes.cc index 815501bcfa..37e647c7dd 100644 --- a/deps/v8/src/runtime/runtime-classes.cc +++ b/deps/v8/src/runtime/runtime-classes.cc @@ -13,6 +13,7 @@ #include "src/elements.h" #include "src/isolate-inl.h" #include "src/messages.h" +#include "src/objects/literal-objects-inl.h" #include "src/runtime/runtime.h" namespace v8 { @@ -104,10 +105,449 @@ RUNTIME_FUNCTION(Runtime_HomeObjectSymbol) { return isolate->heap()->home_object_symbol(); } -static MaybeHandle<Object> DefineClass(Isolate* isolate, - Handle<Object> super_class, - Handle<JSFunction> constructor, - int start_position, int end_position) { +namespace { + +template <typename Dictionary> +Handle<Name> KeyToName(Isolate* isolate, Handle<Object> key); + +template <> +Handle<Name> KeyToName<NameDictionary>(Isolate* isolate, Handle<Object> key) { + DCHECK(key->IsName()); + return Handle<Name>::cast(key); +} + +template <> +Handle<Name> KeyToName<NumberDictionary>(Isolate* isolate, Handle<Object> key) { + DCHECK(key->IsNumber()); + return isolate->factory()->NumberToString(key); +} + +inline void SetHomeObject(Isolate* isolate, JSFunction* method, + JSObject* home_object) { + if (method->shared()->needs_home_object()) { + const int kPropertyIndex = JSFunction::kMaybeHomeObjectDescriptorIndex; + CHECK_EQ(method->map()->instance_descriptors()->GetKey(kPropertyIndex), + isolate->heap()->home_object_symbol()); + + FieldIndex field_index = + FieldIndex::ForDescriptor(method->map(), kPropertyIndex); + method->RawFastPropertyAtPut(field_index, home_object); + } +} + +// Gets |index|'th argument which may be a class constructor object, a class +// prototype object or a class method. In the latter case the following +// post-processing may be required: +// 1) set [[HomeObject]] slot to given |home_object| value if the method's +// shared function info indicates that the method requires that; +// 2) set method's name to a concatenation of |name_prefix| and |key| if the +// method's shared function info indicates that method does not have a +// shared name. +template <typename Dictionary> +MaybeHandle<Object> GetMethodAndSetHomeObjectAndName( + Isolate* isolate, Arguments& args, Smi* index, Handle<JSObject> home_object, + Handle<String> name_prefix, Handle<Object> key) { + int int_index = Smi::ToInt(index); + + // Class constructor and prototype values do not require post processing. + if (int_index < ClassBoilerplate::kFirstDynamicArgumentIndex) { + return args.at<Object>(int_index); + } + + Handle<JSFunction> method = args.at<JSFunction>(int_index); + + SetHomeObject(isolate, *method, *home_object); + + if (!method->shared()->has_shared_name()) { + // TODO(ishell): method does not have a shared name at this point only if + // the key is a computed property name. However, the bytecode generator + // explicitly generates ToName bytecodes to ensure that the computed + // property name is properly converted to Name. So, we can actually be smart + // here and avoid converting Smi keys back to Name. + Handle<Name> name = KeyToName<Dictionary>(isolate, key); + if (!JSFunction::SetName(method, name, name_prefix)) { + return MaybeHandle<Object>(); + } + } + return method; +} + +// Gets |index|'th argument which may be a class constructor object, a class +// prototype object or a class method. In the latter case the following +// post-processing may be required: +// 1) set [[HomeObject]] slot to given |home_object| value if the method's +// shared function info indicates that the method requires that; +// This is a simplified version of GetMethodWithSharedNameAndSetHomeObject() +// function above that is used when it's guaranteed that the method has +// shared name. +Object* GetMethodWithSharedNameAndSetHomeObject(Isolate* isolate, + Arguments& args, Object* index, + JSObject* home_object) { + DisallowHeapAllocation no_gc; + int int_index = Smi::ToInt(index); + + // Class constructor and prototype values do not require post processing. + if (int_index < ClassBoilerplate::kFirstDynamicArgumentIndex) { + return args[int_index]; + } + + Handle<JSFunction> method = args.at<JSFunction>(int_index); + + SetHomeObject(isolate, *method, home_object); + + DCHECK(method->shared()->has_shared_name()); + return *method; +} + +template <typename Dictionary> +Handle<Dictionary> ShallowCopyDictionaryTemplate( + Isolate* isolate, Handle<Dictionary> dictionary_template) { + Handle<Map> dictionary_map(dictionary_template->map(), isolate); + Handle<Dictionary> dictionary = + Handle<Dictionary>::cast(isolate->factory()->CopyFixedArrayWithMap( + dictionary_template, dictionary_map)); + // Clone all AccessorPairs in the dictionary. + int capacity = dictionary->Capacity(); + for (int i = 0; i < capacity; i++) { + Object* value = dictionary->ValueAt(i); + if (value->IsAccessorPair()) { + Handle<AccessorPair> pair(AccessorPair::cast(value), isolate); + pair = AccessorPair::Copy(pair); + dictionary->ValueAtPut(i, *pair); + } + } + return dictionary; +} + +template <typename Dictionary> +bool SubstituteValues(Isolate* isolate, Handle<Dictionary> dictionary, + Handle<JSObject> receiver, Arguments& args, + bool* install_name_accessor = nullptr) { + Handle<Name> name_string = isolate->factory()->name_string(); + + // Replace all indices with proper methods. + int capacity = dictionary->Capacity(); + for (int i = 0; i < capacity; i++) { + Object* maybe_key = dictionary->KeyAt(i); + if (!Dictionary::IsKey(isolate, maybe_key)) continue; + if (install_name_accessor && *install_name_accessor && + (maybe_key == *name_string)) { + *install_name_accessor = false; + } + Handle<Object> key(maybe_key, isolate); + Handle<Object> value(dictionary->ValueAt(i), isolate); + if (value->IsAccessorPair()) { + Handle<AccessorPair> pair = Handle<AccessorPair>::cast(value); + Object* tmp = pair->getter(); + if (tmp->IsSmi()) { + Handle<Object> result; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, result, + GetMethodAndSetHomeObjectAndName<Dictionary>( + isolate, args, Smi::cast(tmp), receiver, + isolate->factory()->get_string(), key), + false); + pair->set_getter(*result); + } + tmp = pair->setter(); + if (tmp->IsSmi()) { + Handle<Object> result; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, result, + GetMethodAndSetHomeObjectAndName<Dictionary>( + isolate, args, Smi::cast(tmp), receiver, + isolate->factory()->set_string(), key), + false); + pair->set_setter(*result); + } + } else if (value->IsSmi()) { + Handle<Object> result; + ASSIGN_RETURN_ON_EXCEPTION_VALUE( + isolate, result, + GetMethodAndSetHomeObjectAndName<Dictionary>( + isolate, args, Smi::cast(*value), receiver, + isolate->factory()->empty_string(), key), + false); + dictionary->ValueAtPut(i, *result); + } + } + return true; +} + +bool AddDescriptorsByTemplate( + Isolate* isolate, Handle<Map> map, + Handle<DescriptorArray> descriptors_template, + Handle<NumberDictionary> elements_dictionary_template, + Handle<JSObject> receiver, Arguments& args) { + int nof_descriptors = descriptors_template->number_of_descriptors(); + + Handle<DescriptorArray> descriptors = + DescriptorArray::Allocate(isolate, nof_descriptors, 0); + + Handle<NumberDictionary> elements_dictionary = + *elements_dictionary_template == + isolate->heap()->empty_slow_element_dictionary() + ? elements_dictionary_template + : ShallowCopyDictionaryTemplate(isolate, + elements_dictionary_template); + + // Read values from |descriptors_template| and store possibly post-processed + // values into "instantiated" |descriptors| array. + for (int i = 0; i < nof_descriptors; i++) { + Object* value = descriptors_template->GetValue(i); + if (value->IsAccessorPair()) { + Handle<AccessorPair> pair = + AccessorPair::Copy(handle(AccessorPair::cast(value), isolate)); + value = *pair; + } + DisallowHeapAllocation no_gc; + Name* name = descriptors_template->GetKey(i); + DCHECK(name->IsUniqueName()); + PropertyDetails details = descriptors_template->GetDetails(i); + if (details.location() == kDescriptor) { + if (details.kind() == kData) { + if (value->IsSmi()) { + value = GetMethodWithSharedNameAndSetHomeObject(isolate, args, value, + *receiver); + } + details = + details.CopyWithRepresentation(value->OptimalRepresentation()); + + } else { + DCHECK_EQ(kAccessor, details.kind()); + if (value->IsAccessorPair()) { + AccessorPair* pair = AccessorPair::cast(value); + Object* tmp = pair->getter(); + if (tmp->IsSmi()) { + pair->set_getter(GetMethodWithSharedNameAndSetHomeObject( + isolate, args, tmp, *receiver)); + } + tmp = pair->setter(); + if (tmp->IsSmi()) { + pair->set_setter(GetMethodWithSharedNameAndSetHomeObject( + isolate, args, tmp, *receiver)); + } + } + } + } else { + DCHECK_EQ(kField, details.location()); + DCHECK(!details.representation().IsDouble()); + } + DCHECK(value->FitsRepresentation(details.representation())); + descriptors->Set(i, name, value, details); + } + + map->InitializeDescriptors(*descriptors, + LayoutDescriptor::FastPointerLayout()); + + if (elements_dictionary->NumberOfElements() > 0) { + if (!SubstituteValues<NumberDictionary>(isolate, elements_dictionary, + receiver, args)) { + return false; + } + map->set_elements_kind(DICTIONARY_ELEMENTS); + } + + // Atomically commit the changes. + receiver->synchronized_set_map(*map); + if (elements_dictionary->NumberOfElements() > 0) { + receiver->set_elements(*elements_dictionary); + } + return true; +} + +bool AddDescriptorsByTemplate( + Isolate* isolate, Handle<Map> map, + Handle<NameDictionary> properties_dictionary_template, + Handle<NumberDictionary> elements_dictionary_template, + Handle<FixedArray> computed_properties, Handle<JSObject> receiver, + bool install_name_accessor, Arguments& args) { + int computed_properties_length = computed_properties->length(); + + // Shallow-copy properties template. + Handle<NameDictionary> properties_dictionary = + ShallowCopyDictionaryTemplate(isolate, properties_dictionary_template); + Handle<NumberDictionary> elements_dictionary = + ShallowCopyDictionaryTemplate(isolate, elements_dictionary_template); + + typedef ClassBoilerplate::ValueKind ValueKind; + typedef ClassBoilerplate::ComputedEntryFlags ComputedEntryFlags; + + // Merge computed properties with properties and elements dictionary + // templates. + int i = 0; + while (i < computed_properties_length) { + int flags = Smi::ToInt(computed_properties->get(i++)); + + ValueKind value_kind = ComputedEntryFlags::ValueKindBits::decode(flags); + int key_index = ComputedEntryFlags::KeyIndexBits::decode(flags); + Object* value = Smi::FromInt(key_index + 1); // Value follows name. + + Handle<Object> key = args.at<Object>(key_index); + DCHECK(key->IsName()); + uint32_t element; + Handle<Name> name = Handle<Name>::cast(key); + if (name->AsArrayIndex(&element)) { + ClassBoilerplate::AddToElementsTemplate( + isolate, elements_dictionary, element, key_index, value_kind, value); + + } else { + name = isolate->factory()->InternalizeName(name); + ClassBoilerplate::AddToPropertiesTemplate( + isolate, properties_dictionary, name, key_index, value_kind, value); + } + } + + // Replace all indices with proper methods. + if (!SubstituteValues<NameDictionary>(isolate, properties_dictionary, + receiver, args, + &install_name_accessor)) { + return false; + } + if (install_name_accessor) { + PropertyAttributes attribs = + static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY); + PropertyDetails details(kAccessor, attribs, PropertyCellType::kNoCell); + Handle<NameDictionary> dict = NameDictionary::Add( + properties_dictionary, isolate->factory()->name_string(), + isolate->factory()->function_name_accessor(), details); + CHECK_EQ(*dict, *properties_dictionary); + } + + if (elements_dictionary->NumberOfElements() > 0) { + if (!SubstituteValues<NumberDictionary>(isolate, elements_dictionary, + receiver, args)) { + return false; + } + map->set_elements_kind(DICTIONARY_ELEMENTS); + } + + // Atomically commit the changes. + receiver->synchronized_set_map(*map); + receiver->set_raw_properties_or_hash(*properties_dictionary); + if (elements_dictionary->NumberOfElements() > 0) { + receiver->set_elements(*elements_dictionary); + } + return true; +} + +Handle<JSObject> CreateClassPrototype(Isolate* isolate) { + Factory* factory = isolate->factory(); + + const int kInobjectFields = 0; + + // Just use some JSObject map of certain size. + Handle<Map> map = factory->ObjectLiteralMapFromCache( + isolate->native_context(), kInobjectFields); + + return factory->NewJSObjectFromMap(map); +} + +bool InitClassPrototype(Isolate* isolate, + Handle<ClassBoilerplate> class_boilerplate, + Handle<JSObject> prototype, + Handle<Object> prototype_parent, + Handle<JSFunction> constructor, Arguments& args) { + Handle<Map> map(prototype->map(), isolate); + map = Map::CopyDropDescriptors(map); + map->set_is_prototype_map(true); + Map::SetPrototype(map, prototype_parent); + constructor->set_prototype_or_initial_map(*prototype); + map->SetConstructor(*constructor); + + Handle<FixedArray> computed_properties( + class_boilerplate->instance_computed_properties(), isolate); + Handle<NumberDictionary> elements_dictionary_template( + NumberDictionary::cast(class_boilerplate->instance_elements_template()), + isolate); + + Handle<Object> properties_template( + class_boilerplate->instance_properties_template(), isolate); + if (properties_template->IsDictionary()) { + Handle<NameDictionary> properties_dictionary_template = + Handle<NameDictionary>::cast(properties_template); + + map->set_dictionary_map(true); + map->set_migration_target(false); + map->set_may_have_interesting_symbols(true); + map->set_construction_counter(Map::kNoSlackTracking); + + // We care about name property only for class constructor. + const bool install_name_accessor = false; + + return AddDescriptorsByTemplate( + isolate, map, properties_dictionary_template, + elements_dictionary_template, computed_properties, prototype, + install_name_accessor, args); + } else { + Handle<DescriptorArray> descriptors_template = + Handle<DescriptorArray>::cast(properties_template); + + // The size of the prototype object is known at this point. + // So we can create it now and then add the rest instance methods to the + // map. + return AddDescriptorsByTemplate(isolate, map, descriptors_template, + elements_dictionary_template, prototype, + args); + } +} + +bool InitClassConstructor(Isolate* isolate, + Handle<ClassBoilerplate> class_boilerplate, + Handle<Object> constructor_parent, + Handle<JSFunction> constructor, Arguments& args) { + Handle<Map> map(constructor->map(), isolate); + map = Map::CopyDropDescriptors(map); + DCHECK(map->is_prototype_map()); + + if (!constructor_parent.is_null()) { + // Set map's prototype without enabling prototype setup mode for superclass + // because it does not make sense. + Map::SetPrototype(map, constructor_parent, false); + } + + Handle<NumberDictionary> elements_dictionary_template( + NumberDictionary::cast(class_boilerplate->static_elements_template()), + isolate); + Handle<FixedArray> computed_properties( + class_boilerplate->static_computed_properties(), isolate); + + Handle<Object> properties_template( + class_boilerplate->static_properties_template(), isolate); + + if (properties_template->IsDictionary()) { + Handle<NameDictionary> properties_dictionary_template = + Handle<NameDictionary>::cast(properties_template); + + map->set_dictionary_map(true); + map->InitializeDescriptors(isolate->heap()->empty_descriptor_array(), + LayoutDescriptor::FastPointerLayout()); + map->set_migration_target(false); + map->set_may_have_interesting_symbols(true); + map->set_construction_counter(Map::kNoSlackTracking); + + bool install_name_accessor = + class_boilerplate->install_class_name_accessor() != 0; + + return AddDescriptorsByTemplate( + isolate, map, properties_dictionary_template, + elements_dictionary_template, computed_properties, constructor, + install_name_accessor, args); + } else { + Handle<DescriptorArray> descriptors_template = + Handle<DescriptorArray>::cast(properties_template); + + return AddDescriptorsByTemplate(isolate, map, descriptors_template, + elements_dictionary_template, constructor, + args); + } +} + +MaybeHandle<Object> DefineClass(Isolate* isolate, + Handle<ClassBoilerplate> class_boilerplate, + Handle<Object> super_class, + Handle<JSFunction> constructor, + Arguments& args) { Handle<Object> prototype_parent; Handle<Object> constructor_parent; @@ -132,7 +572,10 @@ static MaybeHandle<Object> DefineClass(Isolate* isolate, prototype_parent), Object); } - constructor_parent = super_class; + // Create new handle to avoid |constructor_parent| corruption because of + // |super_class| handle value overwriting via storing to + // args[ClassBoilerplate::kPrototypeArgumentIndex] below. + constructor_parent = handle(*super_class, isolate); } else { THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kExtendsValueNotConstructor, @@ -141,95 +584,33 @@ static MaybeHandle<Object> DefineClass(Isolate* isolate, } } - Handle<Map> map = - isolate->factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); - map->set_is_prototype_map(true); - Map::SetPrototype(map, prototype_parent); - map->SetConstructor(*constructor); - Handle<JSObject> prototype = isolate->factory()->NewJSObjectFromMap(map); - - JSFunction::SetPrototype(constructor, prototype); - PropertyAttributes attribs = - static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY); - RETURN_ON_EXCEPTION(isolate, - JSObject::SetOwnPropertyIgnoreAttributes( - constructor, isolate->factory()->prototype_string(), - prototype, attribs), - Object); + Handle<JSObject> prototype = CreateClassPrototype(isolate); + DCHECK_EQ(*constructor, args[ClassBoilerplate::kConstructorArgumentIndex]); + args[ClassBoilerplate::kPrototypeArgumentIndex] = *prototype; - if (!constructor_parent.is_null()) { - MAYBE_RETURN_NULL(JSObject::SetPrototype(constructor, constructor_parent, - false, Object::THROW_ON_ERROR)); + if (!InitClassConstructor(isolate, class_boilerplate, constructor_parent, + constructor, args) || + !InitClassPrototype(isolate, class_boilerplate, prototype, + prototype_parent, constructor, args)) { + DCHECK(isolate->has_pending_exception()); + return MaybeHandle<Object>(); } - - JSObject::AddProperty(prototype, isolate->factory()->constructor_string(), - constructor, DONT_ENUM); - - // Install private properties that are used to construct the FunctionToString. - RETURN_ON_EXCEPTION( - isolate, - Object::SetProperty( - constructor, isolate->factory()->class_start_position_symbol(), - handle(Smi::FromInt(start_position), isolate), STRICT), - Object); - RETURN_ON_EXCEPTION( - isolate, Object::SetProperty( - constructor, isolate->factory()->class_end_position_symbol(), - handle(Smi::FromInt(end_position), isolate), STRICT), - Object); - - // Caller already has access to constructor, so return the prototype. return prototype; } +} // namespace RUNTIME_FUNCTION(Runtime_DefineClass) { HandleScope scope(isolate); - DCHECK_EQ(4, args.length()); - CONVERT_ARG_HANDLE_CHECKED(Object, super_class, 0); + DCHECK_LE(ClassBoilerplate::kFirstDynamicArgumentIndex, args.length()); + CONVERT_ARG_HANDLE_CHECKED(ClassBoilerplate, class_boilerplate, 0); CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 1); - CONVERT_SMI_ARG_CHECKED(start_position, 2); - CONVERT_SMI_ARG_CHECKED(end_position, 3); + CONVERT_ARG_HANDLE_CHECKED(Object, super_class, 2); + DCHECK_EQ(class_boilerplate->arguments_count(), args.length()); RETURN_RESULT_OR_FAILURE( - isolate, DefineClass(isolate, super_class, constructor, start_position, - end_position)); -} - -namespace { -void InstallClassNameAccessor(Isolate* isolate, Handle<JSObject> object) { - PropertyAttributes attrs = - static_cast<PropertyAttributes>(DONT_ENUM | READ_ONLY); - // Cannot fail since this should only be called when creating an object - // literal. - CHECK(!JSObject::SetAccessor( - object, Accessors::FunctionNameInfo(object->GetIsolate(), attrs)) - .is_null()); -} -} // anonymous namespace - -RUNTIME_FUNCTION(Runtime_InstallClassNameAccessor) { - HandleScope scope(isolate); - DCHECK_EQ(1, args.length()); - CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0); - InstallClassNameAccessor(isolate, object); - return *object; -} - -RUNTIME_FUNCTION(Runtime_InstallClassNameAccessorWithCheck) { - HandleScope scope(isolate); - DCHECK_EQ(1, args.length()); - CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0); - - // If a property named "name" is already defined, exit. - Handle<Name> key = isolate->factory()->name_string(); - if (JSObject::HasRealNamedProperty(object, key).FromMaybe(false)) { - return *object; - } - - // Define the "name" accessor. - InstallClassNameAccessor(isolate, object); - return *object; + isolate, + DefineClass(isolate, class_boilerplate, super_class, constructor, args)); } namespace { @@ -376,8 +757,9 @@ RUNTIME_FUNCTION(Runtime_StoreToSuper_Strict) { CONVERT_ARG_HANDLE_CHECKED(Name, name, 2); CONVERT_ARG_HANDLE_CHECKED(Object, value, 3); - RETURN_RESULT_OR_FAILURE(isolate, StoreToSuper(isolate, home_object, receiver, - name, value, STRICT)); + RETURN_RESULT_OR_FAILURE( + isolate, StoreToSuper(isolate, home_object, receiver, name, value, + LanguageMode::kStrict)); } @@ -389,8 +771,9 @@ RUNTIME_FUNCTION(Runtime_StoreToSuper_Sloppy) { CONVERT_ARG_HANDLE_CHECKED(Name, name, 2); CONVERT_ARG_HANDLE_CHECKED(Object, value, 3); - RETURN_RESULT_OR_FAILURE(isolate, StoreToSuper(isolate, home_object, receiver, - name, value, SLOPPY)); + RETURN_RESULT_OR_FAILURE( + isolate, StoreToSuper(isolate, home_object, receiver, name, value, + LanguageMode::kSloppy)); } static MaybeHandle<Object> StoreKeyedToSuper( @@ -424,8 +807,8 @@ RUNTIME_FUNCTION(Runtime_StoreKeyedToSuper_Strict) { CONVERT_ARG_HANDLE_CHECKED(Object, value, 3); RETURN_RESULT_OR_FAILURE( - isolate, - StoreKeyedToSuper(isolate, home_object, receiver, key, value, STRICT)); + isolate, StoreKeyedToSuper(isolate, home_object, receiver, key, value, + LanguageMode::kStrict)); } @@ -438,8 +821,8 @@ RUNTIME_FUNCTION(Runtime_StoreKeyedToSuper_Sloppy) { CONVERT_ARG_HANDLE_CHECKED(Object, value, 3); RETURN_RESULT_OR_FAILURE( - isolate, - StoreKeyedToSuper(isolate, home_object, receiver, key, value, SLOPPY)); + isolate, StoreKeyedToSuper(isolate, home_object, receiver, key, value, + LanguageMode::kSloppy)); } |