Commit 4ef4deae authored by Creddy's avatar Creddy Committed by Commit Bot

[runtime] Change the default values of Proxy.prototype to undefined from null


The CSA fast path returned null for Proxy.prototype whereas runtime GetProperty
returned undefined. The CL fixes this discrepancy by returning undefined for
both cases and  this makes it complaint with the spec.

Change-Id: I35b75c09dc99e8fd629671e30eacd2cabea8c1d4
Reviewed-on: https://chromium-review.googlesource.com/1145438Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Commit-Queue: Chandan Reddy <chandanreddy@google.com>
Cr-Commit-Position: refs/heads/master@{#54745}
parent 85d9cd7e
......@@ -327,6 +327,7 @@ void Accessors::FunctionPrototypeGetter(
HandleScope scope(isolate);
Handle<JSFunction> function =
Handle<JSFunction>::cast(Utils::OpenHandle(*info.Holder()));
DCHECK(function->has_prototype_property());
Handle<Object> result = GetFunctionPrototype(isolate, function);
info.GetReturnValue().Set(Utils::ToLocal(result));
}
......@@ -341,6 +342,7 @@ void Accessors::FunctionPrototypeSetter(
Handle<Object> value = Utils::OpenHandle(*val);
Handle<JSFunction> object =
Handle<JSFunction>::cast(Utils::OpenHandle(*info.Holder()));
DCHECK(object->has_prototype_property());
JSFunction::SetPrototype(object, value);
info.GetReturnValue().Set(true);
}
......
......@@ -3436,21 +3436,11 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
{ // -- P r o x y
CreateJSProxyMaps();
// Proxy function map has prototype slot for storing initial map but does
// not have a prototype property.
Handle<Map> proxy_function_map = Map::Copy(
isolate_, isolate_->strict_function_without_prototype_map(), "Proxy");
// Re-set the unused property fields after changing the instance size.
// TODO(ulan): Do not change instance size after map creation.
int unused_property_fields = proxy_function_map->UnusedPropertyFields();
proxy_function_map->set_instance_size(JSFunction::kSizeWithPrototype);
// The prototype slot shifts the in-object properties area by one slot.
proxy_function_map->SetInObjectPropertiesStartInWords(
proxy_function_map->GetInObjectPropertiesStartInWords() + 1);
proxy_function_map->set_has_prototype_slot(true);
proxy_function_map->set_is_constructor(true);
proxy_function_map->SetInObjectUnusedPropertyFields(unused_property_fields);
Handle<String> name = factory->Proxy_string();
......@@ -3458,9 +3448,6 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
name, proxy_function_map, Builtins::kProxyConstructor);
Handle<JSFunction> proxy_function = factory->NewFunction(args);
JSFunction::SetInitialMap(proxy_function, isolate_->proxy_map(),
factory->null_value());
proxy_function->shared()->set_internal_formal_parameter_count(2);
proxy_function->shared()->set_length(2);
......@@ -3468,6 +3455,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
InstallFunction(isolate_, global, name, proxy_function,
factory->Object_string());
DCHECK(!proxy_function->has_prototype_property());
SimpleInstallFunction(isolate_, proxy_function, "revocable",
Builtins::kProxyRevocable, 2, true);
......
......@@ -2436,6 +2436,48 @@ TNode<Map> CodeStubAssembler::LoadJSArrayElementsMap(
LoadContextElement(native_context, Context::ArrayMapIndex(kind)));
}
TNode<Word32T> CodeStubAssembler::IsGeneratorFunction(
TNode<JSFunction> function) {
TNode<SharedFunctionInfo> const shared_function_info =
CAST(LoadObjectField(function, JSFunction::kSharedFunctionInfoOffset));
TNode<Uint32T> const function_kind =
DecodeWord32<SharedFunctionInfo::FunctionKindBits>(LoadObjectField(
shared_function_info, SharedFunctionInfo::kFlagsOffset,
MachineType::Uint32()));
return Word32Or(
Word32Or(
Word32Or(
Word32Equal(function_kind,
Int32Constant(FunctionKind::kAsyncGeneratorFunction)),
Word32Equal(
function_kind,
Int32Constant(FunctionKind::kAsyncConciseGeneratorMethod))),
Word32Equal(function_kind,
Int32Constant(FunctionKind::kGeneratorFunction))),
Word32Equal(function_kind,
Int32Constant(FunctionKind::kConciseGeneratorMethod)));
}
TNode<Word32T> CodeStubAssembler::HasPrototypeProperty(
TNode<JSFunction> function) {
TNode<Int32T> mask = Int32Constant(Map::HasPrototypeSlotBit::kMask |
Map::IsConstructorBit::kMask);
return Word32Or(
Word32Equal(Word32And(LoadMapBitField(LoadMap(function)), mask), mask),
IsGeneratorFunction(function));
}
TNode<Word32T> CodeStubAssembler::PrototypeRequiresRuntimeLookup(
TNode<JSFunction> function) {
TNode<Map> map = LoadMap(function);
return Word32Or(
Word32Not(HasPrototypeProperty(function)),
IsSetWord32<Map::HasNonInstancePrototypeBit>(LoadMapBitField(map)));
}
Node* CodeStubAssembler::LoadJSFunctionPrototype(Node* function,
Label* if_bailout) {
CSA_ASSERT(this, TaggedIsNotSmi(function));
......@@ -8256,15 +8298,7 @@ TNode<Object> CodeStubAssembler::CallGetterIfAccessor(
LoadObjectField(accessor_info, AccessorInfo::kNameOffset)),
if_bailout);
// if (!(has_prototype_slot() && !has_non_instance_prototype())) use
// generic property loading mechanism.
GotoIfNot(
Word32Equal(
Word32And(LoadMapBitField(receiver_map),
Int32Constant(Map::HasPrototypeSlotBit::kMask |
Map::HasNonInstancePrototypeBit::kMask)),
Int32Constant(Map::HasPrototypeSlotBit::kMask)),
if_bailout);
GotoIf(PrototypeRequiresRuntimeLookup(CAST(receiver)), if_bailout);
var_value.Bind(LoadJSFunctionPrototype(receiver, if_bailout));
Goto(&done);
}
......@@ -8717,15 +8751,7 @@ Node* CodeStubAssembler::OrdinaryHasInstance(Node* context, Node* callable,
GotoIfNot(InstanceTypeEqual(callable_instance_type, JS_FUNCTION_TYPE),
&return_runtime);
// Goto runtime if {callable} is not a constructor or has
// a non-instance "prototype".
Node* callable_bitfield = LoadMapBitField(callable_map);
GotoIfNot(Word32Equal(
Word32And(callable_bitfield,
Int32Constant(Map::HasNonInstancePrototypeBit::kMask |
Map::IsConstructorBit::kMask)),
Int32Constant(Map::IsConstructorBit::kMask)),
&return_runtime);
GotoIf(PrototypeRequiresRuntimeLookup(CAST(callable)), &return_runtime);
// Get the "prototype" (or initial map) of the {callable}.
Node* callable_prototype =
......
......@@ -1109,6 +1109,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
TNode<Map> LoadJSArrayElementsMap(SloppyTNode<Int32T> kind,
SloppyTNode<Context> native_context);
TNode<Word32T> IsGeneratorFunction(TNode<JSFunction> function);
TNode<Word32T> HasPrototypeProperty(TNode<JSFunction> function);
TNode<Word32T> PrototypeRequiresRuntimeLookup(TNode<JSFunction> function);
// Load the "prototype" property of a JSFunction.
Node* LoadJSFunctionPrototype(Node* function, Label* if_bailout);
......
......@@ -1102,7 +1102,7 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
p.name().is_identical_to(factory()->prototype_string())) {
// Optimize "prototype" property of functions.
Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
if (function->IsConstructor()) {
if (!function->PrototypeRequiresRuntimeLookup()) {
// We need to add a code dependency on the initial map of the
// {function} in order to be notified about changes to the
// "prototype" of {function}.
......
......@@ -2493,15 +2493,8 @@ void AccessorAssembler::LoadIC_Uninitialized(const LoadICParameters* p) {
&not_function_prototype);
GotoIfNot(IsPrototypeString(p->name), &not_function_prototype);
// if (!(has_prototype_slot() && !has_non_instance_prototype())) use generic
// property loading mechanism.
GotoIfNot(
Word32Equal(
Word32And(LoadMapBitField(receiver_map),
Int32Constant(Map::HasPrototypeSlotBit::kMask |
Map::HasNonInstancePrototypeBit::kMask)),
Int32Constant(Map::HasPrototypeSlotBit::kMask)),
&not_function_prototype);
GotoIf(PrototypeRequiresRuntimeLookup(CAST(receiver)),
&not_function_prototype);
Return(LoadJSFunctionPrototype(receiver, &miss));
BIND(&not_function_prototype);
}
......
......@@ -753,8 +753,7 @@ Handle<Object> LoadIC::ComputeHandler(LookupIterator* lookup) {
// Use specialized code for getting prototype of functions.
if (receiver->IsJSFunction() && *lookup->name() == roots.prototype_string() &&
JSFunction::cast(*receiver)->has_prototype_slot() &&
!JSFunction::cast(*receiver)->map()->has_non_instance_prototype()) {
!JSFunction::cast(*receiver)->PrototypeRequiresRuntimeLookup()) {
Handle<Code> stub;
TRACE_HANDLER_STATS(isolate(), LoadIC_FunctionPrototypeStub);
return BUILTIN_CODE(isolate(), LoadIC_FunctionPrototype);
......
......@@ -938,9 +938,21 @@ void JSFunction::JSFunctionVerify(Isolate* isolate) {
CHECK(feedback_cell()->IsFeedbackCell());
CHECK(code()->IsCode());
CHECK(map()->is_callable());
Handle<JSFunction> function(this, isolate);
LookupIterator it(isolate, function, isolate->factory()->prototype_string(),
LookupIterator::OWN_SKIP_INTERCEPTOR);
if (has_prototype_slot()) {
VerifyObjectField(isolate, kPrototypeOrInitialMapOffset);
}
if (has_prototype_property()) {
CHECK(it.IsFound());
CHECK_EQ(LookupIterator::ACCESSOR, it.state());
CHECK(it.GetAccessors()->IsAccessorInfo());
} else {
CHECK(!it.IsFound() || it.state() != LookupIterator::ACCESSOR ||
!it.GetAccessors()->IsAccessorInfo());
}
}
void SharedFunctionInfo::SharedFunctionInfoVerify(Isolate* isolate) {
......
......@@ -2607,6 +2607,14 @@ bool JSFunction::has_prototype() {
return map()->has_non_instance_prototype() || has_instance_prototype();
}
bool JSFunction::has_prototype_property() {
return (has_prototype_slot() && IsConstructor()) ||
IsGeneratorFunction(shared()->kind());
}
bool JSFunction::PrototypeRequiresRuntimeLookup() {
return !has_prototype_property() || map()->has_non_instance_prototype();
}
Object* JSFunction::instance_prototype() {
DCHECK(has_instance_prototype());
......
......@@ -3136,6 +3136,8 @@ class JSFunction: public JSObject {
inline bool has_instance_prototype();
inline Object* prototype();
inline Object* instance_prototype();
inline bool has_prototype_property();
inline bool PrototypeRequiresRuntimeLookup();
static void SetPrototype(Handle<JSFunction> function,
Handle<Object> value);
......
......@@ -55,7 +55,7 @@ function TestWithFunctionProxy(test, x, y, z) {
(function TestProxyProperties() {
assertEquals(2, Proxy.length);
assertEquals(Function.__proto__, Proxy.__proto__);
assertEquals(null, Proxy.prototype);
assertEquals(undefined, Proxy.prototype);
assertEquals(undefined, Object.getOwnPropertyDescriptor(Proxy, "arguments"));
assertThrows(() => Proxy.arguments, TypeError);
assertEquals(undefined, Object.getOwnPropertyDescriptor(Proxy, "caller"));
......
......@@ -4,11 +4,63 @@
// Flags: --allow-natives-syntax
g = async function () {
await 10;
}
assertEquals(undefined, g.prototype)
g();
assertEquals(undefined, g.prototype)
gen = function* () {
yield 10;
}
assertTrue(gen.prototype != undefined && gen.prototype != null)
gen()
assertTrue(gen.prototype != undefined && gen.prototype != null)
async_gen = async function* () {
yield 10;
}
assertTrue(async_gen.prototype != undefined && async_gen.prototype != null)
async_gen()
assertTrue(async_gen.prototype != undefined && async_gen.prototype != null)
function foo(x) {
return x instanceof Proxy;
}
assertFalse(foo({}));
assertFalse(foo({}));
function test_for_exception() {
caught_exception = false;
try {
foo({});
} catch (e) {
caught_exception = true;
assertEquals(
'Function has non-object prototype \'undefined\' in instanceof check',
e.message);
} finally {
assertTrue(caught_exception)
}
}
test_for_exception();
test_for_exception();
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo({}));
test_for_exception();
Proxy.__proto__.prototype = Function.prototype;
assertTrue((() => {}) instanceof Proxy);
assertEquals(
new Proxy({}, {
get(o, s) {
return s
}
}).test,
'test');
Proxy.__proto__ = {
prototype: {b: 2},
a: 1
};
assertEquals(Proxy.prototype, {b: 2});
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