Reland ^3 "[builtins] Port getting property from Proxy to CSA""

This is a reland of e86c066b77
 
With fixes for crbug.com/752846, crbug.com/752712, crbug.com/752850

Previously landed as: 47a97aa53b / 47113
Previously landed as: 15ef03cbf3 / 47159

TBR=jkummerow@chromium.org, franzih@chromium.org, bmeurer@chromium.org,
jgruber@chromium.org, mstarzinger@chromium.org

Bug: v8:6559, v8:6557
Change-Id: I12ccae44331b05dd3f304ac538c0154133b43c35
Reviewed-on: https://chromium-review.googlesource.com/608187
Reviewed-by: Georg Neis <neis@chromium.org>
Commit-Queue: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47245}
This commit is contained in:
Maya Lekova 2017-08-09 09:27:11 +02:00 committed by Commit Bot
parent c5263279eb
commit edc4ae14c8
20 changed files with 501 additions and 34 deletions

View File

@ -994,6 +994,8 @@ v8_source_set("v8_builtins_generators") {
"src/builtins/builtins-promise-gen.cc", "src/builtins/builtins-promise-gen.cc",
"src/builtins/builtins-promise-gen.h", "src/builtins/builtins-promise-gen.h",
"src/builtins/builtins-proxy-gen.cc", "src/builtins/builtins-proxy-gen.cc",
"src/builtins/builtins-proxy-helpers-gen.cc",
"src/builtins/builtins-proxy-helpers-gen.h",
"src/builtins/builtins-regexp-gen.cc", "src/builtins/builtins-regexp-gen.cc",
"src/builtins/builtins-regexp-gen.h", "src/builtins/builtins-regexp-gen.h",
"src/builtins/builtins-sharedarraybuffer-gen.cc", "src/builtins/builtins-sharedarraybuffer-gen.cc",

View File

@ -802,6 +802,7 @@ namespace internal {
TFJ(ProxyConstructor, 0) \ TFJ(ProxyConstructor, 0) \
TFJ(ProxyConstructor_ConstructStub, \ TFJ(ProxyConstructor_ConstructStub, \
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \ SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
TFS(ProxyGetProperty, kProxy, kName, kReceiverValue) \
\ \
/* Reflect */ \ /* Reflect */ \
ASM(ReflectApply) \ ASM(ReflectApply) \

View File

@ -0,0 +1,160 @@
// Copyright 2017 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/builtins/builtins-proxy-helpers-gen.h"
#include "src/builtins/builtins-utils-gen.h"
namespace v8 {
namespace internal {
TF_BUILTIN(ProxyGetProperty, ProxyAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* proxy = Parameter(Descriptor::kProxy);
Node* name = Parameter(Descriptor::kName);
Node* receiver = Parameter(Descriptor::kReceiverValue);
CSA_ASSERT(this, IsJSProxy(proxy));
// 1. Assert: IsPropertyKey(P) is true.
CSA_ASSERT(this, IsName(name));
Label throw_proxy_handler_revoked(this, Label::kDeferred),
trap_undefined(this), no_target_desc(this, Label::kDeferred),
trap_not_callable(this, Label::kDeferred);
// 2. Let handler be O.[[ProxyHandler]].
Node* handler = LoadObjectField(proxy, JSProxy::kHandlerOffset);
// 3. If handler is null, throw a TypeError exception.
GotoIf(IsNull(handler), &throw_proxy_handler_revoked);
// 4. Assert: Type(handler) is Object.
CSA_ASSERT(this, IsJSReceiver(handler));
// 5. Let target be O.[[ProxyTarget]].
Node* target = LoadObjectField(proxy, JSProxy::kTargetOffset);
// 6. Let trap be ? GetMethod(handler, "get").
// 7. If trap is undefined, then (see 7.a below).
Handle<Name> trap_name = factory()->get_string();
Node* trap = GetMethod(context, handler, trap_name, &trap_undefined);
GotoIf(TaggedIsSmi(trap), &trap_not_callable);
GotoIfNot(IsCallable(trap), &trap_not_callable);
// 8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »).
Node* trap_result = CallJS(CodeFactory::Call(isolate()), context, trap,
handler, target, name, receiver);
// 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
Label return_result(this);
CheckGetTrapResult(context, target, proxy, name, trap_result, &return_result,
&no_target_desc);
BIND(&return_result);
{
// 11. Return trapResult.
Return(trap_result);
}
BIND(&no_target_desc);
{
CSA_ASSERT(this, IsJSReceiver(target));
CallRuntime(Runtime::kCheckProxyGetTrapResult, context, name, target,
trap_result);
Return(trap_result);
}
BIND(&trap_undefined);
{
// 7.a. Return ? target.[[Get]](P, Receiver).
Return(CallRuntime(Runtime::kGetPropertyWithReceiver, context, target, name,
receiver));
}
BIND(&throw_proxy_handler_revoked);
{ ThrowTypeError(context, MessageTemplate::kProxyRevoked, "get"); }
BIND(&trap_not_callable);
{
ThrowTypeError(context, MessageTemplate::kPropertyNotFunction, trap,
StringConstant("get"), receiver);
}
}
void ProxyAssembler::CheckGetTrapResult(Node* context, Node* target,
Node* proxy, Node* name,
Node* trap_result, Label* check_passed,
Label* if_bailout) {
Node* map = LoadMap(target);
VARIABLE(var_value, MachineRepresentation::kTagged);
VARIABLE(var_details, MachineRepresentation::kWord32);
VARIABLE(var_raw_value, MachineRepresentation::kTagged);
Label if_found_value(this, Label::kDeferred);
Node* instance_type = LoadInstanceType(target);
TryGetOwnProperty(context, proxy, target, map, instance_type, name,
&if_found_value, &var_value, &var_details, &var_raw_value,
check_passed, if_bailout);
BIND(&if_found_value);
{
Label throw_non_configurable_data(this, Label::kDeferred),
throw_non_configurable_accessor(this, Label::kDeferred),
check_accessor(this), check_data(this);
// 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is
// false, then:
GotoIfNot(IsSetWord32(var_details.value(),
PropertyDetails::kAttributesDontDeleteMask),
check_passed);
// 10.a. If IsDataDescriptor(targetDesc) is true and
// targetDesc.[[Writable]] is false, then:
BranchIfAccessorPair(var_raw_value.value(), &check_accessor, &check_data);
BIND(&check_data);
{
Node* read_only = IsSetWord32(var_details.value(),
PropertyDetails::kAttributesReadOnlyMask);
GotoIfNot(read_only, check_passed);
// 10.a.i. If SameValue(trapResult, targetDesc.[[Value]]) is false,
// throw a TypeError exception.
GotoIfNot(SameValue(trap_result, var_value.value()),
&throw_non_configurable_data);
Goto(check_passed);
}
BIND(&check_accessor);
{
// 10.b. If IsAccessorDescriptor(targetDesc) is true and
// targetDesc.[[Get]] is undefined, then:
Node* accessor_pair = var_raw_value.value();
Node* getter =
LoadObjectField(accessor_pair, AccessorPair::kGetterOffset);
// Here we check for null as well because if the getter was never
// defined it's set as null.
GotoIfNot(Word32Or(IsUndefined(getter), IsNull(getter)), check_passed);
// 10.b.i. If trapResult is not undefined, throw a TypeError exception.
GotoIfNot(IsUndefined(trap_result), &throw_non_configurable_accessor);
Goto(check_passed);
}
BIND(&throw_non_configurable_data);
{
ThrowTypeError(context, MessageTemplate::kProxyGetNonConfigurableData,
name, var_value.value(), trap_result);
}
BIND(&throw_non_configurable_accessor);
{
ThrowTypeError(context, MessageTemplate::kProxyGetNonConfigurableAccessor,
name, trap_result);
}
}
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,38 @@
// Copyright 2017 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.
#ifndef V8_BUILTINS_BUILTINS_PROXY_HELPERS_GEN_H_
#define V8_BUILTINS_BUILTINS_PROXY_HELPERS_GEN_H_
#include "src/code-stub-assembler.h"
namespace v8 {
namespace internal {
using compiler::Node;
class ProxyAssembler : public CodeStubAssembler {
public:
explicit ProxyAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
void BranchIfAccessorPair(Node* value, Label* if_accessor_pair,
Label* if_not_accessor_pair) {
GotoIf(TaggedIsSmi(value), if_not_accessor_pair);
Branch(IsAccessorPair(value), if_accessor_pair, if_not_accessor_pair);
}
// ES6 section 9.5.8 [[Get]] ( P, Receiver )
Node* ProxyGetProperty(Node* context, Node* proxy, Node* name,
Node* receiver);
protected:
void CheckGetTrapResult(Node* context, Node* target, Node* proxy, Node* name,
Node* trap_result, Label* if_not_found,
Label* if_bailout);
};
} // namespace internal
} // namespace v8
#endif // V8_BUILTINS_BUILTINS_PROXY_HELPERS_GEN_H_

View File

@ -6033,6 +6033,16 @@ void CodeStubAssembler::TryGetOwnProperty(
Node* context, Node* receiver, Node* object, Node* map, Node* instance_type, Node* context, Node* receiver, Node* object, Node* map, Node* instance_type,
Node* unique_name, Label* if_found_value, Variable* var_value, Node* unique_name, Label* if_found_value, Variable* var_value,
Label* if_not_found, Label* if_bailout) { Label* if_not_found, Label* if_bailout) {
TryGetOwnProperty(context, receiver, object, map, instance_type, unique_name,
if_found_value, var_value, nullptr, nullptr, if_not_found,
if_bailout);
}
void CodeStubAssembler::TryGetOwnProperty(
Node* context, Node* receiver, Node* object, Node* map, Node* instance_type,
Node* unique_name, Label* if_found_value, Variable* var_value,
Variable* var_details, Variable* var_raw_value, Label* if_not_found,
Label* if_bailout) {
DCHECK_EQ(MachineRepresentation::kTagged, var_value->rep()); DCHECK_EQ(MachineRepresentation::kTagged, var_value->rep());
Comment("TryGetOwnProperty"); Comment("TryGetOwnProperty");
@ -6041,8 +6051,11 @@ void CodeStubAssembler::TryGetOwnProperty(
Label if_found_fast(this), if_found_dict(this), if_found_global(this); Label if_found_fast(this), if_found_dict(this), if_found_global(this);
VARIABLE(var_details, MachineRepresentation::kWord32); VARIABLE(local_var_details, MachineRepresentation::kWord32);
Variable* vars[] = {var_value, &var_details}; if (!var_details) {
var_details = &local_var_details;
}
Variable* vars[] = {var_value, var_details};
Label if_found(this, 2, vars); Label if_found(this, 2, vars);
TryLookupProperty(object, map, instance_type, unique_name, &if_found_fast, TryLookupProperty(object, map, instance_type, unique_name, &if_found_fast,
@ -6054,14 +6067,14 @@ void CodeStubAssembler::TryGetOwnProperty(
Node* name_index = var_entry.value(); Node* name_index = var_entry.value();
LoadPropertyFromFastObject(object, map, descriptors, name_index, LoadPropertyFromFastObject(object, map, descriptors, name_index,
&var_details, var_value); var_details, var_value);
Goto(&if_found); Goto(&if_found);
} }
BIND(&if_found_dict); BIND(&if_found_dict);
{ {
Node* dictionary = var_meta_storage.value(); Node* dictionary = var_meta_storage.value();
Node* entry = var_entry.value(); Node* entry = var_entry.value();
LoadPropertyFromNameDictionary(dictionary, entry, &var_details, var_value); LoadPropertyFromNameDictionary(dictionary, entry, var_details, var_value);
Goto(&if_found); Goto(&if_found);
} }
BIND(&if_found_global); BIND(&if_found_global);
@ -6069,14 +6082,17 @@ void CodeStubAssembler::TryGetOwnProperty(
Node* dictionary = var_meta_storage.value(); Node* dictionary = var_meta_storage.value();
Node* entry = var_entry.value(); Node* entry = var_entry.value();
LoadPropertyFromGlobalDictionary(dictionary, entry, &var_details, var_value, LoadPropertyFromGlobalDictionary(dictionary, entry, var_details, var_value,
if_not_found); if_not_found);
Goto(&if_found); Goto(&if_found);
} }
// Here we have details and value which could be an accessor. // Here we have details and value which could be an accessor.
BIND(&if_found); BIND(&if_found);
{ {
Node* value = CallGetterIfAccessor(var_value->value(), var_details.value(), if (var_raw_value) {
var_raw_value->Bind(var_value->value());
}
Node* value = CallGetterIfAccessor(var_value->value(), var_details->value(),
context, receiver, if_bailout); context, receiver, if_bailout);
var_value->Bind(value); var_value->Bind(value);
Goto(if_found_value); Goto(if_found_value);

View File

@ -1304,6 +1304,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* instance_type, Node* unique_name, Node* instance_type, Node* unique_name,
Label* if_found, Variable* var_value, Label* if_found, Variable* var_value,
Label* if_not_found, Label* if_bailout); Label* if_not_found, Label* if_bailout);
void TryGetOwnProperty(Node* context, Node* receiver, Node* object, Node* map,
Node* instance_type, Node* unique_name,
Label* if_found, Variable* var_value,
Variable* var_details, Variable* var_raw_value,
Label* if_not_found, Label* if_bailout);
Node* GetProperty(Node* context, Node* receiver, Handle<Name> name) { Node* GetProperty(Node* context, Node* receiver, Handle<Name> name) {
return GetProperty(context, receiver, HeapConstant(name)); return GetProperty(context, receiver, HeapConstant(name));

View File

@ -261,7 +261,8 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase(
Label constant(this), field(this), normal(this, Label::kDeferred), Label constant(this), field(this), normal(this, Label::kDeferred),
interceptor(this, Label::kDeferred), nonexistent(this), interceptor(this, Label::kDeferred), nonexistent(this),
accessor(this, Label::kDeferred), global(this, Label::kDeferred); accessor(this, Label::kDeferred), proxy(this, Label::kDeferred),
global(this, Label::kDeferred);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kField)), &field); GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kField)), &field);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kConstant)), GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kConstant)),
@ -276,6 +277,8 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase(
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kAccessor)), GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kAccessor)),
&accessor); &accessor);
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kProxy)), &proxy);
Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kGlobal)), &global, Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kGlobal)), &global,
&interceptor); &interceptor);
@ -362,6 +365,13 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase(
exit_point->Return(CallJS(callable, p->context, getter, p->receiver)); exit_point->Return(CallJS(callable, p->context, getter, p->receiver));
} }
BIND(&proxy);
{
exit_point->ReturnCallStub(
Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty),
p->context, holder, p->name, p->receiver);
}
BIND(&global); BIND(&global);
{ {
CSA_ASSERT(this, IsPropertyCell(holder)); CSA_ASSERT(this, IsPropertyCell(holder));
@ -1471,15 +1481,15 @@ void AccessorAssembler::GenericPropertyLoad(Node* receiver, Node* receiver_map,
Comment("key is unique name"); Comment("key is unique name");
Label if_found_on_receiver(this), if_property_dictionary(this), Label if_found_on_receiver(this), if_property_dictionary(this),
lookup_prototype_chain(this); lookup_prototype_chain(this), special_receiver(this);
VARIABLE(var_details, MachineRepresentation::kWord32); VARIABLE(var_details, MachineRepresentation::kWord32);
VARIABLE(var_value, MachineRepresentation::kTagged); VARIABLE(var_value, MachineRepresentation::kTagged);
// Receivers requiring non-standard accesses (interceptors, access // Receivers requiring non-standard accesses (interceptors, access
// checks, strings and string wrappers, proxies) are handled in the runtime. // checks, strings and string wrappers) are handled in the runtime.
GotoIf(Int32LessThanOrEqual(instance_type, GotoIf(Int32LessThanOrEqual(instance_type,
Int32Constant(LAST_SPECIAL_RECEIVER_TYPE)), Int32Constant(LAST_SPECIAL_RECEIVER_TYPE)),
slow); &special_receiver);
// Check if the receiver has fast or slow properties. // Check if the receiver has fast or slow properties.
Node* bitfield3 = LoadMapBitField3(receiver_map); Node* bitfield3 = LoadMapBitField3(receiver_map);
@ -1600,6 +1610,16 @@ void AccessorAssembler::GenericPropertyLoad(Node* receiver, Node* receiver_map,
BIND(&return_undefined); BIND(&return_undefined);
Return(UndefinedConstant()); Return(UndefinedConstant());
} }
BIND(&special_receiver);
{
GotoIfNot(Word32Equal(instance_type, Int32Constant(JS_PROXY_TYPE)), slow);
direct_exit.ReturnCallStub(
Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty),
p->context, receiver /*holder is the same as receiver*/, p->name,
receiver);
}
} }
//////////////////// Stub cache access helpers. //////////////////// Stub cache access helpers.

