Commit 5ea95feb authored by Igor Sheludko's avatar Igor Sheludko Committed by Commit Bot

[ic] Do access checks when storing via JSGlobalProxy.

Bug: chromium:764219
Change-Id: I99d1192c5c0f2b8bf47e0f193a0c4d9c00477466
Reviewed-on: https://chromium-review.googlesource.com/712454
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48492}
parent d5b29f43
......@@ -537,43 +537,51 @@ void AccessorAssembler::HandleLoadICProtoHandlerCase(
}
}
void AccessorAssembler::EmitAccessCheck(Node* expected_native_context,
Node* context, Node* receiver,
Label* can_access, Label* miss) {
CSA_ASSERT(this, IsNativeContext(expected_native_context));
Node* native_context = LoadNativeContext(context);
GotoIf(WordEqual(expected_native_context, native_context), can_access);
// If the receiver is not a JSGlobalProxy then we miss.
GotoIfNot(IsJSGlobalProxy(receiver), miss);
// For JSGlobalProxy receiver try to compare security tokens of current
// and expected native contexts.
Node* expected_token = LoadContextElement(expected_native_context,
Context::SECURITY_TOKEN_INDEX);
Node* current_token =
LoadContextElement(native_context, Context::SECURITY_TOKEN_INDEX);
Branch(WordEqual(expected_token, current_token), can_access, miss);
}
Node* AccessorAssembler::EmitLoadICProtoArrayCheck(const LoadICParameters* p,
Node* handler,
Node* handler_length,
Node* handler_flags,
Label* miss) {
VARIABLE(start_index, MachineType::PointerRepresentation());
start_index.Bind(IntPtrConstant(LoadHandler::kFirstPrototypeIndex));
VARIABLE(var_start_index, MachineType::PointerRepresentation(),
IntPtrConstant(LoadHandler::kFirstPrototypeIndex));
Label can_access(this);
GotoIfNot(IsSetWord<LoadHandler::DoAccessCheckOnReceiverBits>(handler_flags),
&can_access);
{
// Skip this entry of a handler.
start_index.Bind(IntPtrConstant(LoadHandler::kFirstPrototypeIndex + 1));
var_start_index.Bind(IntPtrConstant(LoadHandler::kFirstPrototypeIndex + 1));
int offset =
FixedArray::OffsetOfElementAt(LoadHandler::kFirstPrototypeIndex);
Node* expected_native_context =
LoadWeakCellValue(LoadObjectField(handler, offset), miss);
CSA_ASSERT(this, IsNativeContext(expected_native_context));
Node* native_context = LoadNativeContext(p->context);
GotoIf(WordEqual(expected_native_context, native_context), &can_access);
// If the receiver is not a JSGlobalProxy then we miss.
GotoIfNot(IsJSGlobalProxy(p->receiver), miss);
// For JSGlobalProxy receiver try to compare security tokens of current
// and expected native contexts.
Node* expected_token = LoadContextElement(expected_native_context,
Context::SECURITY_TOKEN_INDEX);
Node* current_token =
LoadContextElement(native_context, Context::SECURITY_TOKEN_INDEX);
Branch(WordEqual(expected_token, current_token), &can_access, miss);
EmitAccessCheck(expected_native_context, p->context, p->receiver,
&can_access, miss);
}
BIND(&can_access);
BuildFastLoop(start_index.value(), handler_length,
[this, p, handler, miss](Node* current) {
BuildFastLoop(var_start_index.value(), handler_length,
[=](Node* current) {
Node* prototype_cell =
LoadFixedArrayElement(handler, current);
CheckPrototype(prototype_cell, p->name, miss);
......@@ -649,16 +657,19 @@ void AccessorAssembler::HandleStoreICHandlerCase(
Label if_fast_smi(this), if_proxy(this);
STATIC_ASSERT(StoreHandler::kStoreGlobalProxy + 1 ==
StoreHandler::kStoreNormal);
STATIC_ASSERT(StoreHandler::kStoreNormal + 1 == StoreHandler::kProxy);
STATIC_ASSERT(StoreHandler::kProxy + 1 == StoreHandler::kKindsNumber);
Node* handler_kind = DecodeWord<StoreHandler::KindBits>(handler_word);
GotoIf(IntPtrLessThan(handler_kind,
IntPtrConstant(StoreHandler::kStoreNormal)),
IntPtrConstant(StoreHandler::kStoreGlobalProxy)),
&if_fast_smi);
GotoIf(WordEqual(handler_kind, IntPtrConstant(StoreHandler::kProxy)),
&if_proxy);
CSA_ASSERT(this, WordEqual(handler_kind,
IntPtrConstant(StoreHandler::kStoreNormal)));
Node* properties = LoadSlowProperties(holder);
VARIABLE(var_name_index, MachineType::PointerRepresentation());
......@@ -716,62 +727,11 @@ void AccessorAssembler::HandleStoreICHandlerCase(
BIND(&store_global);
{
// Load value or miss if the {handler} weak cell is cleared.
Node* cell = LoadWeakCellValue(handler, miss);
CSA_ASSERT(this, IsPropertyCell(cell));
// Load the payload of the global parameter cell. A hole indicates that
// the cell has been invalidated and that the store must be handled by the
// runtime.
Node* cell_contents = LoadObjectField(cell, PropertyCell::kValueOffset);
Node* details =
LoadAndUntagToWord32ObjectField(cell, PropertyCell::kDetailsOffset);
Node* type = DecodeWord32<PropertyDetails::PropertyCellTypeField>(details);
Label constant(this), store(this), not_smi(this);
GotoIf(
Word32Equal(
type, Int32Constant(static_cast<int>(PropertyCellType::kConstant))),
&constant);
GotoIf(IsTheHole(cell_contents), miss);
GotoIf(
Word32Equal(
type, Int32Constant(static_cast<int>(PropertyCellType::kMutable))),
&store);
CSA_ASSERT(this,
Word32Or(Word32Equal(type,
Int32Constant(static_cast<int>(
PropertyCellType::kConstantType))),
Word32Equal(type,
Int32Constant(static_cast<int>(
PropertyCellType::kUndefined)))));
GotoIfNot(TaggedIsSmi(cell_contents), &not_smi);
GotoIfNot(TaggedIsSmi(p->value), miss);
Goto(&store);
BIND(&not_smi);
{
GotoIf(TaggedIsSmi(p->value), miss);
Node* expected_map = LoadMap(cell_contents);
Node* map = LoadMap(p->value);
GotoIfNot(WordEqual(expected_map, map), miss);
Goto(&store);
}
BIND(&store);
{
StoreObjectField(cell, PropertyCell::kValueOffset, p->value);
Return(p->value);
}
BIND(&constant);
{
GotoIfNot(WordEqual(cell_contents, p->value), miss);
Return(p->value);
}
ExitPoint direct_exit(this);
StoreGlobalIC_PropertyCellCase(cell, p->value, &direct_exit, miss);
}
}
......@@ -794,6 +754,8 @@ void AccessorAssembler::HandleStoreICElementHandlerCase(
void AccessorAssembler::HandleStoreICProtoHandler(
const StoreICParameters* p, Node* handler, Label* miss,
ElementSupport support_elements) {
Comment("HandleStoreICProtoHandler");
// IC dispatchers rely on these assumptions to be held.
STATIC_ASSERT(FixedArray::kLengthOffset ==
StoreHandler::kTransitionCellOffset);
......@@ -823,7 +785,8 @@ void AccessorAssembler::HandleStoreICProtoHandler(
VARIABLE(var_transition, MachineRepresentation::kTagged);
Label if_transition(this), if_transition_to_constant(this),
if_store_normal(this), if_proxy(this), do_store(this);
if_store_normal(this), if_store_global_proxy(this), if_proxy(this),
do_store(this);
BIND(&tuple_handler);
{
Node* transition = LoadWeakCellValue(maybe_transition_cell, miss);
......@@ -833,9 +796,35 @@ void AccessorAssembler::HandleStoreICProtoHandler(
BIND(&array_handler);
{
VARIABLE(var_start_index, MachineType::PointerRepresentation(),
IntPtrConstant(StoreHandler::kFirstPrototypeIndex));
Comment("array_handler");
Label can_access(this);
// Only Tuple3 handlers are allowed to have code handlers.
CSA_ASSERT(this, TaggedIsSmi(smi_or_code));
GotoIfNot(
IsSetSmi(smi_or_code, StoreHandler::DoAccessCheckOnReceiverBits::kMask),
&can_access);
{
// Skip this entry of a handler.
var_start_index.Bind(
IntPtrConstant(StoreHandler::kFirstPrototypeIndex + 1));
int offset =
FixedArray::OffsetOfElementAt(StoreHandler::kFirstPrototypeIndex);
Node* expected_native_context =
LoadWeakCellValue(LoadObjectField(handler, offset), miss);
EmitAccessCheck(expected_native_context, p->context, p->receiver,
&can_access, miss);
}
BIND(&can_access);
Node* length = SmiUntag(maybe_transition_cell);
BuildFastLoop(IntPtrConstant(StoreHandler::kFirstPrototypeIndex), length,
[this, p, handler, miss](Node* current) {
BuildFastLoop(var_start_index.value(), length,
[=](Node* current) {
Node* prototype_cell =
LoadFixedArrayElement(handler, current);
CheckPrototype(prototype_cell, p->name, miss);
......@@ -866,6 +855,7 @@ void AccessorAssembler::HandleStoreICProtoHandler(
Node* holder = p->receiver;
Node* transition = var_transition.value();
GotoIfNot(IsMap(transition), &if_store_global_proxy);
GotoIf(IsDeprecatedMap(transition), miss);
if (support_elements == kSupportElements) {
......@@ -892,6 +882,10 @@ void AccessorAssembler::HandleStoreICProtoHandler(
GotoIf(WordEqual(handler_kind,
IntPtrConstant(StoreHandler::kTransitionToConstant)),
&if_transition_to_constant);
// This case is already handled above.
CSA_ASSERT(this,
WordNotEqual(handler_kind,
IntPtrConstant(StoreHandler::kStoreGlobalProxy)));
// Handle transitioning field stores.
HandleStoreICSmiHandlerCase(handler_word, holder, p->value, transition,
......@@ -954,6 +948,11 @@ void AccessorAssembler::HandleStoreICProtoHandler(
p->receiver, p->name, p->value);
}
}
BIND(&if_store_global_proxy);
{
ExitPoint direct_exit(this);
StoreGlobalIC_PropertyCellCase(transition, p->value, &direct_exit, miss);
}
}
}
......@@ -2396,6 +2395,65 @@ void AccessorAssembler::StoreIC(const StoreICParameters* p) {
}
}
void AccessorAssembler::StoreGlobalIC_PropertyCellCase(Node* property_cell,
Node* value,
ExitPoint* exit_point,
Label* miss) {
Comment("StoreGlobalIC_TryPropertyCellCase");
CSA_ASSERT(this, IsPropertyCell(property_cell));
// Load the payload of the global parameter cell. A hole indicates that
// the cell has been invalidated and that the store must be handled by the
// runtime.
Node* cell_contents =
LoadObjectField(property_cell, PropertyCell::kValueOffset);
Node* details = LoadAndUntagToWord32ObjectField(property_cell,
PropertyCell::kDetailsOffset);
Node* type = DecodeWord32<PropertyDetails::PropertyCellTypeField>(details);
Label constant(this), store(this), not_smi(this);
GotoIf(Word32Equal(type, Int32Constant(
static_cast<int>(PropertyCellType::kConstant))),
&constant);
GotoIf(IsTheHole(cell_contents), miss);
GotoIf(Word32Equal(
type, Int32Constant(static_cast<int>(PropertyCellType::kMutable))),
&store);
CSA_ASSERT(this,
Word32Or(Word32Equal(type, Int32Constant(static_cast<int>(
PropertyCellType::kConstantType))),
Word32Equal(type, Int32Constant(static_cast<int>(
PropertyCellType::kUndefined)))));
GotoIfNot(TaggedIsSmi(cell_contents), &not_smi);
GotoIfNot(TaggedIsSmi(value), miss);
Goto(&store);
BIND(&not_smi);
{
GotoIf(TaggedIsSmi(value), miss);
Node* expected_map = LoadMap(cell_contents);
Node* map = LoadMap(value);
GotoIfNot(WordEqual(expected_map, map), miss);
Goto(&store);
}
BIND(&store);
{
StoreObjectField(property_cell, PropertyCell::kValueOffset, value);
exit_point->Return(value);
}
BIND(&constant);
{
GotoIfNot(WordEqual(cell_contents, value), miss);
exit_point->Return(value);
}
}
void AccessorAssembler::KeyedStoreIC(const StoreICParameters* p) {
Label miss(this, Label::kDeferred);
{
......
......@@ -115,6 +115,8 @@ class AccessorAssembler : public CodeStubAssembler {
void KeyedLoadIC(const LoadICParameters* p);
void KeyedLoadICGeneric(const LoadICParameters* p);
void StoreIC(const StoreICParameters* p);
void StoreGlobalIC_PropertyCellCase(Node* property_cell, Node* value,
ExitPoint* exit_point, Label* miss);
void KeyedStoreIC(const StoreICParameters* p);
// IC dispatcher behavior.
......@@ -150,6 +152,9 @@ class AccessorAssembler : public CodeStubAssembler {
Variable* var_double_value, Label* rebox_double,
ExitPoint* exit_point);
void EmitAccessCheck(Node* expected_native_context, Node* context,
Node* receiver, Label* can_access, Label* miss);
Node* EmitLoadICProtoArrayCheck(const LoadICParameters* p, Node* handler,
Node* handler_length, Node* handler_flags,
Label* miss);
......
......@@ -108,6 +108,11 @@ Handle<Smi> LoadHandler::LoadElement(Isolate* isolate,
return handle(Smi::FromInt(config), isolate);
}
Handle<Smi> StoreHandler::StoreGlobalProxy(Isolate* isolate) {
int config = KindBits::encode(kStoreGlobalProxy);
return handle(Smi::FromInt(config), isolate);
}
Handle<Smi> StoreHandler::StoreNormal(Isolate* isolate) {
int config = KindBits::encode(kStoreNormal);
return handle(Smi::FromInt(config), isolate);
......@@ -118,6 +123,13 @@ Handle<Smi> StoreHandler::StoreProxy(Isolate* isolate) {
return handle(Smi::FromInt(config), isolate);
}
Handle<Smi> StoreHandler::EnableAccessCheckOnReceiver(Isolate* isolate,
Handle<Smi> smi_handler) {
int config = smi_handler->value();
config = DoAccessCheckOnReceiverBits::update(config, true);
return handle(Smi::FromInt(config), isolate);
}
Handle<Smi> StoreHandler::StoreField(Isolate* isolate, Kind kind,
int descriptor, FieldIndex field_index,
Representation representation,
......
......@@ -232,41 +232,59 @@ Handle<Object> StoreHandler::StoreElementTransition(
Handle<Object> StoreHandler::StoreTransition(Isolate* isolate,
Handle<Map> receiver_map,
Handle<JSObject> holder,
Handle<Map> transition,
Handle<HeapObject> transition,
Handle<Name> name) {
Handle<Object> smi_handler;
if (transition->is_dictionary_map()) {
smi_handler = StoreNormal(isolate);
} else {
int descriptor = transition->LastAdded();
Handle<DescriptorArray> descriptors(transition->instance_descriptors());
PropertyDetails details = descriptors->GetDetails(descriptor);
Representation representation = details.representation();
DCHECK(!representation.IsNone());
Handle<Smi> smi_handler;
Handle<WeakCell> transition_cell;
if (transition->IsMap()) {
Handle<Map> transition_map = Handle<Map>::cast(transition);
if (transition_map->is_dictionary_map()) {
smi_handler = StoreNormal(isolate);
} else {
int descriptor = transition_map->LastAdded();
Handle<DescriptorArray> descriptors(
transition_map->instance_descriptors());
PropertyDetails details = descriptors->GetDetails(descriptor);
Representation representation = details.representation();
DCHECK(!representation.IsNone());
// Declarative handlers don't support access checks.
DCHECK(!transition->is_access_check_needed());
// Declarative handlers don't support access checks.
DCHECK(!transition_map->is_access_check_needed());
DCHECK_EQ(kData, details.kind());
if (details.location() == kDescriptor) {
smi_handler = TransitionToConstant(isolate, descriptor);
DCHECK_EQ(kData, details.kind());
if (details.location() == kDescriptor) {
smi_handler = TransitionToConstant(isolate, descriptor);
} else {
DCHECK_EQ(kField, details.location());
bool extend_storage = Map::cast(transition_map->GetBackPointer())
->unused_property_fields() == 0;
FieldIndex index =
FieldIndex::ForDescriptor(*transition_map, descriptor);
smi_handler = TransitionToField(isolate, descriptor, index,
representation, extend_storage);
}
}
// |holder| is either a receiver if the property is non-existent or
// one of the prototypes.
DCHECK(!holder.is_null());
bool is_nonexistent = holder->map() == transition_map->GetBackPointer();
if (is_nonexistent) holder = Handle<JSObject>::null();
transition_cell = Map::WeakCellForMap(transition_map);
} else {
DCHECK(transition->IsPropertyCell());
if (receiver_map->IsJSGlobalObjectMap()) {
// TODO(ishell): this must be handled by StoreGlobalIC once it's finished.
return StoreGlobal(isolate, Handle<PropertyCell>::cast(transition));
} else {
DCHECK_EQ(kField, details.location());
bool extend_storage =
Map::cast(transition->GetBackPointer())->unused_property_fields() ==
0;
FieldIndex index = FieldIndex::ForDescriptor(*transition, descriptor);
smi_handler = TransitionToField(isolate, descriptor, index,
representation, extend_storage);
DCHECK(receiver_map->IsJSGlobalProxyMap());
smi_handler = StoreGlobalProxy(isolate);
transition_cell = isolate->factory()->NewWeakCell(transition);
}
}
// |holder| is either a receiver if the property is non-existent or
// one of the prototypes.
DCHECK(!holder.is_null());
bool is_nonexistent = holder->map() == transition->GetBackPointer();
if (is_nonexistent) holder = Handle<JSObject>::null();
int checks_count =
GetPrototypeCheckCount(isolate, receiver_map, holder, name);
......@@ -274,6 +292,12 @@ Handle<Object> StoreHandler::StoreTransition(Isolate* isolate,
DCHECK_LE(0, checks_count);
DCHECK(!receiver_map->IsJSGlobalObjectMap());
if (receiver_map->is_access_check_needed()) {
DCHECK(!receiver_map->is_dictionary_map());
DCHECK_LE(1, checks_count); // For native context.
smi_handler = EnableAccessCheckOnReceiver(isolate, smi_handler);
}
Handle<Object> validity_cell =
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
if (validity_cell.is_null()) {
......@@ -281,8 +305,6 @@ Handle<Object> StoreHandler::StoreTransition(Isolate* isolate,
validity_cell = handle(Smi::kZero, isolate);
}
Handle<WeakCell> transition_cell = Map::WeakCellForMap(transition);
Factory* factory = isolate->factory();
if (checks_count == 0) {
return factory->NewTuple3(transition_cell, smi_handler, validity_cell,
......@@ -298,13 +320,19 @@ Handle<Object> StoreHandler::StoreTransition(Isolate* isolate,
return handler_array;
}
// static
Handle<Object> StoreHandler::StoreGlobal(Isolate* isolate,
Handle<PropertyCell> cell) {
return isolate->factory()->NewWeakCell(cell);
}
// static
Handle<Object> StoreHandler::StoreProxy(Isolate* isolate,
Handle<Map> receiver_map,
Handle<JSProxy> proxy,
Handle<JSReceiver> receiver,
Handle<Name> name) {
Handle<Object> smi_handler = StoreProxy(isolate);
Handle<Smi> smi_handler = StoreProxy(isolate);
if (receiver.is_identical_to(proxy)) return smi_handler;
......@@ -312,6 +340,12 @@ Handle<Object> StoreHandler::StoreProxy(Isolate* isolate,
DCHECK_LE(0, checks_count);
if (receiver_map->is_access_check_needed()) {
DCHECK(!receiver_map->is_dictionary_map());
DCHECK_LE(1, checks_count); // For native context.
smi_handler = EnableAccessCheckOnReceiver(isolate, smi_handler);
}
Handle<Object> validity_cell =
Map::GetOrCreatePrototypeChainValidityCell(receiver_map, isolate);
if (validity_cell.is_null()) {
......
......@@ -178,12 +178,13 @@ class StoreHandler {
kStoreElement,
kStoreField,
kStoreConstField,
// TODO(ishell): remove once constant field tracking is done.
kTransitionToConstant = kStoreConstField,
kTransitionToField,
kStoreGlobalProxy,
kStoreNormal,
kProxy,
kKindsNumber, // Keep last
// TODO(ishell): remove once constant field tracking is done.
kTransitionToConstant = kStoreConstField
kKindsNumber // Keep last
};
class KindBits : public BitField<Kind, 0, 3> {};
......@@ -191,12 +192,19 @@ class StoreHandler {
static inline bool IsHandler(Object* maybe_handler);
// Applicable to kStoreGlobalProxy, kProxy kinds.
// Defines whether access rights check should be done on receiver object.
class DoAccessCheckOnReceiverBits
: public BitField<bool, KindBits::kNext, 1> {};
// Applicable to kStoreField, kTransitionToField and kTransitionToConstant
// kinds.
// Index of a value entry in the descriptor array.
class DescriptorBits
: public BitField<unsigned, KindBits::kNext, kDescriptorIndexBitCount> {};
: public BitField<unsigned, DoAccessCheckOnReceiverBits::kNext,
kDescriptorIndexBitCount> {};
//
// Encoding when KindBits contains kTransitionToConstant.
//
......@@ -245,7 +253,7 @@ class StoreHandler {
static Handle<Object> StoreTransition(Isolate* isolate,
Handle<Map> receiver_map,
Handle<JSObject> holder,
Handle<Map> transition,
Handle<HeapObject> transition,
Handle<Name> name);
static Handle<Object> StoreElementTransition(Isolate* isolate,
......@@ -258,6 +266,14 @@ class StoreHandler {
Handle<JSReceiver> receiver,
Handle<Name> name);
// Creates a handler for storing a property to the property cell of a global
// object.
static Handle<Object> StoreGlobal(Isolate* isolate,
Handle<PropertyCell> cell);
// Creates a Smi-handler for storing a property to a global proxy object.
static inline Handle<Smi> StoreGlobalProxy(Isolate* isolate);
// Creates a Smi-handler for storing a property to a slow object.
static inline Handle<Smi> StoreNormal(Isolate* isolate);
......@@ -265,6 +281,11 @@ class StoreHandler {
static inline Handle<Smi> StoreProxy(Isolate* isolate);
private:
// Sets DoAccessCheckOnReceiverBits in given Smi-handler. The receiver
// check is a part of a prototype chain check.
static inline Handle<Smi> EnableAccessCheckOnReceiver(
Isolate* isolate, Handle<Smi> smi_handler);
static inline Handle<Smi> StoreField(Isolate* isolate, Kind kind,
int descriptor, FieldIndex field_index,
Representation representation,
......
......@@ -1366,23 +1366,19 @@ void StoreIC::UpdateCaches(LookupIterator* lookup, Handle<Object> value,
TRACE_IC("StoreIC", lookup->name());
}
namespace {
Handle<Object> StoreGlobal(Isolate* isolate, Handle<PropertyCell> cell) {
return isolate->factory()->NewWeakCell(cell);
}
} // namespace
Handle<Object> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) {
switch (lookup->state()) {
case LookupIterator::TRANSITION: {
Handle<JSObject> holder = lookup->GetHolder<JSObject>();
auto store_target = lookup->GetStoreTarget();
Handle<JSObject> store_target = lookup->GetStoreTarget();
if (store_target->IsJSGlobalObject()) {
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreGlobalTransitionDH);
return StoreGlobal(isolate(), lookup->transition_cell());
Handle<Object> handler = StoreHandler::StoreTransition(
isolate(), receiver_map(), store_target, lookup->transition_cell(),
lookup->name());
return handler;
}
// Currently not handled by CompileStoreTransition.
if (!holder->HasFastProperties()) {
......@@ -1477,7 +1473,8 @@ Handle<Object> StoreIC::GetMapIndependentHandler(LookupIterator* lookup) {
if (lookup->is_dictionary_holder()) {
if (holder->IsJSGlobalObject()) {
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreGlobalDH);
return StoreGlobal(isolate(), lookup->GetPropertyCell());
return StoreHandler::StoreGlobal(isolate(),
lookup->GetPropertyCell());
}
TRACE_HANDLER_STATS(isolate(), StoreIC_StoreNormalDH);
DCHECK(holder.is_identical_to(receiver));
......
// 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.
(function() {
function f(o) {
o.x = 42;
};
f({});
f(this);
f(this);
})();
(function() {
function f(o) {
o.y = 153;
};
Object.setPrototypeOf(this, new Proxy({}, {}));
f({});
f(this);
f(this);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment