| // 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/arguments.h" |
| #include "src/debug/debug.h" |
| #include "src/frames-inl.h" |
| #include "src/isolate-inl.h" |
| #include "src/messages.h" |
| #include "src/runtime/runtime.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| |
| RUNTIME_FUNCTION(Runtime_ThrowNonMethodError) { |
| HandleScope scope(isolate); |
| DCHECK(args.length() == 0); |
| THROW_NEW_ERROR_RETURN_FAILURE( |
| isolate, NewReferenceError(MessageTemplate::kNonMethod)); |
| } |
| |
| |
| RUNTIME_FUNCTION(Runtime_ThrowUnsupportedSuperError) { |
| HandleScope scope(isolate); |
| DCHECK(args.length() == 0); |
| THROW_NEW_ERROR_RETURN_FAILURE( |
| isolate, NewReferenceError(MessageTemplate::kUnsupportedSuper)); |
| } |
| |
| |
| RUNTIME_FUNCTION(Runtime_ThrowConstructorNonCallableError) { |
| HandleScope scope(isolate); |
| DCHECK(args.length() == 0); |
| THROW_NEW_ERROR_RETURN_FAILURE( |
| isolate, NewTypeError(MessageTemplate::kConstructorNonCallable)); |
| } |
| |
| |
| RUNTIME_FUNCTION(Runtime_ThrowArrayNotSubclassableError) { |
| HandleScope scope(isolate); |
| DCHECK(args.length() == 0); |
| THROW_NEW_ERROR_RETURN_FAILURE( |
| isolate, NewTypeError(MessageTemplate::kArrayNotSubclassable)); |
| } |
| |
| |
| static Object* ThrowStaticPrototypeError(Isolate* isolate) { |
| THROW_NEW_ERROR_RETURN_FAILURE( |
| isolate, NewTypeError(MessageTemplate::kStaticPrototype)); |
| } |
| |
| |
| RUNTIME_FUNCTION(Runtime_ThrowStaticPrototypeError) { |
| HandleScope scope(isolate); |
| DCHECK(args.length() == 0); |
| return ThrowStaticPrototypeError(isolate); |
| } |
| |
| |
| RUNTIME_FUNCTION(Runtime_ThrowIfStaticPrototype) { |
| HandleScope scope(isolate); |
| DCHECK(args.length() == 1); |
| CONVERT_ARG_HANDLE_CHECKED(Name, name, 0); |
| if (Name::Equals(name, isolate->factory()->prototype_string())) { |
| return ThrowStaticPrototypeError(isolate); |
| } |
| return *name; |
| } |
| |
| |
| RUNTIME_FUNCTION(Runtime_HomeObjectSymbol) { |
| DCHECK(args.length() == 0); |
| return isolate->heap()->home_object_symbol(); |
| } |
| |
| |
| static MaybeHandle<Object> DefineClass(Isolate* isolate, Handle<Object> name, |
| Handle<Object> super_class, |
| Handle<JSFunction> constructor, |
| int start_position, int end_position) { |
| Handle<Object> prototype_parent; |
| Handle<Object> constructor_parent; |
| |
| if (super_class->IsTheHole()) { |
| prototype_parent = isolate->initial_object_prototype(); |
| } else { |
| if (super_class->IsNull()) { |
| prototype_parent = isolate->factory()->null_value(); |
| } else if (super_class->IsConstructor()) { |
| if (super_class->IsJSFunction() && |
| Handle<JSFunction>::cast(super_class)->shared()->is_generator()) { |
| THROW_NEW_ERROR( |
| isolate, |
| NewTypeError(MessageTemplate::kExtendsValueGenerator, super_class), |
| Object); |
| } |
| ASSIGN_RETURN_ON_EXCEPTION( |
| isolate, prototype_parent, |
| Runtime::GetObjectProperty(isolate, super_class, |
| isolate->factory()->prototype_string(), |
| SLOPPY), |
| Object); |
| if (!prototype_parent->IsNull() && !prototype_parent->IsSpecObject()) { |
| THROW_NEW_ERROR( |
| isolate, NewTypeError(MessageTemplate::kPrototypeParentNotAnObject, |
| prototype_parent), |
| Object); |
| } |
| constructor_parent = super_class; |
| } else { |
| THROW_NEW_ERROR( |
| isolate, |
| NewTypeError(MessageTemplate::kExtendsValueNotFunction, super_class), |
| Object); |
| } |
| } |
| |
| Handle<Map> map = |
| isolate->factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); |
| map->set_is_prototype_map(true); |
| if (constructor->map()->is_strong()) { |
| map->set_is_strong(); |
| if (super_class->IsNull()) { |
| // Strong class is not permitted to extend null. |
| THROW_NEW_ERROR(isolate, NewTypeError(MessageTemplate::kStrongExtendNull), |
| Object); |
| } |
| } |
| Map::SetPrototype(map, prototype_parent); |
| map->SetConstructor(*constructor); |
| Handle<JSObject> prototype = isolate->factory()->NewJSObjectFromMap(map); |
| |
| Handle<String> name_string = name->IsString() |
| ? Handle<String>::cast(name) |
| : isolate->factory()->empty_string(); |
| constructor->shared()->set_name(*name_string); |
| |
| if (!super_class->IsTheHole()) { |
| Handle<Code> stub(isolate->builtins()->JSConstructStubForDerived()); |
| constructor->shared()->set_construct_stub(*stub); |
| } |
| |
| 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); |
| |
| // TODO(arv): Only do this conditionally. |
| Handle<Symbol> home_object_symbol(isolate->heap()->home_object_symbol()); |
| RETURN_ON_EXCEPTION( |
| isolate, JSObject::SetOwnPropertyIgnoreAttributes( |
| constructor, home_object_symbol, prototype, DONT_ENUM), |
| Object); |
| |
| if (!constructor_parent.is_null()) { |
| MAYBE_RETURN_NULL(JSObject::SetPrototype(constructor, constructor_parent, |
| false, Object::THROW_ON_ERROR)); |
| } |
| |
| 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); |
| |
| return constructor; |
| } |
| |
| |
| RUNTIME_FUNCTION(Runtime_DefineClass) { |
| HandleScope scope(isolate); |
| DCHECK(args.length() == 5); |
| CONVERT_ARG_HANDLE_CHECKED(Object, name, 0); |
| CONVERT_ARG_HANDLE_CHECKED(Object, super_class, 1); |
| CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 2); |
| CONVERT_SMI_ARG_CHECKED(start_position, 3); |
| CONVERT_SMI_ARG_CHECKED(end_position, 4); |
| |
| Handle<Object> result; |
| ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
| isolate, result, DefineClass(isolate, name, super_class, constructor, |
| start_position, end_position)); |
| return *result; |
| } |
| |
| |
| RUNTIME_FUNCTION(Runtime_DefineClassMethod) { |
| HandleScope scope(isolate); |
| DCHECK(args.length() == 3); |
| CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0); |
| CONVERT_ARG_HANDLE_CHECKED(Name, name, 1); |
| CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 2); |
| |
| RETURN_FAILURE_ON_EXCEPTION(isolate, |
| JSObject::DefinePropertyOrElementIgnoreAttributes( |
| object, name, function, DONT_ENUM)); |
| return isolate->heap()->undefined_value(); |
| } |
| |
| |
| RUNTIME_FUNCTION(Runtime_FinalizeClassDefinition) { |
| HandleScope scope(isolate); |
| DCHECK(args.length() == 2); |
| CONVERT_ARG_HANDLE_CHECKED(JSObject, constructor, 0); |
| CONVERT_ARG_HANDLE_CHECKED(JSObject, prototype, 1); |
| |
| JSObject::MigrateSlowToFast(constructor, 0, "RuntimeToFastProperties"); |
| |
| if (constructor->map()->is_strong()) { |
| DCHECK(prototype->map()->is_strong()); |
| RETURN_FAILURE_ON_EXCEPTION(isolate, JSObject::Freeze(prototype)); |
| Handle<Object> result; |
| ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, |
| JSObject::Freeze(constructor)); |
| return *result; |
| } |
| return *constructor; |
| } |
| |
| |
| RUNTIME_FUNCTION(Runtime_ClassGetSourceCode) { |
| HandleScope shs(isolate); |
| DCHECK(args.length() == 1); |
| CONVERT_ARG_HANDLE_CHECKED(JSFunction, fun, 0); |
| |
| Handle<Symbol> start_position_symbol( |
| isolate->heap()->class_start_position_symbol()); |
| Handle<Object> start_position = |
| JSReceiver::GetDataProperty(fun, start_position_symbol); |
| if (!start_position->IsSmi()) return isolate->heap()->undefined_value(); |
| |
| Handle<Symbol> end_position_symbol( |
| isolate->heap()->class_end_position_symbol()); |
| Handle<Object> end_position = |
| JSReceiver::GetDataProperty(fun, end_position_symbol); |
| CHECK(end_position->IsSmi()); |
| |
| Handle<String> source( |
| String::cast(Script::cast(fun->shared()->script())->source())); |
| return *isolate->factory()->NewSubString( |
| source, Handle<Smi>::cast(start_position)->value(), |
| Handle<Smi>::cast(end_position)->value()); |
| } |
| |
| |
| static MaybeHandle<Object> LoadFromSuper(Isolate* isolate, |
| Handle<Object> receiver, |
| Handle<JSObject> home_object, |
| Handle<Name> name, |
| LanguageMode language_mode) { |
| if (home_object->IsAccessCheckNeeded() && |
| !isolate->MayAccess(handle(isolate->context()), home_object)) { |
| isolate->ReportFailedAccessCheck(home_object); |
| RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| } |
| |
| PrototypeIterator iter(isolate, home_object); |
| Handle<Object> proto = PrototypeIterator::GetCurrent(iter); |
| if (!proto->IsJSReceiver()) { |
| return Object::ReadAbsentProperty(isolate, proto, name, language_mode); |
| } |
| |
| LookupIterator it(receiver, name, Handle<JSReceiver>::cast(proto)); |
| Handle<Object> result; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, result, |
| Object::GetProperty(&it, language_mode), Object); |
| return result; |
| } |
| |
| |
| static MaybeHandle<Object> LoadElementFromSuper(Isolate* isolate, |
| Handle<Object> receiver, |
| Handle<JSObject> home_object, |
| uint32_t index, |
| LanguageMode language_mode) { |
| if (home_object->IsAccessCheckNeeded() && |
| !isolate->MayAccess(handle(isolate->context()), home_object)) { |
| isolate->ReportFailedAccessCheck(home_object); |
| RETURN_EXCEPTION_IF_SCHEDULED_EXCEPTION(isolate, Object); |
| } |
| |
| PrototypeIterator iter(isolate, home_object); |
| Handle<Object> proto = PrototypeIterator::GetCurrent(iter); |
| if (!proto->IsJSReceiver()) { |
| Handle<Object> name = isolate->factory()->NewNumberFromUint(index); |
| return Object::ReadAbsentProperty(isolate, proto, name, language_mode); |
| } |
| |
| LookupIterator it(isolate, receiver, index, Handle<JSReceiver>::cast(proto)); |
| Handle<Object> result; |
| ASSIGN_RETURN_ON_EXCEPTION(isolate, result, |
| Object::GetProperty(&it, language_mode), Object); |
| return result; |
| } |
| |
| |
| // TODO(conradw): It would be more efficient to have a separate runtime function |
| // for strong mode. |
| RUNTIME_FUNCTION(Runtime_LoadFromSuper) { |
| HandleScope scope(isolate); |
| DCHECK(args.length() == 4); |
| CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0); |
| CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1); |
| CONVERT_ARG_HANDLE_CHECKED(Name, name, 2); |
| CONVERT_LANGUAGE_MODE_ARG_CHECKED(language_mode, 3); |
| |
| Handle<Object> result; |
| ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
| isolate, result, |
| LoadFromSuper(isolate, receiver, home_object, name, language_mode)); |
| return *result; |
| } |
| |
| |
| RUNTIME_FUNCTION(Runtime_LoadKeyedFromSuper) { |
| HandleScope scope(isolate); |
| DCHECK(args.length() == 4); |
| CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 0); |
| CONVERT_ARG_HANDLE_CHECKED(JSObject, home_object, 1); |
| CONVERT_ARG_HANDLE_CHECKED(Object, key, 2); |
| CONVERT_LANGUAGE_MODE_ARG_CHECKED(language_mode, 3); |
| |
| uint32_t index = 0; |
| Handle<Object> result; |
| |
| if (key->ToArrayIndex(&index)) { |
| ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
| isolate, result, LoadElementFromSuper(isolate, receiver, home_object, |
| index, language_mode)); |
| return *result; |
| } |
| |
| Handle<Name> name; |
| ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name, |
| Object::ToName(isolate, key)); |
| // TODO(verwaest): Unify using LookupIterator. |
| if (name->AsArrayIndex(&index)) { |
| ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
| isolate, result, LoadElementFromSuper(isolate, receiver, home_object, |
| index, language_mode)); |
| return *result; |
| } |
| ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
| isolate, result, |
| LoadFromSuper(isolate, receiver, home_object, name, language_mode)); |
| return *result; |
| } |
| |
| |
| static Object* StoreToSuper(Isolate* isolate, Handle<JSObject> home_object, |
| Handle<Object> receiver, Handle<Name> name, |
| Handle<Object> value, LanguageMode language_mode) { |
| if (home_object->IsAccessCheckNeeded() && |
| !isolate->MayAccess(handle(isolate->context()), home_object)) { |
| isolate->ReportFailedAccessCheck(home_object); |
| RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); |
| } |
| |
| PrototypeIterator iter(isolate, home_object); |
| Handle<Object> proto = PrototypeIterator::GetCurrent(iter); |
| if (!proto->IsJSReceiver()) return isolate->heap()->undefined_value(); |
| |
| LookupIterator it(receiver, name, Handle<JSReceiver>::cast(proto)); |
| Handle<Object> result; |
| ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
| isolate, result, |
| Object::SetSuperProperty(&it, value, language_mode, |
| Object::CERTAINLY_NOT_STORE_FROM_KEYED)); |
| return *result; |
| } |
| |
| |
| static Object* StoreElementToSuper(Isolate* isolate, |
| Handle<JSObject> home_object, |
| Handle<Object> receiver, uint32_t index, |
| Handle<Object> value, |
| LanguageMode language_mode) { |
| if (home_object->IsAccessCheckNeeded() && |
| !isolate->MayAccess(handle(isolate->context()), home_object)) { |
| isolate->ReportFailedAccessCheck(home_object); |
| RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); |
| } |
| |
| PrototypeIterator iter(isolate, home_object); |
| Handle<Object> proto = PrototypeIterator::GetCurrent(iter); |
| if (!proto->IsJSReceiver()) return isolate->heap()->undefined_value(); |
| |
| LookupIterator it(isolate, receiver, index, Handle<JSReceiver>::cast(proto)); |
| Handle<Object> result; |
| ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
| isolate, result, |
| Object::SetSuperProperty(&it, value, language_mode, |
| Object::MAY_BE_STORE_FROM_KEYED)); |
| return *result; |
| } |
| |
| |
| RUNTIME_FUNCTION(Runtime_StoreToSuper_Strict) { |
| HandleScope scope(isolate); |
| DCHECK(args.length() == 4); |
| 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 StoreToSuper(isolate, home_object, receiver, name, value, STRICT); |
| } |
| |
| |
| RUNTIME_FUNCTION(Runtime_StoreToSuper_Sloppy) { |
| HandleScope scope(isolate); |
| DCHECK(args.length() == 4); |
| 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 StoreToSuper(isolate, home_object, receiver, name, value, SLOPPY); |
| } |
| |
| |
| static 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_FAILURE_ON_EXCEPTION(isolate, name, |
| Object::ToName(isolate, key)); |
| // 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(args.length() == 4); |
| 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 StoreKeyedToSuper(isolate, home_object, receiver, key, value, STRICT); |
| } |
| |
| |
| RUNTIME_FUNCTION(Runtime_StoreKeyedToSuper_Sloppy) { |
| HandleScope scope(isolate); |
| DCHECK(args.length() == 4); |
| 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 StoreKeyedToSuper(isolate, home_object, receiver, key, value, SLOPPY); |
| } |
| |
| |
| RUNTIME_FUNCTION(Runtime_HandleStepInForDerivedConstructors) { |
| HandleScope scope(isolate); |
| DCHECK(args.length() == 1); |
| CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); |
| Debug* debug = isolate->debug(); |
| // Handle stepping into constructors if step into is active. |
| if (debug->StepInActive()) debug->HandleStepIn(function, true); |
| return *isolate->factory()->undefined_value(); |
| } |
| |
| |
| RUNTIME_FUNCTION(Runtime_DefaultConstructorCallSuper) { |
| HandleScope scope(isolate); |
| DCHECK(args.length() == 2); |
| CONVERT_ARG_HANDLE_CHECKED(JSFunction, original_constructor, 0); |
| CONVERT_ARG_HANDLE_CHECKED(JSFunction, super_constructor, 1); |
| JavaScriptFrameIterator it(isolate); |
| |
| // Determine the actual arguments passed to the function. |
| int argument_count = 0; |
| base::SmartArrayPointer<Handle<Object>> arguments = |
| Runtime::GetCallerArguments(isolate, 0, &argument_count); |
| |
| Handle<Object> result; |
| ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
| isolate, result, |
| Execution::New(isolate, super_constructor, original_constructor, |
| argument_count, arguments.get())); |
| |
| return *result; |
| } |
| |
| } // namespace internal |
| } // namespace v8 |