View File

@ -53,6 +53,11 @@ Handle<Smi> LoadHandler::LoadAccessor(Isolate* isolate, int descriptor) {
return handle(Smi::FromInt(config), isolate); return handle(Smi::FromInt(config), isolate);
} }
Handle<Smi> LoadHandler::LoadProxy(Isolate* isolate) {
int config = KindBits::encode(kProxy);
return handle(Smi::FromInt(config), isolate);
}
Handle<Smi> LoadHandler::LoadApiGetter(Isolate* isolate, int descriptor) { Handle<Smi> LoadHandler::LoadApiGetter(Isolate* isolate, int descriptor) {
int config = KindBits::encode(kConstant) | IsAccessorInfoBits::encode(true) | int config = KindBits::encode(kConstant) | IsAccessorInfoBits::encode(true) |
DescriptorBits::encode(descriptor); DescriptorBits::encode(descriptor);

View File

@ -25,9 +25,10 @@ class LoadHandler {
kConstant, kConstant,
kAccessor, kAccessor,
kInterceptor, kInterceptor,
kProxy,
kNonExistent kNonExistent
}; };
class KindBits : public BitField<Kind, 0, 3> {}; class KindBits : public BitField<Kind, 0, 4> {};
// Defines whether access rights check should be done on receiver object. // Defines whether access rights check should be done on receiver object.
// Applicable to named property kinds only when loading value from prototype // Applicable to named property kinds only when loading value from prototype
@ -113,6 +114,9 @@ class LoadHandler {
// Creates a Smi-handler for calling a getter on a fast object. // Creates a Smi-handler for calling a getter on a fast object.
static inline Handle<Smi> LoadAccessor(Isolate* isolate, int descriptor); static inline Handle<Smi> LoadAccessor(Isolate* isolate, int descriptor);
// Creates a Smi-handler for calling a getter on a proxy.
static inline Handle<Smi> LoadProxy(Isolate* isolate);
// Creates a Smi-handler for loading an Api getter property from fast object. // Creates a Smi-handler for loading an Api getter property from fast object.
static inline Handle<Smi> LoadApiGetter(Isolate* isolate, int descriptor); static inline Handle<Smi> LoadApiGetter(Isolate* isolate, int descriptor);

View File

@ -772,7 +772,7 @@ namespace {
template <bool fill_array = true> template <bool fill_array = true>
int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map, int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
Handle<JSObject> holder, Handle<Name> name, Handle<JSReceiver> holder, Handle<Name> name,
Handle<FixedArray> array, int first_index) { Handle<FixedArray> array, int first_index) {
if (!holder.is_null() && holder->map() == *receiver_map) return 0; if (!holder.is_null() && holder->map() == *receiver_map) return 0;
@ -813,7 +813,8 @@ int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
: PrototypeIterator::END_AT_NULL; : PrototypeIterator::END_AT_NULL;
for (PrototypeIterator iter(receiver_map, end); !iter.IsAtEnd(); for (PrototypeIterator iter(receiver_map, end); !iter.IsAtEnd();
iter.Advance()) { iter.Advance()) {
Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter); Handle<JSReceiver> current =
PrototypeIterator::GetCurrent<JSReceiver>(iter);
if (holder.is_identical_to(current)) break; if (holder.is_identical_to(current)) break;
Handle<Map> current_map(current->map(), isolate); Handle<Map> current_map(current->map(), isolate);
@ -850,7 +851,7 @@ int InitPrototypeChecks(Isolate* isolate, Handle<Map> receiver_map,
// Returns -1 if the handler has to be compiled or the number of prototype // Returns -1 if the handler has to be compiled or the number of prototype
// checks otherwise. // checks otherwise.
int GetPrototypeCheckCount(Isolate* isolate, Handle<Map> receiver_map, int GetPrototypeCheckCount(Isolate* isolate, Handle<Map> receiver_map,
Handle<JSObject> holder, Handle<Name> name) { Handle<JSReceiver> holder, Handle<Name> name) {
return InitPrototypeChecks<false>(isolate, receiver_map, holder, name, return InitPrototypeChecks<false>(isolate, receiver_map, holder, name,
Handle<FixedArray>(), 0); Handle<FixedArray>(), 0);
} }
@ -860,7 +861,7 @@ enum class HolderCellRequest {
kHolder, kHolder,
}; };
Handle<WeakCell> HolderCell(Isolate* isolate, Handle<JSObject> holder, Handle<WeakCell> HolderCell(Isolate* isolate, Handle<JSReceiver> holder,
Handle<Name> name, HolderCellRequest request) { Handle<Name> name, HolderCellRequest request) {
if (request == HolderCellRequest::kGlobalPropertyCell) { if (request == HolderCellRequest::kGlobalPropertyCell) {
DCHECK(holder->IsJSGlobalObject()); DCHECK(holder->IsJSGlobalObject());
@ -877,7 +878,7 @@ Handle<WeakCell> HolderCell(Isolate* isolate, Handle<JSObject> holder,
} // namespace } // namespace
Handle<Object> LoadIC::LoadFromPrototype(Handle<Map> receiver_map, Handle<Object> LoadIC::LoadFromPrototype(Handle<Map> receiver_map,
Handle<JSObject> holder, Handle<JSReceiver> holder,
Handle<Name> name, Handle<Name> name,
Handle<Smi> smi_handler) { Handle<Smi> smi_handler) {
int checks_count = int checks_count =
@ -925,7 +926,7 @@ Handle<Object> LoadIC::LoadFromPrototype(Handle<Map> receiver_map,
Handle<Object> LoadIC::LoadFullChain(Handle<Map> receiver_map, Handle<Object> LoadIC::LoadFullChain(Handle<Map> receiver_map,
Handle<Object> holder, Handle<Name> name, Handle<Object> holder, Handle<Name> name,
Handle<Smi> smi_handler) { Handle<Smi> smi_handler) {
Handle<JSObject> end; // null handle Handle<JSReceiver> end; // null handle
int checks_count = GetPrototypeCheckCount(isolate(), receiver_map, end, name); int checks_count = GetPrototypeCheckCount(isolate(), receiver_map, end, name);
DCHECK_LE(0, checks_count); DCHECK_LE(0, checks_count);
@ -976,8 +977,7 @@ void LoadIC::UpdateCaches(LookupIterator* lookup) {
} }
Handle<Object> code; Handle<Object> code;
if (lookup->state() == LookupIterator::JSPROXY || if (lookup->state() == LookupIterator::ACCESS_CHECK) {
lookup->state() == LookupIterator::ACCESS_CHECK) {
code = slow_stub(); code = slow_stub();
} else if (!lookup->IsFound()) { } else if (!lookup->IsFound()) {
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNonexistentDH); TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNonexistentDH);
@ -1101,8 +1101,13 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
} }
Handle<Map> map = receiver_map(); Handle<Map> map = receiver_map();
Handle<JSObject> holder = lookup->GetHolder<JSObject>(); Handle<JSObject> holder;
bool receiver_is_holder = receiver.is_identical_to(holder); bool receiver_is_holder;
if (lookup->state() != LookupIterator::JSPROXY) {
holder = lookup->GetHolder<JSObject>();
receiver_is_holder = receiver.is_identical_to(holder);
}
switch (lookup->state()) { switch (lookup->state()) {
case LookupIterator::INTERCEPTOR: { case LookupIterator::INTERCEPTOR: {
Handle<Smi> smi_handler = LoadHandler::LoadInterceptor(isolate()); Handle<Smi> smi_handler = LoadHandler::LoadInterceptor(isolate());
@ -1263,8 +1268,16 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
case LookupIterator::INTEGER_INDEXED_EXOTIC: case LookupIterator::INTEGER_INDEXED_EXOTIC:
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadIntegerIndexedExoticDH); TRACE_HANDLER_STATS(isolate(), LoadIC_LoadIntegerIndexedExoticDH);
return LoadHandler::LoadNonExistent(isolate()); return LoadHandler::LoadNonExistent(isolate());
case LookupIterator::JSPROXY: {
Handle<JSProxy> holder_proxy = lookup->GetHolder<JSProxy>();
bool receiver_is_holder_proxy = receiver.is_identical_to(holder_proxy);
Handle<Smi> smi_handler = LoadHandler::LoadProxy(isolate());
if (receiver_is_holder_proxy) {
return smi_handler;
}
return LoadFromPrototype(map, holder_proxy, lookup->name(), smi_handler);
}
case LookupIterator::ACCESS_CHECK: case LookupIterator::ACCESS_CHECK:
case LookupIterator::JSPROXY:
case LookupIterator::NOT_FOUND: case LookupIterator::NOT_FOUND:
case LookupIterator::TRANSITION: case LookupIterator::TRANSITION:
UNREACHABLE(); UNREACHABLE();
@ -1721,6 +1734,7 @@ Handle<Object> StoreIC::StoreTransition(Handle<Map> receiver_map,
int checks_count = int checks_count =
GetPrototypeCheckCount(isolate(), receiver_map, holder, name); GetPrototypeCheckCount(isolate(), receiver_map, holder, name);
DCHECK_LE(0, checks_count); DCHECK_LE(0, checks_count);
DCHECK(!receiver_map->IsJSGlobalObjectMap()); DCHECK(!receiver_map->IsJSGlobalObjectMap());

View File

@ -283,7 +283,7 @@ class LoadIC : public IC {
// by given Smi-handler that encoded a load from the holder. // by given Smi-handler that encoded a load from the holder.
// Can be used only if GetPrototypeCheckCount() returns non negative value. // Can be used only if GetPrototypeCheckCount() returns non negative value.
Handle<Object> LoadFromPrototype(Handle<Map> receiver_map, Handle<Object> LoadFromPrototype(Handle<Map> receiver_map,
Handle<JSObject> holder, Handle<Name> name, Handle<JSReceiver> holder, Handle<Name> name,
Handle<Smi> smi_handler); Handle<Smi> smi_handler);
// Creates a data handler that represents a load of a non-existent property. // Creates a data handler that represents a load of a non-existent property.

View File

@ -1105,6 +1105,22 @@ MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate,
ASSIGN_RETURN_ON_EXCEPTION( ASSIGN_RETURN_ON_EXCEPTION(
isolate, trap_result, isolate, trap_result,
Execution::Call(isolate, trap, handler, arraysize(args), args), Object); Execution::Call(isolate, trap, handler, arraysize(args), args), Object);
MaybeHandle<Object> result =
JSProxy::CheckGetTrapResult(isolate, name, target, trap_result);
if (result.is_null()) {
return result;
}
// 11. Return trap_result
return trap_result;
}
// static
MaybeHandle<Object> JSProxy::CheckGetTrapResult(Isolate* isolate,
Handle<Name> name,
Handle<JSReceiver> target,
Handle<Object> trap_result) {
// 9. Let targetDesc be ? target.[[GetOwnProperty]](P). // 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
PropertyDescriptor target_desc; PropertyDescriptor target_desc;
Maybe<bool> target_found = Maybe<bool> target_found =
@ -1141,8 +1157,7 @@ MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate,
Object); Object);
} }
} }
// 11. Return trap_result return isolate->factory()->undefined_value();
return trap_result;
} }
@ -12594,7 +12609,12 @@ Handle<Cell> Map::GetOrCreatePrototypeChainValidityCell(Handle<Map> map,
} else { } else {
maybe_prototype = maybe_prototype =
handle(map->GetPrototypeChainRootMap(isolate)->prototype(), isolate); handle(map->GetPrototypeChainRootMap(isolate)->prototype(), isolate);
if (!maybe_prototype->IsJSObject()) return Handle<Cell>::null(); if (!maybe_prototype->IsJSReceiver()) return Handle<Cell>::null();
}
if (maybe_prototype->IsJSProxy()) {
Handle<Cell> cell = isolate->factory()->NewCell(
handle(Smi::FromInt(Map::kPrototypeChainValid), isolate));
return cell;
} }
Handle<JSObject> prototype = Handle<JSObject>::cast(maybe_prototype); Handle<JSObject> prototype = Handle<JSObject>::cast(maybe_prototype);
// Ensure the prototype is registered with its own prototypes so its cell // Ensure the prototype is registered with its own prototypes so its cell
@ -12619,11 +12639,16 @@ Handle<Cell> Map::GetOrCreatePrototypeChainValidityCell(Handle<Map> map,
} }
// static // static
Handle<WeakCell> Map::GetOrCreatePrototypeWeakCell(Handle<JSObject> prototype, Handle<WeakCell> Map::GetOrCreatePrototypeWeakCell(Handle<JSReceiver> prototype,
Isolate* isolate) { Isolate* isolate) {
DCHECK(!prototype.is_null()); DCHECK(!prototype.is_null());
if (prototype->IsJSProxy()) {
Handle<WeakCell> cell = isolate->factory()->NewWeakCell(prototype);
return cell;
}
Handle<PrototypeInfo> proto_info = Handle<PrototypeInfo> proto_info =
GetOrCreatePrototypeInfo(prototype, isolate); GetOrCreatePrototypeInfo(Handle<JSObject>::cast(prototype), isolate);
Object* maybe_cell = proto_info->weak_cell(); Object* maybe_cell = proto_info->weak_cell();
// Return existing cell if it's already created. // Return existing cell if it's already created.
if (maybe_cell->IsWeakCell()) { if (maybe_cell->IsWeakCell()) {

View File

@ -6296,6 +6296,11 @@ class JSProxy: public JSReceiver {
Isolate* isolate, Handle<JSProxy> proxy, Handle<Name> name, Isolate* isolate, Handle<JSProxy> proxy, Handle<Name> name,
Handle<Object> receiver, bool* was_found); Handle<Object> receiver, bool* was_found);
static MaybeHandle<Object> CheckGetTrapResult(Isolate* isolate,
Handle<Name> name,
Handle<JSReceiver> target,
Handle<Object> trap_result);
// ES6 9.5.9 // ES6 9.5.9
MUST_USE_RESULT static Maybe<bool> SetProperty(Handle<JSProxy> proxy, MUST_USE_RESULT static Maybe<bool> SetProperty(Handle<JSProxy> proxy,
Handle<Name> name, Handle<Name> name,

View File

@ -319,7 +319,7 @@ class Map : public HeapObject {
// Returns a WeakCell object containing given prototype. The cell is cached // Returns a WeakCell object containing given prototype. The cell is cached
// in PrototypeInfo which is created lazily. // in PrototypeInfo which is created lazily.
static Handle<WeakCell> GetOrCreatePrototypeWeakCell( static Handle<WeakCell> GetOrCreatePrototypeWeakCell(
Handle<JSObject> prototype, Isolate* isolate); Handle<JSReceiver> prototype, Isolate* isolate);
Map* FindRootMap() const; Map* FindRootMap() const;
Map* FindFieldOwner(int descriptor) const; Map* FindFieldOwner(int descriptor) const;

View File

@ -46,5 +46,30 @@ RUNTIME_FUNCTION(Runtime_JSProxyRevoke) {
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
RUNTIME_FUNCTION(Runtime_GetPropertyWithReceiver) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, holder, 0);
CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 2);
LookupIterator it =
LookupIterator::PropertyOrElement(isolate, receiver, name, holder);
RETURN_RESULT_OR_FAILURE(isolate, Object::GetProperty(&it));
}
RUNTIME_FUNCTION(Runtime_CheckProxyGetTrapResult) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(Name, name, 0);
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, target, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, trap_result, 2);
RETURN_RESULT_OR_FAILURE(
isolate, JSProxy::CheckGetTrapResult(isolate, name, target, trap_result));
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8

View File

@ -460,11 +460,13 @@ namespace internal {
F(PromiseStatus, 1, 1) \ F(PromiseStatus, 1, 1) \
F(ReportPromiseReject, 2, 1) F(ReportPromiseReject, 2, 1)
#define FOR_EACH_INTRINSIC_PROXY(F) \ #define FOR_EACH_INTRINSIC_PROXY(F) \
F(IsJSProxy, 1, 1) \ F(IsJSProxy, 1, 1) \
F(JSProxyGetTarget, 1, 1) \ F(JSProxyGetTarget, 1, 1) \
F(JSProxyGetHandler, 1, 1) \ F(JSProxyGetHandler, 1, 1) \
F(JSProxyRevoke, 1, 1) F(JSProxyRevoke, 1, 1) \
F(GetPropertyWithReceiver, 2, 1) \
F(CheckProxyGetTrapResult, 2, 1)
#define FOR_EACH_INTRINSIC_REGEXP(F) \ #define FOR_EACH_INTRINSIC_REGEXP(F) \
F(IsRegExp, 1, 1) \ F(IsRegExp, 1, 1) \

View File

@ -207,6 +207,8 @@
'builtins/builtins-promise-gen.cc', 'builtins/builtins-promise-gen.cc',
'builtins/builtins-promise-gen.h', 'builtins/builtins-promise-gen.h',
'builtins/builtins-proxy-gen.cc', 'builtins/builtins-proxy-gen.cc',
'builtins/builtins-proxy-helpers-gen.cc',
'builtins/builtins-proxy-helpers-gen.h',
'builtins/builtins-regexp-gen.cc', 'builtins/builtins-regexp-gen.cc',
'builtins/builtins-regexp-gen.h', 'builtins/builtins-regexp-gen.h',
'builtins/builtins-sharedarraybuffer-gen.cc', 'builtins/builtins-sharedarraybuffer-gen.cc',

View File

@ -35,6 +35,12 @@
assertThrows("proxy.property", Error); assertThrows("proxy.property", Error);
})(); })();
(function testThrowOnTrapNotCallable() {
var handler = new Proxy({}, {get: 'not_callable' });
var proxy = new Proxy({}, handler);
assertThrows("proxy.property", Error);
})();
(function testFallback() { (function testFallback() {
var target = {property:"value"}; var target = {property:"value"};
var proxy = new Proxy(target, {}); var proxy = new Proxy(target, {});
@ -125,3 +131,83 @@
"[[Get]](iterator, next)" "[[Get]](iterator, next)"
], log); ], log);
})(); })();
(function testGetterWithSideEffect() {
var obj = {
key: 0
}
assertEquals(obj.key, 0);
var p = new Proxy(obj, {});
var q = new Proxy(p, {
get(target, name) {
if (name != 'key') return Reflect.get(target, name);
target.key++;
return target.key;
}
});
assertEquals(0, p.key);
// Assert the trap is not called twice.
assertEquals(1, q.key);
})();
(function testReceiverWithTrap() {
var obj = {};
var p = new Proxy(obj, {
get(target, name, receiver) {
if (name != 'key') return Reflect.get(target, name);
assertSame(target, obj);
assertSame(receiver, p);
return 42;
}
});
assertEquals(42, p.key);
})();
(function testReceiverWithoutTrap() {
var obj = {
get prop() {
assertSame(this, p);
return 42;
}
};
var p = new Proxy(obj, {});
assertEquals(42, p.prop);
})();
(function testGetPropertyDetailsBailout() {
var obj = {
}
var p = new Proxy(obj, {
getOwnPropertyDescriptor() {
throw new Error('Error from proxy getOwnPropertyDescriptor trap');
}
});
var q = new Proxy(p, {
get(target, name) {
return 42;
}
});
assertThrows(function(){ q.prop }, Error,
'Error from proxy getOwnPropertyDescriptor trap');
})();
(function testGetPropertyDetailsBailout2() {
var obj = {};
Object.defineProperty(obj, 'prop', {
value: 53,
configurable: false
});
var p = new Proxy(obj, {});
var q = new Proxy(p, {
get(target, name) {
return 42;
}
});
assertThrows(function(){ q.prop }, TypeError,
"'get' on proxy: property 'prop' is a read-only and non-configurable data" +
" property on the proxy target but the proxy did not return its actual" +
" value (expected '53' but got '42')");
})();

View File

@ -0,0 +1,36 @@
// Copyright 2017 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.
// Flags: --allow-natives-syntax
// Proxy get trap doesn't fail when the value returned by it
// is a number.
function __getProperties(obj, type) {
if(typeof obj === "undefined" || obj === null) {
return [];
}
let properties = [];
for(let name of Object.getOwnPropertyNames(obj)) {
properties.push(name);
}
return properties;
}
var number = 1;
(function testFailingInvariant() {
var obj = {};
var handler = {
get: function() {}
};
var proxy = new Proxy(obj, handler);
Object.defineProperty(handler, 'get', {
get: function() {
return number;
}
});
assertThrows(function(){ proxy.property; }, TypeError);
})();

View File

@ -0,0 +1,21 @@
// Copyright 2017 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.
// Flags: --allow-natives-syntax
// Check that the receiver of Runtime_GetPropertyWithReceiver can be
// a plain JS value.
var values = [
10,
false,
"test"
];
for (let val of values) {
var proto = Object.getPrototypeOf(val);
var proxy = new Proxy({}, {});
Object.setPrototypeOf(proto, proxy);
}