| Index: src/api-natives.cc
|
| diff --git a/src/api-natives.cc b/src/api-natives.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..8233b8abd1662845be33ecfea1077d22638a07aa
|
| --- /dev/null
|
| +++ b/src/api-natives.cc
|
| @@ -0,0 +1,529 @@
|
| +// Copyright 2015 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/api-natives.h"
|
| +#include "src/isolate-inl.h"
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +
|
| +namespace {
|
| +
|
| +// Transform getter or setter into something DefineAccessor can handle.
|
| +Handle<Object> InstantiateAccessorComponent(Isolate* isolate,
|
| + Handle<Object> component) {
|
| + if (component->IsUndefined()) return isolate->factory()->undefined_value();
|
| + Handle<FunctionTemplateInfo> info =
|
| + Handle<FunctionTemplateInfo>::cast(component);
|
| + // TODO(dcarney): instantiate directly.
|
| + return Utils::OpenHandle(*Utils::ToLocal(info)->GetFunction());
|
| +}
|
| +
|
| +
|
| +MaybeHandle<Object> DefineApiAccessorProperty(
|
| + Isolate* isolate, Handle<JSObject> object, Handle<Name> name,
|
| + Handle<Object> getter, Handle<Object> setter, Smi* attribute) {
|
| + DCHECK(PropertyDetails::AttributesField::is_valid(
|
| + static_cast<PropertyAttributes>(attribute->value())));
|
| + RETURN_ON_EXCEPTION(
|
| + isolate, JSObject::DefineAccessor(
|
| + object, name, InstantiateAccessorComponent(isolate, getter),
|
| + InstantiateAccessorComponent(isolate, setter),
|
| + static_cast<PropertyAttributes>(attribute->value())),
|
| + Object);
|
| + return object;
|
| +}
|
| +
|
| +
|
| +MaybeHandle<Object> AddPropertyForTemplate(Isolate* isolate,
|
| + Handle<JSObject> object,
|
| + Handle<Object> key,
|
| + Handle<Object> value,
|
| + Smi* unchecked_attributes) {
|
| + DCHECK((unchecked_attributes->value() &
|
| + ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
|
| + // Compute attributes.
|
| + PropertyAttributes attributes =
|
| + static_cast<PropertyAttributes>(unchecked_attributes->value());
|
| +
|
| +#ifdef DEBUG
|
| + bool duplicate;
|
| + if (key->IsName()) {
|
| + LookupIterator it(object, Handle<Name>::cast(key),
|
| + LookupIterator::OWN_SKIP_INTERCEPTOR);
|
| + Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it);
|
| + DCHECK(maybe.has_value);
|
| + duplicate = it.IsFound();
|
| + } else {
|
| + uint32_t index = 0;
|
| + key->ToArrayIndex(&index);
|
| + Maybe<bool> maybe = JSReceiver::HasOwnElement(object, index);
|
| + if (!maybe.has_value) return MaybeHandle<Object>();
|
| + duplicate = maybe.value;
|
| + }
|
| + if (duplicate) {
|
| + Handle<Object> args[1] = {key};
|
| + THROW_NEW_ERROR(isolate, NewTypeError("duplicate_template_property",
|
| + HandleVector(args, 1)),
|
| + Object);
|
| + }
|
| +#endif
|
| +
|
| + RETURN_ON_EXCEPTION(
|
| + isolate, Runtime::DefineObjectProperty(object, key, value, attributes),
|
| + Object);
|
| + return object;
|
| +}
|
| +
|
| +
|
| +void DisableAccessChecks(Isolate* isolate, Handle<JSObject> object) {
|
| + Handle<Map> old_map(object->map());
|
| + // Copy map so it won't interfere constructor's initial map.
|
| + Handle<Map> new_map = Map::Copy(old_map, "DisableAccessChecks");
|
| + new_map->set_is_access_check_needed(false);
|
| + JSObject::MigrateToMap(Handle<JSObject>::cast(object), new_map);
|
| +}
|
| +
|
| +
|
| +void EnableAccessChecks(Isolate* isolate, Handle<JSObject> object) {
|
| + Handle<Map> old_map(object->map());
|
| + // Copy map so it won't interfere constructor's initial map.
|
| + Handle<Map> new_map = Map::Copy(old_map, "EnableAccessChecks");
|
| + new_map->set_is_access_check_needed(true);
|
| + JSObject::MigrateToMap(object, new_map);
|
| +}
|
| +
|
| +
|
| +MaybeHandle<JSObject> InstantiateObject(Isolate* isolate,
|
| + Handle<ObjectTemplateInfo> data);
|
| +
|
| +
|
| +MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate,
|
| + Handle<FunctionTemplateInfo> data,
|
| + Handle<Name> name = Handle<Name>());
|
| +
|
| +
|
| +MaybeHandle<Object> Instantiate(Isolate* isolate, Handle<Object> data,
|
| + Handle<Name> name = Handle<Name>()) {
|
| + if (data->IsFunctionTemplateInfo()) {
|
| + return InstantiateFunction(isolate,
|
| + Handle<FunctionTemplateInfo>::cast(data), name);
|
| + } else if (data->IsObjectTemplateInfo()) {
|
| + return InstantiateObject(isolate, Handle<ObjectTemplateInfo>::cast(data));
|
| + } else {
|
| + // TODO(dcarney): CHECK data is JSObject or Primitive.
|
| + return data;
|
| + }
|
| +}
|
| +
|
| +
|
| +class AccessCheckDisableScope {
|
| + public:
|
| + AccessCheckDisableScope(Isolate* isolate, Handle<JSObject> obj)
|
| + : isolate_(isolate),
|
| + disabled_(obj->map()->is_access_check_needed()),
|
| + obj_(obj) {
|
| + if (disabled_) {
|
| + DisableAccessChecks(isolate_, obj_);
|
| + }
|
| + }
|
| + ~AccessCheckDisableScope() {
|
| + if (disabled_) {
|
| + EnableAccessChecks(isolate_, obj_);
|
| + }
|
| + }
|
| +
|
| + private:
|
| + Isolate* isolate_;
|
| + const bool disabled_;
|
| + Handle<JSObject> obj_;
|
| +};
|
| +
|
| +
|
| +MaybeHandle<JSObject> ConfigureInstance(Isolate* isolate, Handle<JSObject> obj,
|
| + Handle<TemplateInfo> data) {
|
| + auto property_list = handle(data->property_list(), isolate);
|
| + if (property_list->IsUndefined()) return obj;
|
| + // TODO(dcarney): just use a FixedArray here.
|
| + NeanderArray properties(property_list);
|
| + if (properties.length() == 0) return obj;
|
| + HandleScope scope(isolate);
|
| + // Disable access checks while instantiating the object.
|
| + AccessCheckDisableScope access_check_scope(isolate, obj);
|
| + for (int i = 0; i < properties.length();) {
|
| + int length = Smi::cast(properties.get(i))->value();
|
| + if (length == 3) {
|
| + auto name = handle(Name::cast(properties.get(i + 1)), isolate);
|
| + auto prop_data = handle(properties.get(i + 2), isolate);
|
| + auto attributes = Smi::cast(properties.get(i + 3));
|
| + Handle<Object> value;
|
| + ASSIGN_RETURN_ON_EXCEPTION(
|
| + isolate, value, Instantiate(isolate, prop_data, name), JSObject);
|
| + RETURN_ON_EXCEPTION(isolate, AddPropertyForTemplate(isolate, obj, name,
|
| + value, attributes),
|
| + JSObject);
|
| + } else {
|
| + DCHECK(length == 4 || length == 5);
|
| + // TODO(verwaest): The 5th value used to be access_control. Remove once
|
| + // the bindings are updated.
|
| + auto name = handle(Name::cast(properties.get(i + 1)), isolate);
|
| + auto getter = handle(properties.get(i + 2), isolate);
|
| + auto setter = handle(properties.get(i + 3), isolate);
|
| + auto attributes = Smi::cast(properties.get(i + 4));
|
| + RETURN_ON_EXCEPTION(isolate,
|
| + DefineApiAccessorProperty(isolate, obj, name, getter,
|
| + setter, attributes),
|
| + JSObject);
|
| + }
|
| + i += length + 1;
|
| + }
|
| + return obj;
|
| +}
|
| +
|
| +
|
| +MaybeHandle<JSObject> InstantiateObject(Isolate* isolate,
|
| + Handle<ObjectTemplateInfo> data) {
|
| + // Enter a new scope. Recursion could otherwise create a lot of handles.
|
| + HandleScope scope(isolate);
|
| + // Fast path.
|
| + Handle<JSObject> result;
|
| + auto info = Handle<ObjectTemplateInfo>::cast(data);
|
| + auto constructor = handle(info->constructor(), isolate);
|
| + Handle<JSFunction> cons;
|
| + if (constructor->IsUndefined()) {
|
| + cons = isolate->object_function();
|
| + } else {
|
| + auto cons_templ = Handle<FunctionTemplateInfo>::cast(constructor);
|
| + ASSIGN_RETURN_ON_EXCEPTION(
|
| + isolate, cons, InstantiateFunction(isolate, cons_templ), JSFunction);
|
| + }
|
| + auto object = isolate->factory()->NewJSObject(cons);
|
| + ASSIGN_RETURN_ON_EXCEPTION(
|
| + isolate, result, ConfigureInstance(isolate, object, info), JSFunction);
|
| + // TODO(dcarney): is this necessary?
|
| + JSObject::MigrateSlowToFast(result, 0, "ApiNatives::InstantiateObject");
|
| + return scope.CloseAndEscape(result);
|
| +}
|
| +
|
| +
|
| +void InstallInCache(Isolate* isolate, int serial_number,
|
| + Handle<JSFunction> function) {
|
| + auto cache = isolate->function_cache();
|
| + if (cache->length() <= serial_number) {
|
| + int new_size;
|
| + if (isolate->next_serial_number() < 50) {
|
| + new_size = 100;
|
| + } else {
|
| + new_size = 3 * isolate->next_serial_number() / 2;
|
| + }
|
| + cache = FixedArray::CopySize(cache, new_size);
|
| + isolate->native_context()->set_function_cache(*cache);
|
| + }
|
| + cache->set(serial_number, *function);
|
| +}
|
| +
|
| +
|
| +MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate,
|
| + Handle<FunctionTemplateInfo> data,
|
| + Handle<Name> name) {
|
| + int serial_number = Smi::cast(data->serial_number())->value();
|
| + // Probe cache.
|
| + if (!data->do_not_cache()) {
|
| + auto cache = isolate->function_cache();
|
| + // Fast case: see if the function has already been instantiated
|
| + if (serial_number < cache->length()) {
|
| + Handle<Object> element = FixedArray::get(cache, serial_number);
|
| + if (element->IsJSFunction()) {
|
| + return Handle<JSFunction>::cast(element);
|
| + }
|
| + }
|
| + }
|
| + // Enter a new scope. Recursion could otherwise create a lot of handles.
|
| + HandleScope scope(isolate);
|
| + Handle<JSObject> prototype;
|
| + if (!data->remove_prototype()) {
|
| + auto prototype_templ = handle(data->prototype_template(), isolate);
|
| + if (prototype_templ->IsUndefined()) {
|
| + prototype = isolate->factory()->NewJSObject(isolate->object_function());
|
| + } else {
|
| + ASSIGN_RETURN_ON_EXCEPTION(
|
| + isolate, prototype,
|
| + InstantiateObject(isolate,
|
| + Handle<ObjectTemplateInfo>::cast(prototype_templ)),
|
| + JSFunction);
|
| + }
|
| + auto parent = handle(data->parent_template(), isolate);
|
| + if (!parent->IsUndefined()) {
|
| + Handle<JSFunction> parent_instance;
|
| + ASSIGN_RETURN_ON_EXCEPTION(
|
| + isolate, parent_instance,
|
| + InstantiateFunction(isolate,
|
| + Handle<FunctionTemplateInfo>::cast(parent)),
|
| + JSFunction);
|
| + // TODO(dcarney): decide what to do here.
|
| + Handle<Object> parent_prototype;
|
| + ASSIGN_RETURN_ON_EXCEPTION(
|
| + isolate, parent_prototype,
|
| + JSObject::GetProperty(parent_instance,
|
| + isolate->factory()->prototype_string()),
|
| + JSFunction);
|
| + RETURN_ON_EXCEPTION(
|
| + isolate, JSObject::SetPrototype(prototype, parent_prototype, false),
|
| + JSFunction);
|
| + }
|
| + }
|
| + auto function = ApiNatives::CreateApiFunction(
|
| + isolate, data, prototype, ApiNatives::JavaScriptObjectType);
|
| + if (!name.is_null() && name->IsString()) {
|
| + function->shared()->set_name(*name);
|
| + }
|
| + if (!data->do_not_cache()) {
|
| + // Cache the function to limit recursion.
|
| + InstallInCache(isolate, serial_number, function);
|
| + }
|
| + auto result = ConfigureInstance(isolate, function, data);
|
| + if (result.is_null()) {
|
| + // uncache on error.
|
| + if (!data->do_not_cache()) {
|
| + auto cache = isolate->function_cache();
|
| + cache->set(serial_number, isolate->heap()->undefined_value());
|
| + }
|
| + return MaybeHandle<JSFunction>();
|
| + }
|
| + return scope.CloseAndEscape(function);
|
| +}
|
| +
|
| +
|
| +class InvokeScope {
|
| + public:
|
| + explicit InvokeScope(Isolate* isolate)
|
| + : isolate_(isolate), save_context_(isolate) {}
|
| + ~InvokeScope() {
|
| + bool has_exception = isolate_->has_pending_exception();
|
| + if (has_exception) {
|
| + isolate_->ReportPendingMessages();
|
| + } else {
|
| + isolate_->clear_pending_message();
|
| + }
|
| + }
|
| +
|
| + private:
|
| + Isolate* isolate_;
|
| + SaveContext save_context_;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +
|
| +MaybeHandle<JSFunction> ApiNatives::InstantiateFunction(
|
| + Handle<FunctionTemplateInfo> data) {
|
| + Isolate* isolate = data->GetIsolate();
|
| + InvokeScope invoke_scope(isolate);
|
| + return ::v8::internal::InstantiateFunction(isolate, data);
|
| +}
|
| +
|
| +
|
| +MaybeHandle<JSObject> ApiNatives::InstantiateObject(
|
| + Handle<ObjectTemplateInfo> data) {
|
| + Isolate* isolate = data->GetIsolate();
|
| + InvokeScope invoke_scope(isolate);
|
| + return ::v8::internal::InstantiateObject(isolate, data);
|
| +}
|
| +
|
| +
|
| +MaybeHandle<FunctionTemplateInfo> ApiNatives::ConfigureInstance(
|
| + Isolate* isolate, Handle<FunctionTemplateInfo> desc,
|
| + Handle<JSObject> instance) {
|
| + // Configure the instance by adding the properties specified by the
|
| + // instance template.
|
| + if (desc->instance_template()->IsUndefined()) return desc;
|
| + InvokeScope invoke_scope(isolate);
|
| + Handle<ObjectTemplateInfo> instance_template(
|
| + ObjectTemplateInfo::cast(desc->instance_template()), isolate);
|
| + RETURN_ON_EXCEPTION(isolate, ::v8::internal::ConfigureInstance(
|
| + isolate, instance, instance_template),
|
| + FunctionTemplateInfo);
|
| + return desc;
|
| +}
|
| +
|
| +
|
| +Handle<JSFunction> ApiNatives::CreateApiFunction(
|
| + Isolate* isolate, Handle<FunctionTemplateInfo> obj,
|
| + Handle<Object> prototype, ApiInstanceType instance_type) {
|
| + Handle<Code> code = isolate->builtins()->HandleApiCall();
|
| + Handle<Code> construct_stub = isolate->builtins()->JSConstructStubApi();
|
| +
|
| + obj->set_instantiated(true);
|
| + Handle<JSFunction> result;
|
| + if (obj->remove_prototype()) {
|
| + result = isolate->factory()->NewFunctionWithoutPrototype(
|
| + isolate->factory()->empty_string(), code);
|
| + } else {
|
| + int internal_field_count = 0;
|
| + if (!obj->instance_template()->IsUndefined()) {
|
| + Handle<ObjectTemplateInfo> instance_template = Handle<ObjectTemplateInfo>(
|
| + ObjectTemplateInfo::cast(obj->instance_template()));
|
| + internal_field_count =
|
| + Smi::cast(instance_template->internal_field_count())->value();
|
| + }
|
| +
|
| + // TODO(svenpanne) Kill ApiInstanceType and refactor things by generalizing
|
| + // JSObject::GetHeaderSize.
|
| + int instance_size = kPointerSize * internal_field_count;
|
| + InstanceType type;
|
| + switch (instance_type) {
|
| + case JavaScriptObjectType:
|
| + type = JS_OBJECT_TYPE;
|
| + instance_size += JSObject::kHeaderSize;
|
| + break;
|
| + case GlobalObjectType:
|
| + type = JS_GLOBAL_OBJECT_TYPE;
|
| + instance_size += JSGlobalObject::kSize;
|
| + break;
|
| + case GlobalProxyType:
|
| + type = JS_GLOBAL_PROXY_TYPE;
|
| + instance_size += JSGlobalProxy::kSize;
|
| + break;
|
| + default:
|
| + UNREACHABLE();
|
| + type = JS_OBJECT_TYPE; // Keep the compiler happy.
|
| + break;
|
| + }
|
| +
|
| + result = isolate->factory()->NewFunction(
|
| + isolate->factory()->empty_string(), code, prototype, type,
|
| + instance_size, obj->read_only_prototype(), true);
|
| + }
|
| +
|
| + result->shared()->set_length(obj->length());
|
| + Handle<Object> class_name(obj->class_name(), isolate);
|
| + if (class_name->IsString()) {
|
| + result->shared()->set_instance_class_name(*class_name);
|
| + result->shared()->set_name(*class_name);
|
| + }
|
| + result->shared()->set_function_data(*obj);
|
| + result->shared()->set_construct_stub(*construct_stub);
|
| + result->shared()->DontAdaptArguments();
|
| +
|
| + if (obj->remove_prototype()) {
|
| + DCHECK(result->shared()->IsApiFunction());
|
| + DCHECK(!result->has_initial_map());
|
| + DCHECK(!result->has_prototype());
|
| + return result;
|
| + }
|
| +
|
| +#ifdef DEBUG
|
| + LookupIterator it(handle(JSObject::cast(result->prototype())),
|
| + isolate->factory()->constructor_string(),
|
| + LookupIterator::OWN_SKIP_INTERCEPTOR);
|
| + MaybeHandle<Object> maybe_prop = Object::GetProperty(&it);
|
| + DCHECK(it.IsFound());
|
| + DCHECK(maybe_prop.ToHandleChecked().is_identical_to(result));
|
| +#endif
|
| +
|
| + // Down from here is only valid for API functions that can be used as a
|
| + // constructor (don't set the "remove prototype" flag).
|
| +
|
| + Handle<Map> map(result->initial_map());
|
| +
|
| + // Mark as undetectable if needed.
|
| + if (obj->undetectable()) {
|
| + map->set_is_undetectable();
|
| + }
|
| +
|
| + // Mark as hidden for the __proto__ accessor if needed.
|
| + if (obj->hidden_prototype()) {
|
| + map->set_is_hidden_prototype();
|
| + }
|
| +
|
| + // Mark as needs_access_check if needed.
|
| + if (obj->needs_access_check()) {
|
| + map->set_is_access_check_needed(true);
|
| + }
|
| +
|
| + // Set interceptor information in the map.
|
| + if (!obj->named_property_handler()->IsUndefined()) {
|
| + map->set_has_named_interceptor();
|
| + }
|
| + if (!obj->indexed_property_handler()->IsUndefined()) {
|
| + map->set_has_indexed_interceptor();
|
| + }
|
| +
|
| + // Set instance call-as-function information in the map.
|
| + if (!obj->instance_call_handler()->IsUndefined()) {
|
| + map->set_has_instance_call_handler();
|
| + }
|
| +
|
| + // Recursively copy parent instance templates' accessors,
|
| + // 'data' may be modified.
|
| + int max_number_of_additional_properties = 0;
|
| + int max_number_of_static_properties = 0;
|
| + FunctionTemplateInfo* info = *obj;
|
| + while (true) {
|
| + if (!info->instance_template()->IsUndefined()) {
|
| + Object* props = ObjectTemplateInfo::cast(info->instance_template())
|
| + ->property_accessors();
|
| + if (!props->IsUndefined()) {
|
| + Handle<Object> props_handle(props, isolate);
|
| + NeanderArray props_array(props_handle);
|
| + max_number_of_additional_properties += props_array.length();
|
| + }
|
| + }
|
| + if (!info->property_accessors()->IsUndefined()) {
|
| + Object* props = info->property_accessors();
|
| + if (!props->IsUndefined()) {
|
| + Handle<Object> props_handle(props, isolate);
|
| + NeanderArray props_array(props_handle);
|
| + max_number_of_static_properties += props_array.length();
|
| + }
|
| + }
|
| + Object* parent = info->parent_template();
|
| + if (parent->IsUndefined()) break;
|
| + info = FunctionTemplateInfo::cast(parent);
|
| + }
|
| +
|
| + Map::EnsureDescriptorSlack(map, max_number_of_additional_properties);
|
| +
|
| + // Use a temporary FixedArray to acculumate static accessors
|
| + int valid_descriptors = 0;
|
| + Handle<FixedArray> array;
|
| + if (max_number_of_static_properties > 0) {
|
| + array = isolate->factory()->NewFixedArray(max_number_of_static_properties);
|
| + }
|
| +
|
| + while (true) {
|
| + // Install instance descriptors
|
| + if (!obj->instance_template()->IsUndefined()) {
|
| + Handle<ObjectTemplateInfo> instance = Handle<ObjectTemplateInfo>(
|
| + ObjectTemplateInfo::cast(obj->instance_template()), isolate);
|
| + Handle<Object> props =
|
| + Handle<Object>(instance->property_accessors(), isolate);
|
| + if (!props->IsUndefined()) {
|
| + Map::AppendCallbackDescriptors(map, props);
|
| + }
|
| + }
|
| + // Accumulate static accessors
|
| + if (!obj->property_accessors()->IsUndefined()) {
|
| + Handle<Object> props = Handle<Object>(obj->property_accessors(), isolate);
|
| + valid_descriptors =
|
| + AccessorInfo::AppendUnique(props, array, valid_descriptors);
|
| + }
|
| + // Climb parent chain
|
| + Handle<Object> parent = Handle<Object>(obj->parent_template(), isolate);
|
| + if (parent->IsUndefined()) break;
|
| + obj = Handle<FunctionTemplateInfo>::cast(parent);
|
| + }
|
| +
|
| + // Install accumulated static accessors
|
| + for (int i = 0; i < valid_descriptors; i++) {
|
| + Handle<AccessorInfo> accessor(AccessorInfo::cast(array->get(i)));
|
| + JSObject::SetAccessor(result, accessor).Assert();
|
| + }
|
| +
|
| + DCHECK(result->shared()->IsApiFunction());
|
| + return result;
|
| +}
|
| +
|
| +} // namespace internal
|
| +} // namespace v8
|
|
|