summaryrefslogtreecommitdiff
path: root/deps/v8/src/interpreter/bytecode-generator.cc
diff options
context:
space:
mode:
authorMyles Borins <mylesborins@google.com>2019-09-24 11:56:38 -0400
committerMyles Borins <myles.borins@gmail.com>2019-10-07 03:19:23 -0400
commitf7f6c928c1c9c136b7926f892b8a2fda11d8b4b2 (patch)
treef5edbccb3ffda2573d70a6e291e7157f290e0ae0 /deps/v8/src/interpreter/bytecode-generator.cc
parentffd22e81983056d09c064c59343a0e488236272d (diff)
downloadandroid-node-v8-f7f6c928c1c9c136b7926f892b8a2fda11d8b4b2.tar.gz
android-node-v8-f7f6c928c1c9c136b7926f892b8a2fda11d8b4b2.tar.bz2
android-node-v8-f7f6c928c1c9c136b7926f892b8a2fda11d8b4b2.zip
deps: update V8 to 7.8.279.9
PR-URL: https://github.com/nodejs/node/pull/29694 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Jiawen Geng <technicalcute@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Ujjwal Sharma <usharma1998@gmail.com>
Diffstat (limited to 'deps/v8/src/interpreter/bytecode-generator.cc')
-rw-r--r--deps/v8/src/interpreter/bytecode-generator.cc631
1 files changed, 495 insertions, 136 deletions
diff --git a/deps/v8/src/interpreter/bytecode-generator.cc b/deps/v8/src/interpreter/bytecode-generator.cc
index d3b27b4375..6a0b02d852 100644
--- a/deps/v8/src/interpreter/bytecode-generator.cc
+++ b/deps/v8/src/interpreter/bytecode-generator.cc
@@ -14,7 +14,9 @@
#include "src/interpreter/bytecode-jump-table.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/bytecode-register-allocator.h"
+#include "src/interpreter/bytecode-register.h"
#include "src/interpreter/control-flow-builders.h"
+#include "src/logging/log.h"
#include "src/objects/debug-objects.h"
#include "src/objects/literal-objects-inl.h"
#include "src/objects/objects-inl.h"
@@ -915,39 +917,67 @@ class BytecodeGenerator::IteratorRecord final {
Register next_;
};
+class BytecodeGenerator::OptionalChainNullLabelScope final {
+ public:
+ explicit OptionalChainNullLabelScope(BytecodeGenerator* bytecode_generator)
+ : bytecode_generator_(bytecode_generator),
+ labels_(bytecode_generator->zone()) {
+ prev_ = bytecode_generator_->optional_chaining_null_labels_;
+ bytecode_generator_->optional_chaining_null_labels_ = &labels_;
+ }
+
+ ~OptionalChainNullLabelScope() {
+ bytecode_generator_->optional_chaining_null_labels_ = prev_;
+ }
+
+ BytecodeLabels* labels() { return &labels_; }
+
+ private:
+ BytecodeGenerator* bytecode_generator_;
+ BytecodeLabels labels_;
+ BytecodeLabels* prev_;
+};
+
namespace {
+template <typename PropertyT>
+struct Accessors : public ZoneObject {
+ Accessors() : getter(nullptr), setter(nullptr) {}
+ PropertyT* getter;
+ PropertyT* setter;
+};
+
// A map from property names to getter/setter pairs allocated in the zone that
// also provides a way of accessing the pairs in the order they were first
// added so that the generated bytecode is always the same.
+template <typename PropertyT>
class AccessorTable
- : public base::TemplateHashMap<Literal, ObjectLiteral::Accessors,
+ : public base::TemplateHashMap<Literal, Accessors<PropertyT>,
bool (*)(void*, void*),
ZoneAllocationPolicy> {
public:
explicit AccessorTable(Zone* zone)
- : base::TemplateHashMap<Literal, ObjectLiteral::Accessors,
+ : base::TemplateHashMap<Literal, Accessors<PropertyT>,
bool (*)(void*, void*), ZoneAllocationPolicy>(
Literal::Match, ZoneAllocationPolicy(zone)),
zone_(zone) {}
- Iterator lookup(Literal* literal) {
- Iterator it = find(literal, true, ZoneAllocationPolicy(zone_));
+ Accessors<PropertyT>* LookupOrInsert(Literal* key) {
+ auto it = this->find(key, true, ZoneAllocationPolicy(zone_));
if (it->second == nullptr) {
- it->second = new (zone_) ObjectLiteral::Accessors();
- ordered_accessors_.push_back({literal, it->second});
+ it->second = new (zone_) Accessors<PropertyT>();
+ ordered_accessors_.push_back({key, it->second});
}
- return it;
+ return it->second;
}
- const std::vector<std::pair<Literal*, ObjectLiteral::Accessors*>>&
+ const std::vector<std::pair<Literal*, Accessors<PropertyT>*>>&
ordered_accessors() {
return ordered_accessors_;
}
private:
- std::vector<std::pair<Literal*, ObjectLiteral::Accessors*>>
- ordered_accessors_;
+ std::vector<std::pair<Literal*, Accessors<PropertyT>*>> ordered_accessors_;
Zone* zone_;
};
@@ -994,6 +1024,7 @@ BytecodeGenerator::BytecodeGenerator(
execution_context_(nullptr),
execution_result_(nullptr),
incoming_new_target_or_generator_(),
+ optional_chaining_null_labels_(nullptr),
dummy_feedback_slot_(feedback_spec(), FeedbackSlotKind::kCompareOp),
generator_jump_table_(nullptr),
suspend_count_(0),
@@ -1012,7 +1043,7 @@ Handle<BytecodeArray> BytecodeGenerator::FinalizeBytecode(
#ifdef DEBUG
// Unoptimized compilation should be context-independent. Verify that we don't
// access the native context by nulling it out during finalization.
- SaveAndSwitchContext save(isolate, Context());
+ NullContextScope null_context_scope(isolate);
#endif
AllocateDeferredConstants(isolate, script);
@@ -1036,6 +1067,32 @@ Handle<BytecodeArray> BytecodeGenerator::FinalizeBytecode(
return bytecode_array;
}
+Handle<ByteArray> BytecodeGenerator::FinalizeSourcePositionTable(
+ Isolate* isolate) {
+ DCHECK_EQ(ThreadId::Current(), isolate->thread_id());
+#ifdef DEBUG
+ // Unoptimized compilation should be context-independent. Verify that we don't
+ // access the native context by nulling it out during finalization.
+ NullContextScope null_context_scope(isolate);
+#endif
+
+ Handle<ByteArray> source_position_table =
+ builder()->ToSourcePositionTable(isolate);
+
+ LOG_CODE_EVENT(isolate,
+ CodeLinePosInfoRecordEvent(
+ info_->bytecode_array()->GetFirstBytecodeAddress(),
+ *source_position_table));
+
+ return source_position_table;
+}
+
+#ifdef DEBUG
+int BytecodeGenerator::CheckBytecodeMatches(Handle<BytecodeArray> bytecode) {
+ return builder()->CheckBytecodeMatches(bytecode);
+}
+#endif
+
void BytecodeGenerator::AllocateDeferredConstants(Isolate* isolate,
Handle<Script> script) {
// Build global declaration pair arrays.
@@ -1383,8 +1440,9 @@ void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) {
BuildVariableAssignment(variable, Token::INIT, HoleCheckMode::kElided);
break;
}
- DCHECK_IMPLIES(decl->fun()->ShouldEagerCompile(),
- IsInEagerLiterals(decl->fun(), *eager_inner_literals_));
+ DCHECK_IMPLIES(
+ eager_inner_literals_ != nullptr && decl->fun()->ShouldEagerCompile(),
+ IsInEagerLiterals(decl->fun(), *eager_inner_literals_));
}
void BytecodeGenerator::VisitModuleNamespaceImports() {
@@ -1755,14 +1813,13 @@ void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) {
return;
}
- BytecodeLabel subject_null_label, subject_undefined_label;
+ BytecodeLabel subject_undefined_label;
FeedbackSlot slot = feedback_spec()->AddForInSlot();
// Prepare the state for executing ForIn.
builder()->SetExpressionAsStatementPosition(stmt->subject());
VisitForAccumulatorValue(stmt->subject());
- builder()->JumpIfUndefined(&subject_undefined_label);
- builder()->JumpIfNull(&subject_null_label);
+ builder()->JumpIfUndefinedOrNull(&subject_undefined_label);
Register receiver = register_allocator()->NewRegister();
builder()->ToObject(receiver);
@@ -1804,7 +1861,6 @@ void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) {
builder()->StoreAccumulatorInRegister(index);
loop_builder.JumpToHeader(loop_depth_);
}
- builder()->Bind(&subject_null_label);
builder()->Bind(&subject_undefined_label);
}
@@ -1978,39 +2034,6 @@ bool BytecodeGenerator::ShouldOptimizeAsOneShot() const {
info()->literal()->is_oneshot_iife();
}
-void BytecodeGenerator::BuildPrivateClassMemberNameAssignment(
- ClassLiteral::Property* property) {
- DCHECK(property->is_private());
- switch (property->kind()) {
- case ClassLiteral::Property::FIELD: {
- // Create the private name symbols for fields during class
- // evaluation and store them on the context. These will be
- // used as keys later during instance or static initialization.
- RegisterAllocationScope private_name_register_scope(this);
- Register private_name = register_allocator()->NewRegister();
- VisitForRegisterValue(property->key(), private_name);
- builder()
- ->LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName())
- .StoreAccumulatorInRegister(private_name)
- .CallRuntime(Runtime::kCreatePrivateNameSymbol, private_name);
- DCHECK_NOT_NULL(property->private_name_var());
- BuildVariableAssignment(property->private_name_var(), Token::INIT,
- HoleCheckMode::kElided);
- break;
- }
- case ClassLiteral::Property::METHOD: {
- // Create the closures for private methods.
- VisitForAccumulatorValue(property->value());
- BuildVariableAssignment(property->private_name_var(), Token::INIT,
- HoleCheckMode::kElided);
- break;
- }
- default:
- // TODO(joyee): Private accessors are not yet supported.
- UNREACHABLE();
- }
-}
-
void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr, Register name) {
size_t class_boilerplate_entry =
builder()->AllocateDeferredConstantPoolEntry();
@@ -2019,6 +2042,7 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr, Register name) {
VisitDeclarations(expr->scope()->declarations());
Register class_constructor = register_allocator()->NewRegister();
+ AccessorTable<ClassLiteral::Property> private_accessors(zone());
{
RegisterAllocationScope register_scope(this);
RegisterList args = register_allocator()->NewGrowableRegisterList();
@@ -2076,7 +2100,44 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr, Register name) {
}
if (property->is_private()) {
- BuildPrivateClassMemberNameAssignment(property);
+ // Assign private class member's name variables.
+ switch (property->kind()) {
+ case ClassLiteral::Property::FIELD: {
+ // Create the private name symbols for fields during class
+ // evaluation and store them on the context. These will be
+ // used as keys later during instance or static initialization.
+ RegisterAllocationScope private_name_register_scope(this);
+ Register private_name = register_allocator()->NewRegister();
+ VisitForRegisterValue(property->key(), private_name);
+ builder()
+ ->LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName())
+ .StoreAccumulatorInRegister(private_name)
+ .CallRuntime(Runtime::kCreatePrivateNameSymbol, private_name);
+ DCHECK_NOT_NULL(property->private_name_var());
+ BuildVariableAssignment(property->private_name_var(), Token::INIT,
+ HoleCheckMode::kElided);
+ break;
+ }
+ case ClassLiteral::Property::METHOD: {
+ // Create the closures for private methods.
+ VisitForAccumulatorValue(property->value());
+ BuildVariableAssignment(property->private_name_var(), Token::INIT,
+ HoleCheckMode::kElided);
+ break;
+ }
+ case ClassLiteral::Property::GETTER: {
+ Literal* key = property->key()->AsLiteral();
+ DCHECK_NULL(private_accessors.LookupOrInsert(key)->getter);
+ private_accessors.LookupOrInsert(key)->getter = property;
+ break;
+ }
+ case ClassLiteral::Property::SETTER: {
+ Literal* key = property->key()->AsLiteral();
+ DCHECK_NULL(private_accessors.LookupOrInsert(key)->setter);
+ private_accessors.LookupOrInsert(key)->setter = property;
+ break;
+ }
+ }
// The private fields are initialized in the initializer function and
// the private brand for the private methods are initialized in the
// constructor instead.
@@ -2122,6 +2183,37 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr, Register name) {
.CallRuntime(Runtime::kCreatePrivateNameSymbol, brand);
BuildVariableAssignment(expr->scope()->brand(), Token::INIT,
HoleCheckMode::kElided);
+
+ // Store the home object for any private methods that need
+ // them. We do this here once the prototype and brand symbol has
+ // been created. Private accessors have their home object set later
+ // when they are defined.
+ for (int i = 0; i < expr->properties()->length(); i++) {
+ RegisterAllocationScope register_scope(this);
+ ClassLiteral::Property* property = expr->properties()->at(i);
+ if (property->NeedsHomeObjectOnClassPrototype()) {
+ Register func = register_allocator()->NewRegister();
+ BuildVariableLoad(property->private_name_var(), HoleCheckMode::kElided);
+ builder()->StoreAccumulatorInRegister(func);
+ VisitSetHomeObject(func, prototype, property);
+ }
+ }
+
+ // Define accessors, using only a single call to the runtime for each pair
+ // of corresponding getters and setters.
+ for (auto accessors : private_accessors.ordered_accessors()) {
+ RegisterAllocationScope inner_register_scope(this);
+ RegisterList accessors_reg = register_allocator()->NewRegisterList(2);
+ ClassLiteral::Property* getter = accessors.second->getter;
+ ClassLiteral::Property* setter = accessors.second->setter;
+ VisitLiteralAccessor(prototype, getter, accessors_reg[0]);
+ VisitLiteralAccessor(prototype, setter, accessors_reg[1]);
+ builder()->CallRuntime(Runtime::kCreatePrivateAccessors, accessors_reg);
+ Variable* var = getter != nullptr ? getter->private_name_var()
+ : setter->private_name_var();
+ DCHECK_NOT_NULL(var);
+ BuildVariableAssignment(var, Token::INIT, HoleCheckMode::kElided);
+ }
}
if (expr->instance_members_initializer_function() != nullptr) {
@@ -2241,12 +2333,13 @@ void BytecodeGenerator::VisitInitializeClassMembersStatement(
}
}
-void BytecodeGenerator::BuildThrowPrivateMethodWriteError(
- const AstRawString* name) {
+void BytecodeGenerator::BuildInvalidPropertyAccess(MessageTemplate tmpl,
+ Property* property) {
RegisterAllocationScope register_scope(this);
+ const AstRawString* name = property->key()->AsVariableProxy()->raw_name();
RegisterList args = register_allocator()->NewRegisterList(2);
builder()
- ->LoadLiteral(Smi::FromEnum(MessageTemplate::kInvalidPrivateMethodWrite))
+ ->LoadLiteral(Smi::FromEnum(tmpl))
.StoreAccumulatorInRegister(args[0])
.LoadLiteral(name)
.StoreAccumulatorInRegister(args[1])
@@ -2437,7 +2530,7 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
}
// Store computed values into the literal.
- AccessorTable accessor_table(zone());
+ AccessorTable<ObjectLiteral::Property> accessor_table(zone());
for (; property_index < expr->properties()->length(); property_index++) {
ObjectLiteral::Property* property = expr->properties()->at(property_index);
if (property->is_computed_name()) break;
@@ -2506,12 +2599,12 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
}
case ObjectLiteral::Property::GETTER:
if (property->emit_store()) {
- accessor_table.lookup(key)->second->getter = property;
+ accessor_table.LookupOrInsert(key)->getter = property;
}
break;
case ObjectLiteral::Property::SETTER:
if (property->emit_store()) {
- accessor_table.lookup(key)->second->setter = property;
+ accessor_table.LookupOrInsert(key)->setter = property;
}
break;
}
@@ -2524,8 +2617,8 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
RegisterList args = register_allocator()->NewRegisterList(5);
builder()->MoveRegister(literal, args[0]);
VisitForRegisterValue(accessors.first, args[1]);
- VisitObjectLiteralAccessor(literal, accessors.second->getter, args[2]);
- VisitObjectLiteralAccessor(literal, accessors.second->setter, args[3]);
+ VisitLiteralAccessor(literal, accessors.second->getter, args[2]);
+ VisitLiteralAccessor(literal, accessors.second->setter, args[3]);
builder()
->LoadLiteral(Smi::FromInt(NONE))
.StoreAccumulatorInRegister(args[4])
@@ -3017,6 +3110,7 @@ void BytecodeGenerator::BuildThrowIfHole(Variable* variable) {
void BytecodeGenerator::BuildHoleCheckForVariableAssignment(Variable* variable,
Token::Value op) {
+ DCHECK(!IsPrivateMethodOrAccessorVariableMode(variable->mode()));
if (variable->is_this() && variable->mode() == VariableMode::kConst &&
op == Token::INIT) {
// Perform an initialization check for 'this'. 'this' variable is the
@@ -3201,10 +3295,10 @@ BytecodeGenerator::AssignmentLhsData::NamedSuperProperty(
}
// static
BytecodeGenerator::AssignmentLhsData
-BytecodeGenerator::AssignmentLhsData::PrivateMethod(Register object,
- const AstRawString* name) {
- return AssignmentLhsData(PRIVATE_METHOD, nullptr, RegisterList(), object,
- Register(), nullptr, name);
+BytecodeGenerator::AssignmentLhsData::PrivateMethodOrAccessor(
+ AssignType type, Property* property) {
+ return AssignmentLhsData(type, property, RegisterList(), Register(),
+ Register(), nullptr, nullptr);
}
// static
BytecodeGenerator::AssignmentLhsData
@@ -3237,12 +3331,12 @@ BytecodeGenerator::AssignmentLhsData BytecodeGenerator::PrepareAssignmentLhs(
Register key = VisitForRegisterValue(property->key());
return AssignmentLhsData::KeyedProperty(object, key);
}
- case PRIVATE_METHOD: {
+ case PRIVATE_METHOD:
+ case PRIVATE_GETTER_ONLY:
+ case PRIVATE_SETTER_ONLY:
+ case PRIVATE_GETTER_AND_SETTER: {
DCHECK(!property->IsSuperAccess());
- AccumulatorPreservingScope scope(this, accumulator_preserving_mode);
- Register object = VisitForRegisterValue(property->obj());
- const AstRawString* name = property->key()->AsVariableProxy()->raw_name();
- return AssignmentLhsData::PrivateMethod(object, name);
+ return AssignmentLhsData::PrivateMethodOrAccessor(assign_type, property);
}
case NAMED_SUPER_PROPERTY: {
AccumulatorPreservingScope scope(this, accumulator_preserving_mode);
@@ -3316,8 +3410,7 @@ void BytecodeGenerator::BuildFinalizeIteration(
ast_string_constants()->return_string(),
feedback_index(feedback_spec()->AddLoadICSlot()))
.StoreAccumulatorInRegister(method)
- .JumpIfUndefined(iterator_is_done.New())
- .JumpIfNull(iterator_is_done.New());
+ .JumpIfUndefinedOrNull(iterator_is_done.New());
{
RegisterAllocationScope register_scope(this);
@@ -3625,22 +3718,6 @@ void BytecodeGenerator::BuildDestructuringObjectAssignment(
LookupHoistingMode lookup_hoisting_mode) {
RegisterAllocationScope scope(this);
- // if (value === null || value === undefined)
- // throw new TypeError(kNonCoercible);
- //
- // TODO(leszeks): Eliminate check if value is known to be non-null (e.g.
- // an object literal).
- BytecodeLabel is_null_or_undefined, not_null_or_undefined;
- builder()
- ->JumpIfNull(&is_null_or_undefined)
- .JumpIfNotUndefined(&not_null_or_undefined);
-
- {
- builder()->Bind(&is_null_or_undefined);
- builder()->SetExpressionPosition(pattern);
- builder()->CallRuntime(Runtime::kThrowPatternAssignmentNonCoercible);
- }
-
// Store the assignment value in a register.
Register value;
RegisterList rest_runtime_callargs;
@@ -3651,7 +3728,34 @@ void BytecodeGenerator::BuildDestructuringObjectAssignment(
} else {
value = register_allocator()->NewRegister();
}
- builder()->Bind(&not_null_or_undefined).StoreAccumulatorInRegister(value);
+ builder()->StoreAccumulatorInRegister(value);
+
+ // if (value === null || value === undefined)
+ // throw new TypeError(kNonCoercible);
+ //
+ // Since the first property access on null/undefined will also trigger a
+ // TypeError, we can elide this check. The exception is when there are no
+ // properties and no rest property (this is an empty literal), or when the
+ // first property is a computed name and accessing it can have side effects.
+ //
+ // TODO(leszeks): Also eliminate this check if the value is known to be
+ // non-null (e.g. an object literal).
+ if (pattern->properties()->is_empty() ||
+ (pattern->properties()->at(0)->is_computed_name() &&
+ pattern->properties()->at(0)->kind() != ObjectLiteralProperty::SPREAD)) {
+ BytecodeLabel is_null_or_undefined, not_null_or_undefined;
+ builder()
+ ->JumpIfUndefinedOrNull(&is_null_or_undefined)
+ .Jump(&not_null_or_undefined);
+
+ {
+ builder()->Bind(&is_null_or_undefined);
+ builder()->SetExpressionPosition(pattern);
+ builder()->CallRuntime(Runtime::kThrowPatternAssignmentNonCoercible,
+ value);
+ }
+ builder()->Bind(&not_null_or_undefined);
+ }
int i = 0;
for (ObjectLiteralProperty* pattern_property : *pattern->properties()) {
@@ -3799,7 +3903,27 @@ void BytecodeGenerator::BuildAssignment(
break;
}
case PRIVATE_METHOD: {
- BuildThrowPrivateMethodWriteError(lhs_data.name());
+ BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateMethodWrite,
+ lhs_data.expr()->AsProperty());
+ break;
+ }
+ case PRIVATE_GETTER_ONLY: {
+ BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateSetterAccess,
+ lhs_data.expr()->AsProperty());
+ break;
+ }
+ case PRIVATE_SETTER_ONLY:
+ case PRIVATE_GETTER_AND_SETTER: {
+ Register value = register_allocator()->NewRegister();
+ builder()->StoreAccumulatorInRegister(value);
+ Property* property = lhs_data.expr()->AsProperty();
+ Register object = VisitForRegisterValue(property->obj());
+ Register key = VisitForRegisterValue(property->key());
+ BuildPrivateBrandCheck(property, object);
+ BuildPrivateSetterAccess(object, key, value);
+ if (!execution_result()->IsEffect()) {
+ builder()->LoadAccumulatorWithRegister(value);
+ }
break;
}
}
@@ -3847,11 +3971,16 @@ void BytecodeGenerator::VisitCompoundAssignment(CompoundAssignment* expr) {
lhs_data.super_property_args().Truncate(3));
break;
}
- case PRIVATE_METHOD: {
- BuildThrowPrivateMethodWriteError(lhs_data.name());
+ case PRIVATE_METHOD:
+ case PRIVATE_GETTER_ONLY:
+ case PRIVATE_SETTER_ONLY:
+ case PRIVATE_GETTER_AND_SETTER: {
+ // ({ #foo: name } = obj) is currently syntactically invalid.
+ UNREACHABLE();
break;
}
}
+
BinaryOperation* binop = expr->AsCompoundAssignment()->binary_operation();
FeedbackSlot slot = feedback_spec()->AddBinaryOpICSlot();
if (expr->value()->IsSmiLiteral()) {
@@ -4284,7 +4413,14 @@ void BytecodeGenerator::VisitThrow(Throw* expr) {
}
void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* property) {
+ if (property->is_optional_chain_link()) {
+ DCHECK_NOT_NULL(optional_chaining_null_labels_);
+ builder()->LoadAccumulatorWithRegister(obj).JumpIfUndefinedOrNull(
+ optional_chaining_null_labels_->New());
+ }
+
AssignType property_kind = Property::GetAssignType(property);
+
switch (property_kind) {
case NON_PROPERTY:
UNREACHABLE();
@@ -4308,18 +4444,20 @@ void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* property) {
case KEYED_SUPER_PROPERTY:
VisitKeyedSuperPropertyLoad(property, Register::invalid_value());
break;
+ case PRIVATE_SETTER_ONLY: {
+ BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateGetterAccess,
+ property);
+ break;
+ }
+ case PRIVATE_GETTER_ONLY:
+ case PRIVATE_GETTER_AND_SETTER: {
+ Register key = VisitForRegisterValue(property->key());
+ BuildPrivateBrandCheck(property, obj);
+ BuildPrivateGetterAccess(obj, key);
+ break;
+ }
case PRIVATE_METHOD: {
- Variable* private_name = property->key()->AsVariableProxy()->var();
-
- // Perform the brand check.
- DCHECK(private_name->requires_brand_check());
- ClassScope* scope = private_name->scope()->AsClassScope();
- Variable* brand = scope->brand();
- BuildVariableLoadForAccumulatorValue(brand, HoleCheckMode::kElided);
- builder()->SetExpressionPosition(property);
- builder()->LoadKeyedProperty(
- obj, feedback_index(feedback_spec()->AddKeyedLoadICSlot()));
-
+ BuildPrivateBrandCheck(property, obj);
// In the case of private methods, property->key() is the function to be
// loaded (stored in a context slot), so load this directly.
VisitForAccumulatorValue(property->key());
@@ -4328,6 +4466,48 @@ void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* property) {
}
}
+void BytecodeGenerator::BuildPrivateGetterAccess(Register object,
+ Register accessor_pair) {
+ RegisterAllocationScope scope(this);
+ Register accessor = register_allocator()->NewRegister();
+ RegisterList args = register_allocator()->NewRegisterList(1);
+
+ builder()
+ ->CallRuntime(Runtime::kLoadPrivateGetter, accessor_pair)
+ .StoreAccumulatorInRegister(accessor)
+ .MoveRegister(object, args[0])
+ .CallProperty(accessor, args,
+ feedback_index(feedback_spec()->AddCallICSlot()));
+}
+
+void BytecodeGenerator::BuildPrivateSetterAccess(Register object,
+ Register accessor_pair,
+ Register value) {
+ RegisterAllocationScope scope(this);
+ Register accessor = register_allocator()->NewRegister();
+ RegisterList args = register_allocator()->NewRegisterList(2);
+
+ builder()
+ ->CallRuntime(Runtime::kLoadPrivateSetter, accessor_pair)
+ .StoreAccumulatorInRegister(accessor)
+ .MoveRegister(object, args[0])
+ .MoveRegister(value, args[1])
+ .CallProperty(accessor, args,
+ feedback_index(feedback_spec()->AddCallICSlot()));
+}
+
+void BytecodeGenerator::BuildPrivateBrandCheck(Property* property,
+ Register object) {
+ Variable* private_name = property->key()->AsVariableProxy()->var();
+ DCHECK(private_name->requires_brand_check());
+ ClassScope* scope = private_name->scope()->AsClassScope();
+ Variable* brand = scope->brand();
+ BuildVariableLoadForAccumulatorValue(brand, HoleCheckMode::kElided);
+ builder()->SetExpressionPosition(property);
+ builder()->LoadKeyedProperty(
+ object, feedback_index(feedback_spec()->AddKeyedLoadICSlot()));
+}
+
void BytecodeGenerator::VisitPropertyLoadForRegister(Register obj,
Property* expr,
Register destination) {
@@ -4376,6 +4556,16 @@ void BytecodeGenerator::VisitKeyedSuperPropertyLoad(Property* property,
}
}
+void BytecodeGenerator::VisitOptionalChain(OptionalChain* expr) {
+ BytecodeLabel done;
+ OptionalChainNullLabelScope label_scope(this);
+ VisitForAccumulatorValue(expr->expression());
+ builder()->Jump(&done);
+ label_scope.labels()->Bind(builder());
+ builder()->LoadUndefined();
+ builder()->Bind(&done);
+}
+
void BytecodeGenerator::VisitProperty(Property* expr) {
AssignType property_kind = Property::GetAssignType(expr);
if (property_kind != NAMED_SUPER_PROPERTY &&
@@ -4509,6 +4699,12 @@ void BytecodeGenerator::VisitCall(Call* expr) {
UNREACHABLE();
}
+ if (expr->is_optional_chain_link()) {
+ DCHECK_NOT_NULL(optional_chaining_null_labels_);
+ builder()->LoadAccumulatorWithRegister(callee).JumpIfUndefinedOrNull(
+ optional_chaining_null_labels_->New());
+ }
+
// Evaluate all arguments to the function call and store in sequential args
// registers.
VisitArguments(expr->arguments(), &args);
@@ -4770,6 +4966,29 @@ void BytecodeGenerator::VisitDelete(UnaryOperation* unary) {
Register object = VisitForRegisterValue(property->obj());
VisitForAccumulatorValue(property->key());
builder()->Delete(object, language_mode());
+ } else if (expr->IsOptionalChain()) {
+ Expression* expr_inner = expr->AsOptionalChain()->expression();
+ if (expr_inner->IsProperty()) {
+ Property* property = expr_inner->AsProperty();
+ DCHECK(!property->IsPrivateReference());
+ BytecodeLabel done;
+ OptionalChainNullLabelScope label_scope(this);
+ VisitForAccumulatorValue(property->obj());
+ if (property->is_optional_chain_link()) {
+ builder()->JumpIfUndefinedOrNull(label_scope.labels()->New());
+ }
+ Register object = register_allocator()->NewRegister();
+ builder()->StoreAccumulatorInRegister(object);
+ VisitForAccumulatorValue(property->key());
+ builder()->Delete(object, language_mode());
+ builder()->Jump(&done);
+ label_scope.labels()->Bind(builder());
+ builder()->LoadTrue();
+ builder()->Bind(&done);
+ } else {
+ VisitForEffect(expr);
+ builder()->LoadTrue();
+ }
} else if (expr->IsVariableProxy() &&
!expr->AsVariableProxy()->is_new_target()) {
// Delete of an unqualified identifier is allowed in sloppy mode but is
@@ -4875,8 +5094,25 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
break;
}
case PRIVATE_METHOD: {
- BuildThrowPrivateMethodWriteError(
- property->key()->AsVariableProxy()->raw_name());
+ BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateMethodWrite,
+ property);
+ return;
+ }
+ case PRIVATE_GETTER_ONLY: {
+ BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateSetterAccess,
+ property);
+ return;
+ }
+ case PRIVATE_SETTER_ONLY: {
+ BuildInvalidPropertyAccess(MessageTemplate::kInvalidPrivateGetterAccess,
+ property);
+ return;
+ }
+ case PRIVATE_GETTER_AND_SETTER: {
+ object = VisitForRegisterValue(property->obj());
+ key = VisitForRegisterValue(property->key());
+ BuildPrivateBrandCheck(property, object);
+ BuildPrivateGetterAccess(object, key);
break;
}
}
@@ -4945,9 +5181,18 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
.CallRuntime(Runtime::kStoreKeyedToSuper, super_property_args);
break;
}
+ case PRIVATE_SETTER_ONLY:
+ case PRIVATE_GETTER_ONLY:
case PRIVATE_METHOD: {
- BuildThrowPrivateMethodWriteError(
- property->key()->AsVariableProxy()->raw_name());
+ UNREACHABLE();
+ }
+ case PRIVATE_GETTER_AND_SETTER: {
+ Register value = register_allocator()->NewRegister();
+ builder()->StoreAccumulatorInRegister(value);
+ BuildPrivateSetterAccess(object, key, value);
+ if (!execution_result()->IsEffect()) {
+ builder()->LoadAccumulatorWithRegister(value);
+ }
break;
}
}
@@ -4969,6 +5214,9 @@ void BytecodeGenerator::VisitBinaryOperation(BinaryOperation* binop) {
case Token::AND:
VisitLogicalAndExpression(binop);
break;
+ case Token::NULLISH:
+ VisitNullishExpression(binop);
+ break;
default:
VisitArithmeticExpression(binop);
break;
@@ -4986,6 +5234,9 @@ void BytecodeGenerator::VisitNaryOperation(NaryOperation* expr) {
case Token::AND:
VisitNaryLogicalAndExpression(expr);
break;
+ case Token::NULLISH:
+ VisitNaryNullishExpression(expr);
+ break;
default:
VisitNaryArithmeticExpression(expr);
break;
@@ -5128,39 +5379,37 @@ void BytecodeGenerator::VisitImportCallExpression(ImportCallExpression* expr) {
}
void BytecodeGenerator::BuildGetIterator(IteratorType hint) {
- RegisterList args = register_allocator()->NewRegisterList(1);
- Register method = register_allocator()->NewRegister();
- Register obj = args[0];
-
if (hint == IteratorType::kAsync) {
+ RegisterAllocationScope scope(this);
+
+ Register obj = register_allocator()->NewRegister();
+ Register method = register_allocator()->NewRegister();
+
// Set method to GetMethod(obj, @@asyncIterator)
builder()->StoreAccumulatorInRegister(obj).LoadAsyncIteratorProperty(
obj, feedback_index(feedback_spec()->AddLoadICSlot()));
- BytecodeLabel async_iterator_undefined, async_iterator_null, done;
- // TODO(ignition): Add a single opcode for JumpIfNullOrUndefined
- builder()->JumpIfUndefined(&async_iterator_undefined);
- builder()->JumpIfNull(&async_iterator_null);
+ BytecodeLabel async_iterator_undefined, done;
+ builder()->JumpIfUndefinedOrNull(&async_iterator_undefined);
// Let iterator be Call(method, obj)
builder()->StoreAccumulatorInRegister(method).CallProperty(
- method, args, feedback_index(feedback_spec()->AddCallICSlot()));
+ method, RegisterList(obj),
+ feedback_index(feedback_spec()->AddCallICSlot()));
// If Type(iterator) is not Object, throw a TypeError exception.
builder()->JumpIfJSReceiver(&done);
builder()->CallRuntime(Runtime::kThrowSymbolAsyncIteratorInvalid);
builder()->Bind(&async_iterator_undefined);
- builder()->Bind(&async_iterator_null);
// If method is undefined,
// Let syncMethod be GetMethod(obj, @@iterator)
builder()
- ->LoadIteratorProperty(obj,
- feedback_index(feedback_spec()->AddLoadICSlot()))
+ ->GetIterator(obj, feedback_index(feedback_spec()->AddLoadICSlot()))
.StoreAccumulatorInRegister(method);
// Let syncIterator be Call(syncMethod, obj)
- builder()->CallProperty(method, args,
+ builder()->CallProperty(method, RegisterList(obj),
feedback_index(feedback_spec()->AddCallICSlot()));
// Return CreateAsyncFromSyncIterator(syncIterator)
@@ -5171,16 +5420,22 @@ void BytecodeGenerator::BuildGetIterator(IteratorType hint) {
builder()->Bind(&done);
} else {
- // Let method be GetMethod(obj, @@iterator).
- builder()
- ->StoreAccumulatorInRegister(obj)
- .LoadIteratorProperty(obj,
- feedback_index(feedback_spec()->AddLoadICSlot()))
- .StoreAccumulatorInRegister(method);
+ {
+ RegisterAllocationScope scope(this);
- // Let iterator be Call(method, obj).
- builder()->CallProperty(method, args,
- feedback_index(feedback_spec()->AddCallICSlot()));
+ Register obj = register_allocator()->NewRegister();
+ Register method = register_allocator()->NewRegister();
+
+ // Let method be GetMethod(obj, @@iterator).
+ builder()
+ ->StoreAccumulatorInRegister(obj)
+ .GetIterator(obj, feedback_index(feedback_spec()->AddLoadICSlot()))
+ .StoreAccumulatorInRegister(method);
+
+ // Let iterator be Call(method, obj).
+ builder()->CallProperty(method, RegisterList(obj),
+ feedback_index(feedback_spec()->AddCallICSlot()));
+ }
// If Type(iterator) is not Object, throw a TypeError exception.
BytecodeLabel no_type_error;
@@ -5241,8 +5496,7 @@ void BytecodeGenerator::BuildCallIteratorMethod(Register iterator,
FeedbackSlot slot = feedback_spec()->AddLoadICSlot();
builder()
->LoadNamedProperty(iterator, method_name, feedback_index(slot))
- .JumpIfUndefined(if_notcalled->New())
- .JumpIfNull(if_notcalled->New())
+ .JumpIfUndefinedOrNull(if_notcalled->New())
.StoreAccumulatorInRegister(method)
.CallProperty(method, receiver_and_args,
feedback_index(feedback_spec()->AddCallICSlot()))
@@ -5375,14 +5629,16 @@ void BytecodeGenerator::VisitNaryCommaExpression(NaryOperation* expr) {
void BytecodeGenerator::VisitLogicalTestSubExpression(
Token::Value token, Expression* expr, BytecodeLabels* then_labels,
BytecodeLabels* else_labels, int coverage_slot) {
- DCHECK(token == Token::OR || token == Token::AND);
+ DCHECK(token == Token::OR || token == Token::AND || token == Token::NULLISH);
BytecodeLabels test_next(zone());
if (token == Token::OR) {
VisitForTest(expr, then_labels, &test_next, TestFallthrough::kElse);
- } else {
- DCHECK_EQ(Token::AND, token);
+ } else if (token == Token::AND) {
VisitForTest(expr, &test_next, else_labels, TestFallthrough::kThen);
+ } else {
+ DCHECK_EQ(Token::NULLISH, token);
+ VisitForNullishTest(expr, then_labels, &test_next, else_labels);
}
test_next.Bind(builder());
@@ -5392,7 +5648,7 @@ void BytecodeGenerator::VisitLogicalTestSubExpression(
void BytecodeGenerator::VisitLogicalTest(Token::Value token, Expression* left,
Expression* right,
int right_coverage_slot) {
- DCHECK(token == Token::OR || token == Token::AND);
+ DCHECK(token == Token::OR || token == Token::AND || token == Token::NULLISH);
TestResultScope* test_result = execution_result()->AsTest();
BytecodeLabels* then_labels = test_result->then_labels();
BytecodeLabels* else_labels = test_result->else_labels();
@@ -5407,7 +5663,7 @@ void BytecodeGenerator::VisitLogicalTest(Token::Value token, Expression* left,
void BytecodeGenerator::VisitNaryLogicalTest(
Token::Value token, NaryOperation* expr,
const NaryCodeCoverageSlots* coverage_slots) {
- DCHECK(token == Token::OR || token == Token::AND);
+ DCHECK(token == Token::OR || token == Token::AND || token == Token::NULLISH);
DCHECK_GT(expr->subsequent_length(), 0);
TestResultScope* test_result = execution_result()->AsTest();
@@ -5463,6 +5719,27 @@ bool BytecodeGenerator::VisitLogicalAndSubExpression(Expression* expr,
return false;
}
+bool BytecodeGenerator::VisitNullishSubExpression(Expression* expr,
+ BytecodeLabels* end_labels,
+ int coverage_slot) {
+ if (expr->IsLiteralButNotNullOrUndefined()) {
+ VisitForAccumulatorValue(expr);
+ end_labels->Bind(builder());
+ return true;
+ } else if (!expr->IsNullOrUndefinedLiteral()) {
+ VisitForAccumulatorValue(expr);
+ BytecodeLabel is_null_or_undefined;
+ builder()
+ ->JumpIfUndefinedOrNull(&is_null_or_undefined)
+ .Jump(end_labels->New());
+ builder()->Bind(&is_null_or_undefined);
+ }
+
+ BuildIncrementBlockCoverageCounterIfEnabled(coverage_slot);
+
+ return false;
+}
+
void BytecodeGenerator::VisitLogicalOrExpression(BinaryOperation* binop) {
Expression* left = binop->left();
Expression* right = binop->right();
@@ -5585,6 +5862,68 @@ void BytecodeGenerator::VisitNaryLogicalAndExpression(NaryOperation* expr) {
}
}
+void BytecodeGenerator::VisitNullishExpression(BinaryOperation* binop) {
+ Expression* left = binop->left();
+ Expression* right = binop->right();
+
+ int right_coverage_slot =
+ AllocateBlockCoverageSlotIfEnabled(binop, SourceRangeKind::kRight);
+
+ if (execution_result()->IsTest()) {
+ TestResultScope* test_result = execution_result()->AsTest();
+ if (left->IsLiteralButNotNullOrUndefined() && left->ToBooleanIsTrue()) {
+ builder()->Jump(test_result->NewThenLabel());
+ } else if (left->IsNullOrUndefinedLiteral() &&
+ right->IsNullOrUndefinedLiteral()) {
+ BuildIncrementBlockCoverageCounterIfEnabled(right_coverage_slot);
+ builder()->Jump(test_result->NewElseLabel());
+ } else {
+ VisitLogicalTest(Token::NULLISH, left, right, right_coverage_slot);
+ }
+ test_result->SetResultConsumedByTest();
+ } else {
+ BytecodeLabels end_labels(zone());
+ if (VisitNullishSubExpression(left, &end_labels, right_coverage_slot)) {
+ return;
+ }
+ VisitForAccumulatorValue(right);
+ end_labels.Bind(builder());
+ }
+}
+
+void BytecodeGenerator::VisitNaryNullishExpression(NaryOperation* expr) {
+ Expression* first = expr->first();
+ DCHECK_GT(expr->subsequent_length(), 0);
+
+ NaryCodeCoverageSlots coverage_slots(this, expr);
+
+ if (execution_result()->IsTest()) {
+ TestResultScope* test_result = execution_result()->AsTest();
+ if (first->IsLiteralButNotNullOrUndefined() && first->ToBooleanIsTrue()) {
+ builder()->Jump(test_result->NewThenLabel());
+ } else {
+ VisitNaryLogicalTest(Token::NULLISH, expr, &coverage_slots);
+ }
+ test_result->SetResultConsumedByTest();
+ } else {
+ BytecodeLabels end_labels(zone());
+ if (VisitNullishSubExpression(first, &end_labels,
+ coverage_slots.GetSlotFor(0))) {
+ return;
+ }
+ for (size_t i = 0; i < expr->subsequent_length() - 1; ++i) {
+ if (VisitNullishSubExpression(expr->subsequent(i), &end_labels,
+ coverage_slots.GetSlotFor(i + 1))) {
+ return;
+ }
+ }
+ // We have to visit the last value even if it's nullish, because we need its
+ // actual value.
+ VisitForAccumulatorValue(expr->subsequent(expr->subsequent_length() - 1));
+ end_labels.Bind(builder());
+ }
+}
+
void BytecodeGenerator::BuildNewLocalActivationContext() {
ValueResultScope value_execution_result(this);
Scope* scope = closure_scope();
@@ -5682,8 +6021,9 @@ void BytecodeGenerator::BuildNewLocalCatchContext(Scope* scope) {
builder()->CreateCatchContext(exception, scope);
}
-void BytecodeGenerator::VisitObjectLiteralAccessor(
- Register home_object, ObjectLiteralProperty* property, Register value_out) {
+void BytecodeGenerator::VisitLiteralAccessor(Register home_object,
+ LiteralProperty* property,
+ Register value_out) {
if (property == nullptr) {
builder()->LoadNull().StoreAccumulatorInRegister(value_out);
} else {
@@ -5929,6 +6269,25 @@ void BytecodeGenerator::VisitForTest(Expression* expr,
}
}
+// Visits the expression |expr| for testing its nullish value and jumping to the
+// |then| or |other| label depending on value and short-circuit semantics
+void BytecodeGenerator::VisitForNullishTest(Expression* expr,
+ BytecodeLabels* then_labels,
+ BytecodeLabels* test_next_labels,
+ BytecodeLabels* else_labels) {
+ // Nullish short circuits on undefined or null, otherwise we fall back to
+ // BuildTest with no fallthrough.
+ // TODO(joshualitt): We should do this in a TestResultScope.
+ TypeHint type_hint = VisitForAccumulatorValue(expr);
+ ToBooleanMode mode = ToBooleanModeFromTypeHint(type_hint);
+
+ // Skip the nullish shortcircuit if we already have a boolean.
+ if (mode != ToBooleanMode::kAlreadyBoolean) {
+ builder()->JumpIfUndefinedOrNull(test_next_labels->New());
+ }
+ BuildTest(mode, then_labels, else_labels, TestFallthrough::kNone);
+}
+
void BytecodeGenerator::VisitInSameTestExecutionScope(Expression* expr) {
DCHECK(execution_result()->IsTest());
{