Reland "[builtins] Port Proxy set trap to CSA"
This is a reland of a9f517e234
Original change's description:
> [builtins] Port Proxy set trap to CSA
>
> Bug: v8:6560, v8:6557
> Change-Id: I329794607e8de324fc696652555aaaeafcf519ec
> Reviewed-on: https://chromium-review.googlesource.com/625940
> Reviewed-by: Igor Sheludko <ishell@chromium.org>
> Commit-Queue: Maya Lekova <mslekova@google.com>
> Cr-Commit-Position: refs/heads/master@{#47760}
Bug: v8:6560, v8:6557
Change-Id: I1b32992eac6cc5583a44703eed901e4ad15f1947
Reviewed-on: https://chromium-review.googlesource.com/647447
Commit-Queue: Maya Lekova <mslekova@google.com>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47772}
This commit is contained in:
parent
d6d720826e
commit
5931cc9409
2
BUILD.gn
2
BUILD.gn
@ -1003,8 +1003,6 @@ v8_source_set("v8_builtins_generators") {
|
|||||||
"src/builtins/builtins-promise-gen.h",
|
"src/builtins/builtins-promise-gen.h",
|
||||||
"src/builtins/builtins-proxy-gen.cc",
|
"src/builtins/builtins-proxy-gen.cc",
|
||||||
"src/builtins/builtins-proxy-gen.h",
|
"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",
|
"src/builtins/builtins-regexp-gen.cc",
|
||||||
"src/builtins/builtins-regexp-gen.h",
|
"src/builtins/builtins-regexp-gen.h",
|
||||||
"src/builtins/builtins-sharedarraybuffer-gen.cc",
|
"src/builtins/builtins-sharedarraybuffer-gen.cc",
|
||||||
|
@ -778,6 +778,7 @@ namespace internal {
|
|||||||
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
||||||
TFS(ProxyGetProperty, kProxy, kName, kReceiverValue) \
|
TFS(ProxyGetProperty, kProxy, kName, kReceiverValue) \
|
||||||
TFS(ProxyHasProperty, kProxy, kName) \
|
TFS(ProxyHasProperty, kProxy, kName) \
|
||||||
|
TFS(ProxySetProperty, kProxy, kName, kValue, kReceiverValue, kLanguageMode) \
|
||||||
\
|
\
|
||||||
/* Reflect */ \
|
/* Reflect */ \
|
||||||
ASM(ReflectApply) \
|
ASM(ReflectApply) \
|
||||||
|
@ -322,6 +322,257 @@ TF_BUILTIN(ProxyHasProperty, ProxiesCodeStubAssembler) {
|
|||||||
StringConstant("has"), proxy);
|
StringConstant("has"), proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TF_BUILTIN(ProxyGetProperty, ProxiesCodeStubAssembler) {
|
||||||
|
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, TaggedIsNotSmi(name));
|
||||||
|
CSA_ASSERT(this, IsName(name));
|
||||||
|
CSA_ASSERT(this, Word32Equal(IsPrivateSymbol(name), Int32Constant(0)));
|
||||||
|
|
||||||
|
Label throw_proxy_handler_revoked(this, Label::kDeferred),
|
||||||
|
trap_undefined(this);
|
||||||
|
|
||||||
|
// 2. Let handler be O.[[ProxyHandler]].
|
||||||
|
Node* handler = LoadObjectField(proxy, JSProxy::kHandlerOffset);
|
||||||
|
|
||||||
|
// 3. If handler is null, throw a TypeError exception.
|
||||||
|
GotoIf(IsNull(handler), &throw_proxy_handler_revoked);
|
||||||
|
|
||||||
|
// 4. Assert: Type(handler) is Object.
|
||||||
|
CSA_ASSERT(this, IsJSReceiver(handler));
|
||||||
|
|
||||||
|
// 5. Let target be O.[[ProxyTarget]].
|
||||||
|
Node* target = LoadObjectField(proxy, JSProxy::kTargetOffset);
|
||||||
|
|
||||||
|
// 6. Let trap be ? GetMethod(handler, "get").
|
||||||
|
// 7. If trap is undefined, then (see 7.a below).
|
||||||
|
Handle<Name> trap_name = factory()->get_string();
|
||||||
|
Node* trap = GetMethod(context, handler, trap_name, &trap_undefined);
|
||||||
|
|
||||||
|
// 8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »).
|
||||||
|
Node* trap_result = CallJS(
|
||||||
|
CodeFactory::Call(isolate(), ConvertReceiverMode::kNotNullOrUndefined),
|
||||||
|
context, trap, handler, target, name, receiver);
|
||||||
|
|
||||||
|
// 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
|
||||||
|
Label return_result(this);
|
||||||
|
CheckGetSetTrapResult(context, target, proxy, name, trap_result,
|
||||||
|
&return_result, JSProxy::kGet);
|
||||||
|
|
||||||
|
BIND(&return_result);
|
||||||
|
{
|
||||||
|
// 11. Return trapResult.
|
||||||
|
Return(trap_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
BIND(&trap_undefined);
|
||||||
|
{
|
||||||
|
// 7.a. Return ? target.[[Get]](P, Receiver).
|
||||||
|
// TODO(mslekova): Introduce GetPropertyWithReceiver stub
|
||||||
|
Return(CallRuntime(Runtime::kGetPropertyWithReceiver, context, target, name,
|
||||||
|
receiver));
|
||||||
|
}
|
||||||
|
|
||||||
|
BIND(&throw_proxy_handler_revoked);
|
||||||
|
ThrowTypeError(context, MessageTemplate::kProxyRevoked, "get");
|
||||||
|
}
|
||||||
|
|
||||||
|
TF_BUILTIN(ProxySetProperty, ProxiesCodeStubAssembler) {
|
||||||
|
Node* context = Parameter(Descriptor::kContext);
|
||||||
|
Node* proxy = Parameter(Descriptor::kProxy);
|
||||||
|
Node* name = Parameter(Descriptor::kName);
|
||||||
|
Node* value = Parameter(Descriptor::kValue);
|
||||||
|
Node* receiver = Parameter(Descriptor::kReceiverValue);
|
||||||
|
Node* language_mode = Parameter(Descriptor::kLanguageMode);
|
||||||
|
|
||||||
|
CSA_ASSERT(this, IsJSProxy(proxy));
|
||||||
|
|
||||||
|
// 1. Assert: IsPropertyKey(P) is true.
|
||||||
|
CSA_ASSERT(this, TaggedIsNotSmi(name));
|
||||||
|
CSA_ASSERT(this, IsName(name));
|
||||||
|
|
||||||
|
Label throw_proxy_handler_revoked(this, Label::kDeferred),
|
||||||
|
trap_undefined(this), failure(this, Label::kDeferred),
|
||||||
|
continue_checks(this), success(this),
|
||||||
|
private_symbol(this, Label::kDeferred);
|
||||||
|
|
||||||
|
GotoIf(IsPrivateSymbol(name), &private_symbol);
|
||||||
|
|
||||||
|
// 2. Let handler be O.[[ProxyHandler]].
|
||||||
|
Node* handler = LoadObjectField(proxy, JSProxy::kHandlerOffset);
|
||||||
|
|
||||||
|
// 3. If handler is null, throw a TypeError exception.
|
||||||
|
GotoIfNot(IsJSReceiver(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, "set").
|
||||||
|
// 7. If trap is undefined, then (see 7.a below).
|
||||||
|
Handle<Name> set_string = factory()->set_string();
|
||||||
|
Node* trap = GetMethod(context, handler, set_string, &trap_undefined);
|
||||||
|
|
||||||
|
// 8. Let booleanTrapResult be ToBoolean(? Call(trap, handler,
|
||||||
|
// « target, P, V, Receiver »)).
|
||||||
|
// 9. If booleanTrapResult is false, return false.
|
||||||
|
BranchIfToBooleanIsTrue(
|
||||||
|
CallJS(CodeFactory::Call(isolate(),
|
||||||
|
ConvertReceiverMode::kNotNullOrUndefined),
|
||||||
|
context, trap, handler, target, name, value, receiver),
|
||||||
|
&continue_checks, &failure);
|
||||||
|
|
||||||
|
BIND(&continue_checks);
|
||||||
|
{
|
||||||
|
// 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
|
||||||
|
Label return_result(this);
|
||||||
|
CheckGetSetTrapResult(context, target, proxy, name, value, &success,
|
||||||
|
JSProxy::kSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
BIND(&failure);
|
||||||
|
{
|
||||||
|
Label if_throw(this, Label::kDeferred);
|
||||||
|
Branch(SmiEqual(language_mode, SmiConstant(STRICT)), &if_throw, &success);
|
||||||
|
|
||||||
|
BIND(&if_throw);
|
||||||
|
ThrowTypeError(context, MessageTemplate::kProxyTrapReturnedFalsishFor,
|
||||||
|
HeapConstant(set_string), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12. Return true.
|
||||||
|
BIND(&success);
|
||||||
|
Return(value);
|
||||||
|
|
||||||
|
BIND(&private_symbol);
|
||||||
|
{
|
||||||
|
Label failure(this), throw_error(this, Label::kDeferred);
|
||||||
|
|
||||||
|
Branch(SmiEqual(language_mode, SmiConstant(STRICT)), &throw_error,
|
||||||
|
&failure);
|
||||||
|
|
||||||
|
BIND(&failure);
|
||||||
|
Return(UndefinedConstant());
|
||||||
|
|
||||||
|
BIND(&throw_error);
|
||||||
|
ThrowTypeError(context, MessageTemplate::kProxyPrivate);
|
||||||
|
}
|
||||||
|
|
||||||
|
BIND(&trap_undefined);
|
||||||
|
{
|
||||||
|
// 7.a. Return ? target.[[Set]](P, V, Receiver).
|
||||||
|
CallRuntime(Runtime::kSetPropertyWithReceiver, context, target, name, value,
|
||||||
|
receiver, language_mode);
|
||||||
|
Return(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
BIND(&throw_proxy_handler_revoked);
|
||||||
|
ThrowTypeError(context, MessageTemplate::kProxyRevoked, "set");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxiesCodeStubAssembler::CheckGetSetTrapResult(
|
||||||
|
Node* context, Node* target, Node* proxy, Node* name, Node* trap_result,
|
||||||
|
Label* check_passed, JSProxy::AccessKind access_kind) {
|
||||||
|
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), check_in_runtime(this, Label::kDeferred);
|
||||||
|
|
||||||
|
Node* instance_type = LoadInstanceType(target);
|
||||||
|
TryGetOwnProperty(context, target, target, map, instance_type, name,
|
||||||
|
&if_found_value, &var_value, &var_details, &var_raw_value,
|
||||||
|
check_passed, &check_in_runtime);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// If targetDesc is not undefined and targetDesc.[[Configurable]] is
|
||||||
|
// false, then:
|
||||||
|
GotoIfNot(IsSetWord32(var_details.value(),
|
||||||
|
PropertyDetails::kAttributesDontDeleteMask),
|
||||||
|
check_passed);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
{
|
||||||
|
Node* accessor_pair = var_raw_value.value();
|
||||||
|
|
||||||
|
if (access_kind == JSProxy::kGet) {
|
||||||
|
Label continue_check(this, Label::kDeferred);
|
||||||
|
// 10.b. If IsAccessorDescriptor(targetDesc) is true and
|
||||||
|
// targetDesc.[[Get]] is undefined, then:
|
||||||
|
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.
|
||||||
|
GotoIf(IsUndefined(getter), &continue_check);
|
||||||
|
GotoIf(IsNull(getter), &continue_check);
|
||||||
|
Goto(check_passed);
|
||||||
|
|
||||||
|
// 10.b.i. If trapResult is not undefined, throw a TypeError exception.
|
||||||
|
BIND(&continue_check);
|
||||||
|
GotoIfNot(IsUndefined(trap_result), &throw_non_configurable_accessor);
|
||||||
|
} else {
|
||||||
|
// 11.b.i. If targetDesc.[[Set]] is undefined, throw a TypeError
|
||||||
|
// exception.
|
||||||
|
Node* setter =
|
||||||
|
LoadObjectField(accessor_pair, AccessorPair::kSetterOffset);
|
||||||
|
GotoIf(IsUndefined(setter), &throw_non_configurable_accessor);
|
||||||
|
GotoIf(IsNull(setter), &throw_non_configurable_accessor);
|
||||||
|
}
|
||||||
|
Goto(check_passed);
|
||||||
|
}
|
||||||
|
|
||||||
|
BIND(&check_in_runtime);
|
||||||
|
{
|
||||||
|
CallRuntime(Runtime::kCheckProxyGetSetTrapResult, context, name, target,
|
||||||
|
trap_result, SmiConstant(access_kind));
|
||||||
|
Return(trap_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ProxiesCodeStubAssembler::CheckHasTrapResult(Node* context, Node* target,
|
void ProxiesCodeStubAssembler::CheckHasTrapResult(Node* context, Node* target,
|
||||||
Node* proxy, Node* name,
|
Node* proxy, Node* name,
|
||||||
Label* check_passed,
|
Label* check_passed,
|
||||||
|
@ -16,6 +16,22 @@ class ProxiesCodeStubAssembler : public CodeStubAssembler {
|
|||||||
explicit ProxiesCodeStubAssembler(compiler::CodeAssemblerState* state)
|
explicit ProxiesCodeStubAssembler(compiler::CodeAssemblerState* state)
|
||||||
: CodeStubAssembler(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 )
|
||||||
|
// name should not be an index.
|
||||||
|
Node* ProxyGetProperty(Node* context, Node* proxy, Node* name,
|
||||||
|
Node* receiver);
|
||||||
|
|
||||||
|
// ES6 section 9.5.9 [[Set]] ( P, V, Receiver )
|
||||||
|
// name should not be an index.
|
||||||
|
Node* ProxySetProperty(Node* context, Node* proxy, Node* name, Node* value,
|
||||||
|
Node* receiver);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void GotoIfRevokedProxy(Node* object, Label* if_proxy_revoked);
|
void GotoIfRevokedProxy(Node* object, Label* if_proxy_revoked);
|
||||||
Node* AllocateProxy(Node* target, Node* handler, Node* context);
|
Node* AllocateProxy(Node* target, Node* handler, Node* context);
|
||||||
@ -24,6 +40,10 @@ class ProxiesCodeStubAssembler : public CodeStubAssembler {
|
|||||||
ParameterMode mode);
|
ParameterMode mode);
|
||||||
void CheckHasTrapResult(Node* context, Node* target, Node* proxy, Node* name,
|
void CheckHasTrapResult(Node* context, Node* target, Node* proxy, Node* name,
|
||||||
Label* check_passed, Label* if_bailout);
|
Label* check_passed, Label* if_bailout);
|
||||||
|
|
||||||
|
void CheckGetSetTrapResult(Node* context, Node* target, Node* proxy,
|
||||||
|
Node* name, Node* trap_result, Label* if_not_found,
|
||||||
|
JSProxy::AccessKind access_kind);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
@ -1,160 +0,0 @@
|
|||||||
// Copyright 2017 the V8 project authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
#include "src/builtins/builtins-proxy-helpers-gen.h"
|
|
||||||
#include "src/builtins/builtins-utils-gen.h"
|
|
||||||
|
|
||||||
namespace v8 {
|
|
||||||
namespace internal {
|
|
||||||
TF_BUILTIN(ProxyGetProperty, ProxyAssembler) {
|
|
||||||
Node* context = Parameter(Descriptor::kContext);
|
|
||||||
Node* proxy = Parameter(Descriptor::kProxy);
|
|
||||||
Node* name = Parameter(Descriptor::kName);
|
|
||||||
Node* receiver = Parameter(Descriptor::kReceiverValue);
|
|
||||||
|
|
||||||
CSA_ASSERT(this, IsJSProxy(proxy));
|
|
||||||
|
|
||||||
// 1. Assert: IsPropertyKey(P) is true.
|
|
||||||
CSA_ASSERT(this, IsName(name));
|
|
||||||
|
|
||||||
Label throw_proxy_handler_revoked(this, Label::kDeferred),
|
|
||||||
trap_undefined(this), no_target_desc(this, Label::kDeferred),
|
|
||||||
trap_not_callable(this, Label::kDeferred);
|
|
||||||
|
|
||||||
// 2. Let handler be O.[[ProxyHandler]].
|
|
||||||
Node* handler = LoadObjectField(proxy, JSProxy::kHandlerOffset);
|
|
||||||
|
|
||||||
// 3. If handler is null, throw a TypeError exception.
|
|
||||||
GotoIf(IsNull(handler), &throw_proxy_handler_revoked);
|
|
||||||
|
|
||||||
// 4. Assert: Type(handler) is Object.
|
|
||||||
CSA_ASSERT(this, IsJSReceiver(handler));
|
|
||||||
|
|
||||||
// 5. Let target be O.[[ProxyTarget]].
|
|
||||||
Node* target = LoadObjectField(proxy, JSProxy::kTargetOffset);
|
|
||||||
|
|
||||||
// 6. Let trap be ? GetMethod(handler, "get").
|
|
||||||
// 7. If trap is undefined, then (see 7.a below).
|
|
||||||
Handle<Name> trap_name = factory()->get_string();
|
|
||||||
Node* trap = GetMethod(context, handler, trap_name, &trap_undefined);
|
|
||||||
|
|
||||||
GotoIf(TaggedIsSmi(trap), &trap_not_callable);
|
|
||||||
GotoIfNot(IsCallable(trap), &trap_not_callable);
|
|
||||||
|
|
||||||
// 8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »).
|
|
||||||
Node* trap_result = CallJS(CodeFactory::Call(isolate()), context, trap,
|
|
||||||
handler, target, name, receiver);
|
|
||||||
// 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
|
|
||||||
Label return_result(this);
|
|
||||||
CheckGetTrapResult(context, target, proxy, name, trap_result, &return_result,
|
|
||||||
&no_target_desc);
|
|
||||||
|
|
||||||
BIND(&return_result);
|
|
||||||
{
|
|
||||||
// 11. Return trapResult.
|
|
||||||
Return(trap_result);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&no_target_desc);
|
|
||||||
{
|
|
||||||
CSA_ASSERT(this, IsJSReceiver(target));
|
|
||||||
CallRuntime(Runtime::kCheckProxyGetTrapResult, context, name, target,
|
|
||||||
trap_result);
|
|
||||||
Return(trap_result);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&trap_undefined);
|
|
||||||
{
|
|
||||||
// 7.a. Return ? target.[[Get]](P, Receiver).
|
|
||||||
Return(CallRuntime(Runtime::kGetPropertyWithReceiver, context, target, name,
|
|
||||||
receiver));
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&throw_proxy_handler_revoked);
|
|
||||||
{ ThrowTypeError(context, MessageTemplate::kProxyRevoked, "get"); }
|
|
||||||
|
|
||||||
BIND(&trap_not_callable);
|
|
||||||
{
|
|
||||||
ThrowTypeError(context, MessageTemplate::kPropertyNotFunction, trap,
|
|
||||||
StringConstant("get"), receiver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProxyAssembler::CheckGetTrapResult(Node* context, Node* target,
|
|
||||||
Node* proxy, Node* name,
|
|
||||||
Node* trap_result, Label* check_passed,
|
|
||||||
Label* if_bailout) {
|
|
||||||
Node* map = LoadMap(target);
|
|
||||||
VARIABLE(var_value, MachineRepresentation::kTagged);
|
|
||||||
VARIABLE(var_details, MachineRepresentation::kWord32);
|
|
||||||
VARIABLE(var_raw_value, MachineRepresentation::kTagged);
|
|
||||||
|
|
||||||
Label if_found_value(this, Label::kDeferred);
|
|
||||||
|
|
||||||
Node* instance_type = LoadInstanceType(target);
|
|
||||||
TryGetOwnProperty(context, proxy, target, map, instance_type, name,
|
|
||||||
&if_found_value, &var_value, &var_details, &var_raw_value,
|
|
||||||
check_passed, if_bailout);
|
|
||||||
|
|
||||||
BIND(&if_found_value);
|
|
||||||
{
|
|
||||||
Label throw_non_configurable_data(this, Label::kDeferred),
|
|
||||||
throw_non_configurable_accessor(this, Label::kDeferred),
|
|
||||||
check_accessor(this), check_data(this);
|
|
||||||
|
|
||||||
// 10. If targetDesc is not undefined and targetDesc.[[Configurable]] is
|
|
||||||
// false, then:
|
|
||||||
GotoIfNot(IsSetWord32(var_details.value(),
|
|
||||||
PropertyDetails::kAttributesDontDeleteMask),
|
|
||||||
check_passed);
|
|
||||||
|
|
||||||
// 10.a. If IsDataDescriptor(targetDesc) is true and
|
|
||||||
// targetDesc.[[Writable]] is false, then:
|
|
||||||
BranchIfAccessorPair(var_raw_value.value(), &check_accessor, &check_data);
|
|
||||||
|
|
||||||
BIND(&check_data);
|
|
||||||
{
|
|
||||||
Node* read_only = IsSetWord32(var_details.value(),
|
|
||||||
PropertyDetails::kAttributesReadOnlyMask);
|
|
||||||
GotoIfNot(read_only, check_passed);
|
|
||||||
|
|
||||||
// 10.a.i. If SameValue(trapResult, targetDesc.[[Value]]) is false,
|
|
||||||
// throw a TypeError exception.
|
|
||||||
GotoIfNot(SameValue(trap_result, var_value.value()),
|
|
||||||
&throw_non_configurable_data);
|
|
||||||
Goto(check_passed);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&check_accessor);
|
|
||||||
{
|
|
||||||
// 10.b. If IsAccessorDescriptor(targetDesc) is true and
|
|
||||||
// targetDesc.[[Get]] is undefined, then:
|
|
||||||
Node* accessor_pair = var_raw_value.value();
|
|
||||||
Node* getter =
|
|
||||||
LoadObjectField(accessor_pair, AccessorPair::kGetterOffset);
|
|
||||||
|
|
||||||
// Here we check for null as well because if the getter was never
|
|
||||||
// defined it's set as null.
|
|
||||||
GotoIfNot(Word32Or(IsUndefined(getter), IsNull(getter)), check_passed);
|
|
||||||
|
|
||||||
// 10.b.i. If trapResult is not undefined, throw a TypeError exception.
|
|
||||||
GotoIfNot(IsUndefined(trap_result), &throw_non_configurable_accessor);
|
|
||||||
Goto(check_passed);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&throw_non_configurable_data);
|
|
||||||
{
|
|
||||||
ThrowTypeError(context, MessageTemplate::kProxyGetNonConfigurableData,
|
|
||||||
name, var_value.value(), trap_result);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&throw_non_configurable_accessor);
|
|
||||||
{
|
|
||||||
ThrowTypeError(context, MessageTemplate::kProxyGetNonConfigurableAccessor,
|
|
||||||
name, trap_result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace internal
|
|
||||||
} // namespace v8
|
|
@ -1,38 +0,0 @@
|
|||||||
// 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_
|
|
@ -6922,6 +6922,7 @@ void CodeStubAssembler::EmitElementStore(Node* object, Node* key, Node* value,
|
|||||||
ElementsKind elements_kind,
|
ElementsKind elements_kind,
|
||||||
KeyedAccessStoreMode store_mode,
|
KeyedAccessStoreMode store_mode,
|
||||||
Label* bailout) {
|
Label* bailout) {
|
||||||
|
CSA_ASSERT(this, Word32BinaryNot(IsJSProxy(object)));
|
||||||
Node* elements = LoadElements(object);
|
Node* elements = LoadElements(object);
|
||||||
if (IsSmiOrObjectElementsKind(elements_kind) &&
|
if (IsSmiOrObjectElementsKind(elements_kind) &&
|
||||||
store_mode != STORE_NO_TRANSITION_HANDLE_COW) {
|
store_mode != STORE_NO_TRANSITION_HANDLE_COW) {
|
||||||
|
@ -839,6 +839,10 @@ KeyedAccessStoreMode KeyedStoreICNexus::GetKeyedAccessStoreMode() const {
|
|||||||
// Element store with prototype chain check.
|
// Element store with prototype chain check.
|
||||||
Handle<Tuple2> data_handler = Handle<Tuple2>::cast(maybe_code_handler);
|
Handle<Tuple2> data_handler = Handle<Tuple2>::cast(maybe_code_handler);
|
||||||
handler = handle(Code::cast(data_handler->value2()));
|
handler = handle(Code::cast(data_handler->value2()));
|
||||||
|
} else if (maybe_code_handler->IsSmi()) {
|
||||||
|
// Skip proxy handlers.
|
||||||
|
DCHECK_EQ(*maybe_code_handler, *StoreHandler::StoreProxy(GetIsolate()));
|
||||||
|
continue;
|
||||||
} else {
|
} else {
|
||||||
// Element store without prototype chain check.
|
// Element store without prototype chain check.
|
||||||
handler = Handle<Code>::cast(maybe_code_handler);
|
handler = Handle<Code>::cast(maybe_code_handler);
|
||||||
|
@ -261,8 +261,8 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase(
|
|||||||
|
|
||||||
Label constant(this), field(this), normal(this, Label::kDeferred),
|
Label constant(this), field(this), normal(this, Label::kDeferred),
|
||||||
interceptor(this, Label::kDeferred), nonexistent(this),
|
interceptor(this, Label::kDeferred), nonexistent(this),
|
||||||
accessor(this, Label::kDeferred), proxy(this, Label::kDeferred),
|
accessor(this, Label::kDeferred), global(this, Label::kDeferred),
|
||||||
global(this, Label::kDeferred), module_export(this, Label::kDeferred);
|
module_export(this, Label::kDeferred), proxy(this, Label::kDeferred);
|
||||||
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kField)), &field);
|
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kField)), &field);
|
||||||
|
|
||||||
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kConstant)),
|
GotoIf(WordEqual(handler_kind, IntPtrConstant(LoadHandler::kConstant)),
|
||||||
@ -370,9 +370,35 @@ void AccessorAssembler::HandleLoadICSmiHandlerCase(
|
|||||||
|
|
||||||
BIND(&proxy);
|
BIND(&proxy);
|
||||||
{
|
{
|
||||||
exit_point->ReturnCallStub(
|
VARIABLE(var_index, MachineType::PointerRepresentation());
|
||||||
Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty),
|
VARIABLE(var_unique, MachineRepresentation::kTagged);
|
||||||
p->context, holder, p->name, p->receiver);
|
|
||||||
|
Label if_index(this), if_unique_name(this),
|
||||||
|
to_name_failed(this, Label::kDeferred);
|
||||||
|
|
||||||
|
if (support_elements == kSupportElements) {
|
||||||
|
TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique,
|
||||||
|
&to_name_failed);
|
||||||
|
|
||||||
|
BIND(&if_unique_name);
|
||||||
|
exit_point->ReturnCallStub(
|
||||||
|
Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty),
|
||||||
|
p->context, holder, var_unique.value(), p->receiver);
|
||||||
|
|
||||||
|
BIND(&if_index);
|
||||||
|
// TODO(mslekova): introduce TryToName that doesn't try to compute
|
||||||
|
// the intptr index value
|
||||||
|
Goto(&to_name_failed);
|
||||||
|
|
||||||
|
BIND(&to_name_failed);
|
||||||
|
exit_point->ReturnCallRuntime(Runtime::kGetPropertyWithReceiver,
|
||||||
|
p->context, holder, p->name, p->receiver);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
exit_point->ReturnCallStub(
|
||||||
|
Builtins::CallableFor(isolate(), Builtins::kProxyGetProperty),
|
||||||
|
p->context, holder, p->name, p->receiver);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BIND(&global);
|
BIND(&global);
|
||||||
@ -612,7 +638,7 @@ void AccessorAssembler::JumpIfDataProperty(Node* details, Label* writable,
|
|||||||
|
|
||||||
void AccessorAssembler::HandleStoreICHandlerCase(
|
void AccessorAssembler::HandleStoreICHandlerCase(
|
||||||
const StoreICParameters* p, Node* handler, Label* miss,
|
const StoreICParameters* p, Node* handler, Label* miss,
|
||||||
ElementSupport support_elements) {
|
LanguageMode language_mode, ElementSupport support_elements) {
|
||||||
Label if_smi_handler(this), if_nonsmi_handler(this);
|
Label if_smi_handler(this), if_nonsmi_handler(this);
|
||||||
Label if_proto_handler(this), if_element_handler(this), call_handler(this),
|
Label if_proto_handler(this), if_element_handler(this), call_handler(this),
|
||||||
store_global(this);
|
store_global(this);
|
||||||
@ -626,10 +652,17 @@ void AccessorAssembler::HandleStoreICHandlerCase(
|
|||||||
Node* holder = p->receiver;
|
Node* holder = p->receiver;
|
||||||
Node* handler_word = SmiUntag(handler);
|
Node* handler_word = SmiUntag(handler);
|
||||||
|
|
||||||
Label if_fast_smi(this), slow(this);
|
Label if_fast_smi(this), if_proxy(this);
|
||||||
GotoIfNot(
|
|
||||||
WordEqual(handler_word, IntPtrConstant(StoreHandler::kStoreNormal)),
|
STATIC_ASSERT(StoreHandler::kStoreNormal + 1 == StoreHandler::kProxy);
|
||||||
&if_fast_smi);
|
STATIC_ASSERT(StoreHandler::kProxy + 1 == StoreHandler::kKindsNumber);
|
||||||
|
|
||||||
|
Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
|
||||||
|
GotoIf(IntPtrLessThan(handler_kind,
|
||||||
|
IntPtrConstant(StoreHandler::kStoreNormal)),
|
||||||
|
&if_fast_smi);
|
||||||
|
GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kProxy)),
|
||||||
|
&if_proxy);
|
||||||
|
|
||||||
Node* properties = LoadSlowProperties(holder);
|
Node* properties = LoadSlowProperties(holder);
|
||||||
|
|
||||||
@ -655,6 +688,9 @@ void AccessorAssembler::HandleStoreICHandlerCase(
|
|||||||
BIND(&if_fast_smi);
|
BIND(&if_fast_smi);
|
||||||
// Handle non-transitioning field stores.
|
// Handle non-transitioning field stores.
|
||||||
HandleStoreICSmiHandlerCase(handler_word, holder, p->value, nullptr, miss);
|
HandleStoreICSmiHandlerCase(handler_word, holder, p->value, nullptr, miss);
|
||||||
|
|
||||||
|
BIND(&if_proxy);
|
||||||
|
HandleStoreToProxy(p, holder, miss, support_elements, language_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
BIND(&if_nonsmi_handler);
|
BIND(&if_nonsmi_handler);
|
||||||
@ -673,7 +709,10 @@ void AccessorAssembler::HandleStoreICHandlerCase(
|
|||||||
}
|
}
|
||||||
|
|
||||||
BIND(&if_proto_handler);
|
BIND(&if_proto_handler);
|
||||||
{ HandleStoreICProtoHandler(p, handler, miss, support_elements); }
|
{
|
||||||
|
HandleStoreICProtoHandler(p, handler, miss, support_elements,
|
||||||
|
language_mode);
|
||||||
|
}
|
||||||
|
|
||||||
// |handler| is a heap object. Must be code, call it.
|
// |handler| is a heap object. Must be code, call it.
|
||||||
BIND(&call_handler);
|
BIND(&call_handler);
|
||||||
@ -762,7 +801,7 @@ void AccessorAssembler::HandleStoreICElementHandlerCase(
|
|||||||
|
|
||||||
void AccessorAssembler::HandleStoreICProtoHandler(
|
void AccessorAssembler::HandleStoreICProtoHandler(
|
||||||
const StoreICParameters* p, Node* handler, Label* miss,
|
const StoreICParameters* p, Node* handler, Label* miss,
|
||||||
ElementSupport support_elements) {
|
ElementSupport support_elements, LanguageMode language_mode) {
|
||||||
// IC dispatchers rely on these assumptions to be held.
|
// IC dispatchers rely on these assumptions to be held.
|
||||||
STATIC_ASSERT(FixedArray::kLengthOffset ==
|
STATIC_ASSERT(FixedArray::kLengthOffset ==
|
||||||
StoreHandler::kTransitionCellOffset);
|
StoreHandler::kTransitionCellOffset);
|
||||||
@ -792,12 +831,12 @@ void AccessorAssembler::HandleStoreICProtoHandler(
|
|||||||
|
|
||||||
VARIABLE(var_transition, MachineRepresentation::kTagged);
|
VARIABLE(var_transition, MachineRepresentation::kTagged);
|
||||||
Label if_transition(this), if_transition_to_constant(this),
|
Label if_transition(this), if_transition_to_constant(this),
|
||||||
if_store_normal(this);
|
if_store_normal(this), if_proxy(this), do_store(this);
|
||||||
BIND(&tuple_handler);
|
BIND(&tuple_handler);
|
||||||
{
|
{
|
||||||
Node* transition = LoadWeakCellValue(maybe_transition_cell, miss);
|
Node* transition = LoadWeakCellValue(maybe_transition_cell, miss);
|
||||||
var_transition.Bind(transition);
|
var_transition.Bind(transition);
|
||||||
Goto(&if_transition);
|
Goto(&do_store);
|
||||||
}
|
}
|
||||||
|
|
||||||
BIND(&array_handler);
|
BIND(&array_handler);
|
||||||
@ -815,7 +854,19 @@ void AccessorAssembler::HandleStoreICProtoHandler(
|
|||||||
LoadFixedArrayElement(handler, StoreHandler::kTransitionCellIndex);
|
LoadFixedArrayElement(handler, StoreHandler::kTransitionCellIndex);
|
||||||
Node* transition = LoadWeakCellValue(maybe_transition_cell, miss);
|
Node* transition = LoadWeakCellValue(maybe_transition_cell, miss);
|
||||||
var_transition.Bind(transition);
|
var_transition.Bind(transition);
|
||||||
Goto(&if_transition);
|
Goto(&do_store);
|
||||||
|
}
|
||||||
|
|
||||||
|
BIND(&do_store);
|
||||||
|
{
|
||||||
|
Branch(SmiEqual(smi_or_code, SmiConstant(StoreHandler::kProxy)), &if_proxy,
|
||||||
|
&if_transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
BIND(&if_proxy);
|
||||||
|
{
|
||||||
|
Node* proxy = var_transition.value();
|
||||||
|
HandleStoreToProxy(p, proxy, miss, support_elements, language_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
BIND(&if_transition);
|
BIND(&if_transition);
|
||||||
@ -866,9 +917,8 @@ void AccessorAssembler::HandleStoreICProtoHandler(
|
|||||||
DescriptorArray::kEntryValueIndex));
|
DescriptorArray::kEntryValueIndex));
|
||||||
Node* descriptors = LoadMapDescriptors(transition);
|
Node* descriptors = LoadMapDescriptors(transition);
|
||||||
CSA_ASSERT(
|
CSA_ASSERT(
|
||||||
this,
|
this, UintPtrLessThan(descriptor,
|
||||||
UintPtrLessThan(descriptor,
|
LoadAndUntagFixedArrayBaseLength(descriptors)));
|
||||||
LoadAndUntagFixedArrayBaseLength(descriptors)));
|
|
||||||
|
|
||||||
Node* constant = LoadFixedArrayElement(descriptors, value_index);
|
Node* constant = LoadFixedArrayElement(descriptors, value_index);
|
||||||
GotoIf(WordNotEqual(p->value, constant), miss);
|
GotoIf(WordNotEqual(p->value, constant), miss);
|
||||||
@ -915,6 +965,42 @@ void AccessorAssembler::HandleStoreICProtoHandler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AccessorAssembler::HandleStoreToProxy(const StoreICParameters* p,
|
||||||
|
Node* proxy, Label* miss,
|
||||||
|
ElementSupport support_elements,
|
||||||
|
LanguageMode language_mode) {
|
||||||
|
VARIABLE(var_index, MachineType::PointerRepresentation());
|
||||||
|
VARIABLE(var_unique, MachineRepresentation::kTagged);
|
||||||
|
|
||||||
|
Label if_index(this), if_unique_name(this),
|
||||||
|
to_name_failed(this, Label::kDeferred);
|
||||||
|
|
||||||
|
if (support_elements == kSupportElements) {
|
||||||
|
TryToName(p->name, &if_index, &var_index, &if_unique_name, &var_unique,
|
||||||
|
&to_name_failed);
|
||||||
|
|
||||||
|
BIND(&if_unique_name);
|
||||||
|
CallBuiltin(Builtins::kProxySetProperty, p->context, proxy,
|
||||||
|
var_unique.value(), p->value, p->receiver,
|
||||||
|
SmiConstant(language_mode));
|
||||||
|
Return(p->value);
|
||||||
|
|
||||||
|
// The index case is handled earlier by the runtime.
|
||||||
|
BIND(&if_index);
|
||||||
|
// TODO(mslekova): introduce TryToName that doesn't try to compute
|
||||||
|
// the intptr index value
|
||||||
|
Goto(&to_name_failed);
|
||||||
|
|
||||||
|
BIND(&to_name_failed);
|
||||||
|
TailCallRuntime(Runtime::kSetPropertyWithReceiver, p->context, proxy,
|
||||||
|
p->name, p->value, p->receiver, SmiConstant(language_mode));
|
||||||
|
} else {
|
||||||
|
Node* name = ToName(p->context, p->name);
|
||||||
|
TailCallBuiltin(Builtins::kProxySetProperty, p->context, proxy, name,
|
||||||
|
p->value, p->receiver, SmiConstant(language_mode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AccessorAssembler::HandleStoreICSmiHandlerCase(Node* handler_word,
|
void AccessorAssembler::HandleStoreICSmiHandlerCase(Node* handler_word,
|
||||||
Node* holder, Node* value,
|
Node* holder, Node* value,
|
||||||
Node* transition,
|
Node* transition,
|
||||||
@ -2237,7 +2323,7 @@ void AccessorAssembler::StoreIC(const StoreICParameters* p,
|
|||||||
BIND(&if_handler);
|
BIND(&if_handler);
|
||||||
{
|
{
|
||||||
Comment("StoreIC_if_handler");
|
Comment("StoreIC_if_handler");
|
||||||
HandleStoreICHandlerCase(p, var_handler.value(), &miss);
|
HandleStoreICHandlerCase(p, var_handler.value(), &miss, language_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
BIND(&try_polymorphic);
|
BIND(&try_polymorphic);
|
||||||
@ -2298,7 +2384,8 @@ void AccessorAssembler::KeyedStoreIC(const StoreICParameters* p,
|
|||||||
BIND(&if_handler);
|
BIND(&if_handler);
|
||||||
{
|
{
|
||||||
Comment("KeyedStoreIC_if_handler");
|
Comment("KeyedStoreIC_if_handler");
|
||||||
HandleStoreICHandlerCase(p, var_handler.value(), &miss, kSupportElements);
|
HandleStoreICHandlerCase(p, var_handler.value(), &miss, language_mode,
|
||||||
|
kSupportElements);
|
||||||
}
|
}
|
||||||
|
|
||||||
BIND(&try_polymorphic);
|
BIND(&try_polymorphic);
|
||||||
|
@ -93,6 +93,7 @@ class AccessorAssembler : public CodeStubAssembler {
|
|||||||
enum ElementSupport { kOnlyProperties, kSupportElements };
|
enum ElementSupport { kOnlyProperties, kSupportElements };
|
||||||
void HandleStoreICHandlerCase(
|
void HandleStoreICHandlerCase(
|
||||||
const StoreICParameters* p, Node* handler, Label* miss,
|
const StoreICParameters* p, Node* handler, Label* miss,
|
||||||
|
LanguageMode language_mode,
|
||||||
ElementSupport support_elements = kOnlyProperties);
|
ElementSupport support_elements = kOnlyProperties);
|
||||||
void JumpIfDataProperty(Node* details, Label* writable, Label* readonly);
|
void JumpIfDataProperty(Node* details, Label* writable, Label* readonly);
|
||||||
|
|
||||||
@ -164,7 +165,8 @@ class AccessorAssembler : public CodeStubAssembler {
|
|||||||
Node* handler, Label* miss);
|
Node* handler, Label* miss);
|
||||||
|
|
||||||
void HandleStoreICProtoHandler(const StoreICParameters* p, Node* handler,
|
void HandleStoreICProtoHandler(const StoreICParameters* p, Node* handler,
|
||||||
Label* miss, ElementSupport support_elements);
|
Label* miss, ElementSupport support_elements,
|
||||||
|
LanguageMode language_mode);
|
||||||
// If |transition| is nullptr then the normal field store is generated or
|
// If |transition| is nullptr then the normal field store is generated or
|
||||||
// transitioning store otherwise.
|
// transitioning store otherwise.
|
||||||
void HandleStoreICSmiHandlerCase(Node* handler_word, Node* holder,
|
void HandleStoreICSmiHandlerCase(Node* handler_word, Node* holder,
|
||||||
@ -175,6 +177,10 @@ class AccessorAssembler : public CodeStubAssembler {
|
|||||||
Representation representation, Node* value,
|
Representation representation, Node* value,
|
||||||
Node* transition, Label* miss);
|
Node* transition, Label* miss);
|
||||||
|
|
||||||
|
void HandleStoreToProxy(const StoreICParameters* p, Node* proxy, Label* miss,
|
||||||
|
ElementSupport support_elements,
|
||||||
|
LanguageMode language_mode);
|
||||||
|
|
||||||
// KeyedLoadIC_Generic implementation.
|
// KeyedLoadIC_Generic implementation.
|
||||||
|
|
||||||
void GenericElementLoad(Node* receiver, Node* receiver_map,
|
void GenericElementLoad(Node* receiver, Node* receiver_map,
|
||||||
|
@ -113,6 +113,11 @@ Handle<Smi> StoreHandler::StoreNormal(Isolate* isolate) {
|
|||||||
return handle(Smi::FromInt(config), isolate);
|
return handle(Smi::FromInt(config), isolate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Handle<Smi> StoreHandler::StoreProxy(Isolate* isolate) {
|
||||||
|
int config = KindBits::encode(kProxy);
|
||||||
|
return handle(Smi::FromInt(config), isolate);
|
||||||
|
}
|
||||||
|
|
||||||
Handle<Smi> StoreHandler::StoreField(Isolate* isolate, Kind kind,
|
Handle<Smi> StoreHandler::StoreField(Isolate* isolate, Kind kind,
|
||||||
int descriptor, FieldIndex field_index,
|
int descriptor, FieldIndex field_index,
|
||||||
Representation representation,
|
Representation representation,
|
||||||
|
@ -160,8 +160,10 @@ class StoreHandler {
|
|||||||
kStoreElement,
|
kStoreElement,
|
||||||
kStoreField,
|
kStoreField,
|
||||||
kStoreConstField,
|
kStoreConstField,
|
||||||
kStoreNormal,
|
|
||||||
kTransitionToField,
|
kTransitionToField,
|
||||||
|
kStoreNormal,
|
||||||
|
kProxy,
|
||||||
|
kKindsNumber, // Keep last
|
||||||
// TODO(ishell): remove once constant field tracking is done.
|
// TODO(ishell): remove once constant field tracking is done.
|
||||||
kTransitionToConstant = kStoreConstField
|
kTransitionToConstant = kStoreConstField
|
||||||
};
|
};
|
||||||
@ -227,6 +229,9 @@ class StoreHandler {
|
|||||||
// Creates a Smi-handler for storing a property to a slow object.
|
// Creates a Smi-handler for storing a property to a slow object.
|
||||||
static inline Handle<Smi> StoreNormal(Isolate* isolate);
|
static inline Handle<Smi> StoreNormal(Isolate* isolate);
|
||||||
|
|
||||||
|
// Creates a Smi-handler for storing a property on a proxy.
|
||||||
|
static inline Handle<Smi> StoreProxy(Isolate* isolate);
|
||||||
|
|
||||||
// Creates a Smi-handler for transitioning store to a field.
|
// Creates a Smi-handler for transitioning store to a field.
|
||||||
static inline Handle<Smi> TransitionToField(Isolate* isolate, int descriptor,
|
static inline Handle<Smi> TransitionToField(Isolate* isolate, int descriptor,
|
||||||
FieldIndex field_index,
|
FieldIndex field_index,
|
||||||
|
103
src/ic/ic.cc
103
src/ic/ic.cc
@ -1223,8 +1223,7 @@ static Handle<Object> TryConvertKey(Handle<Object> key, Isolate* isolate) {
|
|||||||
|
|
||||||
void KeyedLoadIC::UpdateLoadElement(Handle<HeapObject> receiver) {
|
void KeyedLoadIC::UpdateLoadElement(Handle<HeapObject> receiver) {
|
||||||
Handle<Map> receiver_map(receiver->map(), isolate());
|
Handle<Map> receiver_map(receiver->map(), isolate());
|
||||||
DCHECK(receiver_map->instance_type() != JS_VALUE_TYPE &&
|
DCHECK(receiver_map->instance_type() != JS_VALUE_TYPE); // Checked by caller.
|
||||||
receiver_map->instance_type() != JS_PROXY_TYPE); // Checked by caller.
|
|
||||||
MapHandles target_receiver_maps;
|
MapHandles target_receiver_maps;
|
||||||
TargetMaps(&target_receiver_maps);
|
TargetMaps(&target_receiver_maps);
|
||||||
|
|
||||||
@ -1253,6 +1252,7 @@ void KeyedLoadIC::UpdateLoadElement(Handle<HeapObject> receiver) {
|
|||||||
// miss again and it will become polymorphic and support both the
|
// miss again and it will become polymorphic and support both the
|
||||||
// untransitioned and transitioned maps.
|
// untransitioned and transitioned maps.
|
||||||
if (state() == MONOMORPHIC && !receiver->IsString() &&
|
if (state() == MONOMORPHIC && !receiver->IsString() &&
|
||||||
|
!receiver->IsJSProxy() &&
|
||||||
IsMoreGeneralElementsKindTransition(
|
IsMoreGeneralElementsKindTransition(
|
||||||
target_receiver_maps.at(0)->elements_kind(),
|
target_receiver_maps.at(0)->elements_kind(),
|
||||||
Handle<JSObject>::cast(receiver)->GetElementsKind())) {
|
Handle<JSObject>::cast(receiver)->GetElementsKind())) {
|
||||||
@ -1306,6 +1306,9 @@ Handle<Object> KeyedLoadIC::LoadElementHandler(Handle<Map> receiver_map) {
|
|||||||
TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_SlowStub);
|
TRACE_HANDLER_STATS(isolate(), KeyedLoadIC_SlowStub);
|
||||||
return BUILTIN_CODE(isolate(), KeyedLoadIC_Slow);
|
return BUILTIN_CODE(isolate(), KeyedLoadIC_Slow);
|
||||||
}
|
}
|
||||||
|
if (instance_type == JS_PROXY_TYPE) {
|
||||||
|
return LoadHandler::LoadProxy(isolate());
|
||||||
|
}
|
||||||
|
|
||||||
ElementsKind elements_kind = receiver_map->elements_kind();
|
ElementsKind elements_kind = receiver_map->elements_kind();
|
||||||
if (IsSloppyArgumentsElementsKind(elements_kind)) {
|
if (IsSloppyArgumentsElementsKind(elements_kind)) {
|
||||||
@ -1378,7 +1381,7 @@ MaybeHandle<Object> KeyedLoadIC::Load(Handle<Object> object,
|
|||||||
Object);
|
Object);
|
||||||
} else if (FLAG_use_ic && !object->IsAccessCheckNeeded() &&
|
} else if (FLAG_use_ic && !object->IsAccessCheckNeeded() &&
|
||||||
!object->IsJSValue()) {
|
!object->IsJSValue()) {
|
||||||
if ((object->IsJSObject() && key->IsSmi()) ||
|
if ((object->IsJSReceiver() && key->IsSmi()) ||
|
||||||
(object->IsString() && key->IsNumber())) {
|
(object->IsString() && key->IsNumber())) {
|
||||||
UpdateLoadElement(Handle<HeapObject>::cast(object));
|
UpdateLoadElement(Handle<HeapObject>::cast(object));
|
||||||
if (is_vector_set()) {
|
if (is_vector_set()) {
|
||||||
@ -1406,6 +1409,7 @@ bool StoreIC::LookupForWrite(LookupIterator* it, Handle<Object> value,
|
|||||||
JSReceiver::StoreFromKeyed store_mode) {
|
JSReceiver::StoreFromKeyed store_mode) {
|
||||||
// Disable ICs for non-JSObjects for now.
|
// Disable ICs for non-JSObjects for now.
|
||||||
Handle<Object> object = it->GetReceiver();
|
Handle<Object> object = it->GetReceiver();
|
||||||
|
if (object->IsJSProxy()) return true;
|
||||||
if (!object->IsJSObject()) return false;
|
if (!object->IsJSObject()) return false;
|
||||||
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
|
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
|
||||||
DCHECK(!receiver->map()->is_deprecated());
|
DCHECK(!receiver->map()->is_deprecated());
|
||||||
@ -1416,7 +1420,7 @@ bool StoreIC::LookupForWrite(LookupIterator* it, Handle<Object> value,
|
|||||||
case LookupIterator::TRANSITION:
|
case LookupIterator::TRANSITION:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
case LookupIterator::JSPROXY:
|
case LookupIterator::JSPROXY:
|
||||||
return false;
|
return true;
|
||||||
case LookupIterator::INTERCEPTOR: {
|
case LookupIterator::INTERCEPTOR: {
|
||||||
Handle<JSObject> holder = it->GetHolder<JSObject>();
|
Handle<JSObject> holder = it->GetHolder<JSObject>();
|
||||||
InterceptorInfo* info = holder->GetNamedInterceptor();
|
InterceptorInfo* info = holder->GetNamedInterceptor();
|
||||||
@ -1518,7 +1522,7 @@ MaybeHandle<Object> StoreIC::Store(Handle<Object> object, Handle<Name> name,
|
|||||||
JSReceiver::StoreFromKeyed store_mode) {
|
JSReceiver::StoreFromKeyed store_mode) {
|
||||||
// TODO(verwaest): Let SetProperty do the migration, since storing a property
|
// TODO(verwaest): Let SetProperty do the migration, since storing a property
|
||||||
// might deprecate the current map again, if value does not fit.
|
// might deprecate the current map again, if value does not fit.
|
||||||
if (MigrateDeprecated(object) || object->IsJSProxy()) {
|
if (MigrateDeprecated(object)) {
|
||||||
Handle<Object> result;
|
Handle<Object> result;
|
||||||
ASSIGN_RETURN_ON_EXCEPTION(
|
ASSIGN_RETURN_ON_EXCEPTION(
|
||||||
isolate(), result,
|
isolate(), result,
|
||||||
@ -1662,6 +1666,45 @@ Handle<Object> StoreIC::StoreTransition(Handle<Map> receiver_map,
|
|||||||
return handler_array;
|
return handler_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Handle<Object> StoreIC::StoreProxy(Handle<Map> receiver_map,
|
||||||
|
Handle<JSProxy> proxy,
|
||||||
|
Handle<JSReceiver> receiver,
|
||||||
|
Handle<Name> name) {
|
||||||
|
Handle<Object> smi_handler = StoreHandler::StoreProxy(isolate());
|
||||||
|
|
||||||
|
if (receiver.is_identical_to(proxy)) {
|
||||||
|
return smi_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
int checks_count =
|
||||||
|
GetPrototypeCheckCount(isolate(), receiver_map, proxy, name);
|
||||||
|
|
||||||
|
DCHECK_LE(0, checks_count);
|
||||||
|
DCHECK(!receiver_map->IsJSGlobalObjectMap());
|
||||||
|
|
||||||
|
Handle<Object> validity_cell =
|
||||||
|
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate());
|
||||||
|
if (validity_cell.is_null()) {
|
||||||
|
DCHECK_EQ(0, checks_count);
|
||||||
|
validity_cell = handle(Smi::kZero, isolate());
|
||||||
|
}
|
||||||
|
|
||||||
|
Factory* factory = isolate()->factory();
|
||||||
|
Handle<WeakCell> holder_cell = factory->NewWeakCell(proxy);
|
||||||
|
|
||||||
|
if (checks_count == 0) {
|
||||||
|
return factory->NewTuple3(holder_cell, smi_handler, validity_cell, TENURED);
|
||||||
|
}
|
||||||
|
Handle<FixedArray> handler_array(factory->NewFixedArray(
|
||||||
|
StoreHandler::kFirstPrototypeIndex + checks_count, TENURED));
|
||||||
|
handler_array->set(StoreHandler::kSmiHandlerIndex, *smi_handler);
|
||||||
|
handler_array->set(StoreHandler::kValidityCellIndex, *validity_cell);
|
||||||
|
handler_array->set(StoreHandler::kTransitionCellIndex, *holder_cell);
|
||||||
|
InitPrototypeChecks(isolate(), receiver_map, proxy, name, handler_array,
|
||||||
|
StoreHandler::kFirstPrototypeIndex);
|
||||||
|
return handler_array;
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
Handle<Object> StoreGlobal(Isolate* isolate, Handle<PropertyCell> cell) {
|
Handle<Object> StoreGlobal(Isolate* isolate, Handle<PropertyCell> cell) {
|
||||||
@ -1671,15 +1714,10 @@ Handle<Object> StoreGlobal(Isolate* isolate, Handle<PropertyCell> cell) {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Handle<Object> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
Handle<Object> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
||||||
DCHECK_NE(LookupIterator::JSPROXY, lookup->state());
|
|
||||||
|
|
||||||
// This is currently guaranteed by checks in StoreIC::Store.
|
|
||||||
Handle<JSObject> receiver = Handle<JSObject>::cast(lookup->GetReceiver());
|
|
||||||
Handle<JSObject> holder = lookup->GetHolder<JSObject>();
|
|
||||||
DCHECK(!receiver->IsAccessCheckNeeded() || lookup->name()->IsPrivate());
|
|
||||||
|
|
||||||
switch (lookup->state()) {
|
switch (lookup->state()) {
|
||||||
case LookupIterator::TRANSITION: {
|
case LookupIterator::TRANSITION: {
|
||||||
|
Handle<JSObject> holder = lookup->GetHolder<JSObject>();
|
||||||
|
|
||||||
auto store_target = lookup->GetStoreTarget();
|
auto store_target = lookup->GetStoreTarget();
|
||||||
if (store_target->IsJSGlobalObject()) {
|
if (store_target->IsJSGlobalObject()) {
|
||||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreGlobalTransitionDH);
|
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreGlobalTransitionDH);
|
||||||
@ -1703,6 +1741,9 @@ Handle<Object> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case LookupIterator::INTERCEPTOR: {
|
case LookupIterator::INTERCEPTOR: {
|
||||||
|
Handle<JSObject> holder = lookup->GetHolder<JSObject>();
|
||||||
|
USE(holder);
|
||||||
|
|
||||||
DCHECK(!holder->GetNamedInterceptor()->setter()->IsUndefined(isolate()));
|
DCHECK(!holder->GetNamedInterceptor()->setter()->IsUndefined(isolate()));
|
||||||
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreInterceptorStub);
|
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreInterceptorStub);
|
||||||
StoreInterceptorStub stub(isolate());
|
StoreInterceptorStub stub(isolate());
|
||||||
@ -1710,6 +1751,11 @@ Handle<Object> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case LookupIterator::ACCESSOR: {
|
case LookupIterator::ACCESSOR: {
|
||||||
|
// This is currently guaranteed by checks in StoreIC::Store.
|
||||||
|
Handle<JSObject> receiver = Handle<JSObject>::cast(lookup->GetReceiver());
|
||||||
|
Handle<JSObject> holder = lookup->GetHolder<JSObject>();
|
||||||
|
DCHECK(!receiver->IsAccessCheckNeeded() || lookup->name()->IsPrivate());
|
||||||
|
|
||||||
if (!holder->HasFastProperties()) {
|
if (!holder->HasFastProperties()) {
|
||||||
TRACE_GENERIC_IC("accessor on slow map");
|
TRACE_GENERIC_IC("accessor on slow map");
|
||||||
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
|
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
|
||||||
@ -1760,6 +1806,12 @@ Handle<Object> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case LookupIterator::DATA: {
|
case LookupIterator::DATA: {
|
||||||
|
// This is currently guaranteed by checks in StoreIC::Store.
|
||||||
|
Handle<JSObject> receiver = Handle<JSObject>::cast(lookup->GetReceiver());
|
||||||
|
USE(receiver);
|
||||||
|
Handle<JSObject> holder = lookup->GetHolder<JSObject>();
|
||||||
|
DCHECK(!receiver->IsAccessCheckNeeded() || lookup->name()->IsPrivate());
|
||||||
|
|
||||||
DCHECK_EQ(kData, lookup->property_details().kind());
|
DCHECK_EQ(kData, lookup->property_details().kind());
|
||||||
if (lookup->is_dictionary_holder()) {
|
if (lookup->is_dictionary_holder()) {
|
||||||
if (holder->IsJSGlobalObject()) {
|
if (holder->IsJSGlobalObject()) {
|
||||||
@ -1792,10 +1844,16 @@ Handle<Object> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) {
|
|||||||
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
|
TRACE_HANDLER_STATS(isolate(), StoreIC_SlowStub);
|
||||||
return slow_stub();
|
return slow_stub();
|
||||||
}
|
}
|
||||||
|
case LookupIterator::JSPROXY: {
|
||||||
|
Handle<JSReceiver> receiver =
|
||||||
|
Handle<JSReceiver>::cast(lookup->GetReceiver());
|
||||||
|
Handle<JSProxy> holder = lookup->GetHolder<JSProxy>();
|
||||||
|
return StoreIC::StoreProxy(receiver_map(), holder, receiver,
|
||||||
|
lookup->name());
|
||||||
|
}
|
||||||
|
|
||||||
case LookupIterator::INTEGER_INDEXED_EXOTIC:
|
case LookupIterator::INTEGER_INDEXED_EXOTIC:
|
||||||
case LookupIterator::ACCESS_CHECK:
|
case LookupIterator::ACCESS_CHECK:
|
||||||
case LookupIterator::JSPROXY:
|
|
||||||
case LookupIterator::NOT_FOUND:
|
case LookupIterator::NOT_FOUND:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
@ -1876,7 +1934,8 @@ void KeyedStoreIC::UpdateStoreElement(Handle<Map> receiver_map,
|
|||||||
// transition to a different GetNonTransitioningStoreMode IC that handles a
|
// transition to a different GetNonTransitioningStoreMode IC that handles a
|
||||||
// superset of the original IC. Handle those here if the receiver map hasn't
|
// superset of the original IC. Handle those here if the receiver map hasn't
|
||||||
// changed or it has transitioned to a more general kind.
|
// changed or it has transitioned to a more general kind.
|
||||||
KeyedAccessStoreMode old_store_mode = GetKeyedAccessStoreMode();
|
KeyedAccessStoreMode old_store_mode;
|
||||||
|
old_store_mode = GetKeyedAccessStoreMode();
|
||||||
Handle<Map> previous_receiver_map = target_receiver_maps.at(0);
|
Handle<Map> previous_receiver_map = target_receiver_maps.at(0);
|
||||||
if (state() == MONOMORPHIC) {
|
if (state() == MONOMORPHIC) {
|
||||||
Handle<Map> transitioned_receiver_map = receiver_map;
|
Handle<Map> transitioned_receiver_map = receiver_map;
|
||||||
@ -2011,6 +2070,10 @@ Handle<Object> KeyedStoreIC::StoreElementHandler(
|
|||||||
store_mode == STORE_NO_TRANSITION_HANDLE_COW);
|
store_mode == STORE_NO_TRANSITION_HANDLE_COW);
|
||||||
DCHECK(!receiver_map->DictionaryElementsInPrototypeChainOnly());
|
DCHECK(!receiver_map->DictionaryElementsInPrototypeChainOnly());
|
||||||
|
|
||||||
|
if (receiver_map->IsJSProxyMap()) {
|
||||||
|
return StoreHandler::StoreProxy(isolate());
|
||||||
|
}
|
||||||
|
|
||||||
ElementsKind elements_kind = receiver_map->elements_kind();
|
ElementsKind elements_kind = receiver_map->elements_kind();
|
||||||
bool is_jsarray = receiver_map->instance_type() == JS_ARRAY_TYPE;
|
bool is_jsarray = receiver_map->instance_type() == JS_ARRAY_TYPE;
|
||||||
Handle<Code> stub;
|
Handle<Code> stub;
|
||||||
@ -2222,15 +2285,17 @@ MaybeHandle<Object> KeyedStoreIC::Store(Handle<Object> object,
|
|||||||
bool is_arguments = false;
|
bool is_arguments = false;
|
||||||
bool key_is_valid_index = false;
|
bool key_is_valid_index = false;
|
||||||
KeyedAccessStoreMode store_mode = STANDARD_STORE;
|
KeyedAccessStoreMode store_mode = STANDARD_STORE;
|
||||||
if (use_ic && object->IsJSObject()) {
|
if (use_ic && object->IsJSReceiver()) {
|
||||||
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
|
Handle<JSReceiver> receiver = Handle<JSReceiver>::cast(object);
|
||||||
old_receiver_map = handle(receiver->map(), isolate());
|
old_receiver_map = handle(receiver->map(), isolate());
|
||||||
is_arguments = receiver->IsJSArgumentsObject();
|
is_arguments = receiver->IsJSArgumentsObject();
|
||||||
if (!is_arguments) {
|
bool is_proxy = receiver->IsJSProxy();
|
||||||
key_is_valid_index = key->IsSmi() && Smi::ToInt(*key) >= 0;
|
key_is_valid_index = key->IsSmi() && Smi::ToInt(*key) >= 0;
|
||||||
|
if (!is_arguments && !is_proxy) {
|
||||||
if (key_is_valid_index) {
|
if (key_is_valid_index) {
|
||||||
uint32_t index = static_cast<uint32_t>(Smi::ToInt(*key));
|
uint32_t index = static_cast<uint32_t>(Smi::ToInt(*key));
|
||||||
store_mode = GetStoreMode(receiver, index, value);
|
Handle<JSObject> receiver_object = Handle<JSObject>::cast(object);
|
||||||
|
store_mode = GetStoreMode(receiver_object, index, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,6 +359,9 @@ class StoreIC : public IC {
|
|||||||
Handle<JSObject> holder,
|
Handle<JSObject> holder,
|
||||||
Handle<Map> transition, Handle<Name> name);
|
Handle<Map> transition, Handle<Name> name);
|
||||||
|
|
||||||
|
Handle<Object> StoreProxy(Handle<Map> receiver_map, Handle<JSProxy> proxy,
|
||||||
|
Handle<JSReceiver> receiver, Handle<Name> name);
|
||||||
|
|
||||||
friend class IC;
|
friend class IC;
|
||||||
|
|
||||||
bool created_new_transition_ = false;
|
bool created_new_transition_ = false;
|
||||||
|
@ -841,7 +841,8 @@ void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
|
|||||||
BIND(&found_handler);
|
BIND(&found_handler);
|
||||||
{
|
{
|
||||||
Comment("KeyedStoreGeneric found transition handler");
|
Comment("KeyedStoreGeneric found transition handler");
|
||||||
HandleStoreICHandlerCase(p, var_handler.value(), notfound);
|
HandleStoreICHandlerCase(p, var_handler.value(), notfound,
|
||||||
|
language_mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -949,7 +950,8 @@ void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
|
|||||||
BIND(&found_handler);
|
BIND(&found_handler);
|
||||||
{
|
{
|
||||||
Comment("KeyedStoreGeneric found handler");
|
Comment("KeyedStoreGeneric found handler");
|
||||||
HandleStoreICHandlerCase(p, var_handler.value(), &stub_cache_miss);
|
HandleStoreICHandlerCase(p, var_handler.value(), &stub_cache_miss,
|
||||||
|
language_mode);
|
||||||
}
|
}
|
||||||
BIND(&stub_cache_miss);
|
BIND(&stub_cache_miss);
|
||||||
{
|
{
|
||||||
@ -1020,13 +1022,14 @@ void KeyedStoreGenericAssembler::StoreIC_Uninitialized(
|
|||||||
Node* vector = Parameter(Descriptor::kVector);
|
Node* vector = Parameter(Descriptor::kVector);
|
||||||
Node* context = Parameter(Descriptor::kContext);
|
Node* context = Parameter(Descriptor::kContext);
|
||||||
|
|
||||||
Label miss(this);
|
Label miss(this), if_proxy(this, Label::kDeferred);
|
||||||
|
|
||||||
GotoIf(TaggedIsSmi(receiver), &miss);
|
GotoIf(TaggedIsSmi(receiver), &miss);
|
||||||
Node* receiver_map = LoadMap(receiver);
|
Node* receiver_map = LoadMap(receiver);
|
||||||
Node* instance_type = LoadMapInstanceType(receiver_map);
|
Node* instance_type = LoadMapInstanceType(receiver_map);
|
||||||
|
GotoIf(Word32Equal(instance_type, Int32Constant(JS_PROXY_TYPE)), &if_proxy);
|
||||||
// Receivers requiring non-standard element accesses (interceptors, access
|
// Receivers requiring non-standard element 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,
|
GotoIf(Int32LessThanOrEqual(instance_type,
|
||||||
Int32Constant(LAST_SPECIAL_RECEIVER_TYPE)),
|
Int32Constant(LAST_SPECIAL_RECEIVER_TYPE)),
|
||||||
&miss);
|
&miss);
|
||||||
@ -1040,6 +1043,12 @@ void KeyedStoreGenericAssembler::StoreIC_Uninitialized(
|
|||||||
EmitGenericPropertyStore(receiver, receiver_map, &p, &miss, language_mode,
|
EmitGenericPropertyStore(receiver, receiver_map, &p, &miss, language_mode,
|
||||||
kDontUseStubCache);
|
kDontUseStubCache);
|
||||||
|
|
||||||
|
BIND(&if_proxy);
|
||||||
|
{
|
||||||
|
CallBuiltin(Builtins::kProxySetProperty, context, receiver, name, value,
|
||||||
|
receiver, SmiConstant(language_mode));
|
||||||
|
Return(value);
|
||||||
|
}
|
||||||
BIND(&miss);
|
BIND(&miss);
|
||||||
{
|
{
|
||||||
// Undo the optimistic state transition.
|
// Undo the optimistic state transition.
|
||||||
|
@ -13,12 +13,42 @@
|
|||||||
namespace v8 {
|
namespace v8 {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
// static
|
||||||
|
LookupIterator LookupIterator::PropertyOrElement(
|
||||||
|
Isolate* isolate, Handle<Object> receiver, Handle<Object> key,
|
||||||
|
bool* success, Handle<JSReceiver> holder, Configuration configuration) {
|
||||||
|
uint32_t index = 0;
|
||||||
|
if (key->ToArrayIndex(&index)) {
|
||||||
|
*success = true;
|
||||||
|
return LookupIterator(isolate, receiver, index, holder, configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Name> name;
|
||||||
|
*success = Object::ToName(isolate, key).ToHandle(&name);
|
||||||
|
if (!*success) {
|
||||||
|
DCHECK(isolate->has_pending_exception());
|
||||||
|
// Return an unusable dummy.
|
||||||
|
return LookupIterator(receiver, isolate->factory()->empty_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name->AsArrayIndex(&index)) {
|
||||||
|
LookupIterator it(isolate, receiver, index, holder, configuration);
|
||||||
|
// Here we try to avoid having to rebuild the string later
|
||||||
|
// by storing it on the indexed LookupIterator.
|
||||||
|
it.name_ = name;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
return LookupIterator(receiver, name, holder, configuration);
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate,
|
LookupIterator LookupIterator::PropertyOrElement(Isolate* isolate,
|
||||||
Handle<Object> receiver,
|
Handle<Object> receiver,
|
||||||
Handle<Object> key,
|
Handle<Object> key,
|
||||||
bool* success,
|
bool* success,
|
||||||
Configuration configuration) {
|
Configuration configuration) {
|
||||||
|
// TODO(mslekova): come up with better way to avoid duplication
|
||||||
uint32_t index = 0;
|
uint32_t index = 0;
|
||||||
if (key->ToArrayIndex(&index)) {
|
if (key->ToArrayIndex(&index)) {
|
||||||
*success = true;
|
*success = true;
|
||||||
|
@ -126,6 +126,11 @@ class V8_EXPORT_PRIVATE LookupIterator final BASE_EMBEDDED {
|
|||||||
return LookupIterator(receiver, name, holder, configuration);
|
return LookupIterator(receiver, name, holder, configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LookupIterator PropertyOrElement(
|
||||||
|
Isolate* isolate, Handle<Object> receiver, Handle<Object> key,
|
||||||
|
bool* success, Handle<JSReceiver> holder,
|
||||||
|
Configuration configuration = DEFAULT);
|
||||||
|
|
||||||
static LookupIterator PropertyOrElement(
|
static LookupIterator PropertyOrElement(
|
||||||
Isolate* isolate, Handle<Object> receiver, Handle<Object> key,
|
Isolate* isolate, Handle<Object> receiver, Handle<Object> key,
|
||||||
bool* success, Configuration configuration = DEFAULT);
|
bool* success, Configuration configuration = DEFAULT);
|
||||||
|
@ -1106,7 +1106,7 @@ MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate,
|
|||||||
Execution::Call(isolate, trap, handler, arraysize(args), args), Object);
|
Execution::Call(isolate, trap, handler, arraysize(args), args), Object);
|
||||||
|
|
||||||
MaybeHandle<Object> result =
|
MaybeHandle<Object> result =
|
||||||
JSProxy::CheckGetTrapResult(isolate, name, target, trap_result);
|
JSProxy::CheckGetSetTrapResult(isolate, name, target, trap_result, kGet);
|
||||||
if (result.is_null()) {
|
if (result.is_null()) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -1116,10 +1116,11 @@ MaybeHandle<Object> JSProxy::GetProperty(Isolate* isolate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
MaybeHandle<Object> JSProxy::CheckGetTrapResult(Isolate* isolate,
|
MaybeHandle<Object> JSProxy::CheckGetSetTrapResult(Isolate* isolate,
|
||||||
Handle<Name> name,
|
Handle<Name> name,
|
||||||
Handle<JSReceiver> target,
|
Handle<JSReceiver> target,
|
||||||
Handle<Object> trap_result) {
|
Handle<Object> trap_result,
|
||||||
|
AccessKind access_kind) {
|
||||||
// 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
|
// 9. Let targetDesc be ? target.[[GetOwnProperty]](P).
|
||||||
PropertyDescriptor target_desc;
|
PropertyDescriptor target_desc;
|
||||||
Maybe<bool> target_found =
|
Maybe<bool> target_found =
|
||||||
@ -1136,24 +1137,43 @@ MaybeHandle<Object> JSProxy::CheckGetTrapResult(Isolate* isolate,
|
|||||||
!target_desc.writable() &&
|
!target_desc.writable() &&
|
||||||
!trap_result->SameValue(*target_desc.value());
|
!trap_result->SameValue(*target_desc.value());
|
||||||
if (inconsistent) {
|
if (inconsistent) {
|
||||||
THROW_NEW_ERROR(
|
if (access_kind == kGet) {
|
||||||
isolate, NewTypeError(MessageTemplate::kProxyGetNonConfigurableData,
|
THROW_NEW_ERROR(
|
||||||
name, target_desc.value(), trap_result),
|
isolate,
|
||||||
Object);
|
NewTypeError(MessageTemplate::kProxyGetNonConfigurableData, name,
|
||||||
|
target_desc.value(), trap_result),
|
||||||
|
Object);
|
||||||
|
} else {
|
||||||
|
isolate->Throw(*isolate->factory()->NewTypeError(
|
||||||
|
MessageTemplate::kProxySetFrozenData, name));
|
||||||
|
return MaybeHandle<Object>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 10.b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]]
|
// 10.b. If IsAccessorDescriptor(targetDesc) and targetDesc.[[Configurable]]
|
||||||
// is false and targetDesc.[[Get]] is undefined, then
|
// is false and targetDesc.[[Get]] is undefined, then
|
||||||
// 10.b.i. If trapResult is not undefined, throw a TypeError exception.
|
// 10.b.i. If trapResult is not undefined, throw a TypeError exception.
|
||||||
inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) &&
|
if (access_kind == kGet) {
|
||||||
!target_desc.configurable() &&
|
inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) &&
|
||||||
target_desc.get()->IsUndefined(isolate) &&
|
!target_desc.configurable() &&
|
||||||
!trap_result->IsUndefined(isolate);
|
target_desc.get()->IsUndefined(isolate) &&
|
||||||
|
!trap_result->IsUndefined(isolate);
|
||||||
|
} else {
|
||||||
|
inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) &&
|
||||||
|
!target_desc.configurable() &&
|
||||||
|
target_desc.set()->IsUndefined(isolate);
|
||||||
|
}
|
||||||
if (inconsistent) {
|
if (inconsistent) {
|
||||||
THROW_NEW_ERROR(
|
if (access_kind == kGet) {
|
||||||
isolate,
|
THROW_NEW_ERROR(
|
||||||
NewTypeError(MessageTemplate::kProxyGetNonConfigurableAccessor, name,
|
isolate,
|
||||||
trap_result),
|
NewTypeError(MessageTemplate::kProxyGetNonConfigurableAccessor,
|
||||||
Object);
|
name, trap_result),
|
||||||
|
Object);
|
||||||
|
} else {
|
||||||
|
isolate->Throw(*isolate->factory()->NewTypeError(
|
||||||
|
MessageTemplate::kProxySetFrozenAccessor, name));
|
||||||
|
return MaybeHandle<Object>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return isolate->factory()->undefined_value();
|
return isolate->factory()->undefined_value();
|
||||||
@ -5527,29 +5547,11 @@ Maybe<bool> JSProxy::SetProperty(Handle<JSProxy> proxy, Handle<Name> name,
|
|||||||
trap_name, name));
|
trap_name, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enforce the invariant.
|
MaybeHandle<Object> result =
|
||||||
PropertyDescriptor target_desc;
|
JSProxy::CheckGetSetTrapResult(isolate, name, target, value, kSet);
|
||||||
Maybe<bool> owned =
|
|
||||||
JSReceiver::GetOwnPropertyDescriptor(isolate, target, name, &target_desc);
|
if (result.is_null()) {
|
||||||
MAYBE_RETURN(owned, Nothing<bool>());
|
return Nothing<bool>();
|
||||||
if (owned.FromJust()) {
|
|
||||||
bool inconsistent = PropertyDescriptor::IsDataDescriptor(&target_desc) &&
|
|
||||||
!target_desc.configurable() &&
|
|
||||||
!target_desc.writable() &&
|
|
||||||
!value->SameValue(*target_desc.value());
|
|
||||||
if (inconsistent) {
|
|
||||||
isolate->Throw(*isolate->factory()->NewTypeError(
|
|
||||||
MessageTemplate::kProxySetFrozenData, name));
|
|
||||||
return Nothing<bool>();
|
|
||||||
}
|
|
||||||
inconsistent = PropertyDescriptor::IsAccessorDescriptor(&target_desc) &&
|
|
||||||
!target_desc.configurable() &&
|
|
||||||
target_desc.set()->IsUndefined(isolate);
|
|
||||||
if (inconsistent) {
|
|
||||||
isolate->Throw(*isolate->factory()->NewTypeError(
|
|
||||||
MessageTemplate::kProxySetFrozenAccessor, name));
|
|
||||||
return Nothing<bool>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return Just(true);
|
return Just(true);
|
||||||
}
|
}
|
||||||
|
@ -6046,10 +6046,13 @@ class JSProxy: public JSReceiver {
|
|||||||
Isolate* isolate, Handle<JSProxy> proxy, Handle<Name> name,
|
Isolate* isolate, Handle<JSProxy> proxy, Handle<Name> name,
|
||||||
Handle<Object> receiver, bool* was_found);
|
Handle<Object> receiver, bool* was_found);
|
||||||
|
|
||||||
static MaybeHandle<Object> CheckGetTrapResult(Isolate* isolate,
|
enum AccessKind { kGet, kSet };
|
||||||
Handle<Name> name,
|
|
||||||
Handle<JSReceiver> target,
|
static MaybeHandle<Object> CheckGetSetTrapResult(Isolate* isolate,
|
||||||
Handle<Object> trap_result);
|
Handle<Name> name,
|
||||||
|
Handle<JSReceiver> target,
|
||||||
|
Handle<Object> trap_result,
|
||||||
|
AccessKind access_kind);
|
||||||
|
|
||||||
// ES6 9.5.9
|
// ES6 9.5.9
|
||||||
MUST_USE_RESULT static Maybe<bool> SetProperty(Handle<JSProxy> proxy,
|
MUST_USE_RESULT static Maybe<bool> SetProperty(Handle<JSProxy> proxy,
|
||||||
|
@ -51,24 +51,47 @@ RUNTIME_FUNCTION(Runtime_GetPropertyWithReceiver) {
|
|||||||
|
|
||||||
DCHECK_EQ(3, args.length());
|
DCHECK_EQ(3, args.length());
|
||||||
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, holder, 0);
|
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, holder, 0);
|
||||||
CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
|
CONVERT_ARG_HANDLE_CHECKED(Object, name, 1);
|
||||||
CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 2);
|
CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 2);
|
||||||
|
|
||||||
LookupIterator it =
|
bool success;
|
||||||
LookupIterator::PropertyOrElement(isolate, receiver, name, holder);
|
LookupIterator it = LookupIterator::PropertyOrElement(isolate, receiver, name,
|
||||||
|
&success, holder);
|
||||||
RETURN_RESULT_OR_FAILURE(isolate, Object::GetProperty(&it));
|
RETURN_RESULT_OR_FAILURE(isolate, Object::GetProperty(&it));
|
||||||
}
|
}
|
||||||
|
|
||||||
RUNTIME_FUNCTION(Runtime_CheckProxyGetTrapResult) {
|
RUNTIME_FUNCTION(Runtime_SetPropertyWithReceiver) {
|
||||||
HandleScope scope(isolate);
|
HandleScope scope(isolate);
|
||||||
|
|
||||||
DCHECK_EQ(3, args.length());
|
DCHECK_EQ(5, args.length());
|
||||||
|
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, holder, 0);
|
||||||
|
CONVERT_ARG_HANDLE_CHECKED(Object, name, 1);
|
||||||
|
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
|
||||||
|
CONVERT_ARG_HANDLE_CHECKED(Object, receiver, 3);
|
||||||
|
CONVERT_LANGUAGE_MODE_ARG_CHECKED(language_mode, 4);
|
||||||
|
|
||||||
|
bool success;
|
||||||
|
LookupIterator it = LookupIterator::PropertyOrElement(isolate, receiver, name,
|
||||||
|
&success, holder);
|
||||||
|
|
||||||
|
Maybe<bool> result = Object::SetSuperProperty(
|
||||||
|
&it, value, language_mode, Object::MAY_BE_STORE_FROM_KEYED);
|
||||||
|
MAYBE_RETURN(result, isolate->heap()->exception());
|
||||||
|
return *isolate->factory()->ToBoolean(result.FromJust());
|
||||||
|
}
|
||||||
|
|
||||||
|
RUNTIME_FUNCTION(Runtime_CheckProxyGetSetTrapResult) {
|
||||||
|
HandleScope scope(isolate);
|
||||||
|
|
||||||
|
DCHECK_EQ(4, args.length());
|
||||||
CONVERT_ARG_HANDLE_CHECKED(Name, name, 0);
|
CONVERT_ARG_HANDLE_CHECKED(Name, name, 0);
|
||||||
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, target, 1);
|
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, target, 1);
|
||||||
CONVERT_ARG_HANDLE_CHECKED(Object, trap_result, 2);
|
CONVERT_ARG_HANDLE_CHECKED(Object, trap_result, 2);
|
||||||
|
CONVERT_NUMBER_CHECKED(int64_t, access_kind, Int64, args[3]);
|
||||||
|
|
||||||
RETURN_RESULT_OR_FAILURE(
|
RETURN_RESULT_OR_FAILURE(isolate, JSProxy::CheckGetSetTrapResult(
|
||||||
isolate, JSProxy::CheckGetTrapResult(isolate, name, target, trap_result));
|
isolate, name, target, trap_result,
|
||||||
|
JSProxy::AccessKind(access_kind)));
|
||||||
}
|
}
|
||||||
|
|
||||||
RUNTIME_FUNCTION(Runtime_CheckProxyHasTrap) {
|
RUNTIME_FUNCTION(Runtime_CheckProxyHasTrap) {
|
||||||
|
@ -468,8 +468,9 @@ namespace internal {
|
|||||||
F(JSProxyGetHandler, 1, 1) \
|
F(JSProxyGetHandler, 1, 1) \
|
||||||
F(JSProxyRevoke, 1, 1) \
|
F(JSProxyRevoke, 1, 1) \
|
||||||
F(GetPropertyWithReceiver, 2, 1) \
|
F(GetPropertyWithReceiver, 2, 1) \
|
||||||
F(CheckProxyGetTrapResult, 2, 1) \
|
F(CheckProxyHasTrap, 2, 1) \
|
||||||
F(CheckProxyHasTrap, 2, 1)
|
F(SetPropertyWithReceiver, 5, 1) \
|
||||||
|
F(CheckProxyGetSetTrapResult, 2, 1)
|
||||||
|
|
||||||
#define FOR_EACH_INTRINSIC_REGEXP(F) \
|
#define FOR_EACH_INTRINSIC_REGEXP(F) \
|
||||||
F(IsRegExp, 1, 1) \
|
F(IsRegExp, 1, 1) \
|
||||||
|
@ -206,8 +206,6 @@
|
|||||||
'builtins/builtins-promise-gen.h',
|
'builtins/builtins-promise-gen.h',
|
||||||
'builtins/builtins-proxy-gen.cc',
|
'builtins/builtins-proxy-gen.cc',
|
||||||
'builtins/builtins-proxy-gen.h',
|
'builtins/builtins-proxy-gen.h',
|
||||||
'builtins/builtins-proxy-helpers-gen.cc',
|
|
||||||
'builtins/builtins-proxy-helpers-gen.h',
|
|
||||||
'builtins/builtins-regexp-gen.cc',
|
'builtins/builtins-regexp-gen.cc',
|
||||||
'builtins/builtins-regexp-gen.h',
|
'builtins/builtins-regexp-gen.h',
|
||||||
'builtins/builtins-sharedarraybuffer-gen.cc',
|
'builtins/builtins-sharedarraybuffer-gen.cc',
|
||||||
|
@ -193,7 +193,6 @@
|
|||||||
'Error from proxy getOwnPropertyDescriptor trap');
|
'Error from proxy getOwnPropertyDescriptor trap');
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
(function testGetPropertyDetailsBailout2() {
|
(function testGetPropertyDetailsBailout2() {
|
||||||
var obj = {};
|
var obj = {};
|
||||||
Object.defineProperty(obj, 'prop', {
|
Object.defineProperty(obj, 'prop', {
|
||||||
@ -211,3 +210,13 @@
|
|||||||
" property on the proxy target but the proxy did not return its actual" +
|
" property on the proxy target but the proxy did not return its actual" +
|
||||||
" value (expected '53' but got '42')");
|
" value (expected '53' but got '42')");
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
(function test32BitIndex() {
|
||||||
|
var index = (1 << 31) + 1;
|
||||||
|
var obj = {};
|
||||||
|
obj[index] = 42;
|
||||||
|
var p = new Proxy(obj, {});
|
||||||
|
for (var i = 0; i < 3; ++i) {
|
||||||
|
assertEquals(42, p[index]);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
@ -308,3 +308,101 @@ TestTrapReceiverArgument(strictReflectSet);
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
function TestTargetProxy(mySet) {
|
||||||
|
var q = new Proxy({}, {});
|
||||||
|
var proxy = new Proxy(q, {
|
||||||
|
set: function(t, k, v) {
|
||||||
|
return Reflect.set(t, k, v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var p of properties) {
|
||||||
|
assertTrueIf(mySet.returnsBool, mySet(proxy, p, 42));
|
||||||
|
assertSame(42, q[p]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TestTargetProxy(sloppyDefaultSet);
|
||||||
|
TestTargetProxy(sloppyReflectSet);
|
||||||
|
TestTargetProxy(strictDefaultSet);
|
||||||
|
TestTargetProxy(strictReflectSet);
|
||||||
|
|
||||||
|
|
||||||
|
(function TestAccessorNoSet() {
|
||||||
|
var target = {
|
||||||
|
};
|
||||||
|
Object.defineProperty(target, 'prop', {
|
||||||
|
get: function() {
|
||||||
|
return 42;
|
||||||
|
},
|
||||||
|
configurable: false
|
||||||
|
})
|
||||||
|
var handler = {
|
||||||
|
set: function() { return true; }
|
||||||
|
}
|
||||||
|
var proxy = new Proxy(target, handler);
|
||||||
|
assertThrows(function() { proxy.prop = 0; }, TypeError);
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function TestProxyInPrototype() {
|
||||||
|
var handler = {
|
||||||
|
set: function(t, k, v) {
|
||||||
|
Reflect.set(t, k, v);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var obj = {};
|
||||||
|
var proxy = new Proxy(obj, handler);
|
||||||
|
var o = Object.create(proxy);
|
||||||
|
|
||||||
|
for (var i = 0; i < 3; ++i) {
|
||||||
|
o.prop = 42 + i;
|
||||||
|
assertEquals(42 + i, obj.prop);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function TestProxyInPrototypeNoTrap() {
|
||||||
|
var handler = {
|
||||||
|
};
|
||||||
|
var obj = {};
|
||||||
|
var proxy = new Proxy(obj, handler);
|
||||||
|
var o = Object.create(proxy);
|
||||||
|
|
||||||
|
for (var i = 0; i < 3; ++i) {
|
||||||
|
o.prop = 42 + i;
|
||||||
|
assertEquals(42 + i, o.prop);
|
||||||
|
assertEquals(undefined, obj.prop);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Note: this case is currently handled by runtime.
|
||||||
|
(function TestDifferentHolder() {
|
||||||
|
var obj = {
|
||||||
|
'1337': 100
|
||||||
|
};
|
||||||
|
var handler = {
|
||||||
|
set(target, name, value, receiver) {
|
||||||
|
if (name != '1337') return Reflect.set(target, name, value, receiver);
|
||||||
|
|
||||||
|
assertSame(target, obj);
|
||||||
|
assertSame(receiver, p);
|
||||||
|
return target[name] = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var p = new Proxy(obj, handler);
|
||||||
|
for (var i = 0; i < 3; ++i) {
|
||||||
|
assertEquals(42, p[1337] = 42);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function test32BitIndex() {
|
||||||
|
var index = (1 << 31) + 1;
|
||||||
|
var obj = {};
|
||||||
|
obj[index] = 42;
|
||||||
|
var p = new Proxy(obj, {});
|
||||||
|
for (var i = 0; i < 3; ++i) {
|
||||||
|
p[index] = 100;
|
||||||
|
assertEquals(100, obj[index]);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
Loading…
Reference in New Issue
Block a user