| Index: src/runtime/runtime-scopes.cc
|
| diff --git a/src/runtime/runtime-scopes.cc b/src/runtime/runtime-scopes.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..454536996ab5c766c92e6822658b79eae1bad917
|
| --- /dev/null
|
| +++ b/src/runtime/runtime-scopes.cc
|
| @@ -0,0 +1,1029 @@
|
| +// 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/v8.h"
|
| +
|
| +#include "src/accessors.h"
|
| +#include "src/arguments.h"
|
| +#include "src/frames-inl.h"
|
| +#include "src/runtime/runtime.h"
|
| +#include "src/runtime/runtime-utils.h"
|
| +#include "src/scopeinfo.h"
|
| +#include "src/scopes.h"
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +
|
| +static Object* ThrowRedeclarationError(Isolate* isolate, Handle<String> name) {
|
| + HandleScope scope(isolate);
|
| + Handle<Object> args[1] = {name};
|
| + THROW_NEW_ERROR_RETURN_FAILURE(
|
| + isolate, NewTypeError("var_redeclaration", HandleVector(args, 1)));
|
| +}
|
| +
|
| +
|
| +// May throw a RedeclarationError.
|
| +static Object* DeclareGlobals(Isolate* isolate, Handle<GlobalObject> global,
|
| + Handle<String> name, Handle<Object> value,
|
| + PropertyAttributes attr, bool is_var,
|
| + bool is_const, bool is_function) {
|
| + // Do the lookup own properties only, see ES5 erratum.
|
| + LookupIterator it(global, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR);
|
| + Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it);
|
| + if (!maybe.has_value) return isolate->heap()->exception();
|
| +
|
| + if (it.IsFound()) {
|
| + PropertyAttributes old_attributes = maybe.value;
|
| + // The name was declared before; check for conflicting re-declarations.
|
| + if (is_const) return ThrowRedeclarationError(isolate, name);
|
| +
|
| + // Skip var re-declarations.
|
| + if (is_var) return isolate->heap()->undefined_value();
|
| +
|
| + DCHECK(is_function);
|
| + if ((old_attributes & DONT_DELETE) != 0) {
|
| + // Only allow reconfiguring globals to functions in user code (no
|
| + // natives, which are marked as read-only).
|
| + DCHECK((attr & READ_ONLY) == 0);
|
| +
|
| + // Check whether we can reconfigure the existing property into a
|
| + // function.
|
| + PropertyDetails old_details = it.property_details();
|
| + // TODO(verwaest): CALLBACKS invalidly includes ExecutableAccessInfo,
|
| + // which are actually data properties, not accessor properties.
|
| + if (old_details.IsReadOnly() || old_details.IsDontEnum() ||
|
| + old_details.type() == CALLBACKS) {
|
| + return ThrowRedeclarationError(isolate, name);
|
| + }
|
| + // If the existing property is not configurable, keep its attributes. Do
|
| + attr = old_attributes;
|
| + }
|
| + }
|
| +
|
| + // Define or redefine own property.
|
| + RETURN_FAILURE_ON_EXCEPTION(isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
| + global, name, value, attr));
|
| +
|
| + return isolate->heap()->undefined_value();
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_DeclareGlobals) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 3);
|
| + Handle<GlobalObject> global(isolate->global_object());
|
| +
|
| + CONVERT_ARG_HANDLE_CHECKED(Context, context, 0);
|
| + CONVERT_ARG_HANDLE_CHECKED(FixedArray, pairs, 1);
|
| + CONVERT_SMI_ARG_CHECKED(flags, 2);
|
| +
|
| + // Traverse the name/value pairs and set the properties.
|
| + int length = pairs->length();
|
| + for (int i = 0; i < length; i += 2) {
|
| + HandleScope scope(isolate);
|
| + Handle<String> name(String::cast(pairs->get(i)));
|
| + Handle<Object> initial_value(pairs->get(i + 1), isolate);
|
| +
|
| + // We have to declare a global const property. To capture we only
|
| + // assign to it when evaluating the assignment for "const x =
|
| + // <expr>" the initial value is the hole.
|
| + bool is_var = initial_value->IsUndefined();
|
| + bool is_const = initial_value->IsTheHole();
|
| + bool is_function = initial_value->IsSharedFunctionInfo();
|
| + DCHECK(is_var + is_const + is_function == 1);
|
| +
|
| + Handle<Object> value;
|
| + if (is_function) {
|
| + // Copy the function and update its context. Use it as value.
|
| + Handle<SharedFunctionInfo> shared =
|
| + Handle<SharedFunctionInfo>::cast(initial_value);
|
| + Handle<JSFunction> function =
|
| + isolate->factory()->NewFunctionFromSharedFunctionInfo(shared, context,
|
| + TENURED);
|
| + value = function;
|
| + } else {
|
| + value = isolate->factory()->undefined_value();
|
| + }
|
| +
|
| + // Compute the property attributes. According to ECMA-262,
|
| + // the property must be non-configurable except in eval.
|
| + bool is_native = DeclareGlobalsNativeFlag::decode(flags);
|
| + bool is_eval = DeclareGlobalsEvalFlag::decode(flags);
|
| + int attr = NONE;
|
| + if (is_const) attr |= READ_ONLY;
|
| + if (is_function && is_native) attr |= READ_ONLY;
|
| + if (!is_const && !is_eval) attr |= DONT_DELETE;
|
| +
|
| + Object* result = DeclareGlobals(isolate, global, name, value,
|
| + static_cast<PropertyAttributes>(attr),
|
| + is_var, is_const, is_function);
|
| + if (isolate->has_pending_exception()) return result;
|
| + }
|
| +
|
| + return isolate->heap()->undefined_value();
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_InitializeVarGlobal) {
|
| + HandleScope scope(isolate);
|
| + // args[0] == name
|
| + // args[1] == language_mode
|
| + // args[2] == value (optional)
|
| +
|
| + // Determine if we need to assign to the variable if it already
|
| + // exists (based on the number of arguments).
|
| + RUNTIME_ASSERT(args.length() == 3);
|
| +
|
| + CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
|
| + CONVERT_STRICT_MODE_ARG_CHECKED(strict_mode, 1);
|
| + CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
|
| +
|
| + Handle<GlobalObject> global(isolate->context()->global_object());
|
| + Handle<Object> result;
|
| + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
| + isolate, result, Object::SetProperty(global, name, value, strict_mode));
|
| + return *result;
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_InitializeConstGlobal) {
|
| + HandleScope handle_scope(isolate);
|
| + // All constants are declared with an initial value. The name
|
| + // of the constant is the first argument and the initial value
|
| + // is the second.
|
| + RUNTIME_ASSERT(args.length() == 2);
|
| + CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
|
| + CONVERT_ARG_HANDLE_CHECKED(Object, value, 1);
|
| +
|
| + Handle<GlobalObject> global = isolate->global_object();
|
| +
|
| + // Lookup the property as own on the global object.
|
| + LookupIterator it(global, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR);
|
| + Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it);
|
| + DCHECK(maybe.has_value);
|
| + PropertyAttributes old_attributes = maybe.value;
|
| +
|
| + PropertyAttributes attr =
|
| + static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
|
| + // Set the value if the property is either missing, or the property attributes
|
| + // allow setting the value without invoking an accessor.
|
| + if (it.IsFound()) {
|
| + // Ignore if we can't reconfigure the value.
|
| + if ((old_attributes & DONT_DELETE) != 0) {
|
| + if ((old_attributes & READ_ONLY) != 0 ||
|
| + it.state() == LookupIterator::ACCESSOR) {
|
| + return *value;
|
| + }
|
| + attr = static_cast<PropertyAttributes>(old_attributes | READ_ONLY);
|
| + }
|
| + }
|
| +
|
| + RETURN_FAILURE_ON_EXCEPTION(isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
| + global, name, value, attr));
|
| +
|
| + return *value;
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_DeclareLookupSlot) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 4);
|
| +
|
| + // Declarations are always made in a function, native, or global context. In
|
| + // the case of eval code, the context passed is the context of the caller,
|
| + // which may be some nested context and not the declaration context.
|
| + CONVERT_ARG_HANDLE_CHECKED(Context, context_arg, 0);
|
| + Handle<Context> context(context_arg->declaration_context());
|
| + CONVERT_ARG_HANDLE_CHECKED(String, name, 1);
|
| + CONVERT_SMI_ARG_CHECKED(attr_arg, 2);
|
| + PropertyAttributes attr = static_cast<PropertyAttributes>(attr_arg);
|
| + RUNTIME_ASSERT(attr == READ_ONLY || attr == NONE);
|
| + CONVERT_ARG_HANDLE_CHECKED(Object, initial_value, 3);
|
| +
|
| + // TODO(verwaest): Unify the encoding indicating "var" with DeclareGlobals.
|
| + bool is_var = *initial_value == NULL;
|
| + bool is_const = initial_value->IsTheHole();
|
| + bool is_function = initial_value->IsJSFunction();
|
| + DCHECK(is_var + is_const + is_function == 1);
|
| +
|
| + int index;
|
| + PropertyAttributes attributes;
|
| + ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
|
| + BindingFlags binding_flags;
|
| + Handle<Object> holder =
|
| + context->Lookup(name, flags, &index, &attributes, &binding_flags);
|
| +
|
| + Handle<JSObject> object;
|
| + Handle<Object> value =
|
| + is_function ? initial_value
|
| + : Handle<Object>::cast(isolate->factory()->undefined_value());
|
| +
|
| + // TODO(verwaest): This case should probably not be covered by this function,
|
| + // but by DeclareGlobals instead.
|
| + if ((attributes != ABSENT && holder->IsJSGlobalObject()) ||
|
| + (context_arg->has_extension() &&
|
| + context_arg->extension()->IsJSGlobalObject())) {
|
| + return DeclareGlobals(isolate, Handle<JSGlobalObject>::cast(holder), name,
|
| + value, attr, is_var, is_const, is_function);
|
| + }
|
| +
|
| + if (attributes != ABSENT) {
|
| + // The name was declared before; check for conflicting re-declarations.
|
| + if (is_const || (attributes & READ_ONLY) != 0) {
|
| + return ThrowRedeclarationError(isolate, name);
|
| + }
|
| +
|
| + // Skip var re-declarations.
|
| + if (is_var) return isolate->heap()->undefined_value();
|
| +
|
| + DCHECK(is_function);
|
| + if (index >= 0) {
|
| + DCHECK(holder.is_identical_to(context));
|
| + context->set(index, *initial_value);
|
| + return isolate->heap()->undefined_value();
|
| + }
|
| +
|
| + object = Handle<JSObject>::cast(holder);
|
| +
|
| + } else if (context->has_extension()) {
|
| + object = handle(JSObject::cast(context->extension()));
|
| + DCHECK(object->IsJSContextExtensionObject() || object->IsJSGlobalObject());
|
| + } else {
|
| + DCHECK(context->IsFunctionContext());
|
| + object =
|
| + isolate->factory()->NewJSObject(isolate->context_extension_function());
|
| + context->set_extension(*object);
|
| + }
|
| +
|
| + RETURN_FAILURE_ON_EXCEPTION(isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
| + object, name, value, attr));
|
| +
|
| + return isolate->heap()->undefined_value();
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_InitializeLegacyConstLookupSlot) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 3);
|
| +
|
| + CONVERT_ARG_HANDLE_CHECKED(Object, value, 0);
|
| + DCHECK(!value->IsTheHole());
|
| + // Initializations are always done in a function or native context.
|
| + CONVERT_ARG_HANDLE_CHECKED(Context, context_arg, 1);
|
| + Handle<Context> context(context_arg->declaration_context());
|
| + CONVERT_ARG_HANDLE_CHECKED(String, name, 2);
|
| +
|
| + int index;
|
| + PropertyAttributes attributes;
|
| + ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
|
| + BindingFlags binding_flags;
|
| + Handle<Object> holder =
|
| + context->Lookup(name, flags, &index, &attributes, &binding_flags);
|
| +
|
| + if (index >= 0) {
|
| + DCHECK(holder->IsContext());
|
| + // Property was found in a context. Perform the assignment if the constant
|
| + // was uninitialized.
|
| + Handle<Context> context = Handle<Context>::cast(holder);
|
| + DCHECK((attributes & READ_ONLY) != 0);
|
| + if (context->get(index)->IsTheHole()) context->set(index, *value);
|
| + return *value;
|
| + }
|
| +
|
| + PropertyAttributes attr =
|
| + static_cast<PropertyAttributes>(DONT_DELETE | READ_ONLY);
|
| +
|
| + // Strict mode handling not needed (legacy const is disallowed in strict
|
| + // mode).
|
| +
|
| + // The declared const was configurable, and may have been deleted in the
|
| + // meanwhile. If so, re-introduce the variable in the context extension.
|
| + DCHECK(context_arg->has_extension());
|
| + if (attributes == ABSENT) {
|
| + holder = handle(context_arg->extension(), isolate);
|
| + } else {
|
| + // For JSContextExtensionObjects, the initializer can be run multiple times
|
| + // if in a for loop: for (var i = 0; i < 2; i++) { const x = i; }. Only the
|
| + // first assignment should go through. For JSGlobalObjects, additionally any
|
| + // code can run in between that modifies the declared property.
|
| + DCHECK(holder->IsJSGlobalObject() || holder->IsJSContextExtensionObject());
|
| +
|
| + LookupIterator it(holder, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR);
|
| + Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it);
|
| + if (!maybe.has_value) return isolate->heap()->exception();
|
| + PropertyAttributes old_attributes = maybe.value;
|
| +
|
| + // Ignore if we can't reconfigure the value.
|
| + if ((old_attributes & DONT_DELETE) != 0) {
|
| + if ((old_attributes & READ_ONLY) != 0 ||
|
| + it.state() == LookupIterator::ACCESSOR) {
|
| + return *value;
|
| + }
|
| + attr = static_cast<PropertyAttributes>(old_attributes | READ_ONLY);
|
| + }
|
| + }
|
| +
|
| + RETURN_FAILURE_ON_EXCEPTION(
|
| + isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
| + Handle<JSObject>::cast(holder), name, value, attr));
|
| +
|
| + return *value;
|
| +}
|
| +
|
| +
|
| +static Handle<JSObject> NewSloppyArguments(Isolate* isolate,
|
| + Handle<JSFunction> callee,
|
| + Object** parameters,
|
| + int argument_count) {
|
| + Handle<JSObject> result =
|
| + isolate->factory()->NewArgumentsObject(callee, argument_count);
|
| +
|
| + // Allocate the elements if needed.
|
| + int parameter_count = callee->shared()->formal_parameter_count();
|
| + if (argument_count > 0) {
|
| + if (parameter_count > 0) {
|
| + int mapped_count = Min(argument_count, parameter_count);
|
| + Handle<FixedArray> parameter_map =
|
| + isolate->factory()->NewFixedArray(mapped_count + 2, NOT_TENURED);
|
| + parameter_map->set_map(isolate->heap()->sloppy_arguments_elements_map());
|
| +
|
| + Handle<Map> map = Map::Copy(handle(result->map()));
|
| + map->set_elements_kind(SLOPPY_ARGUMENTS_ELEMENTS);
|
| +
|
| + result->set_map(*map);
|
| + result->set_elements(*parameter_map);
|
| +
|
| + // Store the context and the arguments array at the beginning of the
|
| + // parameter map.
|
| + Handle<Context> context(isolate->context());
|
| + Handle<FixedArray> arguments =
|
| + isolate->factory()->NewFixedArray(argument_count, NOT_TENURED);
|
| + parameter_map->set(0, *context);
|
| + parameter_map->set(1, *arguments);
|
| +
|
| + // Loop over the actual parameters backwards.
|
| + int index = argument_count - 1;
|
| + while (index >= mapped_count) {
|
| + // These go directly in the arguments array and have no
|
| + // corresponding slot in the parameter map.
|
| + arguments->set(index, *(parameters - index - 1));
|
| + --index;
|
| + }
|
| +
|
| + Handle<ScopeInfo> scope_info(callee->shared()->scope_info());
|
| + while (index >= 0) {
|
| + // Detect duplicate names to the right in the parameter list.
|
| + Handle<String> name(scope_info->ParameterName(index));
|
| + int context_local_count = scope_info->ContextLocalCount();
|
| + bool duplicate = false;
|
| + for (int j = index + 1; j < parameter_count; ++j) {
|
| + if (scope_info->ParameterName(j) == *name) {
|
| + duplicate = true;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (duplicate) {
|
| + // This goes directly in the arguments array with a hole in the
|
| + // parameter map.
|
| + arguments->set(index, *(parameters - index - 1));
|
| + parameter_map->set_the_hole(index + 2);
|
| + } else {
|
| + // The context index goes in the parameter map with a hole in the
|
| + // arguments array.
|
| + int context_index = -1;
|
| + for (int j = 0; j < context_local_count; ++j) {
|
| + if (scope_info->ContextLocalName(j) == *name) {
|
| + context_index = j;
|
| + break;
|
| + }
|
| + }
|
| + DCHECK(context_index >= 0);
|
| + arguments->set_the_hole(index);
|
| + parameter_map->set(
|
| + index + 2,
|
| + Smi::FromInt(Context::MIN_CONTEXT_SLOTS + context_index));
|
| + }
|
| +
|
| + --index;
|
| + }
|
| + } else {
|
| + // If there is no aliasing, the arguments object elements are not
|
| + // special in any way.
|
| + Handle<FixedArray> elements =
|
| + isolate->factory()->NewFixedArray(argument_count, NOT_TENURED);
|
| + result->set_elements(*elements);
|
| + for (int i = 0; i < argument_count; ++i) {
|
| + elements->set(i, *(parameters - i - 1));
|
| + }
|
| + }
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +
|
| +static Handle<JSObject> NewStrictArguments(Isolate* isolate,
|
| + Handle<JSFunction> callee,
|
| + Object** parameters,
|
| + int argument_count) {
|
| + Handle<JSObject> result =
|
| + isolate->factory()->NewArgumentsObject(callee, argument_count);
|
| +
|
| + if (argument_count > 0) {
|
| + Handle<FixedArray> array =
|
| + isolate->factory()->NewUninitializedFixedArray(argument_count);
|
| + DisallowHeapAllocation no_gc;
|
| + WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
|
| + for (int i = 0; i < argument_count; i++) {
|
| + array->set(i, *--parameters, mode);
|
| + }
|
| + result->set_elements(*array);
|
| + }
|
| + return result;
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_NewArguments) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 1);
|
| + CONVERT_ARG_HANDLE_CHECKED(JSFunction, callee, 0);
|
| + JavaScriptFrameIterator it(isolate);
|
| +
|
| + // Find the frame that holds the actual arguments passed to the function.
|
| + it.AdvanceToArgumentsFrame();
|
| + JavaScriptFrame* frame = it.frame();
|
| +
|
| + // Determine parameter location on the stack and dispatch on language mode.
|
| + int argument_count = frame->GetArgumentsLength();
|
| + Object** parameters = reinterpret_cast<Object**>(frame->GetParameterSlot(-1));
|
| + return callee->shared()->strict_mode() == STRICT
|
| + ? *NewStrictArguments(isolate, callee, parameters, argument_count)
|
| + : *NewSloppyArguments(isolate, callee, parameters, argument_count);
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_NewSloppyArguments) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 3);
|
| + CONVERT_ARG_HANDLE_CHECKED(JSFunction, callee, 0);
|
| + Object** parameters = reinterpret_cast<Object**>(args[1]);
|
| + CONVERT_SMI_ARG_CHECKED(argument_count, 2);
|
| + return *NewSloppyArguments(isolate, callee, parameters, argument_count);
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_NewStrictArguments) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 3);
|
| + CONVERT_ARG_HANDLE_CHECKED(JSFunction, callee, 0)
|
| + Object** parameters = reinterpret_cast<Object**>(args[1]);
|
| + CONVERT_SMI_ARG_CHECKED(argument_count, 2);
|
| + return *NewStrictArguments(isolate, callee, parameters, argument_count);
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_NewClosureFromStubFailure) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 1);
|
| + CONVERT_ARG_HANDLE_CHECKED(SharedFunctionInfo, shared, 0);
|
| + Handle<Context> context(isolate->context());
|
| + PretenureFlag pretenure_flag = NOT_TENURED;
|
| + return *isolate->factory()->NewFunctionFromSharedFunctionInfo(shared, context,
|
| + pretenure_flag);
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_NewClosure) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 3);
|
| + CONVERT_ARG_HANDLE_CHECKED(Context, context, 0);
|
| + CONVERT_ARG_HANDLE_CHECKED(SharedFunctionInfo, shared, 1);
|
| + CONVERT_BOOLEAN_ARG_CHECKED(pretenure, 2);
|
| +
|
| + // The caller ensures that we pretenure closures that are assigned
|
| + // directly to properties.
|
| + PretenureFlag pretenure_flag = pretenure ? TENURED : NOT_TENURED;
|
| + return *isolate->factory()->NewFunctionFromSharedFunctionInfo(shared, context,
|
| + pretenure_flag);
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_NewGlobalContext) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 2);
|
| +
|
| + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
| + CONVERT_ARG_HANDLE_CHECKED(ScopeInfo, scope_info, 1);
|
| + Handle<Context> result =
|
| + isolate->factory()->NewGlobalContext(function, scope_info);
|
| +
|
| + DCHECK(function->context() == isolate->context());
|
| + DCHECK(function->context()->global_object() == result->global_object());
|
| + result->global_object()->set_global_context(*result);
|
| + return *result;
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_NewFunctionContext) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 1);
|
| +
|
| + CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
|
| +
|
| + DCHECK(function->context() == isolate->context());
|
| + int length = function->shared()->scope_info()->ContextLength();
|
| + return *isolate->factory()->NewFunctionContext(length, function);
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_PushWithContext) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 2);
|
| + Handle<JSReceiver> extension_object;
|
| + if (args[0]->IsJSReceiver()) {
|
| + extension_object = args.at<JSReceiver>(0);
|
| + } else {
|
| + // Try to convert the object to a proper JavaScript object.
|
| + MaybeHandle<JSReceiver> maybe_object =
|
| + Object::ToObject(isolate, args.at<Object>(0));
|
| + if (!maybe_object.ToHandle(&extension_object)) {
|
| + Handle<Object> handle = args.at<Object>(0);
|
| + THROW_NEW_ERROR_RETURN_FAILURE(
|
| + isolate, NewTypeError("with_expression", HandleVector(&handle, 1)));
|
| + }
|
| + }
|
| +
|
| + Handle<JSFunction> function;
|
| + if (args[1]->IsSmi()) {
|
| + // A smi sentinel indicates a context nested inside global code rather
|
| + // than some function. There is a canonical empty function that can be
|
| + // gotten from the native context.
|
| + function = handle(isolate->native_context()->closure());
|
| + } else {
|
| + function = args.at<JSFunction>(1);
|
| + }
|
| +
|
| + Handle<Context> current(isolate->context());
|
| + Handle<Context> context =
|
| + isolate->factory()->NewWithContext(function, current, extension_object);
|
| + isolate->set_context(*context);
|
| + return *context;
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_PushCatchContext) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 3);
|
| + CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
|
| + CONVERT_ARG_HANDLE_CHECKED(Object, thrown_object, 1);
|
| + Handle<JSFunction> function;
|
| + if (args[2]->IsSmi()) {
|
| + // A smi sentinel indicates a context nested inside global code rather
|
| + // than some function. There is a canonical empty function that can be
|
| + // gotten from the native context.
|
| + function = handle(isolate->native_context()->closure());
|
| + } else {
|
| + function = args.at<JSFunction>(2);
|
| + }
|
| + Handle<Context> current(isolate->context());
|
| + Handle<Context> context = isolate->factory()->NewCatchContext(
|
| + function, current, name, thrown_object);
|
| + isolate->set_context(*context);
|
| + return *context;
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_PushBlockContext) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 2);
|
| + CONVERT_ARG_HANDLE_CHECKED(ScopeInfo, scope_info, 0);
|
| + Handle<JSFunction> function;
|
| + if (args[1]->IsSmi()) {
|
| + // A smi sentinel indicates a context nested inside global code rather
|
| + // than some function. There is a canonical empty function that can be
|
| + // gotten from the native context.
|
| + function = handle(isolate->native_context()->closure());
|
| + } else {
|
| + function = args.at<JSFunction>(1);
|
| + }
|
| + Handle<Context> current(isolate->context());
|
| + Handle<Context> context =
|
| + isolate->factory()->NewBlockContext(function, current, scope_info);
|
| + isolate->set_context(*context);
|
| + return *context;
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_IsJSModule) {
|
| + SealHandleScope shs(isolate);
|
| + DCHECK(args.length() == 1);
|
| + CONVERT_ARG_CHECKED(Object, obj, 0);
|
| + return isolate->heap()->ToBoolean(obj->IsJSModule());
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_PushModuleContext) {
|
| + SealHandleScope shs(isolate);
|
| + DCHECK(args.length() == 2);
|
| + CONVERT_SMI_ARG_CHECKED(index, 0);
|
| +
|
| + if (!args[1]->IsScopeInfo()) {
|
| + // Module already initialized. Find hosting context and retrieve context.
|
| + Context* host = Context::cast(isolate->context())->global_context();
|
| + Context* context = Context::cast(host->get(index));
|
| + DCHECK(context->previous() == isolate->context());
|
| + isolate->set_context(context);
|
| + return context;
|
| + }
|
| +
|
| + CONVERT_ARG_HANDLE_CHECKED(ScopeInfo, scope_info, 1);
|
| +
|
| + // Allocate module context.
|
| + HandleScope scope(isolate);
|
| + Factory* factory = isolate->factory();
|
| + Handle<Context> context = factory->NewModuleContext(scope_info);
|
| + Handle<JSModule> module = factory->NewJSModule(context, scope_info);
|
| + context->set_module(*module);
|
| + Context* previous = isolate->context();
|
| + context->set_previous(previous);
|
| + context->set_closure(previous->closure());
|
| + context->set_global_object(previous->global_object());
|
| + isolate->set_context(*context);
|
| +
|
| + // Find hosting scope and initialize internal variable holding module there.
|
| + previous->global_context()->set(index, *context);
|
| +
|
| + return *context;
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_DeclareModules) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 1);
|
| + CONVERT_ARG_HANDLE_CHECKED(FixedArray, descriptions, 0);
|
| + Context* host_context = isolate->context();
|
| +
|
| + for (int i = 0; i < descriptions->length(); ++i) {
|
| + Handle<ModuleInfo> description(ModuleInfo::cast(descriptions->get(i)));
|
| + int host_index = description->host_index();
|
| + Handle<Context> context(Context::cast(host_context->get(host_index)));
|
| + Handle<JSModule> module(context->module());
|
| +
|
| + for (int j = 0; j < description->length(); ++j) {
|
| + Handle<String> name(description->name(j));
|
| + VariableMode mode = description->mode(j);
|
| + int index = description->index(j);
|
| + switch (mode) {
|
| + case VAR:
|
| + case LET:
|
| + case CONST:
|
| + case CONST_LEGACY: {
|
| + PropertyAttributes attr =
|
| + IsImmutableVariableMode(mode) ? FROZEN : SEALED;
|
| + Handle<AccessorInfo> info =
|
| + Accessors::MakeModuleExport(name, index, attr);
|
| + Handle<Object> result =
|
| + JSObject::SetAccessor(module, info).ToHandleChecked();
|
| + DCHECK(!result->IsUndefined());
|
| + USE(result);
|
| + break;
|
| + }
|
| + case MODULE: {
|
| + Object* referenced_context = Context::cast(host_context)->get(index);
|
| + Handle<JSModule> value(Context::cast(referenced_context)->module());
|
| + JSObject::SetOwnPropertyIgnoreAttributes(module, name, value, FROZEN)
|
| + .Assert();
|
| + break;
|
| + }
|
| + case INTERNAL:
|
| + case TEMPORARY:
|
| + case DYNAMIC:
|
| + case DYNAMIC_GLOBAL:
|
| + case DYNAMIC_LOCAL:
|
| + UNREACHABLE();
|
| + }
|
| + }
|
| +
|
| + JSObject::PreventExtensions(module).Assert();
|
| + }
|
| +
|
| + DCHECK(!isolate->has_pending_exception());
|
| + return isolate->heap()->undefined_value();
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_DeleteLookupSlot) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 2);
|
| +
|
| + CONVERT_ARG_HANDLE_CHECKED(Context, context, 0);
|
| + CONVERT_ARG_HANDLE_CHECKED(String, name, 1);
|
| +
|
| + int index;
|
| + PropertyAttributes attributes;
|
| + ContextLookupFlags flags = FOLLOW_CHAINS;
|
| + BindingFlags binding_flags;
|
| + Handle<Object> holder =
|
| + context->Lookup(name, flags, &index, &attributes, &binding_flags);
|
| +
|
| + // If the slot was not found the result is true.
|
| + if (holder.is_null()) {
|
| + return isolate->heap()->true_value();
|
| + }
|
| +
|
| + // If the slot was found in a context, it should be DONT_DELETE.
|
| + if (holder->IsContext()) {
|
| + return isolate->heap()->false_value();
|
| + }
|
| +
|
| + // The slot was found in a JSObject, either a context extension object,
|
| + // the global object, or the subject of a with. Try to delete it
|
| + // (respecting DONT_DELETE).
|
| + Handle<JSObject> object = Handle<JSObject>::cast(holder);
|
| + Handle<Object> result;
|
| + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result,
|
| + JSReceiver::DeleteProperty(object, name));
|
| + return *result;
|
| +}
|
| +
|
| +
|
| +static Object* ComputeReceiverForNonGlobal(Isolate* isolate, JSObject* holder) {
|
| + DCHECK(!holder->IsGlobalObject());
|
| + Context* top = isolate->context();
|
| + // Get the context extension function.
|
| + JSFunction* context_extension_function =
|
| + top->native_context()->context_extension_function();
|
| + // If the holder isn't a context extension object, we just return it
|
| + // as the receiver. This allows arguments objects to be used as
|
| + // receivers, but only if they are put in the context scope chain
|
| + // explicitly via a with-statement.
|
| + Object* constructor = holder->map()->constructor();
|
| + if (constructor != context_extension_function) return holder;
|
| + // Fall back to using the global object as the implicit receiver if
|
| + // the property turns out to be a local variable allocated in a
|
| + // context extension object - introduced via eval.
|
| + return isolate->heap()->undefined_value();
|
| +}
|
| +
|
| +
|
| +static ObjectPair LoadLookupSlotHelper(Arguments args, Isolate* isolate,
|
| + bool throw_error) {
|
| + HandleScope scope(isolate);
|
| + DCHECK_EQ(2, args.length());
|
| +
|
| + if (!args[0]->IsContext() || !args[1]->IsString()) {
|
| + return MakePair(isolate->ThrowIllegalOperation(), NULL);
|
| + }
|
| + Handle<Context> context = args.at<Context>(0);
|
| + Handle<String> name = args.at<String>(1);
|
| +
|
| + int index;
|
| + PropertyAttributes attributes;
|
| + ContextLookupFlags flags = FOLLOW_CHAINS;
|
| + BindingFlags binding_flags;
|
| + Handle<Object> holder =
|
| + context->Lookup(name, flags, &index, &attributes, &binding_flags);
|
| + if (isolate->has_pending_exception()) {
|
| + return MakePair(isolate->heap()->exception(), NULL);
|
| + }
|
| +
|
| + // If the index is non-negative, the slot has been found in a context.
|
| + if (index >= 0) {
|
| + DCHECK(holder->IsContext());
|
| + // If the "property" we were looking for is a local variable, the
|
| + // receiver is the global object; see ECMA-262, 3rd., 10.1.6 and 10.2.3.
|
| + Handle<Object> receiver = isolate->factory()->undefined_value();
|
| + Object* value = Context::cast(*holder)->get(index);
|
| + // Check for uninitialized bindings.
|
| + switch (binding_flags) {
|
| + case MUTABLE_CHECK_INITIALIZED:
|
| + case IMMUTABLE_CHECK_INITIALIZED_HARMONY:
|
| + if (value->IsTheHole()) {
|
| + Handle<Object> error;
|
| + MaybeHandle<Object> maybe_error =
|
| + isolate->factory()->NewReferenceError("not_defined",
|
| + HandleVector(&name, 1));
|
| + if (maybe_error.ToHandle(&error)) isolate->Throw(*error);
|
| + return MakePair(isolate->heap()->exception(), NULL);
|
| + }
|
| + // FALLTHROUGH
|
| + case MUTABLE_IS_INITIALIZED:
|
| + case IMMUTABLE_IS_INITIALIZED:
|
| + case IMMUTABLE_IS_INITIALIZED_HARMONY:
|
| + DCHECK(!value->IsTheHole());
|
| + return MakePair(value, *receiver);
|
| + case IMMUTABLE_CHECK_INITIALIZED:
|
| + if (value->IsTheHole()) {
|
| + DCHECK((attributes & READ_ONLY) != 0);
|
| + value = isolate->heap()->undefined_value();
|
| + }
|
| + return MakePair(value, *receiver);
|
| + case MISSING_BINDING:
|
| + UNREACHABLE();
|
| + return MakePair(NULL, NULL);
|
| + }
|
| + }
|
| +
|
| + // Otherwise, if the slot was found the holder is a context extension
|
| + // object, subject of a with, or a global object. We read the named
|
| + // property from it.
|
| + if (!holder.is_null()) {
|
| + Handle<JSReceiver> object = Handle<JSReceiver>::cast(holder);
|
| +#ifdef DEBUG
|
| + if (!object->IsJSProxy()) {
|
| + Maybe<bool> maybe = JSReceiver::HasProperty(object, name);
|
| + DCHECK(maybe.has_value);
|
| + DCHECK(maybe.value);
|
| + }
|
| +#endif
|
| + // GetProperty below can cause GC.
|
| + Handle<Object> receiver_handle(
|
| + object->IsGlobalObject()
|
| + ? Object::cast(isolate->heap()->undefined_value())
|
| + : object->IsJSProxy() ? static_cast<Object*>(*object)
|
| + : ComputeReceiverForNonGlobal(
|
| + isolate, JSObject::cast(*object)),
|
| + isolate);
|
| +
|
| + // No need to unhole the value here. This is taken care of by the
|
| + // GetProperty function.
|
| + Handle<Object> value;
|
| + ASSIGN_RETURN_ON_EXCEPTION_VALUE(
|
| + isolate, value, Object::GetProperty(object, name),
|
| + MakePair(isolate->heap()->exception(), NULL));
|
| + return MakePair(*value, *receiver_handle);
|
| + }
|
| +
|
| + if (throw_error) {
|
| + // The property doesn't exist - throw exception.
|
| + Handle<Object> error;
|
| + MaybeHandle<Object> maybe_error = isolate->factory()->NewReferenceError(
|
| + "not_defined", HandleVector(&name, 1));
|
| + if (maybe_error.ToHandle(&error)) isolate->Throw(*error);
|
| + return MakePair(isolate->heap()->exception(), NULL);
|
| + } else {
|
| + // The property doesn't exist - return undefined.
|
| + return MakePair(isolate->heap()->undefined_value(),
|
| + isolate->heap()->undefined_value());
|
| + }
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION_RETURN_PAIR(Runtime_LoadLookupSlot) {
|
| + return LoadLookupSlotHelper(args, isolate, true);
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION_RETURN_PAIR(Runtime_LoadLookupSlotNoReferenceError) {
|
| + return LoadLookupSlotHelper(args, isolate, false);
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_StoreLookupSlot) {
|
| + HandleScope scope(isolate);
|
| + DCHECK(args.length() == 4);
|
| +
|
| + CONVERT_ARG_HANDLE_CHECKED(Object, value, 0);
|
| + CONVERT_ARG_HANDLE_CHECKED(Context, context, 1);
|
| + CONVERT_ARG_HANDLE_CHECKED(String, name, 2);
|
| + CONVERT_STRICT_MODE_ARG_CHECKED(strict_mode, 3);
|
| +
|
| + int index;
|
| + PropertyAttributes attributes;
|
| + ContextLookupFlags flags = FOLLOW_CHAINS;
|
| + BindingFlags binding_flags;
|
| + Handle<Object> holder =
|
| + context->Lookup(name, flags, &index, &attributes, &binding_flags);
|
| + // In case of JSProxy, an exception might have been thrown.
|
| + if (isolate->has_pending_exception()) return isolate->heap()->exception();
|
| +
|
| + // The property was found in a context slot.
|
| + if (index >= 0) {
|
| + if ((attributes & READ_ONLY) == 0) {
|
| + Handle<Context>::cast(holder)->set(index, *value);
|
| + } else if (strict_mode == STRICT) {
|
| + // Setting read only property in strict mode.
|
| + THROW_NEW_ERROR_RETURN_FAILURE(
|
| + isolate,
|
| + NewTypeError("strict_cannot_assign", HandleVector(&name, 1)));
|
| + }
|
| + return *value;
|
| + }
|
| +
|
| + // Slow case: The property is not in a context slot. It is either in a
|
| + // context extension object, a property of the subject of a with, or a
|
| + // property of the global object.
|
| + Handle<JSReceiver> object;
|
| + if (attributes != ABSENT) {
|
| + // The property exists on the holder.
|
| + object = Handle<JSReceiver>::cast(holder);
|
| + } else if (strict_mode == STRICT) {
|
| + // If absent in strict mode: throw.
|
| + THROW_NEW_ERROR_RETURN_FAILURE(
|
| + isolate, NewReferenceError("not_defined", HandleVector(&name, 1)));
|
| + } else {
|
| + // If absent in sloppy mode: add the property to the global object.
|
| + object = Handle<JSReceiver>(context->global_object());
|
| + }
|
| +
|
| + RETURN_FAILURE_ON_EXCEPTION(
|
| + isolate, Object::SetProperty(object, name, value, strict_mode));
|
| +
|
| + return *value;
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(Runtime_GetArgumentsProperty) {
|
| + SealHandleScope shs(isolate);
|
| + DCHECK(args.length() == 1);
|
| + CONVERT_ARG_HANDLE_CHECKED(Object, raw_key, 0);
|
| +
|
| + // Compute the frame holding the arguments.
|
| + JavaScriptFrameIterator it(isolate);
|
| + it.AdvanceToArgumentsFrame();
|
| + JavaScriptFrame* frame = it.frame();
|
| +
|
| + // Get the actual number of provided arguments.
|
| + const uint32_t n = frame->ComputeParametersCount();
|
| +
|
| + // Try to convert the key to an index. If successful and within
|
| + // index return the the argument from the frame.
|
| + uint32_t index;
|
| + if (raw_key->ToArrayIndex(&index) && index < n) {
|
| + return frame->GetParameter(index);
|
| + }
|
| +
|
| + HandleScope scope(isolate);
|
| + if (raw_key->IsSymbol()) {
|
| + Handle<Symbol> symbol = Handle<Symbol>::cast(raw_key);
|
| + if (symbol->Equals(isolate->native_context()->iterator_symbol())) {
|
| + return isolate->native_context()->array_values_iterator();
|
| + }
|
| + // Lookup in the initial Object.prototype object.
|
| + Handle<Object> result;
|
| + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
| + isolate, result,
|
| + Object::GetProperty(isolate->initial_object_prototype(),
|
| + Handle<Symbol>::cast(raw_key)));
|
| + return *result;
|
| + }
|
| +
|
| + // Convert the key to a string.
|
| + Handle<Object> converted;
|
| + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, converted,
|
| + Execution::ToString(isolate, raw_key));
|
| + Handle<String> key = Handle<String>::cast(converted);
|
| +
|
| + // Try to convert the string key into an array index.
|
| + if (key->AsArrayIndex(&index)) {
|
| + if (index < n) {
|
| + return frame->GetParameter(index);
|
| + } else {
|
| + Handle<Object> initial_prototype(isolate->initial_object_prototype());
|
| + Handle<Object> result;
|
| + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
| + isolate, result,
|
| + Object::GetElement(isolate, initial_prototype, index));
|
| + return *result;
|
| + }
|
| + }
|
| +
|
| + // Handle special arguments properties.
|
| + if (String::Equals(isolate->factory()->length_string(), key)) {
|
| + return Smi::FromInt(n);
|
| + }
|
| + if (String::Equals(isolate->factory()->callee_string(), key)) {
|
| + JSFunction* function = frame->function();
|
| + if (function->shared()->strict_mode() == STRICT) {
|
| + THROW_NEW_ERROR_RETURN_FAILURE(
|
| + isolate, NewTypeError("strict_arguments_callee",
|
| + HandleVector<Object>(NULL, 0)));
|
| + }
|
| + return function;
|
| + }
|
| +
|
| + // Lookup in the initial Object.prototype object.
|
| + Handle<Object> result;
|
| + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
| + isolate, result,
|
| + Object::GetProperty(isolate->initial_object_prototype(), key));
|
| + return *result;
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(RuntimeReference_ArgumentsLength) {
|
| + SealHandleScope shs(isolate);
|
| + DCHECK(args.length() == 0);
|
| + JavaScriptFrameIterator it(isolate);
|
| + JavaScriptFrame* frame = it.frame();
|
| + return Smi::FromInt(frame->GetArgumentsLength());
|
| +}
|
| +
|
| +
|
| +RUNTIME_FUNCTION(RuntimeReference_Arguments) {
|
| + SealHandleScope shs(isolate);
|
| + return __RT_impl_Runtime_GetArgumentsProperty(args, isolate);
|
| +}
|
| +}
|
| +} // namespace v8::internal
|
|
|