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 { ...@@ -5094,7 +5094,11 @@ class V8_EXPORT FunctionTemplate : public Template {
/** Get the InstanceTemplate. */ /** Get the InstanceTemplate. */
Local<ObjectTemplate> 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); void Inherit(Local<FunctionTemplate> parent);
/** /**
...@@ -5103,6 +5107,14 @@ class V8_EXPORT FunctionTemplate : public Template { ...@@ -5103,6 +5107,14 @@ class V8_EXPORT FunctionTemplate : public Template {
*/ */
Local<ObjectTemplate> PrototypeTemplate(); 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 * Set the class name of the FunctionTemplate. This is used for
* printing objects created with the function created from the * printing objects created with the function created from the
......
...@@ -395,6 +395,28 @@ MaybeHandle<JSObject> InstantiateObject(Isolate* isolate, ...@@ -395,6 +395,28 @@ MaybeHandle<JSObject> InstantiateObject(Isolate* isolate,
return result; 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, MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate,
Handle<FunctionTemplateInfo> data, Handle<FunctionTemplateInfo> data,
...@@ -406,11 +428,18 @@ MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate, ...@@ -406,11 +428,18 @@ MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate,
return Handle<JSFunction>::cast(result); return Handle<JSFunction>::cast(result);
} }
} }
Handle<JSObject> prototype; Handle<Object> prototype;
if (!data->remove_prototype()) { if (!data->remove_prototype()) {
Object* prototype_templ = data->prototype_template(); Object* prototype_templ = data->prototype_template();
if (prototype_templ->IsUndefined(isolate)) { 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 { } else {
ASSIGN_RETURN_ON_EXCEPTION( ASSIGN_RETURN_ON_EXCEPTION(
isolate, prototype, isolate, prototype,
...@@ -422,22 +451,12 @@ MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate, ...@@ -422,22 +451,12 @@ MaybeHandle<JSFunction> InstantiateFunction(Isolate* isolate,
} }
Object* parent = data->parent_template(); Object* parent = data->parent_template();
if (!parent->IsUndefined(isolate)) { 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; Handle<Object> parent_prototype;
ASSIGN_RETURN_ON_EXCEPTION( ASSIGN_RETURN_ON_EXCEPTION(isolate, parent_prototype,
isolate, parent_prototype, GetInstancePrototype(isolate, parent),
JSObject::GetProperty(parent_instance, JSFunction);
isolate->factory()->prototype_string()), JSObject::ForceSetPrototype(Handle<JSObject>::cast(prototype),
JSFunction); parent_prototype);
JSObject::ForceSetPrototype(prototype, parent_prototype);
} }
} }
Handle<JSFunction> function = ApiNatives::CreateApiFunction( Handle<JSFunction> function = ApiNatives::CreateApiFunction(
...@@ -606,7 +625,7 @@ Handle<JSFunction> ApiNatives::CreateApiFunction( ...@@ -606,7 +625,7 @@ Handle<JSFunction> ApiNatives::CreateApiFunction(
if (prototype->IsTheHole(isolate)) { if (prototype->IsTheHole(isolate)) {
prototype = isolate->factory()->NewFunctionPrototype(result); prototype = isolate->factory()->NewFunctionPrototype(result);
} else { } else if (obj->prototype_provider_template()->IsUndefined(isolate)) {
JSObject::AddProperty(Handle<JSObject>::cast(prototype), JSObject::AddProperty(Handle<JSObject>::cast(prototype),
isolate->factory()->constructor_string(), result, isolate->factory()->constructor_string(), result,
DONT_ENUM); DONT_ENUM);
......
...@@ -1174,6 +1174,16 @@ Local<ObjectTemplate> FunctionTemplate::PrototypeTemplate() { ...@@ -1174,6 +1174,16 @@ Local<ObjectTemplate> FunctionTemplate::PrototypeTemplate() {
return ToApiHandle<ObjectTemplate>(result); 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, static void EnsureNotInstantiated(i::Handle<i::FunctionTemplateInfo> info,
const char* func) { const char* func) {
...@@ -1185,8 +1195,9 @@ static void EnsureNotInstantiated(i::Handle<i::FunctionTemplateInfo> info, ...@@ -1185,8 +1195,9 @@ static void EnsureNotInstantiated(i::Handle<i::FunctionTemplateInfo> info,
void FunctionTemplate::Inherit(v8::Local<FunctionTemplate> value) { void FunctionTemplate::Inherit(v8::Local<FunctionTemplate> value) {
auto info = Utils::OpenHandle(this); auto info = Utils::OpenHandle(this);
EnsureNotInstantiated(info, "v8::FunctionTemplate::Inherit"); EnsureNotInstantiated(info, "v8::FunctionTemplate::Inherit");
i::Isolate* isolate = info->GetIsolate(); i::Isolate* i_isolate = info->GetIsolate();
ENTER_V8(isolate); ENTER_V8(i_isolate);
CHECK(info->prototype_provider_template()->IsUndefined(i_isolate));
info->set_parent_template(*Utils::OpenHandle(*value)); info->set_parent_template(*Utils::OpenHandle(*value));
} }
......
...@@ -5840,6 +5840,9 @@ ACCESSORS(TemplateInfo, property_accessors, Object, kPropertyAccessorsOffset) ...@@ -5840,6 +5840,9 @@ ACCESSORS(TemplateInfo, property_accessors, Object, kPropertyAccessorsOffset)
ACCESSORS(FunctionTemplateInfo, call_code, Object, kCallCodeOffset) ACCESSORS(FunctionTemplateInfo, call_code, Object, kCallCodeOffset)
ACCESSORS(FunctionTemplateInfo, prototype_template, Object, ACCESSORS(FunctionTemplateInfo, prototype_template, Object,
kPrototypeTemplateOffset) kPrototypeTemplateOffset)
ACCESSORS(FunctionTemplateInfo, prototype_provider_template, Object,
kPrototypeProviderTemplateOffset)
ACCESSORS(FunctionTemplateInfo, parent_template, Object, kParentTemplateOffset) ACCESSORS(FunctionTemplateInfo, parent_template, Object, kParentTemplateOffset)
ACCESSORS(FunctionTemplateInfo, named_property_handler, Object, ACCESSORS(FunctionTemplateInfo, named_property_handler, Object,
kNamedPropertyHandlerOffset) kNamedPropertyHandlerOffset)
......
...@@ -11586,6 +11586,7 @@ class FunctionTemplateInfo: public TemplateInfo { ...@@ -11586,6 +11586,7 @@ class FunctionTemplateInfo: public TemplateInfo {
public: public:
DECL_ACCESSORS(call_code, Object) DECL_ACCESSORS(call_code, Object)
DECL_ACCESSORS(prototype_template, Object) DECL_ACCESSORS(prototype_template, Object)
DECL_ACCESSORS(prototype_provider_template, Object)
DECL_ACCESSORS(parent_template, Object) DECL_ACCESSORS(parent_template, Object)
DECL_ACCESSORS(named_property_handler, Object) DECL_ACCESSORS(named_property_handler, Object)
DECL_ACCESSORS(indexed_property_handler, Object) DECL_ACCESSORS(indexed_property_handler, Object)
...@@ -11623,8 +11624,10 @@ class FunctionTemplateInfo: public TemplateInfo { ...@@ -11623,8 +11624,10 @@ class FunctionTemplateInfo: public TemplateInfo {
static const int kCallCodeOffset = TemplateInfo::kHeaderSize; static const int kCallCodeOffset = TemplateInfo::kHeaderSize;
static const int kPrototypeTemplateOffset = static const int kPrototypeTemplateOffset =
kCallCodeOffset + kPointerSize; kCallCodeOffset + kPointerSize;
static const int kParentTemplateOffset = static const int kPrototypeProviderTemplateOffset =
kPrototypeTemplateOffset + kPointerSize; kPrototypeTemplateOffset + kPointerSize;
static const int kParentTemplateOffset =
kPrototypeProviderTemplateOffset + kPointerSize;
static const int kNamedPropertyHandlerOffset = static const int kNamedPropertyHandlerOffset =
kParentTemplateOffset + kPointerSize; kParentTemplateOffset + kPointerSize;
static const int kIndexedPropertyHandlerOffset = static const int kIndexedPropertyHandlerOffset =
......
...@@ -26159,3 +26159,29 @@ TEST(InternalFieldsOnDataView) { ...@@ -26159,3 +26159,29 @@ TEST(InternalFieldsOnDataView) {
array->GetAlignedPointerFromInternalField(i)); 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