[class] Add IC support for defining class fields to replace runtime call
Introduces several new runtime mechanics for defining private fields, including: - Bytecode StaKeyedPropertyAsDefine - Builtins StoreOwnIC{Trampoline|Baseline|_NoFeedback} - Builtins KeyedDefineOwnIC{Trampoline|Baseline|_Megamorphic} - TurboFan IR opcode JSDefineProperty These new operations can reduce a runtime call per class field into a more traditional Store equivalent. In the microbenchmarks, this results in a substantial win over the status quo (~8x benchmark score for single fields with the changes, ~20x with multiple fields). The TurboFan JSDefineProperty op is lowered in JSNativeContextSpecialization, however this required some hacks. Because private fields are defined as DONT_ENUM when added to the object, we can't find a suitable transition using the typical data property (NONE) flags. I've added a mechanism to specify the required PropertyAttributes for the transition we want to look up. Details: New bytecodes: - StaKeyedPropertyAsDefine, which is essentially StaKeyedProperty but with a different IC builtin (KeyedDefineOwnIC). This is a bytecode rather than a flag for the existing StaKeyedProperty in order to avoid impacting typical keyed stores in any way due to additional branching and testing. New builtins: - StoreOwnIC{TTrampoline|Baseline|_NoFeedback} is now used for StaNamedOwnProperty. Unlike the regular StoreIC, this variant will no longer look up the property name in the prototype. In adddition, this CL changes an assumption that StoreNamedOwnProperty can't result in a map transition, as we can't rely on the property already being present in the Map due to an object literal boilerplate. In the context of class features, this replaces the runtime function %CreateDataProperty(). - KeyedDefineOwnIC{Trampoline|Baseline|_Megamorphic} is used by the new StaKeyedPropertyAsDefine bytecode. This is similar to an ordinary KeyedStoreIC, but will not check the prototype for setters, and for private fields, will take the slow path if the field already exists. In the context of class features, this replaces the runtime function %AddPrivateField(). TurboFan IR: - JSDefineProperty is introduced to represent a situation where we need to use "Define" semantics, in particular, it codifies that we do not consult the prototype chain, and the semantics relating to private fields are implied as well. R=leszeks@chromium.org, syg@chromium.org, rmcilroy@chromium.org Bug: v8:9888 Change-Id: Idcc947585c0e612f9e8533aa4e2e0f8f0df8875d Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2795831 Reviewed-by: Igor Sheludko <ishell@chromium.org> Reviewed-by: Michael Stanton <mvstanton@chromium.org> Reviewed-by: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Reviewed-by: Shu-yu Guo <syg@chromium.org> Commit-Queue: Joyee Cheung <joyee@igalia.com> Cr-Commit-Position: refs/heads/main@{#77377}
This commit is contained in:
parent
debf0896ce
commit
713ebae3b4
@ -930,6 +930,14 @@ void BaselineCompiler::VisitStaKeyedProperty() {
|
||||
IndexAsTagged(2)); // slot
|
||||
}
|
||||
|
||||
void BaselineCompiler::VisitStaKeyedPropertyAsDefine() {
|
||||
CallBuiltin<Builtin::kKeyedDefineOwnICBaseline>(
|
||||
RegisterOperand(0), // object
|
||||
RegisterOperand(1), // key
|
||||
kInterpreterAccumulatorRegister, // value
|
||||
IndexAsTagged(2)); // slot
|
||||
}
|
||||
|
||||
void BaselineCompiler::VisitStaInArrayLiteral() {
|
||||
CallBuiltin<Builtin::kStoreInArrayLiteralICBaseline>(
|
||||
RegisterOperand(0), // object
|
||||
|
@ -273,6 +273,7 @@ namespace internal {
|
||||
/* Handlers */ \
|
||||
TFH(KeyedLoadIC_PolymorphicName, LoadWithVector) \
|
||||
TFH(KeyedStoreIC_Megamorphic, Store) \
|
||||
TFH(KeyedDefineOwnIC_Megamorphic, Store) \
|
||||
TFH(LoadGlobalIC_NoFeedback, LoadGlobalNoFeedback) \
|
||||
TFH(LoadIC_FunctionPrototype, LoadWithVector) \
|
||||
TFH(LoadIC_StringLength, LoadWithVector) \
|
||||
@ -280,6 +281,7 @@ namespace internal {
|
||||
TFH(LoadIC_NoFeedback, LoadNoFeedback) \
|
||||
TFH(StoreGlobalIC_Slow, StoreWithVector) \
|
||||
TFH(StoreIC_NoFeedback, Store) \
|
||||
TFH(StoreOwnIC_NoFeedback, Store) \
|
||||
TFH(KeyedLoadIC_SloppyArguments, LoadWithVector) \
|
||||
TFH(LoadIndexedInterceptorIC, LoadWithVector) \
|
||||
TFH(KeyedStoreIC_SloppyArguments_Standard, StoreWithVector) \
|
||||
@ -630,9 +632,15 @@ namespace internal {
|
||||
TFH(StoreIC, StoreWithVector) \
|
||||
TFH(StoreICTrampoline, Store) \
|
||||
TFH(StoreICBaseline, StoreBaseline) \
|
||||
TFH(StoreOwnIC, StoreWithVector) \
|
||||
TFH(StoreOwnICTrampoline, Store) \
|
||||
TFH(StoreOwnICBaseline, StoreBaseline) \
|
||||
TFH(KeyedStoreIC, StoreWithVector) \
|
||||
TFH(KeyedStoreICTrampoline, Store) \
|
||||
TFH(KeyedStoreICBaseline, StoreBaseline) \
|
||||
TFH(KeyedDefineOwnIC, StoreWithVector) \
|
||||
TFH(KeyedDefineOwnICTrampoline, Store) \
|
||||
TFH(KeyedDefineOwnICBaseline, StoreBaseline) \
|
||||
TFH(StoreInArrayLiteralIC, StoreWithVector) \
|
||||
TFH(StoreInArrayLiteralICBaseline, StoreBaseline) \
|
||||
TFH(LookupContextBaseline, LookupBaseline) \
|
||||
|
@ -58,11 +58,21 @@ void Builtins::Generate_KeyedStoreIC_Megamorphic(
|
||||
KeyedStoreGenericGenerator::Generate(state);
|
||||
}
|
||||
|
||||
void Builtins::Generate_KeyedDefineOwnIC_Megamorphic(
|
||||
compiler::CodeAssemblerState* state) {
|
||||
KeyedDefineOwnGenericGenerator::Generate(state);
|
||||
}
|
||||
|
||||
void Builtins::Generate_StoreIC_NoFeedback(
|
||||
compiler::CodeAssemblerState* state) {
|
||||
StoreICNoFeedbackGenerator::Generate(state);
|
||||
}
|
||||
|
||||
void Builtins::Generate_StoreOwnIC_NoFeedback(
|
||||
compiler::CodeAssemblerState* state) {
|
||||
StoreOwnICNoFeedbackGenerator::Generate(state);
|
||||
}
|
||||
|
||||
// All possible fast-to-fast transitions. Transitions to dictionary mode are not
|
||||
// handled by ElementsTransitionAndStore builtins.
|
||||
#define ELEMENTS_KIND_TRANSITIONS(V) \
|
||||
|
@ -109,6 +109,20 @@ void Builtins::Generate_StoreICBaseline(compiler::CodeAssemblerState* state) {
|
||||
AccessorAssembler assembler(state);
|
||||
assembler.GenerateStoreICBaseline();
|
||||
}
|
||||
void Builtins::Generate_StoreOwnIC(compiler::CodeAssemblerState* state) {
|
||||
AccessorAssembler assembler(state);
|
||||
assembler.GenerateStoreOwnIC();
|
||||
}
|
||||
void Builtins::Generate_StoreOwnICTrampoline(
|
||||
compiler::CodeAssemblerState* state) {
|
||||
AccessorAssembler assembler(state);
|
||||
assembler.GenerateStoreOwnICTrampoline();
|
||||
}
|
||||
void Builtins::Generate_StoreOwnICBaseline(
|
||||
compiler::CodeAssemblerState* state) {
|
||||
AccessorAssembler assembler(state);
|
||||
assembler.GenerateStoreOwnICBaseline();
|
||||
}
|
||||
void Builtins::Generate_KeyedStoreIC(compiler::CodeAssemblerState* state) {
|
||||
AccessorAssembler assembler(state);
|
||||
assembler.GenerateKeyedStoreIC();
|
||||
@ -123,6 +137,20 @@ void Builtins::Generate_KeyedStoreICBaseline(
|
||||
AccessorAssembler assembler(state);
|
||||
assembler.GenerateKeyedStoreICBaseline();
|
||||
}
|
||||
void Builtins::Generate_KeyedDefineOwnIC(compiler::CodeAssemblerState* state) {
|
||||
AccessorAssembler assembler(state);
|
||||
assembler.GenerateKeyedDefineOwnIC();
|
||||
}
|
||||
void Builtins::Generate_KeyedDefineOwnICTrampoline(
|
||||
compiler::CodeAssemblerState* state) {
|
||||
AccessorAssembler assembler(state);
|
||||
assembler.GenerateKeyedDefineOwnICTrampoline();
|
||||
}
|
||||
void Builtins::Generate_KeyedDefineOwnICBaseline(
|
||||
compiler::CodeAssemblerState* state) {
|
||||
AccessorAssembler assembler(state);
|
||||
assembler.GenerateKeyedDefineOwnICBaseline();
|
||||
}
|
||||
void Builtins::Generate_StoreInArrayLiteralIC(
|
||||
compiler::CodeAssemblerState* state) {
|
||||
AccessorAssembler assembler(state);
|
||||
|
@ -96,15 +96,11 @@ Callable CodeFactory::LoadGlobalICInOptimizedCode(Isolate* isolate,
|
||||
}
|
||||
|
||||
Callable CodeFactory::StoreOwnIC(Isolate* isolate) {
|
||||
// TODO(ishell): Currently we use StoreOwnIC only for storing properties that
|
||||
// already exist in the boilerplate therefore we can use StoreIC.
|
||||
return Builtins::CallableFor(isolate, Builtin::kStoreICTrampoline);
|
||||
return Builtins::CallableFor(isolate, Builtin::kStoreOwnICTrampoline);
|
||||
}
|
||||
|
||||
Callable CodeFactory::StoreOwnICInOptimizedCode(Isolate* isolate) {
|
||||
// TODO(ishell): Currently we use StoreOwnIC only for storing properties that
|
||||
// already exist in the boilerplate therefore we can use StoreIC.
|
||||
return Builtins::CallableFor(isolate, Builtin::kStoreIC);
|
||||
return Builtins::CallableFor(isolate, Builtin::kStoreOwnIC);
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
// Copyright 2015 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.
|
||||
@ -78,6 +79,8 @@ std::ostream& operator<<(std::ostream& os, AccessMode access_mode) {
|
||||
return os << "StoreInLiteral";
|
||||
case AccessMode::kHas:
|
||||
return os << "Has";
|
||||
case AccessMode::kDefine:
|
||||
return os << "Define";
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
@ -321,7 +324,8 @@ bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
|
||||
break;
|
||||
}
|
||||
case AccessMode::kStore:
|
||||
case AccessMode::kStoreInLiteral: {
|
||||
case AccessMode::kStoreInLiteral:
|
||||
case AccessMode::kDefine: {
|
||||
// For stores, the field map and field representation information
|
||||
// must match exactly, otherwise we cannot merge the stores. We
|
||||
// also need to make sure that in case of transitioning stores,
|
||||
@ -798,7 +802,7 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
|
||||
// prototype. According to ES6 section 9.1.9 [[Set]], we need to
|
||||
// create a new data property on the receiver. We can still optimize
|
||||
// if such a transition already exists.
|
||||
return LookupTransition(receiver_map, name, holder);
|
||||
return LookupTransition(receiver_map, name, holder, NONE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -873,9 +877,17 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
|
||||
}
|
||||
}
|
||||
|
||||
// Don't search on the prototype when storing in literals.
|
||||
if (access_mode == AccessMode::kStoreInLiteral) {
|
||||
return LookupTransition(receiver_map, name, holder);
|
||||
// Don't search on the prototype when storing in literals, or performing a
|
||||
// Define operation
|
||||
if (access_mode == AccessMode::kStoreInLiteral ||
|
||||
access_mode == AccessMode::kDefine) {
|
||||
PropertyAttributes attrs = NONE;
|
||||
if (name.object()->IsPrivate()) {
|
||||
// When PrivateNames are added to an object, they are by definition
|
||||
// non-enumerable.
|
||||
attrs = DONT_ENUM;
|
||||
}
|
||||
return LookupTransition(receiver_map, name, holder, attrs);
|
||||
}
|
||||
|
||||
// Don't lookup private symbols on the prototype chain.
|
||||
@ -929,7 +941,7 @@ PropertyAccessInfo AccessInfoFactory::ComputePropertyAccessInfo(
|
||||
// to transition to a new data property.
|
||||
// Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver)
|
||||
if (access_mode == AccessMode::kStore) {
|
||||
return LookupTransition(receiver_map, name, holder);
|
||||
return LookupTransition(receiver_map, name, holder, NONE);
|
||||
}
|
||||
|
||||
// The property was not found (access returns undefined or throws
|
||||
@ -1109,11 +1121,12 @@ PropertyAccessInfo AccessInfoFactory::LookupSpecialFieldAccessor(
|
||||
}
|
||||
|
||||
PropertyAccessInfo AccessInfoFactory::LookupTransition(
|
||||
MapRef map, NameRef name, base::Optional<JSObjectRef> holder) const {
|
||||
MapRef map, NameRef name, base::Optional<JSObjectRef> holder,
|
||||
PropertyAttributes attrs) const {
|
||||
// Check if the {map} has a data transition with the given {name}.
|
||||
Map transition = TransitionsAccessor(isolate(), map.object(),
|
||||
broker()->is_concurrent_inlining())
|
||||
.SearchTransition(*name.object(), kData, NONE);
|
||||
.SearchTransition(*name.object(), kData, attrs);
|
||||
if (transition.is_null()) return Invalid();
|
||||
|
||||
base::Optional<MapRef> maybe_transition_map =
|
||||
|
@ -286,7 +286,8 @@ class AccessInfoFactory final {
|
||||
ElementAccessFeedback const& feedback) const;
|
||||
PropertyAccessInfo LookupSpecialFieldAccessor(MapRef map, NameRef name) const;
|
||||
PropertyAccessInfo LookupTransition(MapRef map, NameRef name,
|
||||
base::Optional<JSObjectRef> holder) const;
|
||||
base::Optional<JSObjectRef> holder,
|
||||
PropertyAttributes attrs) const;
|
||||
PropertyAccessInfo ComputeDataFieldAccessInfo(
|
||||
MapRef receiver_map, MapRef map, base::Optional<JSObjectRef> holder,
|
||||
InternalIndex descriptor, AccessMode access_mode) const;
|
||||
|
@ -2152,6 +2152,40 @@ void BytecodeGraphBuilder::VisitStaKeyedProperty() {
|
||||
environment()->RecordAfterState(node, Environment::kAttachFrameState);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitStaKeyedPropertyAsDefine() {
|
||||
PrepareEagerCheckpoint();
|
||||
Node* value = environment()->LookupAccumulator();
|
||||
Node* object =
|
||||
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
|
||||
Node* key =
|
||||
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
|
||||
FeedbackSource source =
|
||||
CreateFeedbackSource(bytecode_iterator().GetIndexOperand(2));
|
||||
LanguageMode language_mode =
|
||||
GetLanguageModeFromSlotKind(broker()->GetFeedbackSlotKind(source));
|
||||
|
||||
const Operator* op = javascript()->DefineProperty(language_mode, source);
|
||||
|
||||
JSTypeHintLowering::LoweringResult lowering =
|
||||
TryBuildSimplifiedStoreKeyed(op, object, key, value, source.slot);
|
||||
if (lowering.IsExit()) return;
|
||||
|
||||
Node* node = nullptr;
|
||||
if (lowering.IsSideEffectFree()) {
|
||||
node = lowering.value();
|
||||
} else {
|
||||
DCHECK(!lowering.Changed());
|
||||
STATIC_ASSERT(JSDefinePropertyNode::ObjectIndex() == 0);
|
||||
STATIC_ASSERT(JSDefinePropertyNode::KeyIndex() == 1);
|
||||
STATIC_ASSERT(JSDefinePropertyNode::ValueIndex() == 2);
|
||||
STATIC_ASSERT(JSDefinePropertyNode::FeedbackVectorIndex() == 3);
|
||||
DCHECK(IrOpcode::IsFeedbackCollectingOpcode(op->opcode()));
|
||||
node = NewNode(op, object, key, value, feedback_vector_node());
|
||||
}
|
||||
|
||||
environment()->RecordAfterState(node, Environment::kAttachFrameState);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitLdaModuleVariable() {
|
||||
int32_t cell_index = bytecode_iterator().GetImmediateOperand(0);
|
||||
uint32_t depth = bytecode_iterator().GetUnsignedImmediateOperand(1);
|
||||
|
@ -49,7 +49,11 @@ class PropertyAccessInfo;
|
||||
|
||||
// Whether we are loading a property or storing to a property.
|
||||
// For a store during literal creation, do not walk up the prototype chain.
|
||||
enum class AccessMode { kLoad, kStore, kStoreInLiteral, kHas };
|
||||
// For a define operation, we behave similarly to kStoreInLiteral, but with
|
||||
// distinct semantics for private class fields (in which private field
|
||||
// accesses must throw when storing a field which does not exist, or
|
||||
// adding/defining a field which already exists).
|
||||
enum class AccessMode { kLoad, kStore, kStoreInLiteral, kHas, kDefine };
|
||||
|
||||
inline bool IsAnyStore(AccessMode mode) {
|
||||
return mode == AccessMode::kStore || mode == AccessMode::kStoreInLiteral;
|
||||
|
@ -422,6 +422,24 @@ void JSGenericLowering::LowerJSStoreProperty(Node* node) {
|
||||
}
|
||||
}
|
||||
|
||||
void JSGenericLowering::LowerJSDefineProperty(Node* node) {
|
||||
JSDefinePropertyNode n(node);
|
||||
const PropertyAccess& p = n.Parameters();
|
||||
FrameState frame_state = n.frame_state();
|
||||
Node* outer_state = frame_state.outer_frame_state();
|
||||
STATIC_ASSERT(n.FeedbackVectorIndex() == 3);
|
||||
if (outer_state->opcode() != IrOpcode::kFrameState) {
|
||||
n->RemoveInput(n.FeedbackVectorIndex());
|
||||
node->InsertInput(zone(), 3,
|
||||
jsgraph()->TaggedIndexConstant(p.feedback().index()));
|
||||
ReplaceWithBuiltinCall(node, Builtin::kKeyedDefineOwnICTrampoline);
|
||||
} else {
|
||||
node->InsertInput(zone(), 3,
|
||||
jsgraph()->TaggedIndexConstant(p.feedback().index()));
|
||||
ReplaceWithBuiltinCall(node, Builtin::kKeyedDefineOwnIC);
|
||||
}
|
||||
}
|
||||
|
||||
void JSGenericLowering::LowerJSStoreNamed(Node* node) {
|
||||
JSStoreNamedNode n(node);
|
||||
NamedAccess const& p = n.Parameters();
|
||||
@ -447,8 +465,8 @@ void JSGenericLowering::LowerJSStoreNamed(Node* node) {
|
||||
}
|
||||
|
||||
void JSGenericLowering::LowerJSStoreNamedOwn(Node* node) {
|
||||
JSStoreNamedOwnNode n(node);
|
||||
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
|
||||
JSStoreNamedOwnNode n(node);
|
||||
StoreNamedOwnParameters const& p = n.Parameters();
|
||||
FrameState frame_state = n.frame_state();
|
||||
Node* outer_state = frame_state.outer_frame_state();
|
||||
|
@ -343,6 +343,10 @@ KeyedAccessMode KeyedAccessMode::FromNexus(FeedbackNexus const& nexus) {
|
||||
if (IsKeyedHasICKind(kind)) {
|
||||
return KeyedAccessMode(AccessMode::kHas, nexus.GetKeyedAccessLoadMode());
|
||||
}
|
||||
if (IsDefineOwnICKind(kind)) {
|
||||
return KeyedAccessMode(AccessMode::kDefine,
|
||||
nexus.GetKeyedAccessStoreMode());
|
||||
}
|
||||
if (IsKeyedStoreICKind(kind)) {
|
||||
return KeyedAccessMode(AccessMode::kStore, nexus.GetKeyedAccessStoreMode());
|
||||
}
|
||||
@ -361,6 +365,7 @@ bool KeyedAccessMode::IsLoad() const {
|
||||
}
|
||||
bool KeyedAccessMode::IsStore() const {
|
||||
return access_mode_ == AccessMode::kStore ||
|
||||
access_mode_ == AccessMode::kDefine ||
|
||||
access_mode_ == AccessMode::kStoreInLiteral;
|
||||
}
|
||||
|
||||
@ -401,7 +406,8 @@ ElementAccessFeedback::ElementAccessFeedback(Zone* zone,
|
||||
DCHECK(IsKeyedLoadICKind(slot_kind) || IsKeyedHasICKind(slot_kind) ||
|
||||
IsStoreDataPropertyInLiteralKind(slot_kind) ||
|
||||
IsKeyedStoreICKind(slot_kind) ||
|
||||
IsStoreInArrayLiteralICKind(slot_kind));
|
||||
IsStoreInArrayLiteralICKind(slot_kind) ||
|
||||
IsDefineOwnICKind(slot_kind));
|
||||
}
|
||||
|
||||
bool ElementAccessFeedback::HasOnlyStringMaps(JSHeapBroker* broker) const {
|
||||
@ -435,7 +441,8 @@ NamedAccessFeedback::NamedAccessFeedback(NameRef const& name,
|
||||
IsStoreOwnICKind(slot_kind) || IsKeyedLoadICKind(slot_kind) ||
|
||||
IsKeyedHasICKind(slot_kind) || IsKeyedStoreICKind(slot_kind) ||
|
||||
IsStoreInArrayLiteralICKind(slot_kind) ||
|
||||
IsStoreDataPropertyInLiteralKind(slot_kind));
|
||||
IsStoreDataPropertyInLiteralKind(slot_kind) ||
|
||||
IsDefineOwnICKind(slot_kind));
|
||||
}
|
||||
|
||||
void JSHeapBroker::SetFeedback(FeedbackSource const& source,
|
||||
|
@ -104,6 +104,8 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) {
|
||||
return ReduceJSLoadProperty(node);
|
||||
case IrOpcode::kJSStoreProperty:
|
||||
return ReduceJSStoreProperty(node);
|
||||
case IrOpcode::kJSDefineProperty:
|
||||
return ReduceJSDefineProperty(node);
|
||||
case IrOpcode::kJSStoreNamedOwn:
|
||||
return ReduceJSStoreNamedOwn(node);
|
||||
case IrOpcode::kJSStoreDataPropertyInLiteral:
|
||||
@ -1095,7 +1097,8 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
|
||||
node->opcode() == IrOpcode::kJSStoreNamedOwn ||
|
||||
node->opcode() == IrOpcode::kJSStoreDataPropertyInLiteral ||
|
||||
node->opcode() == IrOpcode::kJSHasProperty ||
|
||||
node->opcode() == IrOpcode::kJSLoadNamedFromSuper);
|
||||
node->opcode() == IrOpcode::kJSLoadNamedFromSuper ||
|
||||
node->opcode() == IrOpcode::kJSDefineProperty);
|
||||
STATIC_ASSERT(JSLoadNamedNode::ObjectIndex() == 0 &&
|
||||
JSStoreNamedNode::ObjectIndex() == 0 &&
|
||||
JSLoadPropertyNode::ObjectIndex() == 0 &&
|
||||
@ -1103,7 +1106,8 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
|
||||
JSStoreNamedOwnNode::ObjectIndex() == 0 &&
|
||||
JSStoreNamedNode::ObjectIndex() == 0 &&
|
||||
JSStoreDataPropertyInLiteralNode::ObjectIndex() == 0 &&
|
||||
JSHasPropertyNode::ObjectIndex() == 0);
|
||||
JSHasPropertyNode::ObjectIndex() == 0 &&
|
||||
JSDefinePropertyNode::ObjectIndex() == 0);
|
||||
STATIC_ASSERT(JSLoadNamedFromSuperNode::ReceiverIndex() == 0);
|
||||
|
||||
Node* context = NodeProperties::GetContextInput(node);
|
||||
@ -1667,7 +1671,8 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
|
||||
node->opcode() == IrOpcode::kJSStoreProperty ||
|
||||
node->opcode() == IrOpcode::kJSStoreInArrayLiteral ||
|
||||
node->opcode() == IrOpcode::kJSStoreDataPropertyInLiteral ||
|
||||
node->opcode() == IrOpcode::kJSHasProperty);
|
||||
node->opcode() == IrOpcode::kJSHasProperty ||
|
||||
node->opcode() == IrOpcode::kJSDefineProperty);
|
||||
STATIC_ASSERT(JSLoadPropertyNode::ObjectIndex() == 0 &&
|
||||
JSStorePropertyNode::ObjectIndex() == 0 &&
|
||||
JSStoreInArrayLiteralNode::ArrayIndex() == 0 &&
|
||||
@ -1979,7 +1984,8 @@ Reduction JSNativeContextSpecialization::ReducePropertyAccess(
|
||||
node->opcode() == IrOpcode::kJSLoadNamed ||
|
||||
node->opcode() == IrOpcode::kJSStoreNamed ||
|
||||
node->opcode() == IrOpcode::kJSStoreNamedOwn ||
|
||||
node->opcode() == IrOpcode::kJSLoadNamedFromSuper);
|
||||
node->opcode() == IrOpcode::kJSLoadNamedFromSuper ||
|
||||
node->opcode() == IrOpcode::kJSDefineProperty);
|
||||
DCHECK_GE(node->op()->ControlOutputCount(), 1);
|
||||
|
||||
ProcessedFeedback const& feedback =
|
||||
@ -2164,6 +2170,15 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreProperty(Node* node) {
|
||||
FeedbackSource(p.feedback()), AccessMode::kStore);
|
||||
}
|
||||
|
||||
Reduction JSNativeContextSpecialization::ReduceJSDefineProperty(Node* node) {
|
||||
JSDefinePropertyNode n(node);
|
||||
PropertyAccess const& p = n.Parameters();
|
||||
if (!p.feedback().IsValid()) return NoChange();
|
||||
return ReducePropertyAccess(node, n.key(), base::nullopt, n.value(),
|
||||
FeedbackSource(p.feedback()),
|
||||
AccessMode::kDefine);
|
||||
}
|
||||
|
||||
Node* JSNativeContextSpecialization::InlinePropertyGetterCall(
|
||||
Node* receiver, ConvertReceiverMode receiver_mode, Node* context,
|
||||
Node* frame_state, Node** effect, Node** control,
|
||||
@ -2373,6 +2388,7 @@ JSNativeContextSpecialization::BuildPropertyAccess(
|
||||
if_exceptions, access_info);
|
||||
case AccessMode::kStore:
|
||||
case AccessMode::kStoreInLiteral:
|
||||
case AccessMode::kDefine:
|
||||
DCHECK_EQ(receiver, lookup_start_object);
|
||||
return BuildPropertyStore(receiver, value, context, frame_state, effect,
|
||||
control, name, if_exceptions, access_info,
|
||||
@ -2394,6 +2410,7 @@ JSNativeContextSpecialization::BuildPropertyStore(
|
||||
base::Optional<JSObjectRef> holder = access_info.holder();
|
||||
if (holder.has_value()) {
|
||||
DCHECK_NE(AccessMode::kStoreInLiteral, access_mode);
|
||||
DCHECK_NE(AccessMode::kDefine, access_mode);
|
||||
dependencies()->DependOnStablePrototypeChains(
|
||||
access_info.lookup_start_object_maps(), kStartAtPrototype,
|
||||
holder.value());
|
||||
@ -2408,7 +2425,8 @@ JSNativeContextSpecialization::BuildPropertyStore(
|
||||
} else {
|
||||
DCHECK(access_info.IsDataField() || access_info.IsFastDataConstant());
|
||||
DCHECK(access_mode == AccessMode::kStore ||
|
||||
access_mode == AccessMode::kStoreInLiteral);
|
||||
access_mode == AccessMode::kStoreInLiteral ||
|
||||
access_mode == AccessMode::kDefine);
|
||||
FieldIndex const field_index = access_info.field_index();
|
||||
Type const field_type = access_info.field_type();
|
||||
MachineRepresentation const field_representation =
|
||||
@ -2819,6 +2837,7 @@ JSNativeContextSpecialization::BuildElementAccess(
|
||||
break;
|
||||
}
|
||||
case AccessMode::kStoreInLiteral:
|
||||
case AccessMode::kDefine:
|
||||
UNREACHABLE();
|
||||
case AccessMode::kStore: {
|
||||
// Ensure that the {value} is actually a Number or an Oddball,
|
||||
@ -3144,7 +3163,8 @@ JSNativeContextSpecialization::BuildElementAccess(
|
||||
}
|
||||
} else {
|
||||
DCHECK(keyed_mode.access_mode() == AccessMode::kStore ||
|
||||
keyed_mode.access_mode() == AccessMode::kStoreInLiteral);
|
||||
keyed_mode.access_mode() == AccessMode::kStoreInLiteral ||
|
||||
keyed_mode.access_mode() == AccessMode::kDefine);
|
||||
|
||||
if (IsSmiElementsKind(elements_kind)) {
|
||||
value = effect = graph()->NewNode(
|
||||
|
@ -90,6 +90,7 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
|
||||
Reduction ReduceJSHasProperty(Node* node);
|
||||
Reduction ReduceJSLoadProperty(Node* node);
|
||||
Reduction ReduceJSStoreProperty(Node* node);
|
||||
Reduction ReduceJSDefineProperty(Node* node);
|
||||
Reduction ReduceJSStoreNamedOwn(Node* node);
|
||||
Reduction ReduceJSStoreDataPropertyInLiteral(Node* node);
|
||||
Reduction ReduceJSStoreInArrayLiteral(Node* node);
|
||||
|
@ -314,7 +314,8 @@ bool operator!=(PropertyAccess const& lhs, PropertyAccess const& rhs) {
|
||||
PropertyAccess const& PropertyAccessOf(const Operator* op) {
|
||||
DCHECK(op->opcode() == IrOpcode::kJSHasProperty ||
|
||||
op->opcode() == IrOpcode::kJSLoadProperty ||
|
||||
op->opcode() == IrOpcode::kJSStoreProperty);
|
||||
op->opcode() == IrOpcode::kJSStoreProperty ||
|
||||
op->opcode() == IrOpcode::kJSDefineProperty);
|
||||
return OpParameter<PropertyAccess>(op);
|
||||
}
|
||||
|
||||
@ -1123,6 +1124,16 @@ const Operator* JSOperatorBuilder::StoreProperty(
|
||||
access); // parameter
|
||||
}
|
||||
|
||||
const Operator* JSOperatorBuilder::DefineProperty(
|
||||
LanguageMode language_mode, FeedbackSource const& feedback) {
|
||||
PropertyAccess access(language_mode, feedback);
|
||||
return zone()->New<Operator1<PropertyAccess>>( // --
|
||||
IrOpcode::kJSDefineProperty, Operator::kNoProperties, // opcode
|
||||
"JSDefineProperty", // name
|
||||
4, 1, 1, 0, 1, 2, // counts
|
||||
access); // parameter
|
||||
}
|
||||
|
||||
const Operator* JSOperatorBuilder::StoreNamedOwn(
|
||||
const NameRef& name, FeedbackSource const& feedback) {
|
||||
static constexpr int kObject = 1;
|
||||
|
@ -1022,6 +1022,8 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
|
||||
|
||||
const Operator* StoreProperty(LanguageMode language_mode,
|
||||
FeedbackSource const& feedback);
|
||||
const Operator* DefineProperty(LanguageMode language_mode,
|
||||
FeedbackSource const& feedback);
|
||||
const Operator* StoreNamed(LanguageMode language_mode, const NameRef& name,
|
||||
FeedbackSource const& feedback);
|
||||
|
||||
@ -1312,6 +1314,26 @@ class JSStorePropertyNode final : public JSNodeWrapperBase {
|
||||
#undef INPUTS
|
||||
};
|
||||
|
||||
class JSDefinePropertyNode final : public JSNodeWrapperBase {
|
||||
public:
|
||||
explicit constexpr JSDefinePropertyNode(Node* node)
|
||||
: JSNodeWrapperBase(node) {
|
||||
DCHECK_EQ(IrOpcode::kJSDefineProperty, node->opcode());
|
||||
}
|
||||
|
||||
const PropertyAccess& Parameters() const {
|
||||
return PropertyAccessOf(node()->op());
|
||||
}
|
||||
|
||||
#define INPUTS(V) \
|
||||
V(Object, object, 0, Object) \
|
||||
V(Key, key, 1, Object) \
|
||||
V(Value, value, 2, Object) \
|
||||
V(FeedbackVector, feedback_vector, 3, HeapObject)
|
||||
INPUTS(DEFINE_INPUT_ACCESSORS)
|
||||
#undef INPUTS
|
||||
};
|
||||
|
||||
namespace js_node_wrapper_utils {
|
||||
// Avoids template definitions in the .cc file.
|
||||
TNode<Oddball> UndefinedConstant(JSGraph* jsgraph);
|
||||
|
@ -553,7 +553,8 @@ JSTypeHintLowering::ReduceStoreKeyedOperation(const Operator* op, Node* obj,
|
||||
FeedbackSlot slot) const {
|
||||
DCHECK(op->opcode() == IrOpcode::kJSStoreProperty ||
|
||||
op->opcode() == IrOpcode::kJSStoreInArrayLiteral ||
|
||||
op->opcode() == IrOpcode::kJSStoreDataPropertyInLiteral);
|
||||
op->opcode() == IrOpcode::kJSStoreDataPropertyInLiteral ||
|
||||
op->opcode() == IrOpcode::kJSDefineProperty);
|
||||
if (Node* node = TryBuildSoftDeopt(
|
||||
slot, effect, control,
|
||||
DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
|
||||
|
@ -175,6 +175,7 @@
|
||||
V(JSLoadNamedFromSuper) \
|
||||
V(JSLoadGlobal) \
|
||||
V(JSStoreProperty) \
|
||||
V(JSDefineProperty) \
|
||||
V(JSStoreNamed) \
|
||||
V(JSStoreNamedOwn) \
|
||||
V(JSStoreGlobal) \
|
||||
@ -1120,6 +1121,7 @@ class V8_EXPORT_PRIVATE IrOpcode {
|
||||
case kJSCreateLiteralArray:
|
||||
case kJSCreateLiteralObject:
|
||||
case kJSCreateLiteralRegExp:
|
||||
case kJSDefineProperty:
|
||||
case kJSForInNext:
|
||||
case kJSForInPrepare:
|
||||
case kJSGetIterator:
|
||||
|
@ -72,6 +72,7 @@ bool OperatorProperties::NeedsExactContext(const Operator* op) {
|
||||
case IrOpcode::kJSCreateCatchContext:
|
||||
case IrOpcode::kJSCreateWithContext:
|
||||
case IrOpcode::kJSDebugger:
|
||||
case IrOpcode::kJSDefineProperty:
|
||||
case IrOpcode::kJSDeleteProperty:
|
||||
case IrOpcode::kJSGeneratorStore:
|
||||
case IrOpcode::kJSGetImportMeta:
|
||||
@ -204,6 +205,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) {
|
||||
case IrOpcode::kJSStoreNamed:
|
||||
case IrOpcode::kJSStoreNamedOwn:
|
||||
case IrOpcode::kJSStoreProperty:
|
||||
case IrOpcode::kJSDefineProperty:
|
||||
|
||||
// Conversions
|
||||
case IrOpcode::kJSToLength:
|
||||
|
@ -1420,6 +1420,8 @@ Type Typer::Visitor::Weaken(Node* node, Type current_type, Type previous_type) {
|
||||
|
||||
Type Typer::Visitor::TypeJSStoreProperty(Node* node) { UNREACHABLE(); }
|
||||
|
||||
Type Typer::Visitor::TypeJSDefineProperty(Node* node) { UNREACHABLE(); }
|
||||
|
||||
Type Typer::Visitor::TypeJSStoreNamed(Node* node) { UNREACHABLE(); }
|
||||
|
||||
Type Typer::Visitor::TypeJSStoreGlobal(Node* node) { UNREACHABLE(); }
|
||||
|
@ -740,6 +740,10 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
|
||||
CheckNotTyped(node);
|
||||
CHECK(PropertyAccessOf(node->op()).feedback().IsValid());
|
||||
break;
|
||||
case IrOpcode::kJSDefineProperty:
|
||||
CheckNotTyped(node);
|
||||
CHECK(PropertyAccessOf(node->op()).feedback().IsValid());
|
||||
break;
|
||||
case IrOpcode::kJSStoreNamed:
|
||||
CheckNotTyped(node);
|
||||
break;
|
||||
|
@ -1212,6 +1212,7 @@ void FeedbackNexus::Print(std::ostream& os) {
|
||||
switch (kind()) {
|
||||
case FeedbackSlotKind::kCall:
|
||||
case FeedbackSlotKind::kCloneObject:
|
||||
case FeedbackSlotKind::kDefineOwnKeyed:
|
||||
case FeedbackSlotKind::kHasKeyed:
|
||||
case FeedbackSlotKind::kInstanceOf:
|
||||
case FeedbackSlotKind::kLoadGlobalInsideTypeof:
|
||||
|
@ -3608,14 +3608,18 @@ void AccessorAssembler::StoreIC(const StoreICParameters* p) {
|
||||
|
||||
BIND(&no_feedback);
|
||||
{
|
||||
TailCallBuiltin(Builtin::kStoreIC_NoFeedback, p->context(), p->receiver(),
|
||||
p->name(), p->value(), p->slot());
|
||||
auto builtin = p->IsStoreOwn() ? Builtin::kStoreOwnIC_NoFeedback
|
||||
: Builtin::kStoreIC_NoFeedback;
|
||||
TailCallBuiltin(builtin, p->context(), p->receiver(), p->name(), p->value(),
|
||||
p->slot());
|
||||
}
|
||||
|
||||
BIND(&miss);
|
||||
{
|
||||
TailCallRuntime(Runtime::kStoreIC_Miss, p->context(), p->value(), p->slot(),
|
||||
p->vector(), p->receiver(), p->name());
|
||||
auto runtime =
|
||||
p->IsStoreOwn() ? Runtime::kStoreOwnIC_Miss : Runtime::kStoreIC_Miss;
|
||||
TailCallRuntime(runtime, p->context(), p->value(), p->slot(), p->vector(),
|
||||
p->receiver(), p->name());
|
||||
}
|
||||
}
|
||||
|
||||
@ -3653,7 +3657,8 @@ void AccessorAssembler::StoreGlobalIC(const StoreICParameters* pp) {
|
||||
StoreICParameters p(
|
||||
pp->context(),
|
||||
LoadContextElement(native_context, Context::GLOBAL_PROXY_INDEX),
|
||||
pp->name(), pp->value(), pp->slot(), pp->vector());
|
||||
pp->name(), pp->value(), pp->slot(), pp->vector(),
|
||||
StoreICMode::kDefault);
|
||||
|
||||
HandleStoreICHandlerCase(&p, handler, &miss, ICMode::kGlobalIC);
|
||||
}
|
||||
@ -3825,6 +3830,80 @@ void AccessorAssembler::KeyedStoreIC(const StoreICParameters* p) {
|
||||
}
|
||||
}
|
||||
|
||||
void AccessorAssembler::KeyedDefineOwnIC(const StoreICParameters* p) {
|
||||
Label miss(this, Label::kDeferred);
|
||||
{
|
||||
TVARIABLE(MaybeObject, var_handler);
|
||||
|
||||
Label if_handler(this, &var_handler),
|
||||
try_polymorphic(this, Label::kDeferred),
|
||||
try_megamorphic(this, Label::kDeferred),
|
||||
no_feedback(this, Label::kDeferred),
|
||||
try_polymorphic_name(this, Label::kDeferred);
|
||||
|
||||
TNode<Map> receiver_map = LoadReceiverMap(p->receiver());
|
||||
GotoIf(IsDeprecatedMap(receiver_map), &miss);
|
||||
|
||||
GotoIf(IsUndefined(p->vector()), &no_feedback);
|
||||
|
||||
// Check monomorphic case.
|
||||
TNode<MaybeObject> feedback =
|
||||
TryMonomorphicCase(p->slot(), CAST(p->vector()), receiver_map,
|
||||
&if_handler, &var_handler, &try_polymorphic);
|
||||
BIND(&if_handler);
|
||||
{
|
||||
Comment("KeyedDefineOwnIC_if_handler");
|
||||
HandleStoreICHandlerCase(p, var_handler.value(), &miss,
|
||||
ICMode::kNonGlobalIC, kSupportElements);
|
||||
}
|
||||
|
||||
BIND(&try_polymorphic);
|
||||
TNode<HeapObject> strong_feedback = GetHeapObjectIfStrong(feedback, &miss);
|
||||
{
|
||||
// CheckPolymorphic case.
|
||||
Comment("KeyedDefineOwnIC_try_polymorphic");
|
||||
GotoIfNot(IsWeakFixedArrayMap(LoadMap(strong_feedback)),
|
||||
&try_megamorphic);
|
||||
HandlePolymorphicCase(receiver_map, CAST(strong_feedback), &if_handler,
|
||||
&var_handler, &miss);
|
||||
}
|
||||
|
||||
BIND(&try_megamorphic);
|
||||
{
|
||||
// Check megamorphic case.
|
||||
Comment("KeyedDefineOwnIC_try_megamorphic");
|
||||
Branch(TaggedEqual(strong_feedback, MegamorphicSymbolConstant()),
|
||||
&no_feedback, &try_polymorphic_name);
|
||||
}
|
||||
|
||||
BIND(&no_feedback);
|
||||
{
|
||||
TailCallBuiltin(Builtin::kKeyedDefineOwnIC_Megamorphic, p->context(),
|
||||
p->receiver(), p->name(), p->value(), p->slot());
|
||||
}
|
||||
|
||||
BIND(&try_polymorphic_name);
|
||||
{
|
||||
// We might have a name in feedback, and a fixed array in the next slot.
|
||||
Comment("KeyedDefineOwnIC_try_polymorphic_name");
|
||||
GotoIfNot(TaggedEqual(strong_feedback, p->name()), &miss);
|
||||
// If the name comparison succeeded, we know we have a feedback vector
|
||||
// with at least one map/handler pair.
|
||||
TNode<MaybeObject> feedback_element =
|
||||
LoadFeedbackVectorSlot(CAST(p->vector()), p->slot(), kTaggedSize);
|
||||
TNode<WeakFixedArray> array = CAST(feedback_element);
|
||||
HandlePolymorphicCase(receiver_map, array, &if_handler, &var_handler,
|
||||
&miss);
|
||||
}
|
||||
}
|
||||
BIND(&miss);
|
||||
{
|
||||
Comment("KeyedDefineOwnIC_miss");
|
||||
TailCallRuntime(Runtime::kKeyedDefineOwnIC_Miss, p->context(), p->value(),
|
||||
p->slot(), p->vector(), p->receiver(), p->name());
|
||||
}
|
||||
}
|
||||
|
||||
void AccessorAssembler::StoreInArrayLiteralIC(const StoreICParameters* p) {
|
||||
Label miss(this, Label::kDeferred), no_feedback(this, Label::kDeferred);
|
||||
{
|
||||
@ -4298,7 +4377,8 @@ void AccessorAssembler::GenerateStoreGlobalIC() {
|
||||
auto vector = Parameter<HeapObject>(Descriptor::kVector);
|
||||
auto context = Parameter<Context>(Descriptor::kContext);
|
||||
|
||||
StoreICParameters p(context, base::nullopt, name, value, slot, vector);
|
||||
StoreICParameters p(context, base::nullopt, name, value, slot, vector,
|
||||
StoreICMode::kDefault);
|
||||
StoreGlobalIC(&p);
|
||||
}
|
||||
|
||||
@ -4336,7 +4416,8 @@ void AccessorAssembler::GenerateStoreIC() {
|
||||
auto vector = Parameter<HeapObject>(Descriptor::kVector);
|
||||
auto context = Parameter<Context>(Descriptor::kContext);
|
||||
|
||||
StoreICParameters p(context, receiver, name, value, slot, vector);
|
||||
StoreICParameters p(context, receiver, name, value, slot, vector,
|
||||
StoreICMode::kDefault);
|
||||
StoreIC(&p);
|
||||
}
|
||||
|
||||
@ -4368,6 +4449,49 @@ void AccessorAssembler::GenerateStoreICBaseline() {
|
||||
vector);
|
||||
}
|
||||
|
||||
void AccessorAssembler::GenerateStoreOwnIC() {
|
||||
using Descriptor = StoreWithVectorDescriptor;
|
||||
|
||||
auto receiver = Parameter<Object>(Descriptor::kReceiver);
|
||||
auto name = Parameter<Object>(Descriptor::kName);
|
||||
auto value = Parameter<Object>(Descriptor::kValue);
|
||||
auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
|
||||
auto vector = Parameter<HeapObject>(Descriptor::kVector);
|
||||
auto context = Parameter<Context>(Descriptor::kContext);
|
||||
|
||||
StoreICParameters p(context, receiver, name, value, slot, vector,
|
||||
StoreICMode::kStoreOwn);
|
||||
StoreIC(&p);
|
||||
}
|
||||
|
||||
void AccessorAssembler::GenerateStoreOwnICTrampoline() {
|
||||
using Descriptor = StoreDescriptor;
|
||||
|
||||
auto receiver = Parameter<Object>(Descriptor::kReceiver);
|
||||
auto name = Parameter<Object>(Descriptor::kName);
|
||||
auto value = Parameter<Object>(Descriptor::kValue);
|
||||
auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
|
||||
auto context = Parameter<Context>(Descriptor::kContext);
|
||||
TNode<FeedbackVector> vector = LoadFeedbackVectorForStub();
|
||||
|
||||
TailCallBuiltin(Builtin::kStoreOwnIC, context, receiver, name, value, slot,
|
||||
vector);
|
||||
}
|
||||
|
||||
void AccessorAssembler::GenerateStoreOwnICBaseline() {
|
||||
using Descriptor = StoreWithVectorDescriptor;
|
||||
|
||||
auto receiver = Parameter<Object>(Descriptor::kReceiver);
|
||||
auto name = Parameter<Object>(Descriptor::kName);
|
||||
auto value = Parameter<Object>(Descriptor::kValue);
|
||||
auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
|
||||
TNode<FeedbackVector> vector = LoadFeedbackVectorFromBaseline();
|
||||
TNode<Context> context = LoadContextFromBaseline();
|
||||
|
||||
TailCallBuiltin(Builtin::kStoreOwnIC, context, receiver, name, value, slot,
|
||||
vector);
|
||||
}
|
||||
|
||||
void AccessorAssembler::GenerateKeyedStoreIC() {
|
||||
using Descriptor = StoreWithVectorDescriptor;
|
||||
|
||||
@ -4378,7 +4502,8 @@ void AccessorAssembler::GenerateKeyedStoreIC() {
|
||||
auto vector = Parameter<HeapObject>(Descriptor::kVector);
|
||||
auto context = Parameter<Context>(Descriptor::kContext);
|
||||
|
||||
StoreICParameters p(context, receiver, name, value, slot, vector);
|
||||
StoreICParameters p(context, receiver, name, value, slot, vector,
|
||||
StoreICMode::kDefault);
|
||||
KeyedStoreIC(&p);
|
||||
}
|
||||
|
||||
@ -4410,6 +4535,49 @@ void AccessorAssembler::GenerateKeyedStoreICBaseline() {
|
||||
vector);
|
||||
}
|
||||
|
||||
void AccessorAssembler::GenerateKeyedDefineOwnIC() {
|
||||
using Descriptor = StoreWithVectorDescriptor;
|
||||
|
||||
auto receiver = Parameter<Object>(Descriptor::kReceiver);
|
||||
auto name = Parameter<Object>(Descriptor::kName);
|
||||
auto value = Parameter<Object>(Descriptor::kValue);
|
||||
auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
|
||||
auto vector = Parameter<HeapObject>(Descriptor::kVector);
|
||||
auto context = Parameter<Context>(Descriptor::kContext);
|
||||
|
||||
StoreICParameters p(context, receiver, name, value, slot, vector,
|
||||
StoreICMode::kDefineOwn);
|
||||
KeyedDefineOwnIC(&p);
|
||||
}
|
||||
|
||||
void AccessorAssembler::GenerateKeyedDefineOwnICTrampoline() {
|
||||
using Descriptor = StoreDescriptor;
|
||||
|
||||
auto receiver = Parameter<Object>(Descriptor::kReceiver);
|
||||
auto name = Parameter<Object>(Descriptor::kName);
|
||||
auto value = Parameter<Object>(Descriptor::kValue);
|
||||
auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
|
||||
auto context = Parameter<Context>(Descriptor::kContext);
|
||||
TNode<FeedbackVector> vector = LoadFeedbackVectorForStub();
|
||||
|
||||
TailCallBuiltin(Builtin::kKeyedDefineOwnIC, context, receiver, name, value,
|
||||
slot, vector);
|
||||
}
|
||||
|
||||
void AccessorAssembler::GenerateKeyedDefineOwnICBaseline() {
|
||||
using Descriptor = StoreBaselineDescriptor;
|
||||
|
||||
auto receiver = Parameter<Object>(Descriptor::kReceiver);
|
||||
auto name = Parameter<Object>(Descriptor::kName);
|
||||
auto value = Parameter<Object>(Descriptor::kValue);
|
||||
auto slot = Parameter<TaggedIndex>(Descriptor::kSlot);
|
||||
TNode<FeedbackVector> vector = LoadFeedbackVectorFromBaseline();
|
||||
TNode<Context> context = LoadContextFromBaseline();
|
||||
|
||||
TailCallBuiltin(Builtin::kKeyedDefineOwnIC, context, receiver, name, value,
|
||||
slot, vector);
|
||||
}
|
||||
|
||||
void AccessorAssembler::GenerateStoreInArrayLiteralIC() {
|
||||
using Descriptor = StoreWithVectorDescriptor;
|
||||
|
||||
@ -4420,7 +4588,8 @@ void AccessorAssembler::GenerateStoreInArrayLiteralIC() {
|
||||
auto vector = Parameter<HeapObject>(Descriptor::kVector);
|
||||
auto context = Parameter<Context>(Descriptor::kContext);
|
||||
|
||||
StoreICParameters p(context, array, index, value, slot, vector);
|
||||
StoreICParameters p(context, array, index, value, slot, vector,
|
||||
StoreICMode::kDefault);
|
||||
StoreInArrayLiteralIC(&p);
|
||||
}
|
||||
|
||||
|
@ -42,6 +42,9 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
|
||||
void GenerateStoreIC();
|
||||
void GenerateStoreICTrampoline();
|
||||
void GenerateStoreICBaseline();
|
||||
void GenerateStoreOwnIC();
|
||||
void GenerateStoreOwnICTrampoline();
|
||||
void GenerateStoreOwnICBaseline();
|
||||
void GenerateStoreGlobalIC();
|
||||
void GenerateStoreGlobalICTrampoline();
|
||||
void GenerateStoreGlobalICBaseline();
|
||||
@ -63,6 +66,10 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
|
||||
void GenerateKeyedStoreICTrampoline();
|
||||
void GenerateKeyedStoreICBaseline();
|
||||
|
||||
void GenerateKeyedDefineOwnIC();
|
||||
void GenerateKeyedDefineOwnICTrampoline();
|
||||
void GenerateKeyedDefineOwnICBaseline();
|
||||
|
||||
void GenerateStoreInArrayLiteralIC();
|
||||
void GenerateStoreInArrayLiteralICBaseline();
|
||||
|
||||
@ -190,17 +197,24 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
|
||||
int data_index);
|
||||
|
||||
protected:
|
||||
enum class StoreICMode {
|
||||
kDefault,
|
||||
kStoreOwn,
|
||||
kDefineOwn,
|
||||
};
|
||||
struct StoreICParameters {
|
||||
StoreICParameters(TNode<Context> context,
|
||||
base::Optional<TNode<Object>> receiver,
|
||||
TNode<Object> name, TNode<Object> value,
|
||||
TNode<TaggedIndex> slot, TNode<HeapObject> vector)
|
||||
TNode<TaggedIndex> slot, TNode<HeapObject> vector,
|
||||
StoreICMode mode)
|
||||
: context_(context),
|
||||
receiver_(receiver),
|
||||
name_(name),
|
||||
value_(value),
|
||||
slot_(slot),
|
||||
vector_(vector) {}
|
||||
vector_(vector),
|
||||
mode_(mode) {}
|
||||
|
||||
TNode<Context> context() const { return context_; }
|
||||
TNode<Object> receiver() const { return receiver_.value(); }
|
||||
@ -213,6 +227,9 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
|
||||
|
||||
bool receiver_is_null() const { return !receiver_.has_value(); }
|
||||
|
||||
bool IsStoreOwn() const { return mode_ == StoreICMode::kStoreOwn; }
|
||||
bool IsDefineOwn() const { return mode_ == StoreICMode::kDefineOwn; }
|
||||
|
||||
private:
|
||||
TNode<Context> context_;
|
||||
base::Optional<TNode<Object>> receiver_;
|
||||
@ -220,6 +237,7 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
|
||||
TNode<Object> value_;
|
||||
TNode<TaggedIndex> slot_;
|
||||
TNode<HeapObject> vector_;
|
||||
StoreICMode mode_;
|
||||
};
|
||||
|
||||
enum class LoadAccessMode { kLoad, kHas };
|
||||
@ -289,12 +307,14 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler {
|
||||
void KeyedLoadICGeneric(const LoadICParameters* p);
|
||||
void KeyedLoadICPolymorphicName(const LoadICParameters* p,
|
||||
LoadAccessMode access_mode);
|
||||
|
||||
void StoreIC(const StoreICParameters* p);
|
||||
void StoreGlobalIC(const StoreICParameters* p);
|
||||
void StoreGlobalIC_PropertyCellCase(TNode<PropertyCell> property_cell,
|
||||
TNode<Object> value,
|
||||
ExitPoint* exit_point, Label* miss);
|
||||
void KeyedStoreIC(const StoreICParameters* p);
|
||||
void KeyedDefineOwnIC(const StoreICParameters* p);
|
||||
void StoreInArrayLiteralIC(const StoreICParameters* p);
|
||||
|
||||
// IC dispatcher behavior.
|
||||
|
88
src/ic/ic.cc
88
src/ic/ic.cc
@ -114,7 +114,8 @@ void IC::TraceIC(const char* type, Handle<Object> name, State old_state,
|
||||
} else if (IsKeyedLoadIC()) {
|
||||
KeyedAccessLoadMode mode = nexus()->GetKeyedAccessLoadMode();
|
||||
modifier = GetModifier(mode);
|
||||
} else if (IsKeyedStoreIC() || IsStoreInArrayLiteralICKind(kind())) {
|
||||
} else if (IsKeyedStoreIC() || IsStoreInArrayLiteralICKind(kind()) ||
|
||||
IsDefineOwnIC()) {
|
||||
KeyedAccessStoreMode mode = nexus()->GetKeyedAccessStoreMode();
|
||||
modifier = GetModifier(mode);
|
||||
}
|
||||
@ -1723,7 +1724,10 @@ MaybeHandle<Object> StoreIC::Store(Handle<Object> object, Handle<Name> name,
|
||||
// might deprecate the current map again, if value does not fit.
|
||||
if (MigrateDeprecated(isolate(), object)) {
|
||||
PropertyKey key(isolate(), name);
|
||||
LookupIterator it(isolate(), object, key);
|
||||
LookupIterator it(
|
||||
isolate(), object, key,
|
||||
IsAnyStoreOwn() ? LookupIterator::OWN : LookupIterator::DEFAULT);
|
||||
DCHECK_IMPLIES(IsStoreOwnIC(), it.IsFound() && it.HolderIsReceiver());
|
||||
MAYBE_RETURN_NULL(Object::SetProperty(&it, value, StoreOrigin::kNamed));
|
||||
return value;
|
||||
}
|
||||
@ -1745,14 +1749,22 @@ MaybeHandle<Object> StoreIC::Store(Handle<Object> object, Handle<Name> name,
|
||||
|
||||
JSObject::MakePrototypesFast(object, kStartAtPrototype, isolate());
|
||||
PropertyKey key(isolate(), name);
|
||||
LookupIterator it(isolate(), object, key);
|
||||
LookupIterator it(
|
||||
isolate(), object, key,
|
||||
IsAnyStoreOwn() ? LookupIterator::OWN : LookupIterator::DEFAULT);
|
||||
|
||||
if (name->IsPrivate()) {
|
||||
if (name->IsPrivateName() && !it.IsFound()) {
|
||||
bool exists = it.IsFound();
|
||||
if (name->IsPrivateName() && exists == IsDefineOwnIC()) {
|
||||
Handle<String> name_string(
|
||||
String::cast(Symbol::cast(*name).description()), isolate());
|
||||
return TypeError(MessageTemplate::kInvalidPrivateMemberWrite, object,
|
||||
name_string);
|
||||
if (exists) {
|
||||
return TypeError(MessageTemplate::kInvalidPrivateFieldReitialization,
|
||||
object, name_string);
|
||||
} else {
|
||||
return TypeError(MessageTemplate::kInvalidPrivateMemberWrite, object,
|
||||
name_string);
|
||||
}
|
||||
}
|
||||
|
||||
// IC handling of private fields/symbols stores on JSProxy is not
|
||||
@ -2332,8 +2344,11 @@ MaybeHandle<Object> KeyedStoreIC::Store(Handle<Object> object,
|
||||
Handle<Object> result;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate(), result,
|
||||
Runtime::SetObjectProperty(isolate(), object, key, value,
|
||||
StoreOrigin::kMaybeKeyed),
|
||||
IsDefineOwnIC()
|
||||
? Runtime::DefineClassField(isolate(), object, key, value,
|
||||
StoreOrigin::kMaybeKeyed)
|
||||
: Runtime::SetObjectProperty(isolate(), object, key, value,
|
||||
StoreOrigin::kMaybeKeyed),
|
||||
Object);
|
||||
return result;
|
||||
}
|
||||
@ -2684,6 +2699,34 @@ RUNTIME_FUNCTION(Runtime_StoreIC_Miss) {
|
||||
RETURN_RESULT_OR_FAILURE(isolate, ic.Store(receiver, key, value));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_StoreOwnIC_Miss) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(5, args.length());
|
||||
// Runtime functions don't follow the IC's calling convention.
|
||||
Handle<Object> value = args.at(0);
|
||||
Handle<TaggedIndex> slot = args.at<TaggedIndex>(1);
|
||||
Handle<HeapObject> maybe_vector = args.at<HeapObject>(2);
|
||||
Handle<Object> receiver = args.at(3);
|
||||
Handle<Name> key = args.at<Name>(4);
|
||||
|
||||
FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value());
|
||||
|
||||
// When there is no feedback vector it is OK to use the StoreOwnNamed
|
||||
// feedback kind. There _should_ be a vector, though.
|
||||
FeedbackSlotKind kind = FeedbackSlotKind::kStoreOwnNamed;
|
||||
Handle<FeedbackVector> vector = Handle<FeedbackVector>();
|
||||
if (!maybe_vector->IsUndefined()) {
|
||||
DCHECK(maybe_vector->IsFeedbackVector());
|
||||
vector = Handle<FeedbackVector>::cast(maybe_vector);
|
||||
kind = vector->GetKind(vector_slot);
|
||||
}
|
||||
|
||||
DCHECK(IsStoreOwnICKind(kind));
|
||||
StoreIC ic(isolate, vector, vector_slot, kind);
|
||||
ic.UpdateState(receiver, key);
|
||||
RETURN_RESULT_OR_FAILURE(isolate, ic.Store(receiver, key, value));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_StoreGlobalIC_Miss) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(4, args.length());
|
||||
@ -2795,7 +2838,7 @@ RUNTIME_FUNCTION(Runtime_KeyedStoreIC_Miss) {
|
||||
|
||||
// The elements store stubs miss into this function, but they are shared by
|
||||
// different ICs.
|
||||
if (IsKeyedStoreICKind(kind)) {
|
||||
if (IsKeyedStoreICKind(kind) || IsKeyedDefineOwnICKind(kind)) {
|
||||
KeyedStoreIC ic(isolate, vector, vector_slot, kind);
|
||||
ic.UpdateState(receiver, key);
|
||||
RETURN_RESULT_OR_FAILURE(isolate, ic.Store(receiver, key, value));
|
||||
@ -2810,6 +2853,33 @@ RUNTIME_FUNCTION(Runtime_KeyedStoreIC_Miss) {
|
||||
}
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_KeyedDefineOwnIC_Miss) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(5, args.length());
|
||||
// Runtime functions don't follow the IC's calling convention.
|
||||
Handle<Object> value = args.at(0);
|
||||
Handle<TaggedIndex> slot = args.at<TaggedIndex>(1);
|
||||
Handle<HeapObject> maybe_vector = args.at<HeapObject>(2);
|
||||
Handle<Object> receiver = args.at(3);
|
||||
Handle<Object> key = args.at(4);
|
||||
FeedbackSlot vector_slot = FeedbackVector::ToSlot(slot->value());
|
||||
|
||||
FeedbackSlotKind kind = FeedbackSlotKind::kDefineOwnKeyed;
|
||||
Handle<FeedbackVector> vector = Handle<FeedbackVector>();
|
||||
if (!maybe_vector->IsUndefined()) {
|
||||
DCHECK(maybe_vector->IsFeedbackVector());
|
||||
vector = Handle<FeedbackVector>::cast(maybe_vector);
|
||||
kind = vector->GetKind(vector_slot);
|
||||
DCHECK(IsDefineOwnICKind(kind));
|
||||
}
|
||||
|
||||
// The elements store stubs miss into this function, but they are shared by
|
||||
// different ICs.
|
||||
KeyedStoreIC ic(isolate, vector, vector_slot, kind);
|
||||
ic.UpdateState(receiver, key);
|
||||
RETURN_RESULT_OR_FAILURE(isolate, ic.Store(receiver, key, value));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_StoreInArrayLiteralIC_Miss) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(5, args.length());
|
||||
|
@ -54,8 +54,10 @@ class IC {
|
||||
}
|
||||
bool IsAnyStore() const {
|
||||
return IsStoreIC() || IsStoreOwnIC() || IsStoreGlobalIC() ||
|
||||
IsKeyedStoreIC() || IsStoreInArrayLiteralICKind(kind());
|
||||
IsKeyedStoreIC() || IsStoreInArrayLiteralICKind(kind()) ||
|
||||
IsDefineOwnIC();
|
||||
}
|
||||
bool IsAnyStoreOwn() const { return IsStoreOwnIC() || IsDefineOwnIC(); }
|
||||
|
||||
static inline bool IsHandler(MaybeObject object);
|
||||
|
||||
@ -119,11 +121,13 @@ class IC {
|
||||
bool IsStoreGlobalIC() const { return IsStoreGlobalICKind(kind_); }
|
||||
bool IsStoreIC() const { return IsStoreICKind(kind_); }
|
||||
bool IsStoreOwnIC() const { return IsStoreOwnICKind(kind_); }
|
||||
bool IsDefineOwnIC() const { return IsDefineOwnICKind(kind_); }
|
||||
bool IsKeyedStoreIC() const { return IsKeyedStoreICKind(kind_); }
|
||||
bool IsKeyedHasIC() const { return IsKeyedHasICKind(kind_); }
|
||||
bool is_keyed() const {
|
||||
return IsKeyedLoadIC() || IsKeyedStoreIC() ||
|
||||
IsStoreInArrayLiteralICKind(kind_) || IsKeyedHasIC();
|
||||
IsStoreInArrayLiteralICKind(kind_) || IsKeyedHasIC() ||
|
||||
IsKeyedDefineOwnICKind(kind_);
|
||||
}
|
||||
bool ShouldRecomputeHandler(Handle<String> name);
|
||||
|
||||
|
@ -16,7 +16,22 @@
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
enum class StoreMode { kOrdinary, kInLiteral };
|
||||
enum class StoreMode {
|
||||
kOrdinary,
|
||||
kInLiteral,
|
||||
|
||||
// kStoreOwn performs an ordinary property store without traversing the
|
||||
// prototype chain. In the case of private fields, it will throw if the
|
||||
// field does not already exist.
|
||||
// kDefineOwn is similar to kStoreOwn, but for private class fields, it
|
||||
// will throw if the field does already exist.
|
||||
kStoreOwn,
|
||||
kDefineOwn
|
||||
};
|
||||
|
||||
// With private symbols, 'define' semantics will throw if the field already
|
||||
// exists, while 'update' semantics will throw if the field does not exist.
|
||||
enum class PrivateNameSemantics { kUpdate, kDefine };
|
||||
|
||||
class KeyedStoreGenericAssembler : public AccessorAssembler {
|
||||
public:
|
||||
@ -131,14 +146,16 @@ class KeyedStoreGenericAssembler : public AccessorAssembler {
|
||||
|
||||
bool IsKeyedStore() const { return mode_ == StoreMode::kOrdinary; }
|
||||
bool IsStoreInLiteral() const { return mode_ == StoreMode::kInLiteral; }
|
||||
bool IsKeyedStoreOwn() const { return mode_ == StoreMode::kStoreOwn; }
|
||||
bool IsKeyedDefineOwn() const { return mode_ == StoreMode::kDefineOwn; }
|
||||
|
||||
bool ShouldCheckPrototype() const { return IsKeyedStore(); }
|
||||
bool ShouldReconfigureExisting() const { return IsStoreInLiteral(); }
|
||||
bool ShouldCallSetter() const { return IsKeyedStore(); }
|
||||
bool ShouldCallSetter() const { return IsKeyedStore() || IsKeyedStoreOwn(); }
|
||||
bool ShouldCheckPrototypeValidity() const {
|
||||
// We don't do this for "in-literal" stores, because it is impossible for
|
||||
// the target object to be a "prototype"
|
||||
return !IsStoreInLiteral();
|
||||
return !IsStoreInLiteral() && !IsKeyedStoreOwn();
|
||||
}
|
||||
};
|
||||
|
||||
@ -147,11 +164,23 @@ void KeyedStoreGenericGenerator::Generate(compiler::CodeAssemblerState* state) {
|
||||
assembler.KeyedStoreGeneric();
|
||||
}
|
||||
|
||||
void KeyedDefineOwnGenericGenerator::Generate(
|
||||
compiler::CodeAssemblerState* state) {
|
||||
KeyedStoreGenericAssembler assembler(state, StoreMode::kDefineOwn);
|
||||
assembler.KeyedStoreGeneric();
|
||||
}
|
||||
|
||||
void StoreICNoFeedbackGenerator::Generate(compiler::CodeAssemblerState* state) {
|
||||
KeyedStoreGenericAssembler assembler(state, StoreMode::kOrdinary);
|
||||
assembler.StoreIC_NoFeedback();
|
||||
}
|
||||
|
||||
void StoreOwnICNoFeedbackGenerator::Generate(
|
||||
compiler::CodeAssemblerState* state) {
|
||||
KeyedStoreGenericAssembler assembler(state, StoreMode::kStoreOwn);
|
||||
assembler.StoreIC_NoFeedback();
|
||||
}
|
||||
|
||||
void KeyedStoreGenericGenerator::SetProperty(
|
||||
compiler::CodeAssemblerState* state, TNode<Context> context,
|
||||
TNode<JSReceiver> receiver, TNode<BoolT> is_simple_receiver,
|
||||
@ -783,6 +812,10 @@ void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
|
||||
|
||||
BIND(&descriptor_found);
|
||||
{
|
||||
if (IsKeyedDefineOwn()) {
|
||||
// Take slow path to throw if a private name already exists
|
||||
GotoIf(IsPrivateSymbol(name), slow);
|
||||
}
|
||||
TNode<IntPtrT> name_index = var_name_index.value();
|
||||
TNode<Uint32T> details = LoadDetailsByKeyIndex(descriptors, name_index);
|
||||
Label data_property(this);
|
||||
@ -841,6 +874,9 @@ void KeyedStoreGenericAssembler::EmitGenericPropertyStore(
|
||||
BIND(&dictionary_found);
|
||||
{
|
||||
Label check_const(this), overwrite(this), done(this);
|
||||
if (IsKeyedDefineOwn()) {
|
||||
GotoIf(IsPrivateSymbol(name), slow);
|
||||
}
|
||||
TNode<Uint32T> details =
|
||||
LoadDetailsByKeyIndex(properties, var_name_index.value());
|
||||
JumpIfDataProperty(details, &check_const,
|
||||
@ -1016,7 +1052,7 @@ void KeyedStoreGenericAssembler::KeyedStoreGeneric(
|
||||
{
|
||||
Comment("key is unique name");
|
||||
StoreICParameters p(context, receiver, var_unique.value(), value, {},
|
||||
UndefinedConstant());
|
||||
UndefinedConstant(), StoreICMode::kDefault);
|
||||
ExitPoint direct_exit(this);
|
||||
EmitGenericPropertyStore(CAST(receiver), receiver_map, &p, &direct_exit,
|
||||
&slow, language_mode);
|
||||
@ -1034,10 +1070,15 @@ void KeyedStoreGenericAssembler::KeyedStoreGeneric(
|
||||
|
||||
BIND(&slow);
|
||||
{
|
||||
if (IsKeyedStore()) {
|
||||
if (IsKeyedStore() || IsKeyedStoreOwn()) {
|
||||
Comment("KeyedStoreGeneric_slow");
|
||||
TailCallRuntime(Runtime::kSetKeyedProperty, context, receiver, key,
|
||||
value);
|
||||
} else if (IsKeyedDefineOwn()) {
|
||||
// Currently, KeyedDefineOwn ICs are only used for class instance
|
||||
// fields, hence %DefineClassField.
|
||||
TailCallRuntime(Runtime::kDefineClassField, context, receiver, key,
|
||||
value);
|
||||
} else {
|
||||
DCHECK(IsStoreInLiteral());
|
||||
TailCallRuntime(Runtime::kStoreDataPropertyInLiteral, context, receiver,
|
||||
@ -1086,16 +1127,19 @@ void KeyedStoreGenericAssembler::StoreIC_NoFeedback() {
|
||||
// checks, strings and string wrappers, proxies) are handled in the runtime.
|
||||
GotoIf(IsSpecialReceiverInstanceType(instance_type), &miss);
|
||||
{
|
||||
StoreICParameters p(context, receiver, name, value, slot,
|
||||
UndefinedConstant());
|
||||
StoreICParameters p(
|
||||
context, receiver, name, value, slot, UndefinedConstant(),
|
||||
IsKeyedStoreOwn() ? StoreICMode::kStoreOwn : StoreICMode::kDefault);
|
||||
EmitGenericPropertyStore(CAST(receiver), receiver_map, &p, &miss);
|
||||
}
|
||||
}
|
||||
|
||||
BIND(&miss);
|
||||
{
|
||||
TailCallRuntime(Runtime::kStoreIC_Miss, context, value, slot,
|
||||
UndefinedConstant(), receiver_maybe_smi, name);
|
||||
auto runtime =
|
||||
IsKeyedStoreOwn() ? Runtime::kStoreOwnIC_Miss : Runtime::kStoreIC_Miss;
|
||||
TailCallRuntime(runtime, context, value, slot, UndefinedConstant(),
|
||||
receiver_maybe_smi, name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1106,7 +1150,7 @@ void KeyedStoreGenericAssembler::SetProperty(TNode<Context> context,
|
||||
TNode<Object> value,
|
||||
LanguageMode language_mode) {
|
||||
StoreICParameters p(context, receiver, unique_name, value, {},
|
||||
UndefinedConstant());
|
||||
UndefinedConstant(), StoreICMode::kDefault);
|
||||
|
||||
Label done(this), slow(this, Label::kDeferred);
|
||||
ExitPoint exit_point(this, [&](TNode<Object> result) { Goto(&done); });
|
||||
|
@ -34,11 +34,21 @@ class KeyedStoreGenericGenerator {
|
||||
TNode<Object> value);
|
||||
};
|
||||
|
||||
class KeyedDefineOwnGenericGenerator {
|
||||
public:
|
||||
static void Generate(compiler::CodeAssemblerState* state);
|
||||
};
|
||||
|
||||
class StoreICNoFeedbackGenerator {
|
||||
public:
|
||||
static void Generate(compiler::CodeAssemblerState* state);
|
||||
};
|
||||
|
||||
class StoreOwnICNoFeedbackGenerator {
|
||||
public:
|
||||
static void Generate(compiler::CodeAssemblerState* state);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
|
@ -919,6 +919,17 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreKeyedProperty(
|
||||
return *this;
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::DefineKeyedProperty(
|
||||
Register object, Register key, int feedback_slot) {
|
||||
// Ensure that the IC uses a strict language mode, as this is the only
|
||||
// supported mode for this use case.
|
||||
DCHECK_EQ(GetLanguageModeFromSlotKind(feedback_vector_spec()->GetKind(
|
||||
FeedbackVector::ToSlot(feedback_slot))),
|
||||
LanguageMode::kStrict);
|
||||
OutputStaKeyedPropertyAsDefine(object, key, feedback_slot);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreInArrayLiteral(
|
||||
Register array, Register index, int feedback_slot) {
|
||||
OutputStaInArrayLiteral(array, index, feedback_slot);
|
||||
|
@ -187,6 +187,9 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final {
|
||||
BytecodeArrayBuilder& StoreKeyedProperty(Register object, Register key,
|
||||
int feedback_slot,
|
||||
LanguageMode language_mode);
|
||||
BytecodeArrayBuilder& DefineKeyedProperty(Register object, Register key,
|
||||
int feedback_slot);
|
||||
|
||||
// Store an own element in an array literal. The value to be stored should be
|
||||
// in the accumulator.
|
||||
BytecodeArrayBuilder& StoreInArrayLiteral(Register array, Register index,
|
||||
|
@ -2759,41 +2759,51 @@ void BytecodeGenerator::VisitClassLiteral(ClassLiteral* expr, Register name) {
|
||||
|
||||
void BytecodeGenerator::BuildClassProperty(ClassLiteral::Property* property) {
|
||||
RegisterAllocationScope register_scope(this);
|
||||
RegisterList args = register_allocator()->NewRegisterList(3);
|
||||
Register constructor = args[0], key = args[1], value = args[2];
|
||||
builder()->MoveRegister(builder()->Receiver(), constructor);
|
||||
Register key;
|
||||
|
||||
// Private methods are not initialized in BuildClassProperty.
|
||||
DCHECK_IMPLIES(property->is_private(),
|
||||
property->kind() == ClassLiteral::Property::FIELD);
|
||||
|
||||
if (property->is_computed_name()) {
|
||||
DCHECK_EQ(property->kind(), ClassLiteral::Property::FIELD);
|
||||
DCHECK(!property->is_private());
|
||||
Variable* var = property->computed_name_var();
|
||||
DCHECK_NOT_NULL(var);
|
||||
// The computed name is already evaluated and stored in a variable at class
|
||||
// definition time.
|
||||
BuildVariableLoad(var, HoleCheckMode::kElided);
|
||||
builder()->StoreAccumulatorInRegister(key);
|
||||
} else if (property->is_private()) {
|
||||
Variable* private_name_var = property->private_name_var();
|
||||
DCHECK_NOT_NULL(private_name_var);
|
||||
BuildVariableLoad(private_name_var, HoleCheckMode::kElided);
|
||||
builder()->StoreAccumulatorInRegister(key);
|
||||
} else {
|
||||
BuildLoadPropertyKey(property, key);
|
||||
bool is_literal_store = property->key()->IsPropertyName() &&
|
||||
!property->is_computed_name() &&
|
||||
!property->is_private();
|
||||
|
||||
if (!is_literal_store) {
|
||||
key = register_allocator()->NewRegister();
|
||||
if (property->is_computed_name()) {
|
||||
DCHECK_EQ(property->kind(), ClassLiteral::Property::FIELD);
|
||||
DCHECK(!property->is_private());
|
||||
Variable* var = property->computed_name_var();
|
||||
DCHECK_NOT_NULL(var);
|
||||
// The computed name is already evaluated and stored in a variable at
|
||||
// class definition time.
|
||||
BuildVariableLoad(var, HoleCheckMode::kElided);
|
||||
builder()->StoreAccumulatorInRegister(key);
|
||||
} else if (property->is_private()) {
|
||||
Variable* private_name_var = property->private_name_var();
|
||||
DCHECK_NOT_NULL(private_name_var);
|
||||
BuildVariableLoad(private_name_var, HoleCheckMode::kElided);
|
||||
builder()->StoreAccumulatorInRegister(key);
|
||||
} else {
|
||||
VisitForRegisterValue(property->key(), key);
|
||||
}
|
||||
}
|
||||
|
||||
builder()->SetExpressionAsStatementPosition(property->value());
|
||||
VisitForRegisterValue(property->value(), value);
|
||||
VisitForAccumulatorValue(property->value());
|
||||
|
||||
Runtime::FunctionId function_id =
|
||||
property->kind() == ClassLiteral::Property::FIELD &&
|
||||
!property->is_private()
|
||||
? Runtime::kCreateDataProperty
|
||||
: Runtime::kAddPrivateField;
|
||||
builder()->CallRuntime(function_id, args);
|
||||
if (is_literal_store) {
|
||||
FeedbackSlot slot = feedback_spec()->AddStoreOwnICSlot();
|
||||
builder()->StoreNamedOwnProperty(
|
||||
builder()->Receiver(),
|
||||
property->key()->AsLiteral()->AsRawPropertyName(),
|
||||
feedback_index(slot));
|
||||
} else {
|
||||
FeedbackSlot slot = feedback_spec()->AddKeyedDefineOwnICSlot();
|
||||
builder()->DefineKeyedProperty(builder()->Receiver(), key,
|
||||
feedback_index(slot));
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeGenerator::VisitInitializeClassMembersStatement(
|
||||
|
@ -151,6 +151,8 @@ namespace interpreter {
|
||||
OperandType::kReg, OperandType::kIdx, OperandType::kIdx) \
|
||||
V(StaKeyedProperty, ImplicitRegisterUse::kReadWriteAccumulator, \
|
||||
OperandType::kReg, OperandType::kReg, OperandType::kIdx) \
|
||||
V(StaKeyedPropertyAsDefine, ImplicitRegisterUse::kReadWriteAccumulator, \
|
||||
OperandType::kReg, OperandType::kReg, OperandType::kIdx) \
|
||||
V(StaInArrayLiteral, ImplicitRegisterUse::kReadWriteAccumulator, \
|
||||
OperandType::kReg, OperandType::kReg, OperandType::kIdx) \
|
||||
V(StaDataPropertyInLiteral, ImplicitRegisterUse::kReadAccumulator, \
|
||||
|
@ -632,7 +632,7 @@ IGNITION_HANDLER(StaNamedProperty, InterpreterStoreNamedPropertyAssembler) {
|
||||
// the name in constant pool entry <name_index> with the value in the
|
||||
// accumulator.
|
||||
IGNITION_HANDLER(StaNamedOwnProperty, InterpreterStoreNamedPropertyAssembler) {
|
||||
Callable ic = CodeFactory::StoreOwnICInOptimizedCode(isolate());
|
||||
Callable ic = Builtins::CallableFor(isolate(), Builtin::kStoreOwnIC);
|
||||
StaNamedProperty(ic, NamedPropertyType::kOwn);
|
||||
}
|
||||
|
||||
@ -659,6 +659,33 @@ IGNITION_HANDLER(StaKeyedProperty, InterpreterAssembler) {
|
||||
Dispatch();
|
||||
}
|
||||
|
||||
// StaKeyedPropertyAsDefine <object> <key> <slot>
|
||||
//
|
||||
// Calls the KeyedDefineOwnIC at FeedbackVector slot <slot> for <object> and
|
||||
// the key <key> with the value in the accumulator.
|
||||
//
|
||||
// This is similar to StaKeyedProperty, but avoids checking the prototype chain,
|
||||
// and in the case of private names, throws if the private name already exists.
|
||||
IGNITION_HANDLER(StaKeyedPropertyAsDefine, InterpreterAssembler) {
|
||||
TNode<Object> object = LoadRegisterAtOperandIndex(0);
|
||||
TNode<Object> name = LoadRegisterAtOperandIndex(1);
|
||||
TNode<Object> value = GetAccumulator();
|
||||
TNode<TaggedIndex> slot = BytecodeOperandIdxTaggedIndex(2);
|
||||
TNode<HeapObject> maybe_vector = LoadFeedbackVector();
|
||||
TNode<Context> context = GetContext();
|
||||
|
||||
TVARIABLE(Object, var_result);
|
||||
var_result = CallBuiltin(Builtin::kKeyedDefineOwnIC, context, object, name,
|
||||
value, slot, maybe_vector);
|
||||
// To avoid special logic in the deoptimizer to re-materialize the value in
|
||||
// the accumulator, we overwrite the accumulator after the IC call. It
|
||||
// doesn't really matter what we write to the accumulator here, since we
|
||||
// restore to the correct value on the outside. Storing the result means we
|
||||
// don't need to keep unnecessary state alive across the callstub.
|
||||
SetAccumulator(var_result.value());
|
||||
Dispatch();
|
||||
}
|
||||
|
||||
// StaInArrayLiteral <array> <index> <slot>
|
||||
//
|
||||
// Calls the StoreInArrayLiteralIC at FeedbackVector slot <slot> for <array> and
|
||||
|
@ -83,6 +83,7 @@ int FeedbackMetadata::GetSlotSize(FeedbackSlotKind kind) {
|
||||
case FeedbackSlotKind::kStoreNamedSloppy:
|
||||
case FeedbackSlotKind::kStoreNamedStrict:
|
||||
case FeedbackSlotKind::kStoreOwnNamed:
|
||||
case FeedbackSlotKind::kDefineOwnKeyed:
|
||||
case FeedbackSlotKind::kStoreGlobalSloppy:
|
||||
case FeedbackSlotKind::kStoreGlobalStrict:
|
||||
case FeedbackSlotKind::kStoreKeyedSloppy:
|
||||
|
@ -164,6 +164,8 @@ const char* FeedbackMetadata::Kind2String(FeedbackSlotKind kind) {
|
||||
return "StoreNamedStrict";
|
||||
case FeedbackSlotKind::kStoreOwnNamed:
|
||||
return "StoreOwnNamed";
|
||||
case FeedbackSlotKind::kDefineOwnKeyed:
|
||||
return "DefineOwnKeyed";
|
||||
case FeedbackSlotKind::kStoreGlobalSloppy:
|
||||
return "StoreGlobalSloppy";
|
||||
case FeedbackSlotKind::kStoreGlobalStrict:
|
||||
@ -304,6 +306,7 @@ Handle<FeedbackVector> FeedbackVector::New(
|
||||
case FeedbackSlotKind::kStoreNamedSloppy:
|
||||
case FeedbackSlotKind::kStoreNamedStrict:
|
||||
case FeedbackSlotKind::kStoreOwnNamed:
|
||||
case FeedbackSlotKind::kDefineOwnKeyed:
|
||||
case FeedbackSlotKind::kStoreKeyedSloppy:
|
||||
case FeedbackSlotKind::kStoreKeyedStrict:
|
||||
case FeedbackSlotKind::kStoreInArrayLiteral:
|
||||
@ -608,6 +611,7 @@ void FeedbackNexus::ConfigureUninitialized() {
|
||||
case FeedbackSlotKind::kStoreKeyedStrict:
|
||||
case FeedbackSlotKind::kStoreInArrayLiteral:
|
||||
case FeedbackSlotKind::kStoreOwnNamed:
|
||||
case FeedbackSlotKind::kDefineOwnKeyed:
|
||||
case FeedbackSlotKind::kLoadProperty:
|
||||
case FeedbackSlotKind::kLoadKeyed:
|
||||
case FeedbackSlotKind::kHasKeyed:
|
||||
@ -646,6 +650,7 @@ bool FeedbackNexus::Clear() {
|
||||
case FeedbackSlotKind::kStoreKeyedStrict:
|
||||
case FeedbackSlotKind::kStoreInArrayLiteral:
|
||||
case FeedbackSlotKind::kStoreOwnNamed:
|
||||
case FeedbackSlotKind::kDefineOwnKeyed:
|
||||
case FeedbackSlotKind::kLoadProperty:
|
||||
case FeedbackSlotKind::kLoadKeyed:
|
||||
case FeedbackSlotKind::kHasKeyed:
|
||||
@ -742,6 +747,7 @@ InlineCacheState FeedbackNexus::ic_state() const {
|
||||
case FeedbackSlotKind::kStoreKeyedStrict:
|
||||
case FeedbackSlotKind::kStoreInArrayLiteral:
|
||||
case FeedbackSlotKind::kStoreOwnNamed:
|
||||
case FeedbackSlotKind::kDefineOwnKeyed:
|
||||
case FeedbackSlotKind::kLoadProperty:
|
||||
case FeedbackSlotKind::kLoadKeyed:
|
||||
case FeedbackSlotKind::kHasKeyed: {
|
||||
@ -768,7 +774,7 @@ InlineCacheState FeedbackNexus::ic_state() const {
|
||||
}
|
||||
if (heap_object.IsName()) {
|
||||
DCHECK(IsKeyedLoadICKind(kind()) || IsKeyedStoreICKind(kind()) ||
|
||||
IsKeyedHasICKind(kind()));
|
||||
IsKeyedHasICKind(kind()) || IsDefineOwnICKind(kind()));
|
||||
Object extra_object = extra->GetHeapObjectAssumeStrong();
|
||||
WeakFixedArray extra_array = WeakFixedArray::cast(extra_object);
|
||||
return extra_array.length() > 2 ? POLYMORPHIC : MONOMORPHIC;
|
||||
@ -1146,7 +1152,7 @@ MaybeObjectHandle FeedbackNexus::FindHandlerForMap(Handle<Map> map) const {
|
||||
|
||||
Name FeedbackNexus::GetName() const {
|
||||
if (IsKeyedStoreICKind(kind()) || IsKeyedLoadICKind(kind()) ||
|
||||
IsKeyedHasICKind(kind())) {
|
||||
IsKeyedHasICKind(kind()) || IsKeyedDefineOwnICKind(kind())) {
|
||||
MaybeObject feedback = GetFeedback();
|
||||
if (IsPropertyNameFeedback(feedback)) {
|
||||
return Name::cast(feedback->GetHeapObjectAssumeStrong());
|
||||
@ -1229,7 +1235,7 @@ KeyedAccessStoreMode KeyedAccessStoreModeForBuiltin(Builtin builtin) {
|
||||
|
||||
KeyedAccessStoreMode FeedbackNexus::GetKeyedAccessStoreMode() const {
|
||||
DCHECK(IsKeyedStoreICKind(kind()) || IsStoreInArrayLiteralICKind(kind()) ||
|
||||
IsStoreDataPropertyInLiteralKind(kind()));
|
||||
IsStoreDataPropertyInLiteralKind(kind()) || IsDefineOwnICKind(kind()));
|
||||
KeyedAccessStoreMode mode = STANDARD_STORE;
|
||||
|
||||
if (GetKeyType() == PROPERTY) return mode;
|
||||
@ -1264,6 +1270,10 @@ KeyedAccessStoreMode FeedbackNexus::GetKeyedAccessStoreMode() const {
|
||||
mode = StoreHandler::GetKeyedAccessStoreMode(*maybe_code_handler);
|
||||
if (mode != STANDARD_STORE) return mode;
|
||||
continue;
|
||||
} else if (IsDefineOwnICKind(kind())) {
|
||||
mode = StoreHandler::GetKeyedAccessStoreMode(*maybe_code_handler);
|
||||
if (mode != STANDARD_STORE) return mode;
|
||||
continue;
|
||||
} else {
|
||||
// Element store without prototype chain check.
|
||||
if (V8_EXTERNAL_CODE_SPACE_BOOL) {
|
||||
@ -1289,7 +1299,7 @@ KeyedAccessStoreMode FeedbackNexus::GetKeyedAccessStoreMode() const {
|
||||
IcCheckType FeedbackNexus::GetKeyType() const {
|
||||
DCHECK(IsKeyedStoreICKind(kind()) || IsKeyedLoadICKind(kind()) ||
|
||||
IsStoreInArrayLiteralICKind(kind()) || IsKeyedHasICKind(kind()) ||
|
||||
IsStoreDataPropertyInLiteralKind(kind()));
|
||||
IsStoreDataPropertyInLiteralKind(kind()) || IsDefineOwnICKind(kind()));
|
||||
auto pair = GetFeedbackPair();
|
||||
MaybeObject feedback = pair.first;
|
||||
if (feedback == MegamorphicSentinel()) {
|
||||
@ -1297,7 +1307,9 @@ IcCheckType FeedbackNexus::GetKeyType() const {
|
||||
Smi::ToInt(pair.second->template cast<Object>()));
|
||||
}
|
||||
MaybeObject maybe_name =
|
||||
IsStoreDataPropertyInLiteralKind(kind()) ? pair.second : feedback;
|
||||
IsStoreDataPropertyInLiteralKind(kind()) || IsDefineOwnICKind(kind())
|
||||
? pair.second
|
||||
: feedback;
|
||||
return IsPropertyNameFeedback(maybe_name) ? PROPERTY : ELEMENT;
|
||||
}
|
||||
|
||||
@ -1450,7 +1462,7 @@ FeedbackIterator::FeedbackIterator(const FeedbackNexus* nexus)
|
||||
IsKeyedStoreICKind(nexus->kind()) || IsStoreOwnICKind(nexus->kind()) ||
|
||||
IsStoreDataPropertyInLiteralKind(nexus->kind()) ||
|
||||
IsStoreInArrayLiteralICKind(nexus->kind()) ||
|
||||
IsKeyedHasICKind(nexus->kind()));
|
||||
IsKeyedHasICKind(nexus->kind()) || IsDefineOwnICKind(nexus->kind()));
|
||||
|
||||
DisallowGarbageCollection no_gc;
|
||||
auto pair = nexus->GetFeedbackPair();
|
||||
|
@ -50,6 +50,7 @@ enum class FeedbackSlotKind : uint8_t {
|
||||
kStoreGlobalStrict,
|
||||
kStoreNamedStrict,
|
||||
kStoreOwnNamed,
|
||||
kDefineOwnKeyed,
|
||||
kStoreKeyedStrict,
|
||||
kStoreInArrayLiteral,
|
||||
kBinaryOp,
|
||||
@ -102,6 +103,14 @@ inline bool IsStoreOwnICKind(FeedbackSlotKind kind) {
|
||||
return kind == FeedbackSlotKind::kStoreOwnNamed;
|
||||
}
|
||||
|
||||
inline bool IsKeyedDefineOwnICKind(FeedbackSlotKind kind) {
|
||||
return kind == FeedbackSlotKind::kDefineOwnKeyed;
|
||||
}
|
||||
|
||||
inline bool IsDefineOwnICKind(FeedbackSlotKind kind) {
|
||||
return IsKeyedDefineOwnICKind(kind);
|
||||
}
|
||||
|
||||
inline bool IsStoreDataPropertyInLiteralKind(FeedbackSlotKind kind) {
|
||||
return kind == FeedbackSlotKind::kStoreDataPropertyInLiteral;
|
||||
}
|
||||
@ -136,7 +145,8 @@ inline TypeofMode GetTypeofModeFromSlotKind(FeedbackSlotKind kind) {
|
||||
|
||||
inline LanguageMode GetLanguageModeFromSlotKind(FeedbackSlotKind kind) {
|
||||
DCHECK(IsStoreICKind(kind) || IsStoreOwnICKind(kind) ||
|
||||
IsStoreGlobalICKind(kind) || IsKeyedStoreICKind(kind));
|
||||
IsStoreGlobalICKind(kind) || IsKeyedStoreICKind(kind) ||
|
||||
IsDefineOwnICKind(kind));
|
||||
STATIC_ASSERT(FeedbackSlotKind::kStoreGlobalSloppy <=
|
||||
FeedbackSlotKind::kLastSloppyKind);
|
||||
STATIC_ASSERT(FeedbackSlotKind::kStoreKeyedSloppy <=
|
||||
@ -424,6 +434,12 @@ class V8_EXPORT_PRIVATE FeedbackVectorSpec {
|
||||
return AddSlot(FeedbackSlotKind::kStoreOwnNamed);
|
||||
}
|
||||
|
||||
// Identical to StoreOwnKeyed, but will throw if a private field already
|
||||
// exists.
|
||||
FeedbackSlot AddKeyedDefineOwnICSlot() {
|
||||
return AddSlot(FeedbackSlotKind::kDefineOwnKeyed);
|
||||
}
|
||||
|
||||
FeedbackSlot AddStoreGlobalICSlot(LanguageMode language_mode) {
|
||||
STATIC_ASSERT(LanguageModeSize == 2);
|
||||
return AddSlot(is_strict(language_mode)
|
||||
|
@ -552,6 +552,41 @@ MaybeHandle<Object> Runtime::SetObjectProperty(
|
||||
return value;
|
||||
}
|
||||
|
||||
MaybeHandle<Object> Runtime::DefineClassField(Isolate* isolate,
|
||||
Handle<Object> object,
|
||||
Handle<Object> key,
|
||||
Handle<Object> value,
|
||||
StoreOrigin store_origin,
|
||||
Maybe<ShouldThrow> should_throw) {
|
||||
if (object->IsNullOrUndefined(isolate)) {
|
||||
THROW_NEW_ERROR(
|
||||
isolate,
|
||||
NewTypeError(MessageTemplate::kNonObjectPropertyStore, key, object),
|
||||
Object);
|
||||
}
|
||||
|
||||
// Check if the given key is an array index.
|
||||
bool success = false;
|
||||
PropertyKey lookup_key(isolate, key, &success);
|
||||
if (!success) return MaybeHandle<Object>();
|
||||
LookupIterator it(isolate, object, lookup_key, LookupIterator::OWN);
|
||||
|
||||
if (it.IsFound() && key->IsSymbol() && Symbol::cast(*key).is_private_name()) {
|
||||
Handle<Object> name_string(Symbol::cast(*key).description(), isolate);
|
||||
DCHECK(name_string->IsString());
|
||||
THROW_NEW_ERROR(
|
||||
isolate,
|
||||
NewTypeError(MessageTemplate::kInvalidPrivateFieldReitialization,
|
||||
name_string),
|
||||
Object);
|
||||
}
|
||||
|
||||
MAYBE_RETURN_NULL(
|
||||
Object::SetProperty(&it, value, store_origin, should_throw));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_InternalSetPrototype) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(2, args.length());
|
||||
@ -822,6 +857,19 @@ RUNTIME_FUNCTION(Runtime_SetKeyedProperty) {
|
||||
StoreOrigin::kMaybeKeyed));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DefineClassField) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(3, args.length());
|
||||
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, value, 2);
|
||||
|
||||
RETURN_RESULT_OR_FAILURE(
|
||||
isolate, Runtime::DefineClassField(isolate, object, key, value,
|
||||
StoreOrigin::kMaybeKeyed));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_SetNamedProperty) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(3, args.length());
|
||||
|
@ -333,6 +333,7 @@ namespace internal {
|
||||
F(OptimizeObjectForAddingMultipleProperties, 2, 1) \
|
||||
F(SetDataProperties, 2, 1) \
|
||||
F(SetKeyedProperty, 3, 1) \
|
||||
F(DefineClassField, 3, 1) \
|
||||
F(SetNamedProperty, 3, 1) \
|
||||
F(SetOwnPropertyIgnoreAttributes, 4, 1) \
|
||||
F(StoreDataPropertyInLiteral, 3, 1) \
|
||||
@ -634,6 +635,7 @@ namespace internal {
|
||||
F(ElementsTransitionAndStoreIC_Miss, 6, 1) \
|
||||
F(KeyedLoadIC_Miss, 4, 1) \
|
||||
F(KeyedStoreIC_Miss, 5, 1) \
|
||||
F(KeyedDefineOwnIC_Miss, 5, 1) \
|
||||
F(StoreInArrayLiteralIC_Miss, 5, 1) \
|
||||
F(KeyedStoreIC_Slow, 3, 1) \
|
||||
F(LoadElementWithInterceptor, 2, 1) \
|
||||
@ -649,6 +651,7 @@ namespace internal {
|
||||
F(StoreGlobalICNoFeedback_Miss, 2, 1) \
|
||||
F(StoreGlobalIC_Slow, 5, 1) \
|
||||
F(StoreIC_Miss, 5, 1) \
|
||||
F(StoreOwnIC_Miss, 5, 1) \
|
||||
F(StoreInArrayLiteralIC_Slow, 5, 1) \
|
||||
F(StorePropertyWithInterceptor, 5, 1) \
|
||||
F(CloneObjectIC_Miss, 4, 1) \
|
||||
@ -794,6 +797,11 @@ class Runtime : public AllStatic {
|
||||
Handle<Object> value, StoreOrigin store_origin,
|
||||
Maybe<ShouldThrow> should_throw = Nothing<ShouldThrow>());
|
||||
|
||||
V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT static MaybeHandle<Object>
|
||||
DefineClassField(Isolate* isolate, Handle<Object> object, Handle<Object> key,
|
||||
Handle<Object> value, StoreOrigin store_origin,
|
||||
Maybe<ShouldThrow> should_throw = Nothing<ShouldThrow>());
|
||||
|
||||
// When "receiver" is not passed, it defaults to "lookup_start_object".
|
||||
V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT static MaybeHandle<Object>
|
||||
GetObjectProperty(Isolate* isolate, Handle<Object> lookup_start_object,
|
||||
|
@ -0,0 +1,60 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --opt
|
||||
let privateSymbol = %CreatePrivateSymbol("private");
|
||||
let privateName = %CreatePrivateNameSymbol("privateName");
|
||||
|
||||
function test() {
|
||||
"use strict";
|
||||
|
||||
// These computed properties are translated into JSStoreDataPropertyInLiteral
|
||||
// ops, and AccessInfoFactory::ComputePropertyAccessInfo should find a
|
||||
// suitable map transition when optimizing. Even if the implementation details
|
||||
// are ignored, we still want to assert that these properties are installed as
|
||||
// non-enumerable, due to being private symbols.
|
||||
|
||||
let obj = {
|
||||
[privateSymbol]: "private",
|
||||
[privateName]: "privateName",
|
||||
};
|
||||
|
||||
assertPropertiesEqual(Object.getOwnPropertyDescriptor(obj, privateSymbol), {
|
||||
value: "private",
|
||||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
});
|
||||
|
||||
assertPropertiesEqual(Object.getOwnPropertyDescriptor(obj, privateName), {
|
||||
value: "privateName",
|
||||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
});
|
||||
|
||||
// They don't leak via Object.keys(), Object.getOwnPropertyNames, or
|
||||
// Object.getOwnPropertySymbols
|
||||
let props = Object.getOwnPropertyNames(obj);
|
||||
assertFalse(props.includes(privateSymbol));
|
||||
assertFalse(props.includes(privateName));
|
||||
|
||||
let symbols = Object.getOwnPropertySymbols(obj);
|
||||
assertFalse(symbols.includes(privateSymbol));
|
||||
assertFalse(symbols.includes(privateName));
|
||||
|
||||
let keys = Object.keys(obj);
|
||||
assertFalse(keys.includes(privateSymbol));
|
||||
assertFalse(keys.includes(privateName));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(test);
|
||||
test();
|
||||
test();
|
||||
test();
|
||||
%OptimizeFunctionOnNextCall(test);
|
||||
test();
|
||||
assertOptimized(test);
|
103
test/mjsunit/harmony/define-private-class-field-stress.js
Normal file
103
test/mjsunit/harmony/define-private-class-field-stress.js
Normal file
@ -0,0 +1,103 @@
|
||||
// Copyright 2021 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
//
|
||||
// Flags: --allow-natives-syntax --opt
|
||||
|
||||
// Single structure, field is inline in the object
|
||||
{
|
||||
class C {
|
||||
#x;
|
||||
constructor(x) { this.#x = x; }
|
||||
x() { return this.#x; }
|
||||
}
|
||||
%PrepareFunctionForOptimization(C);
|
||||
assertSame(new C(1).x(), 1);
|
||||
assertSame(new C(2).x(), 2);
|
||||
assertSame(new C(3).x(), 3);
|
||||
assertSame(new C(4).x(), 4);
|
||||
assertSame(new C(5).x(), 5);
|
||||
%OptimizeFunctionOnNextCall(C);
|
||||
assertSame(new C(10).x(), 10);
|
||||
}
|
||||
|
||||
// Single structure, field is out of line
|
||||
{
|
||||
class C {
|
||||
a = (() => this.z = this.q = this.alphabet =
|
||||
this[Symbol.toStringTag] = this["ninteen eighty four"] = 42)()
|
||||
b = (() => this[47] = this.extra = this.fortran = this.derble =
|
||||
this["high quality zebras"] = 73)()
|
||||
c = (() => this.moreprops = this.jimbo = this["flabberghast"] = 198)()
|
||||
#x;
|
||||
constructor(x) { this.#x = x; }
|
||||
x() { return this.#x; }
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(C);
|
||||
assertSame(new C(1).x(), 1);
|
||||
assertSame(new C(6000).x(), 6000);
|
||||
assertSame(new C(37).x(), 37);
|
||||
assertSame(new C(-1).x(), -1);
|
||||
assertSame(new C(4900).x(), 4900);
|
||||
%OptimizeFunctionOnNextCall(C);
|
||||
assertSame(new C(9999).x(), 9999);
|
||||
}
|
||||
|
||||
// Multiple structures, field is inline (probably)
|
||||
{
|
||||
let i = 0;
|
||||
class C {
|
||||
a = (() => {
|
||||
if (i > 1000)
|
||||
this.tenEtwo = i;
|
||||
if ((i & 0x7F) > 64)
|
||||
this.boxing = i;
|
||||
if (i > 9000)
|
||||
this["It's over nine thousand!"] = i;
|
||||
})()
|
||||
#x;
|
||||
constructor(x) { this.#x = x; }
|
||||
x() { return this.#x; }
|
||||
}
|
||||
%PrepareFunctionForOptimization(C);
|
||||
assertSame(new C(1).x(), 1);
|
||||
assertSame(new C(256).x(), 256);
|
||||
assertSame(new C(36).x(), 36);
|
||||
assertSame(new C(16384).x(), 16384);
|
||||
assertSame(new C(17).x(), 17);
|
||||
%OptimizeFunctionOnNextCall(C);
|
||||
assertSame(new C(74).x(), 74);
|
||||
}
|
||||
|
||||
// Multiple structures, field should be out of object
|
||||
{
|
||||
let i = 0;
|
||||
class C {
|
||||
a = (() => this.z = this.q = this.alphabet =
|
||||
this[Symbol.toStringTag] = this["ninteen eighty four"] = 42)()
|
||||
b = (() => this[47] = this.extra = this.fortran = this.derble =
|
||||
this["high quality zebras"] = 73)()
|
||||
c = (() => this.moreprops = this.jimbo = this["flabberghast"] = 198)()
|
||||
d = (() => {
|
||||
if (i > 1000)
|
||||
this.tenEtwo = i;
|
||||
if ((i & 0x7F) > 64)
|
||||
this.boxing = i;
|
||||
if (i > 9000)
|
||||
this["It's over nine thousand again!"] = i;
|
||||
})()
|
||||
#x;
|
||||
constructor(x) { this.#x = x; }
|
||||
x() { return this.#x; }
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(C);
|
||||
assertSame(new C(1).x(), 1);
|
||||
assertSame(new C(38000).x(), 38000);
|
||||
assertSame(new C(9080).x(), 9080);
|
||||
assertSame(new C(92).x(), 92);
|
||||
assertSame(new C(4000).x(), 4000);
|
||||
%OptimizeFunctionOnNextCall(C);
|
||||
assertSame(new C(10000).x(), 10000);
|
||||
}
|
@ -151,6 +151,7 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
|
||||
.StoreKeyedProperty(reg, reg, strict_keyed_store_slot.ToInt(),
|
||||
LanguageMode::kStrict)
|
||||
.StoreNamedOwnProperty(reg, name, store_own_slot.ToInt())
|
||||
.DefineKeyedProperty(reg, reg, store_own_slot.ToInt())
|
||||
.StoreInArrayLiteral(reg, reg, store_array_element_slot.ToInt());
|
||||
|
||||
// Emit Iterator-protocol operations
|
||||
|
Loading…
Reference in New Issue
Block a user