Commit 85321456 authored by cbruni's avatar cbruni Committed by Commit bot

[api] Support sharing prototypes between FunctionTemplates

FunctionTemplateInfo::SetPrototypeProviderTemplate adds support for sharing
prototypes between several function templates. This is used to properly set up
Image.prototype and HTMLImageElement.protoype which should be equal according
to the spec.

BUG=chromium:2969

Review-Url: https://codereview.chromium.org/2531653002
Cr-Commit-Position: refs/heads/master@{#41343}
parent 810fcb28
......@@ -5094,7 +5094,11 @@ class V8_EXPORT FunctionTemplate : public Template {
/** Get the InstanceTemplate. */
Local<ObjectTemplate> InstanceTemplate();
/** Causes the function template to inherit from a parent function template.*/
/**
* Causes the function template to inherit from a parent function template.
* This means the the function's prototype.__proto__ is set to the parent
* function's prototype.
**/
void Inherit(Local<FunctionTemplate> parent);
/**
......@@ -5103,6 +5107,14 @@ class V8_EXPORT FunctionTemplate : public Template {
*/
Local<ObjectTemplate> PrototypeTemplate();
/**
* A PrototypeProviderTemplate is another function template whose prototype
* property is used for this template. This is mutually exclusive with setting
* a prototype template indirectly by calling PrototypeTemplate() or using
* Inherit().
**/
void SetPrototypeProviderTemplate(Local<FunctionTemplate> prototype_provider);
/**
* Set the class name of the FunctionTemplate. This is used for
* printing objects created with the function created from the
......
......@@ -395,6 +395,28 @@ MaybeHandle<JSObject> InstantiateObject(Isolate* isolate,
return result;
}
namespace {
MaybeHandle<Object> GetInstancePrototype(Isolate* isolate,
Object* function_template) {
// Enter a new scope. Recursion could otherwise create a lot of handles.
HandleScope scope(isolate);
Handle<JSFunction> parent_instance;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, parent_instance,
InstantiateFunction(
isolate,
handle(FunctionTemplateInfo::cast(function_template), isolate)),
JSFunction);
Handle<Object> instance_prototype;
// TODO(cbruni): decide what to do here.
ASSIGN_RETURN_ON_EXCEPTION(
isolate, instance_prototype,
JSObject::GetProperty(parent_instance,
isolate->factory()->prototype_string()),
JSFunction);
return scope.CloseAndEscape(instance_prototype);
}
} // namespace
MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate,
Handle<FunctionTemplateInfo> data,
......@@ -406,11 +428,18 @@ MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate,
return Handle<JSFunction>::cast(result);
}
}
Handle<JSObject> prototype;
Handle<Object> prototype;
if (!data->remove_prototype()) {
Object* prototype_templ = data->prototype_template();
if (prototype_templ->IsUndefined(isolate)) {
prototype = isolate->factory()->NewJSObject(isolate->object_function());
Object* protoype_provider_templ = data->prototype_provider_template();
if (protoype_provider_templ->IsUndefined(isolate)) {
prototype = isolate->factory()->NewJSObject(isolate->object_function());
} else {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, prototype,
GetInstancePrototype(isolate, protoype_provider_templ), JSFunction);
}
} else {
ASSIGN_RETURN_ON_EXCEPTION(
isolate, prototype,
......@@ -422,22 +451,12 @@ MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate,
}
Object* parent = data->parent_template();
if (!parent->IsUndefined(isolate)) {
// Enter a new scope. Recursion could otherwise create a lot of handles.
HandleScope scope(isolate);
Handle<JSFunction> parent_instance;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, parent_instance,
InstantiateFunction(
isolate, handle(FunctionTemplateInfo::cast(parent), isolate)),
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);
JSObject::ForceSetPrototype(prototype, parent_prototype);
ASSIGN_RETURN_ON_EXCEPTION(isolate, parent_prototype,
GetInstancePrototype(isolate, parent),
JSFunction);
JSObject::ForceSetPrototype(Handle<JSObject>::cast(prototype),
parent_prototype);
}
}
Handle<JSFunction> function = ApiNatives::CreateApiFunction(
......@@ -606,7 +625,7 @@ Handle<JSFunction> ApiNatives::CreateApiFunction(
if (prototype->IsTheHole(isolate)) {
prototype = isolate->factory()->NewFunctionPrototype(result);
} else {
} else if (obj->prototype_provider_template()->IsUndefined(isolate)) {
JSObject::AddProperty(Handle<JSObject>::cast(prototype),
isolate->factory()->constructor_string(), result,
DONT_ENUM);
......
......@@ -1174,6 +1174,16 @@ Local<ObjectTemplate> FunctionTemplate::PrototypeTemplate() {
return ToApiHandle<ObjectTemplate>(result);
}
void FunctionTemplate::SetPrototypeProviderTemplate(
Local<FunctionTemplate> prototype_provider) {
i::Isolate* i_isolate = Utils::OpenHandle(this)->GetIsolate();
ENTER_V8(i_isolate);
i::Handle<i::Object> result = Utils::OpenHandle(*prototype_provider);
auto info = Utils::OpenHandle(this);
CHECK(info->prototype_template()->IsUndefined(i_isolate));
CHECK(info->parent_template()->IsUndefined(i_isolate));
info->set_prototype_provider_template(*result);
}
static void EnsureNotInstantiated(i::Handle<i::FunctionTemplateInfo> info,
const char* func) {
......@@ -1185,8 +1195,9 @@ static void EnsureNotInstantiated(i::Handle<i::FunctionTemplateInfo> info,
void FunctionTemplate::Inherit(v8::Local<FunctionTemplate> value) {
auto info = Utils::OpenHandle(this);
EnsureNotInstantiated(info, "v8::FunctionTemplate::Inherit");
i::Isolate* isolate = info->GetIsolate();
ENTER_V8(isolate);
i::Isolate* i_isolate = info->GetIsolate();
ENTER_V8(i_isolate);
CHECK(info->prototype_provider_template()->IsUndefined(i_isolate));
info->set_parent_template(*Utils::OpenHandle(*value));
}
......
......@@ -5840,6 +5840,9 @@ ACCESSORS(TemplateInfo, property_accessors, Object, kPropertyAccessorsOffset)
ACCESSORS(FunctionTemplateInfo, call_code, Object, kCallCodeOffset)
ACCESSORS(FunctionTemplateInfo, prototype_template, Object,
kPrototypeTemplateOffset)
ACCESSORS(FunctionTemplateInfo, prototype_provider_template, Object,
kPrototypeProviderTemplateOffset)
ACCESSORS(FunctionTemplateInfo, parent_template, Object, kParentTemplateOffset)
ACCESSORS(FunctionTemplateInfo, named_property_handler, Object,
kNamedPropertyHandlerOffset)
......
......@@ -11586,6 +11586,7 @@ class FunctionTemplateInfo: public TemplateInfo {
public:
DECL_ACCESSORS(call_code, Object)
DECL_ACCESSORS(prototype_template, Object)
DECL_ACCESSORS(prototype_provider_template, Object)
DECL_ACCESSORS(parent_template, Object)
DECL_ACCESSORS(named_property_handler, Object)
DECL_ACCESSORS(indexed_property_handler, Object)
......@@ -11623,8 +11624,10 @@ class FunctionTemplateInfo: public TemplateInfo {
static const int kCallCodeOffset = TemplateInfo::kHeaderSize;
static const int kPrototypeTemplateOffset =
kCallCodeOffset + kPointerSize;
static const int kParentTemplateOffset =
static const int kPrototypeProviderTemplateOffset =
kPrototypeTemplateOffset + kPointerSize;
static const int kParentTemplateOffset =
kPrototypeProviderTemplateOffset + kPointerSize;
static const int kNamedPropertyHandlerOffset =
kParentTemplateOffset + kPointerSize;
static const int kIndexedPropertyHandlerOffset =
......
......@@ -26159,3 +26159,29 @@ TEST(InternalFieldsOnDataView) {
array->GetAlignedPointerFromInternalField(i));
}
}
TEST(SetPrototypeTemplate) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
Local<FunctionTemplate> HTMLElementTemplate = FunctionTemplate::New(isolate);
Local<FunctionTemplate> HTMLImageElementTemplate =
FunctionTemplate::New(isolate);
HTMLImageElementTemplate->Inherit(HTMLElementTemplate);
Local<FunctionTemplate> ImageTemplate = FunctionTemplate::New(isolate);
ImageTemplate->SetPrototypeProviderTemplate(HTMLImageElementTemplate);
Local<Function> HTMLImageElement =
HTMLImageElementTemplate->GetFunction(env.local()).ToLocalChecked();
Local<Function> Image =
ImageTemplate->GetFunction(env.local()).ToLocalChecked();
CHECK(env->Global()
->Set(env.local(), v8_str("HTMLImageElement"), HTMLImageElement)
.FromJust());
CHECK(env->Global()->Set(env.local(), v8_str("Image"), Image).FromJust());
ExpectTrue("Image.prototype === HTMLImageElement.prototype");
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment