Reland^2 "[builtins] Port getting property from Proxy to CSA"
With fixes for crbug.com/752846, crbug.com/752712, crbug.com/752850 Previously landed as:47a97aa53b
/ 47113 Previously landed as:15ef03cbf3
/ 47159 Bug: v8:6559, v8:6557 This is a reland of15ef03cbf3
Change-Id: Ia53ffb80ebe44581fdb923d9f572be92ee3ed080 Reviewed-on: https://chromium-review.googlesource.com/603796 Commit-Queue: Maya Lekova <mslekova@google.com> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Reviewed-by: Franziska Hinkelmann <franzih@chromium.org> Cr-Commit-Position: refs/heads/master@{#47235}
This commit is contained in:
parent
180d81ea25
commit
e86c066b77
2
BUILD.gn
2
BUILD.gn
@ -975,6 +975,8 @@ v8_source_set("v8_builtins_generators") {
|
||||
"src/builtins/builtins-promise-gen.cc",
|
||||
"src/builtins/builtins-promise-gen.h",
|
||||
"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.h",
|
||||
"src/builtins/builtins-sharedarraybuffer-gen.cc",
|
||||
|
@ -802,6 +802,7 @@ namespace internal {
|
||||
TFJ(ProxyConstructor, 0) \
|
||||
TFJ(ProxyConstructor_ConstructStub, \
|
||||
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
||||
TFS(ProxyGetProperty, kProxy, kName, kReceiverValue) \
|
||||
\
|
||||
/* Reflect */ \
|
||||
ASM(ReflectApply) \
|
||||
|
160
src/builtins/builtins-proxy-helpers-gen.cc
Normal file
160
src/builtins/builtins-proxy-helpers-gen.cc
Normal 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
|
38
src/builtins/builtins-proxy-helpers-gen.h
Normal file
38
src/builtins/builtins-proxy-helpers-gen.h
Normal 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_
|
@ -6033,6 +6033,16 @@ void CodeStubAssembler::TryGetOwnProperty(
|
||||
Node* context, Node* receiver, Node* object, Node* map, Node* instance_type,
|
||||
Node* unique_name, Label* if_found_value, Variable* var_value,
|
||||
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());
|
||||
Comment("TryGetOwnProperty");
|
||||
|
||||
@ -6041,8 +6051,11 @@ void CodeStubAssembler::TryGetOwnProperty(
|
||||
|
||||
Label if_found_fast(this), if_found_dict(this), if_found_global(this);
|
||||
|
||||
VARIABLE(var_details, MachineRepresentation::kWord32);
|
||||
Variable* vars[] = {var_value, &var_details};
|
||||
VARIABLE(local_var_details, MachineRepresentation::kWord32);
|
||||
if (!var_details) {
|
||||
var_details = &local_var_details;
|
||||
}
|
||||
Variable* vars[] = {var_value, var_details};
|
||||
Label if_found(this, 2, vars);
|
||||
|
||||
TryLookupProperty(object, map, instance_type, unique_name, &if_found_fast,
|
||||
@ -6054,14 +6067,14 @@ void CodeStubAssembler::TryGetOwnProperty(
|
||||
Node* name_index = var_entry.value();
|
||||
|
||||
LoadPropertyFromFastObject(object, map, descriptors, name_index,
|
||||
&var_details, var_value);
|
||||
var_details, var_value);
|
||||
Goto(&if_found);
|
||||
}
|
||||
BIND(&if_found_dict);
|
||||
{
|
||||
Node* dictionary = var_meta_storage.value();
|
||||
Node* entry = var_entry.value();
|
||||
LoadPropertyFromNameDictionary(dictionary, entry, &var_details, var_value);
|
||||
LoadPropertyFromNameDictionary(dictionary, entry, var_details, var_value);
|
||||
Goto(&if_found);
|
||||
}
|
||||
BIND(&if_found_global);
|
||||
@ -6069,14 +6082,17 @@ void CodeStubAssembler::TryGetOwnProperty(
|
||||
Node* dictionary = var_meta_storage.value();
|
||||
Node* entry = var_entry.value();
|
||||
|
||||
LoadPropertyFromGlobalDictionary(dictionary, entry, &var_details, var_value,
|
||||
LoadPropertyFromGlobalDictionary(dictionary, entry, var_details, var_value,
|
||||
if_not_found);
|
||||
Goto(&if_found);
|
||||
}
|
||||
// Here we have details and value which could be an accessor.
|
||||
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);
|
||||
var_value->Bind(value);
|
||||
Goto(if_found_value);
|
||||
|
@ -1304,6 +1304,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
Node* instance_type, Node* unique_name,
|
||||
Label* if_found, Variable* var_value,
|
||||
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) {
|
||||
return GetProperty(context, receiver, HeapConstant(name));
|
||||
|
@ -261,7 +261,8 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase(
|
||||
|
||||
Label constant(this), field(this), normal(this, Label::kDeferred),
|
||||
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::kConstant)),
|
||||
@ -276,6 +277,8 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase(
|
||||
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kAccessor)),
|
||||
&accessor);
|
||||
|
||||
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kProxy)), &proxy);
|
||||
|
||||
Branch(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kGlobal)), &global,
|
||||
&interceptor);
|
||||
|
||||
@ -362,6 +365,13 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase(
|
||||
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);
|
||||
{
|
||||
CSA_ASSERT(this, IsPropertyCell(holder));
|
||||
@ -1471,15 +1481,15 @@ void AccessorAssembler::GenericPropertyLoad(Node* receiver, Node* receiver_map,
|
||||
|
||||
Comment("key is unique name");
|
||||
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_value, MachineRepresentation::kTagged);
|
||||
|
||||
// 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,
|
||||
Int32Constant(LAST_SPECIAL_RECEIVER_TYPE)),
|
||||
slow);
|
||||
&special_receiver);
|
||||
|
||||
// Check if the receiver has fast or slow properties.
|
||||
Node* bitfield3 = LoadMapBitField3(receiver_map);
|
||||
@ -1600,6 +1610,16 @@ void AccessorAssembler::GenericPropertyLoad(Node* receiver, Node* receiver_map,
|
||||
BIND(&return_undefined);
|
||||
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.
|
||||
|
@ -53,6 +53,11 @@ Handle<Smi> LoadHandler::LoadAccessor(Isolate* isolate, int descriptor) {
|
||||
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) {
|
||||
int config = KindBits::encode(kConstant) | IsAccessorInfoBits::encode(true) |
|
||||
DescriptorBits::encode(descriptor);
|
||||
|
@ -25,9 +25,10 @@ class LoadHandler {
|
||||
kConstant,
|
||||
kAccessor,
|
||||
kInterceptor,
|
||||
kProxy,
|
||||
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.
|
||||
// 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.
|
||||
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.
|
||||
static inline Handle<Smi> LoadApiGetter(Isolate* isolate, int descriptor);
|
||||
|
||||
|
36
src/ic/ic.cc
36
src/ic/ic.cc
@ -772,7 +772,7 @@ namespace {
|
||||
|
||||
template <bool fill_array = true>
|
||||
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) {
|
||||
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;
|
||||
for (PrototypeIterator iter(receiver_map, end); !iter.IsAtEnd();
|
||||
iter.Advance()) {
|
||||
Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter);
|
||||
Handle<JSReceiver> current =
|
||||
PrototypeIterator::GetCurrent<JSReceiver>(iter);
|
||||
if (holder.is_identical_to(current)) break;
|
||||
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
|
||||
// checks otherwise.
|
||||
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,
|
||||
Handle<FixedArray>(), 0);
|
||||
}
|
||||
@ -860,7 +861,7 @@ enum class HolderCellRequest {
|
||||
kHolder,
|
||||
};
|
||||
|
||||
Handle<WeakCell> HolderCell(Isolate* isolate, Handle<JSObject> holder,
|
||||
Handle<WeakCell> HolderCell(Isolate* isolate, Handle<JSReceiver> holder,
|
||||
Handle<Name> name, HolderCellRequest request) {
|
||||
if (request == HolderCellRequest::kGlobalPropertyCell) {
|
||||
DCHECK(holder->IsJSGlobalObject());
|
||||
@ -877,7 +878,7 @@ Handle<WeakCell> HolderCell(Isolate* isolate, Handle<JSObject> holder,
|
||||
} // namespace
|
||||
|
||||
Handle<Object> LoadIC::LoadFromPrototype(Handle<Map> receiver_map,
|
||||
Handle<JSObject> holder,
|
||||
Handle<JSReceiver> holder,
|
||||
Handle<Name> name,
|
||||
Handle<Smi> smi_handler) {
|
||||
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> holder, Handle<Name> name,
|
||||
Handle<Smi> smi_handler) {
|
||||
Handle<JSObject> end; // null handle
|
||||
Handle<JSReceiver> end; // null handle
|
||||
int checks_count = GetPrototypeCheckCount(isolate(), receiver_map, end, name);
|
||||
DCHECK_LE(0, checks_count);
|
||||
|
||||
@ -976,8 +977,7 @@ void LoadIC::UpdateCaches(LookupIterator* lookup) {
|
||||
}
|
||||
|
||||
Handle<Object> code;
|
||||
if (lookup->state() == LookupIterator::JSPROXY ||
|
||||
lookup->state() == LookupIterator::ACCESS_CHECK) {
|
||||
if (lookup->state() == LookupIterator::ACCESS_CHECK) {
|
||||
code = slow_stub();
|
||||
} else if (!lookup->IsFound()) {
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadNonexistentDH);
|
||||
@ -1101,8 +1101,13 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
}
|
||||
|
||||
Handle<Map> map = receiver_map();
|
||||
Handle<JSObject> holder = lookup->GetHolder<JSObject>();
|
||||
bool receiver_is_holder = receiver.is_identical_to(holder);
|
||||
Handle<JSObject> 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()) {
|
||||
case LookupIterator::INTERCEPTOR: {
|
||||
Handle<Smi> smi_handler = LoadHandler::LoadInterceptor(isolate());
|
||||
@ -1263,8 +1268,16 @@ Handle<Object> LoadIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||
case LookupIterator::INTEGER_INDEXED_EXOTIC:
|
||||
TRACE_HANDLER_STATS(isolate(), LoadIC_LoadIntegerIndexedExoticDH);
|
||||
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::JSPROXY:
|
||||
case LookupIterator::NOT_FOUND:
|
||||
case LookupIterator::TRANSITION:
|
||||
UNREACHABLE();
|
||||
@ -1721,6 +1734,7 @@ Handle<Object> StoreIC::StoreTransition(Handle<Map> receiver_map,
|
||||
|
||||
int checks_count =
|
||||
GetPrototypeCheckCount(isolate(), receiver_map, holder, name);
|
||||
|
||||
DCHECK_LE(0, checks_count);
|
||||
DCHECK(!receiver_map->IsJSGlobalObjectMap());
|
||||
|
||||
|
@ -283,7 +283,7 @@ class LoadIC : public IC {
|
||||
// by given Smi-handler that encoded a load from the holder.
|
||||
// Can be used only if GetPrototypeCheckCount() returns non negative value.
|
||||
Handle<Object> LoadFromPrototype(Handle<Map> receiver_map,
|
||||
Handle<JSObject> holder, Handle<Name> name,
|
||||
Handle<JSReceiver> holder, Handle<Name> name,
|
||||
Handle<Smi> smi_handler);
|
||||
|
||||
// Creates a data handler that represents a load of a non-existent property.
|
||||
|
@ -1105,6 +1105,22 @@ MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate,
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate, trap_result,
|
||||
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).
|
||||
PropertyDescriptor target_desc;
|
||||
Maybe<bool> target_found =
|
||||
@ -1141,8 +1157,7 @@ MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate,
|
||||
Object);
|
||||
}
|
||||
}
|
||||
// 11. Return trap_result
|
||||
return trap_result;
|
||||
return isolate->factory()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
@ -12595,7 +12610,12 @@ Handle<Cell> Map::GetOrCreatePrototypeChainValidityCell(Handle<Map> map,
|
||||
} else {
|
||||
maybe_prototype =
|
||||
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);
|
||||
// Ensure the prototype is registered with its own prototypes so its cell
|
||||
@ -12620,11 +12640,16 @@ Handle<Cell> Map::GetOrCreatePrototypeChainValidityCell(Handle<Map> map,
|
||||
}
|
||||
|
||||
// static
|
||||
Handle<WeakCell> Map::GetOrCreatePrototypeWeakCell(Handle<JSObject> prototype,
|
||||
Handle<WeakCell> Map::GetOrCreatePrototypeWeakCell(Handle<JSReceiver> prototype,
|
||||
Isolate* isolate) {
|
||||
DCHECK(!prototype.is_null());
|
||||
if (prototype->IsJSProxy()) {
|
||||
Handle<WeakCell> cell = isolate->factory()->NewWeakCell(prototype);
|
||||
return cell;
|
||||
}
|
||||
|
||||
Handle<PrototypeInfo> proto_info =
|
||||
GetOrCreatePrototypeInfo(prototype, isolate);
|
||||
GetOrCreatePrototypeInfo(Handle<JSObject>::cast(prototype), isolate);
|
||||
Object* maybe_cell = proto_info->weak_cell();
|
||||
// Return existing cell if it's already created.
|
||||
if (maybe_cell->IsWeakCell()) {
|
||||
|
@ -6296,6 +6296,11 @@ class JSProxy: public JSReceiver {
|
||||
Isolate* isolate, Handle<JSProxy> proxy, Handle<Name> name,
|
||||
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
|
||||
MUST_USE_RESULT static Maybe<bool> SetProperty(Handle<JSProxy> proxy,
|
||||
Handle<Name> name,
|
||||
|
@ -319,7 +319,7 @@ class Map : public HeapObject {
|
||||
// Returns a WeakCell object containing given prototype. The cell is cached
|
||||
// in PrototypeInfo which is created lazily.
|
||||
static Handle<WeakCell> GetOrCreatePrototypeWeakCell(
|
||||
Handle<JSObject> prototype, Isolate* isolate);
|
||||
Handle<JSReceiver> prototype, Isolate* isolate);
|
||||
|
||||
Map* FindRootMap() const;
|
||||
Map* FindFieldOwner(int descriptor) const;
|
||||
|
@ -46,5 +46,30 @@ RUNTIME_FUNCTION(Runtime_JSProxyRevoke) {
|
||||
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 v8
|
||||
|
@ -460,11 +460,13 @@ namespace internal {
|
||||
F(PromiseStatus, 1, 1) \
|
||||
F(ReportPromiseReject, 2, 1)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_PROXY(F) \
|
||||
F(IsJSProxy, 1, 1) \
|
||||
F(JSProxyGetTarget, 1, 1) \
|
||||
F(JSProxyGetHandler, 1, 1) \
|
||||
F(JSProxyRevoke, 1, 1)
|
||||
#define FOR_EACH_INTRINSIC_PROXY(F) \
|
||||
F(IsJSProxy, 1, 1) \
|
||||
F(JSProxyGetTarget, 1, 1) \
|
||||
F(JSProxyGetHandler, 1, 1) \
|
||||
F(JSProxyRevoke, 1, 1) \
|
||||
F(GetPropertyWithReceiver, 2, 1) \
|
||||
F(CheckProxyGetTrapResult, 2, 1)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_REGEXP(F) \
|
||||
F(IsRegExp, 1, 1) \
|
||||
|
@ -207,6 +207,8 @@
|
||||
'builtins/builtins-promise-gen.cc',
|
||||
'builtins/builtins-promise-gen.h',
|
||||
'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.h',
|
||||
'builtins/builtins-sharedarraybuffer-gen.cc',
|
||||
|
@ -35,6 +35,12 @@
|
||||
assertThrows("proxy.property", Error);
|
||||
})();
|
||||
|
||||
(function testThrowOnTrapNotCallable() {
|
||||
var handler = new Proxy({}, {get: 'not_callable' });
|
||||
var proxy = new Proxy({}, handler);
|
||||
assertThrows("proxy.property", Error);
|
||||
})();
|
||||
|
||||
(function testFallback() {
|
||||
var target = {property:"value"};
|
||||
var proxy = new Proxy(target, {});
|
||||
@ -125,3 +131,83 @@
|
||||
"[[Get]](iterator, next)"
|
||||
], 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')");
|
||||
})();
|
||||
|
36
test/mjsunit/regress/regress-crbug-752712.js
Normal file
36
test/mjsunit/regress/regress-crbug-752712.js
Normal 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);
|
||||
})();
|
21
test/mjsunit/regress/regress-crbug-752846.js
Normal file
21
test/mjsunit/regress/regress-crbug-752846.js
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user