[builtins] Port Proxy has trap to CSA
Bug: v8:6664, v8:6557 Change-Id: Ib2180e38c8b07cda102ccb160dfd44197d828be0 Reviewed-on: https://chromium-review.googlesource.com/602229 Commit-Queue: Maya Lekova <mslekova@google.com> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Cr-Commit-Position: refs/heads/master@{#47372}
This commit is contained in:
parent
b0d09b4967
commit
221e54ddbc
1
BUILD.gn
1
BUILD.gn
@ -994,6 +994,7 @@ 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-gen.h",
|
||||
"src/builtins/builtins-proxy-helpers-gen.cc",
|
||||
"src/builtins/builtins-proxy-helpers-gen.h",
|
||||
"src/builtins/builtins-regexp-gen.cc",
|
||||
|
@ -547,7 +547,7 @@ class ArrayBuiltinCodeStubAssembler : public CodeStubAssembler {
|
||||
|
||||
// b. Let kPresent be HasProperty(O, Pk).
|
||||
// c. ReturnIfAbrupt(kPresent).
|
||||
Node* k_present = HasProperty(o(), p_k, context());
|
||||
Node* k_present = HasProperty(o(), p_k, context(), kHasProperty);
|
||||
|
||||
// d. If kPresent is true, then
|
||||
GotoIf(WordNotEqual(k_present, TrueConstant()), &done_element);
|
||||
|
@ -799,6 +799,7 @@ namespace internal {
|
||||
TFJ(ProxyConstructor_ConstructStub, \
|
||||
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
||||
TFS(ProxyGetProperty, kProxy, kName, kReceiverValue) \
|
||||
TFS(ProxyHasProperty, kProxy, kName) \
|
||||
\
|
||||
/* Reflect */ \
|
||||
ASM(ReflectApply) \
|
||||
|
@ -25,8 +25,7 @@ Node* ForInBuiltinsAssembler::ForInFilter(Node* key, Node* object,
|
||||
|
||||
VARIABLE(var_result, MachineRepresentation::kTagged, key);
|
||||
|
||||
Node* has_property =
|
||||
HasProperty(object, key, context, Runtime::kForInHasProperty);
|
||||
Node* has_property = HasProperty(object, key, context, kForInHasProperty);
|
||||
|
||||
Label end(this);
|
||||
GotoIf(WordEqual(has_property, BooleanConstant(true)), &end);
|
||||
|
@ -609,7 +609,7 @@ TF_BUILTIN(HasProperty, ObjectBuiltinsAssembler) {
|
||||
Node* object = Parameter(Descriptor::kObject);
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
|
||||
Return(HasProperty(object, key, context, Runtime::kHasProperty));
|
||||
Return(HasProperty(object, key, context, kHasProperty));
|
||||
}
|
||||
|
||||
TF_BUILTIN(InstanceOf, ObjectBuiltinsAssembler) {
|
||||
|
@ -2,18 +2,16 @@
|
||||
// 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-gen.h"
|
||||
#include "src/builtins/builtins-utils-gen.h"
|
||||
#include "src/builtins/builtins-utils.h"
|
||||
#include "src/builtins/builtins.h"
|
||||
#include "src/code-stub-assembler.h"
|
||||
|
||||
#include "src/counters.h"
|
||||
#include "src/objects-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
using compiler::Node;
|
||||
using compiler::CodeAssembler;
|
||||
|
||||
// ES6 section 26.2.1.1 Proxy ( target, handler ) for the [[Call]] case.
|
||||
TF_BUILTIN(ProxyConstructor, CodeStubAssembler) {
|
||||
@ -21,94 +19,80 @@ TF_BUILTIN(ProxyConstructor, CodeStubAssembler) {
|
||||
ThrowTypeError(context, MessageTemplate::kConstructorNotFunction, "Proxy");
|
||||
}
|
||||
|
||||
class ProxiesCodeStubAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
explicit ProxiesCodeStubAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
void ProxiesCodeStubAssembler::GotoIfRevokedProxy(Node* object,
|
||||
Label* if_proxy_revoked) {
|
||||
Label proxy_not_revoked(this);
|
||||
GotoIfNot(IsJSProxy(object), &proxy_not_revoked);
|
||||
Branch(IsJSReceiver(LoadObjectField(object, JSProxy::kHandlerOffset)),
|
||||
&proxy_not_revoked, if_proxy_revoked);
|
||||
BIND(&proxy_not_revoked);
|
||||
}
|
||||
|
||||
Node* IsProxyRevoked(Node* proxy) {
|
||||
CSA_ASSERT(this, IsJSProxy(proxy));
|
||||
Node* ProxiesCodeStubAssembler::AllocateProxy(Node* target, Node* handler,
|
||||
Node* context) {
|
||||
VARIABLE(map, MachineRepresentation::kTagged);
|
||||
|
||||
Node* handler = LoadObjectField(proxy, JSProxy::kHandlerOffset);
|
||||
CSA_ASSERT(this, Word32Or(IsJSReceiver(handler), IsNull(handler)));
|
||||
Label callable_target(this), constructor_target(this), none_target(this),
|
||||
create_proxy(this);
|
||||
|
||||
return IsNull(handler);
|
||||
Node* nativeContext = LoadNativeContext(context);
|
||||
|
||||
Branch(IsCallable(target), &callable_target, &none_target);
|
||||
|
||||
BIND(&callable_target);
|
||||
{
|
||||
// Every object that is a constructor is implicitly callable
|
||||
// so it's okay to nest this check here
|
||||
GotoIf(IsConstructor(target), &constructor_target);
|
||||
map.Bind(
|
||||
LoadContextElement(nativeContext, Context::PROXY_CALLABLE_MAP_INDEX));
|
||||
Goto(&create_proxy);
|
||||
}
|
||||
BIND(&constructor_target);
|
||||
{
|
||||
map.Bind(LoadContextElement(nativeContext,
|
||||
Context::PROXY_CONSTRUCTOR_MAP_INDEX));
|
||||
Goto(&create_proxy);
|
||||
}
|
||||
BIND(&none_target);
|
||||
{
|
||||
map.Bind(LoadContextElement(nativeContext, Context::PROXY_MAP_INDEX));
|
||||
Goto(&create_proxy);
|
||||
}
|
||||
|
||||
void GotoIfProxyRevoked(Node* object, Label* if_proxy_revoked) {
|
||||
Label continue_checks(this);
|
||||
GotoIfNot(IsJSProxy(object), &continue_checks);
|
||||
GotoIf(IsProxyRevoked(object), if_proxy_revoked);
|
||||
Goto(&continue_checks);
|
||||
BIND(&continue_checks);
|
||||
}
|
||||
BIND(&create_proxy);
|
||||
Node* proxy = Allocate(JSProxy::kSize);
|
||||
StoreMapNoWriteBarrier(proxy, map.value());
|
||||
StoreObjectFieldRoot(proxy, JSProxy::kPropertiesOrHashOffset,
|
||||
Heap::kEmptyPropertiesDictionaryRootIndex);
|
||||
StoreObjectFieldNoWriteBarrier(proxy, JSProxy::kTargetOffset, target);
|
||||
StoreObjectFieldNoWriteBarrier(proxy, JSProxy::kHandlerOffset, handler);
|
||||
StoreObjectFieldNoWriteBarrier(proxy, JSProxy::kHashOffset,
|
||||
UndefinedConstant());
|
||||
|
||||
Node* AllocateProxy(Node* target, Node* handler, Node* context) {
|
||||
VARIABLE(map, MachineRepresentation::kTagged);
|
||||
return proxy;
|
||||
}
|
||||
|
||||
Label callable_target(this), constructor_target(this), none_target(this),
|
||||
create_proxy(this);
|
||||
Node* ProxiesCodeStubAssembler::AllocateJSArrayForCodeStubArguments(
|
||||
Node* context, CodeStubArguments& args, Node* argc, ParameterMode mode) {
|
||||
Node* array = nullptr;
|
||||
Node* elements = nullptr;
|
||||
Node* native_context = LoadNativeContext(context);
|
||||
Node* array_map = LoadJSArrayElementsMap(PACKED_ELEMENTS, native_context);
|
||||
Node* argc_smi = ParameterToTagged(argc, mode);
|
||||
std::tie(array, elements) = AllocateUninitializedJSArrayWithElements(
|
||||
PACKED_ELEMENTS, array_map, argc_smi, nullptr, argc, INTPTR_PARAMETERS);
|
||||
|
||||
Node* nativeContext = LoadNativeContext(context);
|
||||
|
||||
Branch(IsCallable(target), &callable_target, &none_target);
|
||||
|
||||
BIND(&callable_target);
|
||||
{
|
||||
// Every object that is a constructor is implicitly callable
|
||||
// so it's okay to nest this check here
|
||||
GotoIf(IsConstructor(target), &constructor_target);
|
||||
map.Bind(
|
||||
LoadContextElement(nativeContext, Context::PROXY_CALLABLE_MAP_INDEX));
|
||||
Goto(&create_proxy);
|
||||
}
|
||||
BIND(&constructor_target);
|
||||
{
|
||||
map.Bind(LoadContextElement(nativeContext,
|
||||
Context::PROXY_CONSTRUCTOR_MAP_INDEX));
|
||||
Goto(&create_proxy);
|
||||
}
|
||||
BIND(&none_target);
|
||||
{
|
||||
map.Bind(LoadContextElement(nativeContext, Context::PROXY_MAP_INDEX));
|
||||
Goto(&create_proxy);
|
||||
}
|
||||
|
||||
BIND(&create_proxy);
|
||||
Node* proxy = Allocate(JSProxy::kSize);
|
||||
StoreMapNoWriteBarrier(proxy, map.value());
|
||||
StoreObjectFieldRoot(proxy, JSProxy::kPropertiesOrHashOffset,
|
||||
Heap::kEmptyPropertiesDictionaryRootIndex);
|
||||
StoreObjectFieldNoWriteBarrier(proxy, JSProxy::kTargetOffset, target);
|
||||
StoreObjectFieldNoWriteBarrier(proxy, JSProxy::kHandlerOffset, handler);
|
||||
StoreObjectFieldNoWriteBarrier(proxy, JSProxy::kHashOffset,
|
||||
UndefinedConstant());
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
Node* AllocateJSArrayForCodeStubArguments(Node* context,
|
||||
CodeStubArguments& args, Node* argc,
|
||||
ParameterMode mode) {
|
||||
Node* array = nullptr;
|
||||
Node* elements = nullptr;
|
||||
Node* native_context = LoadNativeContext(context);
|
||||
Node* array_map = LoadJSArrayElementsMap(PACKED_ELEMENTS, native_context);
|
||||
Node* argc_smi = ParameterToTagged(argc, mode);
|
||||
std::tie(array, elements) = AllocateUninitializedJSArrayWithElements(
|
||||
PACKED_ELEMENTS, array_map, argc_smi, nullptr, argc, INTPTR_PARAMETERS);
|
||||
|
||||
VARIABLE(index, MachineType::PointerRepresentation());
|
||||
index.Bind(IntPtrConstant(FixedArrayBase::kHeaderSize - kHeapObjectTag));
|
||||
VariableList list({&index}, zone());
|
||||
args.ForEach(list, [this, elements, &index](Node* arg) {
|
||||
StoreNoWriteBarrier(MachineRepresentation::kTagged, elements,
|
||||
index.value(), arg);
|
||||
Increment(&index, kPointerSize);
|
||||
});
|
||||
return array;
|
||||
}
|
||||
};
|
||||
VARIABLE(index, MachineType::PointerRepresentation());
|
||||
index.Bind(IntPtrConstant(FixedArrayBase::kHeaderSize - kHeapObjectTag));
|
||||
VariableList list({&index}, zone());
|
||||
args.ForEach(list, [this, elements, &index](Node* arg) {
|
||||
StoreNoWriteBarrier(MachineRepresentation::kTagged, elements, index.value(),
|
||||
arg);
|
||||
Increment(&index, kPointerSize);
|
||||
});
|
||||
return array;
|
||||
}
|
||||
|
||||
// ES6 section 26.2.1.1 Proxy ( target, handler ) for the [[Construct]] case.
|
||||
TF_BUILTIN(ProxyConstructor_ConstructStub, ProxiesCodeStubAssembler) {
|
||||
@ -129,11 +113,11 @@ TF_BUILTIN(ProxyConstructor_ConstructStub, ProxiesCodeStubAssembler) {
|
||||
|
||||
GotoIf(TaggedIsSmi(target), &throw_proxy_non_object);
|
||||
GotoIfNot(IsJSReceiver(target), &throw_proxy_non_object);
|
||||
GotoIfProxyRevoked(target, &throw_proxy_handler_or_target_revoked);
|
||||
GotoIfRevokedProxy(target, &throw_proxy_handler_or_target_revoked);
|
||||
|
||||
GotoIf(TaggedIsSmi(handler), &throw_proxy_non_object);
|
||||
GotoIfNot(IsJSReceiver(handler), &throw_proxy_non_object);
|
||||
GotoIfProxyRevoked(handler, &throw_proxy_handler_or_target_revoked);
|
||||
GotoIfRevokedProxy(handler, &throw_proxy_handler_or_target_revoked);
|
||||
|
||||
args.PopAndReturn(AllocateProxy(target, handler, context));
|
||||
|
||||
@ -161,7 +145,7 @@ TF_BUILTIN(CallProxy, ProxiesCodeStubAssembler) {
|
||||
|
||||
// 2. If handler is null, throw a TypeError exception.
|
||||
CSA_ASSERT(this, IsNullOrJSReceiver(handler));
|
||||
GotoIf(IsNull(handler), &throw_proxy_handler_revoked);
|
||||
GotoIfNot(IsJSReceiver(handler), &throw_proxy_handler_revoked);
|
||||
|
||||
// 3. Assert: Type(handler) is Object.
|
||||
CSA_ASSERT(this, IsJSReceiver(handler));
|
||||
@ -214,7 +198,7 @@ TF_BUILTIN(ConstructProxy, ProxiesCodeStubAssembler) {
|
||||
|
||||
// 2. If handler is null, throw a TypeError exception.
|
||||
CSA_ASSERT(this, IsNullOrJSReceiver(handler));
|
||||
GotoIf(IsNull(handler), &throw_proxy_handler_revoked);
|
||||
GotoIfNot(IsJSReceiver(handler), &throw_proxy_handler_revoked);
|
||||
|
||||
// 3. Assert: Type(handler) is Object.
|
||||
CSA_ASSERT(this, IsJSReceiver(handler));
|
||||
@ -263,5 +247,122 @@ TF_BUILTIN(ConstructProxy, ProxiesCodeStubAssembler) {
|
||||
{ ThrowTypeError(context, MessageTemplate::kProxyRevoked, "construct"); }
|
||||
}
|
||||
|
||||
TF_BUILTIN(ProxyHasProperty, ProxiesCodeStubAssembler) {
|
||||
Node* context = Parameter(Descriptor::kContext);
|
||||
Node* proxy = Parameter(Descriptor::kProxy);
|
||||
Node* name = Parameter(Descriptor::kName);
|
||||
|
||||
CSA_ASSERT(this, IsJSProxy(proxy));
|
||||
|
||||
// 1. Assert: IsPropertyKey(P) is true.
|
||||
CSA_ASSERT(this, IsName(name));
|
||||
CSA_ASSERT(this, Word32Equal(IsPrivateSymbol(name), Int32Constant(0)));
|
||||
|
||||
Label throw_proxy_handler_revoked(this, Label::kDeferred),
|
||||
trap_undefined(this),
|
||||
if_try_get_own_property_bailout(this, Label::kDeferred),
|
||||
trap_not_callable(this, Label::kDeferred), return_true(this),
|
||||
return_false(this), check_target_desc(this);
|
||||
|
||||
// 2. Let handler be O.[[ProxyHandler]].
|
||||
Node* handler = LoadObjectField(proxy, JSProxy::kHandlerOffset);
|
||||
|
||||
// 3. If handler is null, throw a TypeError exception.
|
||||
// 4. Assert: Type(handler) is Object.
|
||||
GotoIfNot(IsJSReceiver(handler), &throw_proxy_handler_revoked);
|
||||
|
||||
// 5. Let target be O.[[ProxyTarget]].
|
||||
Node* target = LoadObjectField(proxy, JSProxy::kTargetOffset);
|
||||
|
||||
// 6. Let trap be ? GetMethod(handler, "has").
|
||||
// 7. If trap is undefined, then (see 7.a below).
|
||||
Handle<Name> trap_name = factory()->has_string();
|
||||
Node* trap = GetMethod(context, handler, trap_name, &trap_undefined);
|
||||
|
||||
GotoIfNot(IsCallable(trap), &trap_not_callable);
|
||||
|
||||
// 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler, « target, P
|
||||
// »)).
|
||||
BranchIfToBooleanIsTrue(CallJS(CodeFactory::Call(isolate()), context, trap,
|
||||
handler, target, name),
|
||||
&return_true, &check_target_desc);
|
||||
|
||||
BIND(&check_target_desc);
|
||||
{
|
||||
// 9. If booleanTrapResult is false, then (see 9.a. in CheckHasTrapResult).
|
||||
CheckHasTrapResult(context, target, proxy, name, &return_false,
|
||||
&if_try_get_own_property_bailout);
|
||||
}
|
||||
|
||||
BIND(&if_try_get_own_property_bailout);
|
||||
{
|
||||
CallRuntime(Runtime::kCheckProxyHasTrap, context, name, target);
|
||||
Return(FalseConstant());
|
||||
}
|
||||
|
||||
BIND(&trap_undefined);
|
||||
{
|
||||
// 7.a. Return ? target.[[HasProperty]](P).
|
||||
TailCallStub(Builtins::CallableFor(isolate(), Builtins::kHasProperty),
|
||||
context, name, target);
|
||||
}
|
||||
|
||||
BIND(&return_false);
|
||||
Return(FalseConstant());
|
||||
|
||||
BIND(&return_true);
|
||||
Return(TrueConstant());
|
||||
|
||||
BIND(&throw_proxy_handler_revoked);
|
||||
ThrowTypeError(context, MessageTemplate::kProxyRevoked, "has");
|
||||
|
||||
BIND(&trap_not_callable);
|
||||
ThrowTypeError(context, MessageTemplate::kPropertyNotFunction, trap,
|
||||
StringConstant("has"), proxy);
|
||||
}
|
||||
|
||||
void ProxiesCodeStubAssembler::CheckHasTrapResult(Node* context, Node* target,
|
||||
Node* proxy, Node* name,
|
||||
Label* check_passed,
|
||||
Label* if_bailout) {
|
||||
Node* target_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),
|
||||
throw_non_configurable(this, Label::kDeferred),
|
||||
throw_non_extensible(this, Label::kDeferred);
|
||||
|
||||
// 9.a. Let targetDesc be ? target.[[GetOwnProperty]](P).
|
||||
Node* instance_type = LoadInstanceType(target);
|
||||
TryGetOwnProperty(context, target, target, target_map, instance_type, name,
|
||||
&if_found_value, &var_value, &var_details, &var_raw_value,
|
||||
check_passed, if_bailout);
|
||||
|
||||
// 9.b. If targetDesc is not undefined, then (see 9.b.i. below).
|
||||
BIND(&if_found_value);
|
||||
{
|
||||
// 9.b.i. If targetDesc.[[Configurable]] is false, throw a TypeError
|
||||
// exception.
|
||||
Node* non_configurable = IsSetWord32(
|
||||
var_details.value(), PropertyDetails::kAttributesDontDeleteMask);
|
||||
GotoIf(non_configurable, &throw_non_configurable);
|
||||
|
||||
// 9.b.ii. Let extensibleTarget be ? IsExtensible(target).
|
||||
Node* target_extensible = IsExtensibleMap(target_map);
|
||||
|
||||
// 9.b.iii. If extensibleTarget is false, throw a TypeError exception.
|
||||
GotoIfNot(target_extensible, &throw_non_extensible);
|
||||
Goto(check_passed);
|
||||
}
|
||||
|
||||
BIND(&throw_non_configurable);
|
||||
{ ThrowTypeError(context, MessageTemplate::kProxyHasNonConfigurable, name); }
|
||||
|
||||
BIND(&throw_non_extensible);
|
||||
{ ThrowTypeError(context, MessageTemplate::kProxyHasNonExtensible, name); }
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
32
src/builtins/builtins-proxy-gen.h
Normal file
32
src/builtins/builtins-proxy-gen.h
Normal file
@ -0,0 +1,32 @@
|
||||
// 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_GEN_H_
|
||||
#define V8_BUILTINS_BUILTINS_PROXY_GEN_H_
|
||||
|
||||
#include "src/code-stub-assembler.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
using compiler::Node;
|
||||
|
||||
class ProxiesCodeStubAssembler : public CodeStubAssembler {
|
||||
public:
|
||||
explicit ProxiesCodeStubAssembler(compiler::CodeAssemblerState* state)
|
||||
: CodeStubAssembler(state) {}
|
||||
|
||||
protected:
|
||||
void GotoIfRevokedProxy(Node* object, Label* if_proxy_revoked);
|
||||
Node* AllocateProxy(Node* target, Node* handler, Node* context);
|
||||
Node* AllocateJSArrayForCodeStubArguments(Node* context,
|
||||
CodeStubArguments& args, Node* argc,
|
||||
ParameterMode mode);
|
||||
void CheckHasTrapResult(Node* context, Node* target, Node* proxy, Node* name,
|
||||
Label* check_passed, Label* if_bailout);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_BUILTINS_BUILTINS_PROXY_GEN_H_
|
@ -3443,6 +3443,11 @@ TNode<BoolT> CodeStubAssembler::IsDictionaryMap(SloppyTNode<Map> map) {
|
||||
return IsSetWord32<Map::DictionaryMap>(bit_field3);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::IsExtensibleMap(Node* map) {
|
||||
CSA_ASSERT(this, IsMap(map));
|
||||
return IsSetWord32(LoadMapBitField2(map), 1 << Map::kIsExtensible);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::IsCallableMap(Node* map) {
|
||||
CSA_ASSERT(this, IsMap(map));
|
||||
return IsSetWord32(LoadMapBitField(map), 1 << Map::kIsCallable);
|
||||
@ -5744,7 +5749,7 @@ void CodeStubAssembler::TryLookupProperty(
|
||||
}
|
||||
BIND(&if_objectisspecial);
|
||||
{
|
||||
// Handle global object here and other special objects in runtime.
|
||||
// Handle global object here and bailout for other special objects.
|
||||
GotoIfNot(Word32Equal(instance_type, Int32Constant(JS_GLOBAL_OBJECT_TYPE)),
|
||||
if_bailout);
|
||||
|
||||
@ -5774,6 +5779,7 @@ void CodeStubAssembler::TryHasOwnProperty(Node* object, Node* map,
|
||||
TryLookupProperty(object, map, instance_type, unique_name, if_found, if_found,
|
||||
&if_found_global, &var_meta_storage, &var_name_index,
|
||||
if_not_found, if_bailout);
|
||||
|
||||
BIND(&if_found_global);
|
||||
{
|
||||
VARIABLE(var_value, MachineRepresentation::kTagged);
|
||||
@ -6244,7 +6250,7 @@ template void CodeStubAssembler::NumberDictionaryLookup<
|
||||
void CodeStubAssembler::TryPrototypeChainLookup(
|
||||
Node* receiver, Node* key, const LookupInHolder& lookup_property_in_holder,
|
||||
const LookupInHolder& lookup_element_in_holder, Label* if_end,
|
||||
Label* if_bailout) {
|
||||
Label* if_bailout, Label* if_proxy) {
|
||||
// Ensure receiver is JSReceiver, otherwise bailout.
|
||||
Label if_objectisnotsmi(this);
|
||||
Branch(TaggedIsSmi(receiver), if_bailout, &if_objectisnotsmi);
|
||||
@ -6256,10 +6262,15 @@ void CodeStubAssembler::TryPrototypeChainLookup(
|
||||
Label if_objectisreceiver(this);
|
||||
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
|
||||
STATIC_ASSERT(FIRST_JS_RECEIVER_TYPE == JS_PROXY_TYPE);
|
||||
Branch(
|
||||
Int32GreaterThan(instance_type, Int32Constant(FIRST_JS_RECEIVER_TYPE)),
|
||||
&if_objectisreceiver, if_bailout);
|
||||
Branch(Int32GreaterThanOrEqual(instance_type,
|
||||
Int32Constant(FIRST_JS_RECEIVER_TYPE)),
|
||||
&if_objectisreceiver, if_bailout);
|
||||
BIND(&if_objectisreceiver);
|
||||
|
||||
if (if_proxy) {
|
||||
GotoIf(Word32Equal(instance_type, Int32Constant(JS_PROXY_TYPE)),
|
||||
if_proxy);
|
||||
}
|
||||
}
|
||||
|
||||
VARIABLE(var_index, MachineType::PointerRepresentation());
|
||||
@ -8851,11 +8862,10 @@ Node* CodeStubAssembler::SameValue(Node* lhs, Node* rhs) {
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::HasProperty(
|
||||
Node* object, Node* key, Node* context,
|
||||
Runtime::FunctionId fallback_runtime_function_id) {
|
||||
Node* CodeStubAssembler::HasProperty(Node* object, Node* key, Node* context,
|
||||
HasPropertyLookupMode mode) {
|
||||
Label call_runtime(this, Label::kDeferred), return_true(this),
|
||||
return_false(this), end(this);
|
||||
return_false(this), end(this), if_proxy(this, Label::kDeferred);
|
||||
|
||||
CodeStubAssembler::LookupInHolder lookup_property_in_holder =
|
||||
[this, &return_true](Node* receiver, Node* holder, Node* holder_map,
|
||||
@ -8876,9 +8886,27 @@ Node* CodeStubAssembler::HasProperty(
|
||||
|
||||
TryPrototypeChainLookup(object, key, lookup_property_in_holder,
|
||||
lookup_element_in_holder, &return_false,
|
||||
&call_runtime);
|
||||
&call_runtime, &if_proxy);
|
||||
|
||||
VARIABLE(result, MachineRepresentation::kTagged);
|
||||
|
||||
BIND(&if_proxy);
|
||||
{
|
||||
Node* name = ToName(context, key);
|
||||
switch (mode) {
|
||||
case kHasProperty:
|
||||
GotoIf(IsPrivateSymbol(name), &return_false);
|
||||
|
||||
result.Bind(
|
||||
CallBuiltin(Builtins::kProxyHasProperty, context, object, name));
|
||||
Goto(&end);
|
||||
break;
|
||||
case kForInHasProperty:
|
||||
Goto(&call_runtime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BIND(&return_true);
|
||||
{
|
||||
result.Bind(BooleanConstant(true));
|
||||
@ -8893,6 +8921,16 @@ Node* CodeStubAssembler::HasProperty(
|
||||
|
||||
BIND(&call_runtime);
|
||||
{
|
||||
Runtime::FunctionId fallback_runtime_function_id;
|
||||
switch (mode) {
|
||||
case kHasProperty:
|
||||
fallback_runtime_function_id = Runtime::kHasProperty;
|
||||
break;
|
||||
case kForInHasProperty:
|
||||
fallback_runtime_function_id = Runtime::kForInHasProperty;
|
||||
break;
|
||||
}
|
||||
|
||||
result.Bind(
|
||||
CallRuntime(fallback_runtime_function_id, context, object, key));
|
||||
Goto(&end);
|
||||
|
@ -868,6 +868,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
Node* IsAllocationSite(Node* object);
|
||||
Node* IsAnyHeapNumber(Node* object);
|
||||
Node* IsBoolean(Node* object);
|
||||
Node* IsExtensibleMap(Node* map);
|
||||
Node* IsCallableMap(Node* map);
|
||||
Node* IsCallable(Node* object);
|
||||
Node* IsConsStringInstanceType(Node* instance_type);
|
||||
@ -1383,10 +1384,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
// Upon reaching the end of prototype chain the control goes to {if_end}.
|
||||
// If it can't handle the case {receiver}/{key} case then the control goes
|
||||
// to {if_bailout}.
|
||||
// If {if_proxy} is nullptr, proxies go to if_bailout.
|
||||
void TryPrototypeChainLookup(Node* receiver, Node* key,
|
||||
const LookupInHolder& lookup_property_in_holder,
|
||||
const LookupInHolder& lookup_element_in_holder,
|
||||
Label* if_end, Label* if_bailout);
|
||||
Label* if_end, Label* if_bailout,
|
||||
Label* if_proxy = nullptr);
|
||||
|
||||
// Instanceof helpers.
|
||||
// Returns true if {object} has {prototype} somewhere in it's prototype
|
||||
@ -1565,9 +1568,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
|
||||
// instructions, e.g. Branch(SameValue(...), &label).
|
||||
Node* SameValue(Node* lhs, Node* rhs);
|
||||
|
||||
Node* HasProperty(
|
||||
Node* object, Node* key, Node* context,
|
||||
Runtime::FunctionId fallback_runtime_function_id = Runtime::kHasProperty);
|
||||
enum HasPropertyLookupMode { kHasProperty, kForInHasProperty };
|
||||
|
||||
Node* HasProperty(Node* object, Node* key, Node* context,
|
||||
HasPropertyLookupMode mode);
|
||||
|
||||
Node* ClassOf(Node* object);
|
||||
|
||||
|
@ -2098,7 +2098,8 @@ IGNITION_HANDLER(TestIn, InterpreterAssembler) {
|
||||
Node* property = LoadRegister(reg_index);
|
||||
Node* object = GetAccumulator();
|
||||
Node* context = GetContext();
|
||||
SetAccumulator(HasProperty(object, property, context));
|
||||
|
||||
SetAccumulator(HasProperty(object, property, context, kHasProperty));
|
||||
Dispatch();
|
||||
}
|
||||
|
||||
|
@ -5474,35 +5474,40 @@ Maybe<bool> JSProxy::HasProperty(Isolate* isolate, Handle<JSProxy> proxy,
|
||||
bool boolean_trap_result = trap_result_obj->BooleanValue();
|
||||
// 9. If booleanTrapResult is false, then:
|
||||
if (!boolean_trap_result) {
|
||||
// 9a. Let targetDesc be ? target.[[GetOwnProperty]](P).
|
||||
PropertyDescriptor target_desc;
|
||||
Maybe<bool> target_found = JSReceiver::GetOwnPropertyDescriptor(
|
||||
isolate, target, name, &target_desc);
|
||||
MAYBE_RETURN(target_found, Nothing<bool>());
|
||||
// 9b. If targetDesc is not undefined, then:
|
||||
if (target_found.FromJust()) {
|
||||
// 9b i. If targetDesc.[[Configurable]] is false, throw a TypeError
|
||||
// exception.
|
||||
if (!target_desc.configurable()) {
|
||||
isolate->Throw(*isolate->factory()->NewTypeError(
|
||||
MessageTemplate::kProxyHasNonConfigurable, name));
|
||||
return Nothing<bool>();
|
||||
}
|
||||
// 9b ii. Let extensibleTarget be ? IsExtensible(target).
|
||||
Maybe<bool> extensible_target = JSReceiver::IsExtensible(target);
|
||||
MAYBE_RETURN(extensible_target, Nothing<bool>());
|
||||
// 9b iii. If extensibleTarget is false, throw a TypeError exception.
|
||||
if (!extensible_target.FromJust()) {
|
||||
isolate->Throw(*isolate->factory()->NewTypeError(
|
||||
MessageTemplate::kProxyHasNonExtensible, name));
|
||||
return Nothing<bool>();
|
||||
}
|
||||
}
|
||||
MAYBE_RETURN(JSProxy::CheckHasTrap(isolate, name, target), Nothing<bool>());
|
||||
}
|
||||
// 10. Return booleanTrapResult.
|
||||
return Just(boolean_trap_result);
|
||||
}
|
||||
|
||||
Maybe<bool> JSProxy::CheckHasTrap(Isolate* isolate, Handle<Name> name,
|
||||
Handle<JSReceiver> target) {
|
||||
// 9a. Let targetDesc be ? target.[[GetOwnProperty]](P).
|
||||
PropertyDescriptor target_desc;
|
||||
Maybe<bool> target_found =
|
||||
JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc);
|
||||
MAYBE_RETURN(target_found, Nothing<bool>());
|
||||
// 9b. If targetDesc is not undefined, then:
|
||||
if (target_found.FromJust()) {
|
||||
// 9b i. If targetDesc.[[Configurable]] is false, throw a TypeError
|
||||
// exception.
|
||||
if (!target_desc.configurable()) {
|
||||
isolate->Throw(*isolate->factory()->NewTypeError(
|
||||
MessageTemplate::kProxyHasNonConfigurable, name));
|
||||
return Nothing<bool>();
|
||||
}
|
||||
// 9b ii. Let extensibleTarget be ? IsExtensible(target).
|
||||
Maybe<bool> extensible_target = JSReceiver::IsExtensible(target);
|
||||
MAYBE_RETURN(extensible_target, Nothing<bool>());
|
||||
// 9b iii. If extensibleTarget is false, throw a TypeError exception.
|
||||
if (!extensible_target.FromJust()) {
|
||||
isolate->Throw(*isolate->factory()->NewTypeError(
|
||||
MessageTemplate::kProxyHasNonExtensible, name));
|
||||
return Nothing<bool>();
|
||||
}
|
||||
}
|
||||
return Just(true);
|
||||
}
|
||||
|
||||
Maybe<bool> JSProxy::SetProperty(Handle<JSProxy> proxy, Handle<Name> name,
|
||||
Handle<Object> value, Handle<Object> receiver,
|
||||
|
@ -6292,6 +6292,12 @@ class JSProxy: public JSReceiver {
|
||||
Handle<JSProxy> proxy,
|
||||
Handle<Name> name);
|
||||
|
||||
// This function never returns false.
|
||||
// It returns either true or throws.
|
||||
MUST_USE_RESULT static Maybe<bool> CheckHasTrap(Isolate* isolate,
|
||||
Handle<Name> name,
|
||||
Handle<JSReceiver> target);
|
||||
|
||||
// ES6 9.5.8
|
||||
MUST_USE_RESULT static MaybeHandle<Object> GetProperty(
|
||||
Isolate* isolate, Handle<JSProxy> proxy, Handle<Name> name,
|
||||
|
@ -71,5 +71,18 @@ RUNTIME_FUNCTION(Runtime_CheckProxyGetTrapResult) {
|
||||
isolate, JSProxy::CheckGetTrapResult(isolate, name, target, trap_result));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_CheckProxyHasTrap) {
|
||||
HandleScope scope(isolate);
|
||||
|
||||
DCHECK_EQ(2, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(Name, name, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, target, 1);
|
||||
|
||||
RETURN_ON_SCHEDULED_EXCEPTION_VALUE(
|
||||
isolate, JSProxy::CheckHasTrap(isolate, name, target),
|
||||
*isolate->factory()->undefined_value());
|
||||
return *isolate->factory()->undefined_value();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -466,7 +466,8 @@ namespace internal {
|
||||
F(JSProxyGetHandler, 1, 1) \
|
||||
F(JSProxyRevoke, 1, 1) \
|
||||
F(GetPropertyWithReceiver, 2, 1) \
|
||||
F(CheckProxyGetTrapResult, 2, 1)
|
||||
F(CheckProxyGetTrapResult, 2, 1) \
|
||||
F(CheckProxyHasTrap, 2, 1)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_REGEXP(F) \
|
||||
F(IsRegExp, 1, 1) \
|
||||
|
@ -207,6 +207,7 @@
|
||||
'builtins/builtins-promise-gen.cc',
|
||||
'builtins/builtins-promise-gen.h',
|
||||
'builtins/builtins-proxy-gen.cc',
|
||||
'builtins/builtins-proxy-gen.h',
|
||||
'builtins/builtins-proxy-helpers-gen.cc',
|
||||
'builtins/builtins-proxy-helpers-gen.h',
|
||||
'builtins/builtins-regexp-gen.cc',
|
||||
|
@ -5,12 +5,13 @@
|
||||
var target = {
|
||||
"target_one": 1
|
||||
};
|
||||
target[0] = 42;
|
||||
target.__proto__ = {
|
||||
"target_two": 2
|
||||
};
|
||||
var handler = {
|
||||
has: function(target, name) {
|
||||
return name == "present";
|
||||
return name == "present" || name == '0';
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +21,25 @@ var proxy = new Proxy(target, handler);
|
||||
assertTrue("present" in proxy);
|
||||
assertFalse("nonpresent" in proxy);
|
||||
|
||||
// Test element cases.
|
||||
assertTrue(0 in proxy);
|
||||
assertFalse(1 in proxy);
|
||||
assertTrue('0' in proxy);
|
||||
assertFalse('1' in proxy);
|
||||
|
||||
var symbol0 = {
|
||||
[Symbol.toPrimitive](hint) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
var symbol1 = {
|
||||
[Symbol.toPrimitive](hint) {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
assertTrue(symbol0 in proxy);
|
||||
assertFalse(symbol1 in proxy);
|
||||
|
||||
// Test interesting algorithm steps:
|
||||
|
||||
// Step 7: Fall through to target if trap is undefined.
|
||||
@ -59,3 +79,48 @@ assertFalse("in_your_dreams" in proxy);
|
||||
var object = Object.create(proxy);
|
||||
object.hasOwnProperty(0);
|
||||
})();
|
||||
|
||||
(function FalseTargetPropExists() {
|
||||
var target2 = {
|
||||
attr: 1
|
||||
};
|
||||
var p = new Proxy(target2, {
|
||||
has: function(t, prop) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
assertFalse("attr" in p);
|
||||
})();
|
||||
|
||||
(function TargetHasAccessorProperty() {
|
||||
var target = {};
|
||||
Object.defineProperty(target, 'prop', {
|
||||
get: function() {
|
||||
assertSame(this, target);
|
||||
return 42;
|
||||
},
|
||||
configurable: true
|
||||
})
|
||||
var proxy = new Proxy(target, {
|
||||
has: function(t, prop) {
|
||||
return false;
|
||||
},
|
||||
});
|
||||
assertFalse('prop' in proxy);
|
||||
})();
|
||||
|
||||
(function TargetHasNonConfigurableProperty() {
|
||||
var target = {};
|
||||
Object.defineProperty(target, 'prop', {
|
||||
value: 42,
|
||||
configurable: false,
|
||||
writable: true
|
||||
})
|
||||
var proxy = new Proxy(target, {
|
||||
has: function(t, prop) {
|
||||
return false;
|
||||
},
|
||||
});
|
||||
assertThrows(function() { 'prop' in proxy; }, TypeError);
|
||||
})();
|
||||
|
@ -28,7 +28,8 @@ for (var key in object2) assertUnreachable();
|
||||
|
||||
// Private symbols must never leak to proxy traps.
|
||||
|
||||
var proxy = new Proxy({}, new Proxy({}, {get() {return () => {throw 666}}}));
|
||||
var proxy = new Proxy({}, new Proxy({}, {get() {return () => {
|
||||
throw new Error()}}}));
|
||||
var object = {__proto__: proxy};
|
||||
|
||||
// [[Set]]
|
||||
|
Loading…
Reference in New Issue
Block a user