// 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
|