[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:
Maya Lekova 2017-08-16 14:33:07 +02:00 committed by Commit Bot
parent b0d09b4967
commit 221e54ddbc
17 changed files with 402 additions and 133 deletions

View File

@ -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",

View File

@ -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);

View File

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

View File

@ -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);

View File

@ -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) {

View File

@ -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

View 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_

View File

@ -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);

View File

@ -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);

View File

@ -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();
}

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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) \

View File

@ -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',

View File

@ -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);
})();

View File

@ -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]]