diff --git a/BUILD.gn b/BUILD.gn index ee73f244f7..80f01c39ea 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -994,6 +994,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", diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h index 2eaa2e2f3c..ad44e3d7c1 100644 --- a/src/builtins/builtins-definitions.h +++ b/src/builtins/builtins-definitions.h @@ -802,6 +802,7 @@ namespace internal { TFJ(ProxyConstructor, 0) \ TFJ(ProxyConstructor_ConstructStub, \ SharedFunctionInfo::kDontAdaptArgumentsSentinel) \ + TFS(ProxyGetProperty, kProxy, kName, kReceiverValue) \ \ /* Reflect */ \ ASM(ReflectApply) \ diff --git a/src/builtins/builtins-proxy-helpers-gen.cc b/src/builtins/builtins-proxy-helpers-gen.cc new file mode 100644 index 0000000000..ca2e9bb6d7 --- /dev/null +++ b/src/builtins/builtins-proxy-helpers-gen.cc @@ -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 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 diff --git a/src/builtins/builtins-proxy-helpers-gen.h b/src/builtins/builtins-proxy-helpers-gen.h new file mode 100644 index 0000000000..a72abbdd4a --- /dev/null +++ b/src/builtins/builtins-proxy-helpers-gen.h @@ -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_ diff --git a/src/code-stub-assembler.cc b/src/code-stub-assembler.cc index bb8210ec1b..a76862499e 100644 --- a/src/code-stub-assembler.cc +++ b/src/code-stub-assembler.cc @@ -6036,6 +6036,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"); @@ -6044,8 +6054,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, @@ -6057,14 +6070,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); @@ -6072,14 +6085,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); diff --git a/src/code-stub-assembler.h b/src/code-stub-assembler.h index 96d6c974cf..cf6148b0ec 100644 --- a/src/code-stub-assembler.h +++ b/src/code-stub-assembler.h @@ -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) { return GetProperty(context, receiver, HeapConstant(name)); diff --git a/src/ic/accessor-assembler.cc b/src/ic/accessor-assembler.cc index 82fd0bba2f..f027c5e974 100644 --- a/src/ic/accessor-assembler.cc +++ b/src/ic/accessor-assembler.cc @@ -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. diff --git a/src/ic/handler-configuration-inl.h b/src/ic/handler-configuration-inl.h index 3e099eeb94..f2ad5dd2f8 100644 --- a/src/ic/handler-configuration-inl.h +++ b/src/ic/handler-configuration-inl.h @@ -53,6 +53,11 @@ Handle LoadHandler::LoadAccessor(Isolate* isolate, int descriptor) { return handle(Smi::FromInt(config), isolate); } +Handle LoadHandler::LoadProxy(Isolate* isolate) { + int config = KindBits::encode(kProxy); + return handle(Smi::FromInt(config), isolate); +} + Handle LoadHandler::LoadApiGetter(Isolate* isolate, int descriptor) { int config = KindBits::encode(kConstant) | IsAccessorInfoBits::encode(true) | DescriptorBits::encode(descriptor); diff --git a/src/ic/handler-configuration.h b/src/ic/handler-configuration.h index 516cefacf4..54dbdc32da 100644 --- a/src/ic/handler-configuration.h +++ b/src/ic/handler-configuration.h @@ -25,9 +25,10 @@ class LoadHandler { kConstant, kAccessor, kInterceptor, + kProxy, kNonExistent }; - class KindBits : public BitField {}; + class KindBits : public BitField {}; // 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 LoadAccessor(Isolate* isolate, int descriptor); + // Creates a Smi-handler for calling a getter on a proxy. + static inline Handle LoadProxy(Isolate* isolate); + // Creates a Smi-handler for loading an Api getter property from fast object. static inline Handle LoadApiGetter(Isolate* isolate, int descriptor); diff --git a/src/ic/ic.cc b/src/ic/ic.cc index cb2c97dd56..d3f4d12a6d 100644 --- a/src/ic/ic.cc +++ b/src/ic/ic.cc @@ -773,7 +773,7 @@ namespace { template int InitPrototypeChecks(Isolate* isolate, Handle receiver_map, - Handle holder, Handle name, + Handle holder, Handle name, Handle array, int first_index) { if (!holder.is_null() && holder->map() == *receiver_map) return 0; @@ -814,7 +814,8 @@ int InitPrototypeChecks(Isolate* isolate, Handle receiver_map, : PrototypeIterator::END_AT_NULL; for (PrototypeIterator iter(receiver_map, end); !iter.IsAtEnd(); iter.Advance()) { - Handle current = PrototypeIterator::GetCurrent(iter); + Handle current = + PrototypeIterator::GetCurrent(iter); if (holder.is_identical_to(current)) break; Handle current_map(current->map(), isolate); @@ -851,7 +852,7 @@ int InitPrototypeChecks(Isolate* isolate, Handle receiver_map, // Returns -1 if the handler has to be compiled or the number of prototype // checks otherwise. int GetPrototypeCheckCount(Isolate* isolate, Handle receiver_map, - Handle holder, Handle name) { + Handle holder, Handle name) { return InitPrototypeChecks(isolate, receiver_map, holder, name, Handle(), 0); } @@ -861,7 +862,7 @@ enum class HolderCellRequest { kHolder, }; -Handle HolderCell(Isolate* isolate, Handle holder, +Handle HolderCell(Isolate* isolate, Handle holder, Handle name, HolderCellRequest request) { if (request == HolderCellRequest::kGlobalPropertyCell) { DCHECK(holder->IsJSGlobalObject()); @@ -878,7 +879,7 @@ Handle HolderCell(Isolate* isolate, Handle holder, } // namespace Handle LoadIC::LoadFromPrototype(Handle receiver_map, - Handle holder, + Handle holder, Handle name, Handle smi_handler) { int checks_count = @@ -926,7 +927,7 @@ Handle LoadIC::LoadFromPrototype(Handle receiver_map, Handle LoadIC::LoadFullChain(Handle receiver_map, Handle holder, Handle name, Handle smi_handler) { - Handle end; // null handle + Handle end; // null handle int checks_count = GetPrototypeCheckCount(isolate(), receiver_map, end, name); DCHECK_LE(0, checks_count); @@ -977,8 +978,7 @@ void LoadIC::UpdateCaches(LookupIterator* lookup) { } Handle 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); @@ -1102,8 +1102,13 @@ Handle LoadIC::GetMapIndependentHandler(LookupIterator* lookup) { } Handle map = receiver_map(); - Handle holder = lookup->GetHolder(); - bool receiver_is_holder = receiver.is_identical_to(holder); + Handle holder; + bool receiver_is_holder; + if (lookup->state() != LookupIterator::JSPROXY) { + holder = lookup->GetHolder(); + receiver_is_holder = receiver.is_identical_to(holder); + } + switch (lookup->state()) { case LookupIterator::INTERCEPTOR: { Handle smi_handler = LoadHandler::LoadInterceptor(isolate()); @@ -1264,8 +1269,16 @@ Handle LoadIC::GetMapIndependentHandler(LookupIterator* lookup) { case LookupIterator::INTEGER_INDEXED_EXOTIC: TRACE_HANDLER_STATS(isolate(), LoadIC_LoadIntegerIndexedExoticDH); return LoadHandler::LoadNonExistent(isolate()); + case LookupIterator::JSPROXY: { + Handle holder_proxy = lookup->GetHolder(); + bool receiver_is_holder_proxy = receiver.is_identical_to(holder_proxy); + Handle 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(); @@ -1722,6 +1735,7 @@ Handle StoreIC::StoreTransition(Handle receiver_map, int checks_count = GetPrototypeCheckCount(isolate(), receiver_map, holder, name); + DCHECK_LE(0, checks_count); DCHECK(!receiver_map->IsJSGlobalObjectMap()); diff --git a/src/ic/ic.h b/src/ic/ic.h index 64b630eca2..6ac2e5b88a 100644 --- a/src/ic/ic.h +++ b/src/ic/ic.h @@ -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 LoadFromPrototype(Handle receiver_map, - Handle holder, Handle name, + Handle holder, Handle name, Handle smi_handler); // Creates a data handler that represents a load of a non-existent property. diff --git a/src/objects.cc b/src/objects.cc index c3a5fca398..f14e465b97 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -1105,6 +1105,22 @@ MaybeHandle JSProxy::GetProperty(Isolate* isolate, ASSIGN_RETURN_ON_EXCEPTION( isolate, trap_result, Execution::Call(isolate, trap, handler, arraysize(args), args), Object); + + MaybeHandle result = + JSProxy::CheckGetTrapResult(isolate, name, target, trap_result); + if (result.is_null()) { + return result; + } + + // 11. Return trap_result + return trap_result; +} + +// static +MaybeHandle JSProxy::CheckGetTrapResult(Isolate* isolate, + Handle name, + Handle target, + Handle trap_result) { // 9. Let targetDesc be ? target.[[GetOwnProperty]](P). PropertyDescriptor target_desc; Maybe target_found = @@ -1141,8 +1157,7 @@ MaybeHandle JSProxy::GetProperty(Isolate* isolate, Object); } } - // 11. Return trap_result - return trap_result; + return isolate->factory()->undefined_value(); } @@ -12605,7 +12620,12 @@ Handle Map::GetOrCreatePrototypeChainValidityCell(Handle map, } else { maybe_prototype = handle(map->GetPrototypeChainRootMap(isolate)->prototype(), isolate); - if (!maybe_prototype->IsJSObject()) return Handle::null(); + if (!maybe_prototype->IsJSReceiver()) return Handle::null(); + } + if (maybe_prototype->IsJSProxy()) { + Handle cell = isolate->factory()->NewCell( + handle(Smi::FromInt(Map::kPrototypeChainValid), isolate)); + return cell; } Handle prototype = Handle::cast(maybe_prototype); // Ensure the prototype is registered with its own prototypes so its cell @@ -12630,11 +12650,16 @@ Handle Map::GetOrCreatePrototypeChainValidityCell(Handle map, } // static -Handle Map::GetOrCreatePrototypeWeakCell(Handle prototype, +Handle Map::GetOrCreatePrototypeWeakCell(Handle prototype, Isolate* isolate) { DCHECK(!prototype.is_null()); + if (prototype->IsJSProxy()) { + Handle cell = isolate->factory()->NewWeakCell(prototype); + return cell; + } + Handle proto_info = - GetOrCreatePrototypeInfo(prototype, isolate); + GetOrCreatePrototypeInfo(Handle::cast(prototype), isolate); Object* maybe_cell = proto_info->weak_cell(); // Return existing cell if it's already created. if (maybe_cell->IsWeakCell()) { diff --git a/src/objects.h b/src/objects.h index 152ded24d6..cee7d5e6ec 100644 --- a/src/objects.h +++ b/src/objects.h @@ -6295,6 +6295,11 @@ class JSProxy: public JSReceiver { Isolate* isolate, Handle proxy, Handle name, Handle receiver, bool* was_found); + static MaybeHandle CheckGetTrapResult(Isolate* isolate, + Handle name, + Handle target, + Handle trap_result); + // ES6 9.5.9 MUST_USE_RESULT static Maybe SetProperty(Handle proxy, Handle name, diff --git a/src/objects/map.h b/src/objects/map.h index bd4a917516..850e5b82fe 100644 --- a/src/objects/map.h +++ b/src/objects/map.h @@ -320,7 +320,7 @@ class Map : public HeapObject { // Returns a WeakCell object containing given prototype. The cell is cached // in PrototypeInfo which is created lazily. static Handle GetOrCreatePrototypeWeakCell( - Handle prototype, Isolate* isolate); + Handle prototype, Isolate* isolate); Map* FindRootMap() const; Map* FindFieldOwner(int descriptor) const; diff --git a/src/runtime/runtime-proxy.cc b/src/runtime/runtime-proxy.cc index 1bcab10d0b..5b32e6e8cf 100644 --- a/src/runtime/runtime-proxy.cc +++ b/src/runtime/runtime-proxy.cc @@ -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 diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index c760e8a796..64bff0c970 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -461,11 +461,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) \ diff --git a/src/v8.gyp b/src/v8.gyp index 1089a056ce..c62906ccdc 100644 --- a/src/v8.gyp +++ b/src/v8.gyp @@ -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', diff --git a/test/mjsunit/es6/proxies-get.js b/test/mjsunit/es6/proxies-get.js index b1b92dbb06..4f7b2d0be4 100644 --- a/test/mjsunit/es6/proxies-get.js +++ b/test/mjsunit/es6/proxies-get.js @@ -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')"); +})(); diff --git a/test/mjsunit/regress/regress-crbug-752712.js b/test/mjsunit/regress/regress-crbug-752712.js new file mode 100644 index 0000000000..12d833ca5f --- /dev/null +++ b/test/mjsunit/regress/regress-crbug-752712.js @@ -0,0 +1,24 @@ +// 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. + +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); +})(); diff --git a/test/mjsunit/regress/regress-crbug-752846.js b/test/mjsunit/regress/regress-crbug-752846.js new file mode 100644 index 0000000000..c2095dc57f --- /dev/null +++ b/test/mjsunit/regress/regress-crbug-752846.js @@ -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); +}