| // Copyright 2014 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/runtime/runtime-utils.h" | |
| #include <stdlib.h> | |
| #include <limits> | |
| #include "src/accessors.h" | |
| #include "src/arguments-inl.h" | |
| #include "src/debug/debug.h" | |
| #include "src/elements.h" | |
| #include "src/isolate-inl.h" | |
| #include "src/messages.h" | |
| #include "src/objects/hash-table-inl.h" | |
| #include "src/objects/literal-objects-inl.h" | |
| #include "src/runtime/runtime.h" | |
| namespace v8 { | |
| namespace internal { | |
| RUNTIME_FUNCTION(Runtime_ThrowUnsupportedSuperError) { | |
| HandleScope scope(isolate); | |
| DCHECK_EQ(0, args.length()); | |
| THROW_NEW_ERROR_RETURN_FAILURE( | |
| isolate, NewReferenceError(MessageTemplate::kUnsupportedSuper)); | |
| } | |
| RUNTIME_FUNCTION(Runtime_ThrowConstructorNonCallableError) { | |
| HandleScope scope(isolate); | |
| DCHECK_EQ(1, args.length()); | |
| CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 0); | |
| Handle<String> name(constructor->shared()->Name(), isolate); | |
| THROW_NEW_ERROR_RETURN_FAILURE( | |
| isolate, NewTypeError(MessageTemplate::kConstructorNonCallable, name)); | |
| } | |
| RUNTIME_FUNCTION(Runtime_ThrowStaticPrototypeError) { | |
| HandleScope scope(isolate); | |
| DCHECK_EQ(0, args.length()); | |
| THROW_NEW_ERROR_RETURN_FAILURE( | |
| isolate, NewTypeError(MessageTemplate::kStaticPrototype)); | |
| } | |
| RUNTIME_FUNCTION(Runtime_ThrowSuperAlreadyCalledError) { | |
| HandleScope scope(isolate); | |
| DCHECK_EQ(0, args.length()); | |
| THROW_NEW_ERROR_RETURN_FAILURE( | |
| isolate, NewReferenceError(MessageTemplate::kSuperAlreadyCalled)); | |
| } | |
| RUNTIME_FUNCTION(Runtime_ThrowSuperNotCalled) { | |
| HandleScope scope(isolate); | |
| DCHECK_EQ(0, args.length()); | |
| THROW_NEW_ERROR_RETURN_FAILURE( | |
| isolate, NewReferenceError(MessageTemplate::kSuperNotCalled)); | |
| } | |
| namespace { | |
| Object* ThrowNotSuperConstructor(Isolate* isolate, Handle<Object> constructor, | |
| Handle<JSFunction> function) { | |
| Handle<String> super_name; | |
| if (constructor->IsJSFunction()) { | |
| super_name = handle(Handle<JSFunction>::cast(constructor)->shared()->Name(), | |
| isolate); | |
| } else if (constructor->IsOddball()) { | |
| DCHECK(constructor->IsNull(isolate)); | |
| super_name = isolate->factory()->null_string(); | |
| } else { | |
| super_name = Object::NoSideEffectsToString(isolate, constructor); | |
| } | |
| // null constructor | |
| if (super_name->length() == 0) { | |
| super_name = isolate->factory()->null_string(); | |
| } | |
| Handle<String> function_name(function->shared()->Name(), isolate); | |
| // anonymous class | |
| if (function_name->length() == 0) { | |
| THROW_NEW_ERROR_RETURN_FAILURE( | |
| isolate, | |
| NewTypeError(MessageTemplate::kNotSuperConstructorAnonymousClass, | |
| super_name)); | |
| } | |
| THROW_NEW_ERROR_RETURN_FAILURE( | |
| isolate, NewTypeError(MessageTemplate::kNotSuperConstructor, super_name, | |
| function_name)); | |
| } | |
| } // namespace | |
| RUNTIME_FUNCTION(Runtime_ThrowNotSuperConstructor) { | |
| HandleScope scope(isolate); | |
| DCHECK_EQ(2, args.length()); | |
| CONVERT_ARG_HANDLE_CHECKED(Object, constructor, 0); | |
| CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 1); | |
| return ThrowNotSuperConstructor(isolate, constructor, function); | |
| } | |
| RUNTIME_FUNCTION(Runtime_HomeObjectSymbol) { | |
| DCHECK_EQ(0, args.length()); | |
| return ReadOnlyRoots(isolate).home_object_symbol(); | |
| } | |
| 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), | |
| ReadOnlyRoots(isolate).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()->HasSharedName()) { | |
| // 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()->HasSharedName()); | |
| 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(isolate, 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(); | |
| ReadOnlyRoots roots(isolate); | |
| for (int i = 0; i < capacity; i++) { | |
| Object* maybe_key = dictionary->KeyAt(i); | |
| if (!Dictionary::IsKey(roots, 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 == | |
| ReadOnlyRoots(isolate).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->GetStrongValue(i); | |
| if (value->IsAccessorPair()) { | |
| Handle<AccessorPair> pair = AccessorPair::Copy( | |
| isolate, 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, MaybeObject::FromObject(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( | |
| isolate, 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(isolate, map); | |
| map->set_is_prototype_map(true); | |
| Map::SetPrototype(isolate, 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->IsNameDictionary()) { | |
| Handle<NameDictionary> properties_dictionary_template = | |
| Handle<NameDictionary>::cast(properties_template); | |
| map->set_is_dictionary_map(true); | |
| map->set_is_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(isolate, 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(isolate, 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->IsNameDictionary()) { | |
| Handle<NameDictionary> properties_dictionary_template = | |
| Handle<NameDictionary>::cast(properties_template); | |
| map->set_is_dictionary_map(true); | |
| map->InitializeDescriptors(ReadOnlyRoots(isolate).empty_descriptor_array(), | |
| LayoutDescriptor::FastPointerLayout()); | |
| map->set_is_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; | |
| if (super_class->IsTheHole(isolate)) { | |
| prototype_parent = isolate->initial_object_prototype(); | |
| } else { | |
| if (super_class->IsNull(isolate)) { | |
| prototype_parent = isolate->factory()->null_value(); | |
| } else if (super_class->IsConstructor()) { | |
| DCHECK(!super_class->IsJSFunction() || | |
| !IsResumableFunction( | |
| Handle<JSFunction>::cast(super_class)->shared()->kind())); | |
| ASSIGN_RETURN_ON_EXCEPTION( | |
| isolate, prototype_parent, | |
| Runtime::GetObjectProperty(isolate, super_class, | |
| isolate->factory()->prototype_string()), | |
| Object); | |
| if (!prototype_parent->IsNull(isolate) && | |
| !prototype_parent->IsJSReceiver()) { | |
| THROW_NEW_ERROR( | |
| isolate, NewTypeError(MessageTemplate::kPrototypeParentNotAnObject, | |
| prototype_parent), | |
| Object); | |
| } | |
| // 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, | |
| super_class), | |
| Object); | |
| } | |
| } | |
| Handle<JSObject> prototype = CreateClassPrototype(isolate); | |
| DCHECK_EQ(*constructor, args[ClassBoilerplate::kConstructorArgumentIndex]); | |
| args[ClassBoilerplate::kPrototypeArgumentIndex] = *prototype; | |
| 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>(); | |
| } | |
| if (FLAG_trace_maps) { | |
| LOG(isolate, | |
| MapEvent("InitialMap", nullptr, constructor->map(), | |
| "init class constructor", constructor->shared()->DebugName())); | |
| LOG(isolate, MapEvent("InitialMap", nullptr, prototype->map(), | |
| "init class prototype")); | |
| } | |
| return prototype; | |
| } | |
| } // namespace | |
| RUNTIME_FUNCTION(Runtime_DefineClass) { | |
| HandleScope scope(isolate); | |
| DCHECK_LE(ClassBoilerplate::kFirstDynamicArgumentIndex, args.length()); | |
| CONVERT_ARG_HANDLE_CHECKED(ClassBoilerplate, class_boilerplate, 0); | |
| CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 1); | |
| CONVERT_ARG_HANDLE_CHECKED(Object, super_class, 2); | |
| DCHECK_EQ(class_boilerplate->arguments_count(), args.length()); | |
| RETURN_RESULT_OR_FAILURE( | |
| isolate, | |
| DefineClass(isolate, class_boilerplate, super_class, constructor, args)); | |
| } | |
| namespace { | |
| enum class SuperMode { kLoad, kStore }; | |
| MaybeHandle<JSReceiver> GetSuperHolder( | |
| Isolate* isolate, Handle<Object> receiver, Handle<JSObject> home_object, | |
| SuperMode mode, MaybeHandle<Name> maybe_name, uint32_t index) { | |
| if (home_object->IsAccessCheckNeeded() && | |
| !isolate->MayAccess(handle(isolate->context(), isolate), home_object)) { | |
| isolate->ReportFailedAccessCheck(home_object); | |
| RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, JSReceiver); | |
| } | |
| PrototypeIterator iter(isolate, home_object); | |
| Handle<Object> proto = PrototypeIterator::GetCurrent(iter); | |
| if (!proto->IsJSReceiver()) { | |
| MessageTemplate::Template message = | |
| mode == SuperMode::kLoad ? MessageTemplate::kNonObjectPropertyLoad | |
| : MessageTemplate::kNonObjectPropertyStore; | |
| Handle<Name> name; | |
| if (!maybe_name.ToHandle(&name)) { | |
| name = isolate->factory()->Uint32ToString(index); | |
| } | |
| THROW_NEW_ERROR(isolate, NewTypeError(message, name, proto), JSReceiver); | |
| } | |
| return Handle<JSReceiver>::cast(proto); | |
| } | |
| MaybeHandle<Object> LoadFromSuper(Isolate* isolate, Handle<Object> receiver, | |
| Handle<JSObject> home_object, | |
| Handle<Name> name) { | |
| Handle<JSReceiver> holder; | |
| ASSIGN_RETURN_ON_EXCEPTION( | |
| isolate, holder, | |
| GetSuperHolder(isolate, receiver, home_object, SuperMode::kLoad, name, 0), | |
| Object); | |
| LookupIterator it(receiver, name, holder); | |
| Handle<Object> result; | |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, result, Object::GetProperty(&it), Object); | |
| return result; | |
| } | |
| MaybeHandle<Object> LoadElementFromSuper(Isolate* isolate, | |
| Handle<Object> receiver, | |
| Handle<JSObject> home_object, | |
| uint32_t index) { | |
| Handle<JSReceiver> holder; | |
| ASSIGN_RETURN_ON_EXCEPTION( | |
| isolate, holder, | |
| GetSuperHolder(isolate, receiver, home_object, SuperMode::kLoad, | |
| MaybeHandle<Name>(), index), | |
| Object); | |
| LookupIterator it(isolate, receiver, index, holder); | |
| Handle<Object> result; | |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, result, Object::GetProperty(&it), Object); | |
| return result; | |
| } | |
| } // anonymous namespace | |
| RUNTIME_FUNCTION(Runtime_LoadFromSuper) { | |
| HandleScope scope(isolate); | |
| DCHECK_EQ(3, args.length()); | |
| CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0); | |
| CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1); | |
| CONVERT_ARG_HANDLE_CHECKED(Name, name, 2); | |
| RETURN_RESULT_OR_FAILURE(isolate, | |
| LoadFromSuper(isolate, receiver, home_object, name)); | |
| } | |
| RUNTIME_FUNCTION(Runtime_LoadKeyedFromSuper) { | |
| HandleScope scope(isolate); | |
| DCHECK_EQ(3, args.length()); | |
| CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0); | |
| CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1); | |
| CONVERT_ARG_HANDLE_CHECKED(Object, key, 2); | |
| uint32_t index = 0; | |
| if (key->ToArrayIndex(&index)) { | |
| RETURN_RESULT_OR_FAILURE( | |
| isolate, LoadElementFromSuper(isolate, receiver, home_object, index)); | |
| } | |
| Handle<Name> name; | |
| ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name, | |
| Object::ToName(isolate, key)); | |
| // TODO(verwaest): Unify using LookupIterator. | |
| if (name->AsArrayIndex(&index)) { | |
| RETURN_RESULT_OR_FAILURE( | |
| isolate, LoadElementFromSuper(isolate, receiver, home_object, index)); | |
| } | |
| RETURN_RESULT_OR_FAILURE(isolate, | |
| LoadFromSuper(isolate, receiver, home_object, name)); | |
| } | |
| namespace { | |
| MaybeHandle<Object> StoreToSuper(Isolate* isolate, Handle<JSObject> home_object, | |
| Handle<Object> receiver, Handle<Name> name, | |
| Handle<Object> value, | |
| LanguageMode language_mode) { | |
| Handle<JSReceiver> holder; | |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, holder, | |
| GetSuperHolder(isolate, receiver, home_object, | |
| SuperMode::kStore, name, 0), | |
| Object); | |
| LookupIterator it(receiver, name, holder); | |
| MAYBE_RETURN(Object::SetSuperProperty(&it, value, language_mode, | |
| Object::CERTAINLY_NOT_STORE_FROM_KEYED), | |
| MaybeHandle<Object>()); | |
| return value; | |
| } | |
| MaybeHandle<Object> StoreElementToSuper(Isolate* isolate, | |
| Handle<JSObject> home_object, | |
| Handle<Object> receiver, uint32_t index, | |
| Handle<Object> value, | |
| LanguageMode language_mode) { | |
| Handle<JSReceiver> holder; | |
| ASSIGN_RETURN_ON_EXCEPTION( | |
| isolate, holder, | |
| GetSuperHolder(isolate, receiver, home_object, SuperMode::kStore, | |
| MaybeHandle<Name>(), index), | |
| Object); | |
| LookupIterator it(isolate, receiver, index, holder); | |
| MAYBE_RETURN(Object::SetSuperProperty(&it, value, language_mode, | |
| Object::MAY_BE_STORE_FROM_KEYED), | |
| MaybeHandle<Object>()); | |
| return value; | |
| } | |
| } // anonymous namespace | |
| RUNTIME_FUNCTION(Runtime_StoreToSuper_Strict) { | |
| HandleScope scope(isolate); | |
| DCHECK_EQ(4, args.length()); | |
| CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0); | |
| CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1); | |
| 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, | |
| LanguageMode::kStrict)); | |
| } | |
| RUNTIME_FUNCTION(Runtime_StoreToSuper_Sloppy) { | |
| HandleScope scope(isolate); | |
| DCHECK_EQ(4, args.length()); | |
| CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0); | |
| CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1); | |
| 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, | |
| LanguageMode::kSloppy)); | |
| } | |
| static MaybeHandle<Object> StoreKeyedToSuper( | |
| Isolate* isolate, Handle<JSObject> home_object, Handle<Object> receiver, | |
| Handle<Object> key, Handle<Object> value, LanguageMode language_mode) { | |
| uint32_t index = 0; | |
| if (key->ToArrayIndex(&index)) { | |
| return StoreElementToSuper(isolate, home_object, receiver, index, value, | |
| language_mode); | |
| } | |
| Handle<Name> name; | |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, name, Object::ToName(isolate, key), | |
| Object); | |
| // TODO(verwaest): Unify using LookupIterator. | |
| if (name->AsArrayIndex(&index)) { | |
| return StoreElementToSuper(isolate, home_object, receiver, index, value, | |
| language_mode); | |
| } | |
| return StoreToSuper(isolate, home_object, receiver, name, value, | |
| language_mode); | |
| } | |
| RUNTIME_FUNCTION(Runtime_StoreKeyedToSuper_Strict) { | |
| HandleScope scope(isolate); | |
| DCHECK_EQ(4, args.length()); | |
| CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0); | |
| CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1); | |
| CONVERT_ARG_HANDLE_CHECKED(Object, key, 2); | |
| CONVERT_ARG_HANDLE_CHECKED(Object, value, 3); | |
| RETURN_RESULT_OR_FAILURE( | |
| isolate, StoreKeyedToSuper(isolate, home_object, receiver, key, value, | |
| LanguageMode::kStrict)); | |
| } | |
| RUNTIME_FUNCTION(Runtime_StoreKeyedToSuper_Sloppy) { | |
| HandleScope scope(isolate); | |
| DCHECK_EQ(4, args.length()); | |
| CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0); | |
| CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1); | |
| CONVERT_ARG_HANDLE_CHECKED(Object, key, 2); | |
| CONVERT_ARG_HANDLE_CHECKED(Object, value, 3); | |
| RETURN_RESULT_OR_FAILURE( | |
| isolate, StoreKeyedToSuper(isolate, home_object, receiver, key, value, | |
| LanguageMode::kSloppy)); | |
| } | |
| } // namespace internal | |
| } // namespace v8 |