Commit 221e54dd authored by Maya Lekova's avatar Maya Lekova Committed by Commit Bot

[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: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47372}
parent b0d09b49
......@@ -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) {
......
This diff is collapsed.
// 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]]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment