[class] Store class fields initializer on the constructor
Previously, the class fields initializer function was stored on a synthetic context allocated variable. This approach had sevaral problems: - We didn't know that class literal had fields until after we had completely parsed the class literal. This meant that we had to go back and fix up the scope of the constructor to have this synthetic variable. This resulted in mismatch between parser and preparsed scope data. - This synthetic variable could potentially resolve to an initializer of an outer class. For ex: class X extends Object { c = 1; constructor() { var t = () => { class P extends Object { constructor() { var t = () => { super(); }; t(); } } super(); } t(); } } In this the inner class P could access the outer class X's initiliazer function. We would have to maintain extra metadata to make sure this doesn't happen. Instead this new approach uses a private symbol to store the initializer function on the class constructor itself. For the base constructor case, we can simply check for a bit on the constructor function literal to see if we need to emit code that loads and calls this initializer function. Therefore, we don't pay the cost of loading this function in case there are no class fields. For the derived constructor case, there are two possiblities: (a) We are in a super() call directly in the derived constructor: In this case we can do a check similar to the base constructor check, we can check for a bit on the derived constructor and emit code for loading and calling the initializer function. This is usually the common case and we don't pay any cost for not using class fields. (b) We are in a super() call inside an arrow function in the derived constructor: In this case, we /always/ emit code to load and call the initializer function. If the function doesn't exist then we have undefined and we don't call anything. Otherwise we call the function. super() can't be called twice so even if we emit code to load and call the initializer function multiple times, it doesn't matter because it would have already been an error. Bug: v8:5367 Change-Id: I7f77cd6493ff84cf0e430a8c1039bc9ac6941a88 Reviewed-on: https://chromium-review.googlesource.com/781660 Commit-Queue: Sathya Gunasekaran <gsathya@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Reviewed-by: Mythri Alle <mythria@chromium.org> Cr-Commit-Position: refs/heads/master@{#49628}
This commit is contained in:
parent
925ed598e3
commit
4ca9d843f8
@ -207,7 +207,6 @@ class AstBigInt {
|
||||
F(dot, ".") \
|
||||
F(dot_for, ".for") \
|
||||
F(dot_generator_object, ".generator_object") \
|
||||
F(dot_instance_fields_initializer, ".instance_fields_initializer") \
|
||||
F(dot_iterator, ".iterator") \
|
||||
F(dot_result, ".result") \
|
||||
F(dot_switch_tag, ".switch_tag") \
|
||||
|
@ -2298,11 +2298,11 @@ class FunctionLiteral final : public Expression {
|
||||
function_literal_id_ = function_literal_id;
|
||||
}
|
||||
|
||||
void set_instance_class_fields_initializer(VariableProxy* initializer) {
|
||||
instance_class_fields_initializer_ = initializer;
|
||||
void set_requires_instance_fields_initializer(bool value) {
|
||||
bit_field_ = RequiresInstanceFieldsInitializer::update(bit_field_, value);
|
||||
}
|
||||
VariableProxy* instance_class_fields_initializer() {
|
||||
return instance_class_fields_initializer_;
|
||||
bool requires_instance_fields_initializer() const {
|
||||
return RequiresInstanceFieldsInitializer::decode(bit_field_);
|
||||
}
|
||||
|
||||
ProducedPreParsedScopeData* produced_preparsed_scope_data() const {
|
||||
@ -2332,13 +2332,13 @@ class FunctionLiteral final : public Expression {
|
||||
body_(body),
|
||||
raw_inferred_name_(ast_value_factory->empty_cons_string()),
|
||||
function_literal_id_(function_literal_id),
|
||||
instance_class_fields_initializer_(nullptr),
|
||||
produced_preparsed_scope_data_(produced_preparsed_scope_data) {
|
||||
bit_field_ |= FunctionTypeBits::encode(function_type) |
|
||||
Pretenure::encode(false) |
|
||||
HasDuplicateParameters::encode(has_duplicate_parameters ==
|
||||
kHasDuplicateParameters) |
|
||||
DontOptimizeReasonField::encode(kNoReason);
|
||||
DontOptimizeReasonField::encode(kNoReason) |
|
||||
RequiresInstanceFieldsInitializer::encode(false);
|
||||
if (eager_compile_hint == kShouldEagerCompile) SetShouldEagerCompile();
|
||||
DCHECK_EQ(body == nullptr, expected_property_count < 0);
|
||||
}
|
||||
@ -2349,6 +2349,8 @@ class FunctionLiteral final : public Expression {
|
||||
class HasDuplicateParameters : public BitField<bool, Pretenure::kNext, 1> {};
|
||||
class DontOptimizeReasonField
|
||||
: public BitField<BailoutReason, HasDuplicateParameters::kNext, 8> {};
|
||||
class RequiresInstanceFieldsInitializer
|
||||
: public BitField<bool, DontOptimizeReasonField::kNext, 1> {};
|
||||
|
||||
int expected_property_count_;
|
||||
int parameter_count_;
|
||||
@ -2363,7 +2365,6 @@ class FunctionLiteral final : public Expression {
|
||||
const AstConsString* raw_inferred_name_;
|
||||
Handle<String> inferred_name_;
|
||||
int function_literal_id_;
|
||||
VariableProxy* instance_class_fields_initializer_;
|
||||
ProducedPreParsedScopeData* produced_preparsed_scope_data_;
|
||||
};
|
||||
|
||||
@ -2438,10 +2439,6 @@ class ClassLiteral final : public Expression {
|
||||
return instance_fields_initializer_function_;
|
||||
}
|
||||
|
||||
VariableProxy* instance_fields_initializer_proxy() const {
|
||||
return instance_fields_initializer_proxy_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AstNodeFactory;
|
||||
|
||||
@ -2449,7 +2446,6 @@ class ClassLiteral final : public Expression {
|
||||
FunctionLiteral* constructor, ZoneList<Property*>* properties,
|
||||
FunctionLiteral* static_fields_initializer,
|
||||
FunctionLiteral* instance_fields_initializer_function,
|
||||
VariableProxy* instance_fields_initializer_proxy,
|
||||
int start_position, int end_position,
|
||||
bool has_name_static_property, bool has_static_computed_names,
|
||||
bool is_anonymous)
|
||||
@ -2462,8 +2458,7 @@ class ClassLiteral final : public Expression {
|
||||
properties_(properties),
|
||||
static_fields_initializer_(static_fields_initializer),
|
||||
instance_fields_initializer_function_(
|
||||
instance_fields_initializer_function),
|
||||
instance_fields_initializer_proxy_(instance_fields_initializer_proxy) {
|
||||
instance_fields_initializer_function) {
|
||||
bit_field_ |= HasNameStaticProperty::encode(has_name_static_property) |
|
||||
HasStaticComputedNames::encode(has_static_computed_names) |
|
||||
IsAnonymousExpression::encode(is_anonymous);
|
||||
@ -2477,7 +2472,6 @@ class ClassLiteral final : public Expression {
|
||||
ZoneList<Property*>* properties_;
|
||||
FunctionLiteral* static_fields_initializer_;
|
||||
FunctionLiteral* instance_fields_initializer_function_;
|
||||
VariableProxy* instance_fields_initializer_proxy_;
|
||||
class HasNameStaticProperty
|
||||
: public BitField<bool, Expression::kNextBitFieldIndex, 1> {};
|
||||
class HasStaticComputedNames
|
||||
@ -3166,15 +3160,14 @@ class AstNodeFactory final BASE_EMBEDDED {
|
||||
FunctionLiteral* constructor,
|
||||
ZoneList<ClassLiteral::Property*>* properties,
|
||||
FunctionLiteral* static_fields_initializer,
|
||||
FunctionLiteral* instance_fields_initializer_function,
|
||||
VariableProxy* instance_fields_initializer_var, int start_position,
|
||||
FunctionLiteral* instance_fields_initializer_function, int start_position,
|
||||
int end_position, bool has_name_static_property,
|
||||
bool has_static_computed_names, bool is_anonymous) {
|
||||
return new (zone_) ClassLiteral(
|
||||
scope, variable, extends, constructor, properties,
|
||||
static_fields_initializer, instance_fields_initializer_function,
|
||||
instance_fields_initializer_var, start_position, end_position,
|
||||
has_name_static_property, has_static_computed_names, is_anonymous);
|
||||
start_position, end_position, has_name_static_property,
|
||||
has_static_computed_names, is_anonymous);
|
||||
}
|
||||
|
||||
NativeFunctionLiteral* NewNativeFunctionLiteral(const AstRawString* name,
|
||||
|
@ -738,9 +738,8 @@ const char* AstPrinter::PrintProgram(FunctionLiteral* program) {
|
||||
if (program->raw_inferred_name()) {
|
||||
PrintLiteralIndented("INFERRED NAME", program->raw_inferred_name(), true);
|
||||
}
|
||||
if (program->instance_class_fields_initializer() != nullptr) {
|
||||
PrintIndentedVisit("INSTANCE FIELDS INITIALIZER",
|
||||
program->instance_class_fields_initializer());
|
||||
if (program->requires_instance_fields_initializer()) {
|
||||
Print(" REQUIRES INSTANCE FIELDS INITIALIZER\n");
|
||||
}
|
||||
PrintParameters(program->scope());
|
||||
PrintDeclarations(program->scope()->declarations());
|
||||
@ -1003,10 +1002,6 @@ void AstPrinter::VisitClassLiteral(ClassLiteral* node) {
|
||||
PrintIndentedVisit("INSTANCE FIELDS INITIALIZER",
|
||||
node->instance_fields_initializer_function());
|
||||
}
|
||||
if (node->instance_fields_initializer_proxy() != nullptr) {
|
||||
PrintIndentedVisit("INSTANCE FIELDS INITIALIZER VAR",
|
||||
node->instance_fields_initializer_proxy());
|
||||
}
|
||||
PrintClassProperties(node->properties());
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,6 @@
|
||||
V(dot_catch_string, ".catch") \
|
||||
V(dot_for_string, ".for") \
|
||||
V(dot_generator_object_string, ".generator_object") \
|
||||
V(dot_instance_fields_initializer_string, ".instance_fields_initializer") \
|
||||
V(dot_iterator_string, ".iterator") \
|
||||
V(dot_result_string, ".result") \
|
||||
V(dot_switch_tag_string, ".switch_tag") \
|
||||
@ -213,6 +212,7 @@
|
||||
V(call_site_frame_index_symbol) \
|
||||
V(console_context_id_symbol) \
|
||||
V(console_context_name_symbol) \
|
||||
V(class_fields_symbol) \
|
||||
V(class_positions_symbol) \
|
||||
V(detailed_stack_trace_symbol) \
|
||||
V(elements_transition_symbol) \
|
||||
|
@ -869,6 +869,20 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreHomeObjectProperty(
|
||||
return StoreNamedProperty(object, name_index, feedback_slot, language_mode);
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::StoreClassFieldsInitializer(
|
||||
Register constructor, int feedback_slot) {
|
||||
size_t name_index = ClassFieldsSymbolConstantPoolEntry();
|
||||
return StoreNamedProperty(constructor, name_index, feedback_slot,
|
||||
LanguageMode::kStrict);
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::LoadClassFieldsInitializer(
|
||||
Register constructor, int feedback_slot) {
|
||||
size_t name_index = ClassFieldsSymbolConstantPoolEntry();
|
||||
OutputLdaNamedProperty(constructor, name_index, feedback_slot);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BytecodeArrayBuilder& BytecodeArrayBuilder::CreateClosure(
|
||||
size_t shared_function_info_entry, int slot, int flags) {
|
||||
OutputCreateClosure(shared_function_info_entry, slot, flags);
|
||||
|
@ -166,6 +166,15 @@ class V8_EXPORT_PRIVATE BytecodeArrayBuilder final {
|
||||
int feedback_slot,
|
||||
LanguageMode language_mode);
|
||||
|
||||
// Store the class fields property. The initializer to be stored should
|
||||
// be in the accumulator.
|
||||
BytecodeArrayBuilder& StoreClassFieldsInitializer(Register constructor,
|
||||
int feedback_slot);
|
||||
|
||||
// Load class fields property.
|
||||
BytecodeArrayBuilder& LoadClassFieldsInitializer(Register constructor,
|
||||
int feedback_slot);
|
||||
|
||||
// Lookup the variable with |name|.
|
||||
BytecodeArrayBuilder& LoadLookupSlot(const AstRawString* name,
|
||||
TypeofMode typeof_mode);
|
||||
|
@ -1051,10 +1051,11 @@ void BytecodeGenerator::GenerateBytecodeBody() {
|
||||
// Perform a stack-check before the body.
|
||||
builder()->StackCheck(info()->literal()->start_position());
|
||||
|
||||
// The derived constructor case is handled in VisitCallSuper.
|
||||
if (IsBaseConstructor(function_kind()) &&
|
||||
info()->literal()->instance_class_fields_initializer() != nullptr) {
|
||||
BuildInstanceFieldInitialization(
|
||||
info()->literal()->instance_class_fields_initializer());
|
||||
info()->literal()->requires_instance_fields_initializer()) {
|
||||
BuildInstanceFieldInitialization(Register::function_closure(),
|
||||
builder()->Receiver());
|
||||
}
|
||||
|
||||
// Visit statements in the function body.
|
||||
@ -1890,9 +1891,11 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) {
|
||||
// TODO(gsathya): Support super property access in instance field
|
||||
// initializers.
|
||||
|
||||
builder()->LoadAccumulatorWithRegister(initializer);
|
||||
BuildVariableAssignment(expr->instance_fields_initializer_proxy()->var(),
|
||||
Token::INIT, HoleCheckMode::kElided);
|
||||
FeedbackSlot slot = feedback_spec()->AddStoreICSlot(language_mode());
|
||||
builder()
|
||||
->LoadAccumulatorWithRegister(initializer)
|
||||
.StoreClassFieldsInitializer(class_constructor, feedback_index(slot))
|
||||
.LoadAccumulatorWithRegister(class_constructor);
|
||||
}
|
||||
|
||||
if (expr->static_fields_initializer() != nullptr) {
|
||||
@ -1955,17 +1958,25 @@ void BytecodeGenerator::VisitInitializeClassFieldsStatement(
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeGenerator::BuildInstanceFieldInitialization(
|
||||
VariableProxy* initializer_proxy) {
|
||||
void BytecodeGenerator::BuildInstanceFieldInitialization(Register constructor,
|
||||
Register instance) {
|
||||
RegisterList args = register_allocator()->NewRegisterList(1);
|
||||
Register initializer = register_allocator()->NewRegister();
|
||||
BuildVariableLoad(initializer_proxy->var(), HoleCheckMode::kElided);
|
||||
|
||||
FeedbackSlot slot = feedback_spec()->AddLoadICSlot();
|
||||
BytecodeLabel done;
|
||||
|
||||
builder()
|
||||
->StoreAccumulatorInRegister(initializer)
|
||||
.MoveRegister(builder()->Receiver(), args[0])
|
||||
->LoadClassFieldsInitializer(constructor, feedback_index(slot))
|
||||
// TODO(gsathya): This jump can be elided for the base
|
||||
// constructor and derived constructor. This is only required
|
||||
// when called from an arrow function.
|
||||
.JumpIfUndefined(&done)
|
||||
.StoreAccumulatorInRegister(initializer)
|
||||
.MoveRegister(instance, args[0])
|
||||
.CallProperty(initializer, args,
|
||||
feedback_index(feedback_spec()->AddCallICSlot()));
|
||||
feedback_index(feedback_spec()->AddCallICSlot()))
|
||||
.Bind(&done);
|
||||
}
|
||||
|
||||
void BytecodeGenerator::VisitNativeFunctionLiteral(
|
||||
@ -3500,9 +3511,11 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
|
||||
SuperCallReference* super = expr->expression()->AsSuperCallReference();
|
||||
|
||||
// Prepare the constructor to the super call.
|
||||
VisitForAccumulatorValue(super->this_function_var());
|
||||
Register this_function = VisitForRegisterValue(super->this_function_var());
|
||||
Register constructor = register_allocator()->NewRegister();
|
||||
builder()->GetSuperConstructor(constructor);
|
||||
builder()
|
||||
->LoadAccumulatorWithRegister(this_function)
|
||||
.GetSuperConstructor(constructor);
|
||||
|
||||
ZoneList<Expression*>* args = expr->arguments();
|
||||
RegisterList args_regs = register_allocator()->NewGrowableRegisterList();
|
||||
@ -3528,6 +3541,24 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
|
||||
// and come up with a better way.
|
||||
builder()->Construct(constructor, args_regs, feedback_slot_index);
|
||||
}
|
||||
|
||||
// The derived constructor has the correct bit set always, so we
|
||||
// don't emit code to load and call the initializer if not
|
||||
// required.
|
||||
//
|
||||
// For the arrow function or eval case, we always emit code to load
|
||||
// and call the initializer.
|
||||
//
|
||||
// TODO(gsathya): In the future, we could tag nested arrow functions
|
||||
// or eval with the correct bit so that we do the load conditionally
|
||||
// if required.
|
||||
if (info()->literal()->requires_instance_fields_initializer() ||
|
||||
!IsDerivedConstructor(info()->literal()->kind())) {
|
||||
Register instance = register_allocator()->NewRegister();
|
||||
builder()->StoreAccumulatorInRegister(instance);
|
||||
BuildInstanceFieldInitialization(this_function, instance);
|
||||
builder()->LoadAccumulatorWithRegister(instance);
|
||||
}
|
||||
}
|
||||
|
||||
void BytecodeGenerator::VisitCallNew(CallNew* expr) {
|
||||
|
@ -158,7 +158,8 @@ class BytecodeGenerator final : public AstVisitor<BytecodeGenerator> {
|
||||
void BuildClassLiteral(ClassLiteral* expr);
|
||||
void VisitNewTargetVariable(Variable* variable);
|
||||
void VisitThisFunctionVariable(Variable* variable);
|
||||
void BuildInstanceFieldInitialization(VariableProxy* initializer_proxy);
|
||||
void BuildInstanceFieldInitialization(Register constructor,
|
||||
Register instance);
|
||||
void BuildGeneratorObjectVariableInitialization();
|
||||
void VisitBlockDeclarationsAndStatements(Block* stmt);
|
||||
void VisitFunctionClosureForContext();
|
||||
|
@ -26,7 +26,8 @@ namespace interpreter {
|
||||
V(IteratorSymbol, iterator_symbol) \
|
||||
V(AsyncIteratorSymbol, async_iterator_symbol) \
|
||||
V(HomeObjectSymbol, home_object_symbol) \
|
||||
V(EmptyFixedArray, empty_fixed_array)
|
||||
V(EmptyFixedArray, empty_fixed_array) \
|
||||
V(ClassFieldsSymbol, class_fields_symbol)
|
||||
|
||||
// A helper class for constructing constant arrays for the
|
||||
// interpreter. Each instance of this class is intended to be used to
|
||||
|
@ -13790,10 +13790,10 @@ void SharedFunctionInfo::InitFromFunctionLiteral(
|
||||
}
|
||||
shared_info->set_needs_home_object(lit->scope()->NeedsHomeObject());
|
||||
shared_info->set_function_literal_id(lit->function_literal_id());
|
||||
DCHECK_IMPLIES(lit->instance_class_fields_initializer() != nullptr,
|
||||
DCHECK_IMPLIES(lit->requires_instance_fields_initializer(),
|
||||
IsClassConstructor(lit->kind()));
|
||||
shared_info->set_requires_instance_fields_initializer(
|
||||
lit->instance_class_fields_initializer() != nullptr);
|
||||
lit->requires_instance_fields_initializer());
|
||||
// For lazy parsed functions, the following flags will be inaccurate since we
|
||||
// don't have the information yet. They're set later in
|
||||
// SetSharedFunctionFlagsFromLiteral (compiler.cc), when the function is
|
||||
|
@ -927,22 +927,15 @@ FunctionLiteral* Parser::DoParseFunction(ParseInfo* info,
|
||||
DCHECK_EQ(scope(), outer);
|
||||
result = DefaultConstructor(raw_name, IsDerivedConstructor(kind),
|
||||
info->start_position(), info->end_position());
|
||||
if (info->requires_instance_fields_initializer()) {
|
||||
result->set_instance_class_fields_initializer(
|
||||
result->scope()->NewUnresolved(
|
||||
factory(),
|
||||
ast_value_factory()->dot_instance_fields_initializer_string()));
|
||||
}
|
||||
} else {
|
||||
result = ParseFunctionLiteral(
|
||||
raw_name, Scanner::Location::invalid(), kSkipFunctionNameCheck, kind,
|
||||
kNoSourcePosition, function_type, info->language_mode(), &ok);
|
||||
if (info->requires_instance_fields_initializer()) {
|
||||
result->set_instance_class_fields_initializer(
|
||||
result->scope()->NewUnresolved(
|
||||
factory(),
|
||||
ast_value_factory()->dot_instance_fields_initializer_string()));
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
result->set_requires_instance_fields_initializer(
|
||||
info->requires_instance_fields_initializer());
|
||||
}
|
||||
// Make sure the results agree.
|
||||
DCHECK(ok == (result != nullptr));
|
||||
@ -3341,26 +3334,17 @@ Expression* Parser::RewriteClassLiteral(Scope* block_scope,
|
||||
}
|
||||
|
||||
FunctionLiteral* instance_fields_initializer_function = nullptr;
|
||||
VariableProxy* instance_fields_initializer_proxy = nullptr;
|
||||
if (class_info->has_instance_class_fields) {
|
||||
instance_fields_initializer_function = CreateInitializerFunction(
|
||||
class_info->instance_fields_scope, class_info->instance_fields);
|
||||
|
||||
Variable* instance_fields_initializer_var = CreateSyntheticContextVariable(
|
||||
ast_value_factory()->dot_instance_fields_initializer_string(),
|
||||
CHECK_OK);
|
||||
instance_fields_initializer_proxy =
|
||||
factory()->NewVariableProxy(instance_fields_initializer_var);
|
||||
class_info->constructor->set_instance_class_fields_initializer(
|
||||
instance_fields_initializer_proxy);
|
||||
class_info->constructor->set_requires_instance_fields_initializer(true);
|
||||
}
|
||||
|
||||
ClassLiteral* class_literal = factory()->NewClassLiteral(
|
||||
block_scope, class_info->variable, class_info->extends,
|
||||
class_info->constructor, class_info->properties,
|
||||
static_fields_initializer, instance_fields_initializer_function,
|
||||
instance_fields_initializer_proxy, pos, end_pos,
|
||||
class_info->has_name_static_property,
|
||||
static_fields_initializer, instance_fields_initializer_function, pos,
|
||||
end_pos, class_info->has_name_static_property,
|
||||
class_info->has_static_computed_names, class_info->is_anonymous);
|
||||
|
||||
AddFunctionForNameInference(class_info->constructor);
|
||||
|
@ -104,18 +104,18 @@ snippet: "
|
||||
test = new B().constructor;
|
||||
})();
|
||||
"
|
||||
frame size: 4
|
||||
frame size: 5
|
||||
parameter count: 1
|
||||
bytecode array length: 40
|
||||
bytecodes: [
|
||||
B(Mov), R(closure), R(1),
|
||||
/* 113 E> */ B(StackCheck),
|
||||
/* 118 S> */ B(Ldar), R(1),
|
||||
B(GetSuperConstructor), R(2),
|
||||
B(GetSuperConstructor), R(3),
|
||||
B(LdaSmi), I8(1),
|
||||
B(Star), R(3),
|
||||
B(Star), R(4),
|
||||
B(Ldar), R(0),
|
||||
/* 118 E> */ B(Construct), R(2), R(3), U8(1), U8(0),
|
||||
/* 118 E> */ B(Construct), R(3), R(4), U8(1), U8(0),
|
||||
B(Star), R(2),
|
||||
B(Ldar), R(this),
|
||||
/* 118 E> */ B(ThrowSuperAlreadyCalledIfNotHole),
|
||||
@ -147,16 +147,16 @@ snippet: "
|
||||
test = new B().constructor;
|
||||
})();
|
||||
"
|
||||
frame size: 3
|
||||
frame size: 4
|
||||
parameter count: 1
|
||||
bytecode array length: 36
|
||||
bytecodes: [
|
||||
B(Mov), R(closure), R(1),
|
||||
/* 112 E> */ B(StackCheck),
|
||||
/* 117 S> */ B(Ldar), R(1),
|
||||
B(GetSuperConstructor), R(2),
|
||||
B(GetSuperConstructor), R(3),
|
||||
B(Ldar), R(0),
|
||||
/* 117 E> */ B(Construct), R(2), R(0), U8(0), U8(0),
|
||||
/* 117 E> */ B(Construct), R(3), R(0), U8(0), U8(0),
|
||||
B(Star), R(2),
|
||||
B(Ldar), R(this),
|
||||
/* 117 E> */ B(ThrowSuperAlreadyCalledIfNotHole),
|
||||
|
138
test/cctest/interpreter/bytecode_expectations/ClassFields.golden
Normal file
138
test/cctest/interpreter/bytecode_expectations/ClassFields.golden
Normal file
@ -0,0 +1,138 @@
|
||||
#
|
||||
# Autogenerated by generate-bytecode-expectations.
|
||||
#
|
||||
|
||||
---
|
||||
wrap: yes
|
||||
class fields: yes
|
||||
|
||||
---
|
||||
snippet: "
|
||||
{
|
||||
class A extends class {} {
|
||||
a;
|
||||
static c;
|
||||
}
|
||||
|
||||
class B extends class {} {
|
||||
a = 1;
|
||||
static c = 3;
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
class C extends class {} {
|
||||
a = 1;
|
||||
static c = 3;
|
||||
constructor() {
|
||||
(() => super())();
|
||||
}
|
||||
}
|
||||
|
||||
new A;
|
||||
new B;
|
||||
new C;
|
||||
}
|
||||
"
|
||||
frame size: 12
|
||||
parameter count: 1
|
||||
bytecode array length: 204
|
||||
bytecodes: [
|
||||
/* 30 E> */ B(StackCheck),
|
||||
B(LdaTheHole),
|
||||
B(Star), R(11),
|
||||
B(CreateClosure), U8(2), U8(0), U8(2),
|
||||
B(Star), R(10),
|
||||
B(LdaConstant), U8(1),
|
||||
B(Star), R(9),
|
||||
B(CallRuntime), U16(Runtime::kDefineClass), R(9), U8(3),
|
||||
B(Star), R(8),
|
||||
B(CreateClosure), U8(3), U8(1), U8(2),
|
||||
B(Star), R(7),
|
||||
B(LdaConstant), U8(0),
|
||||
B(Star), R(6),
|
||||
B(CallRuntime), U16(Runtime::kDefineClass), R(6), U8(3),
|
||||
B(Star), R(2),
|
||||
B(CreateClosure), U8(4), U8(2), U8(2),
|
||||
B(Star), R(7),
|
||||
B(StaNamedProperty), R(2), U8(5), U8(3),
|
||||
B(CreateClosure), U8(6), U8(5), U8(2),
|
||||
B(Star), R(9),
|
||||
B(CallProperty0), R(9), R(2), U8(6),
|
||||
B(Mov), R(2), R(3),
|
||||
B(LdaTheHole),
|
||||
B(Star), R(11),
|
||||
/* 38 E> */ B(CreateClosure), U8(9), U8(8), U8(2),
|
||||
B(Star), R(10),
|
||||
B(LdaConstant), U8(8),
|
||||
B(Star), R(9),
|
||||
B(CallRuntime), U16(Runtime::kDefineClass), R(9), U8(3),
|
||||
B(Star), R(8),
|
||||
B(CreateClosure), U8(10), U8(9), U8(2),
|
||||
B(Star), R(7),
|
||||
B(LdaConstant), U8(7),
|
||||
B(Star), R(6),
|
||||
B(CallRuntime), U16(Runtime::kDefineClass), R(6), U8(3),
|
||||
B(Star), R(1),
|
||||
B(CreateClosure), U8(11), U8(10), U8(2),
|
||||
B(Star), R(7),
|
||||
B(StaNamedProperty), R(1), U8(5), U8(11),
|
||||
B(CreateClosure), U8(12), U8(13), U8(2),
|
||||
B(Star), R(9),
|
||||
B(CallProperty0), R(9), R(1), U8(14),
|
||||
B(Mov), R(1), R(4),
|
||||
B(LdaTheHole),
|
||||
B(Star), R(11),
|
||||
/* 93 E> */ B(CreateClosure), U8(15), U8(16), U8(2),
|
||||
B(Star), R(10),
|
||||
B(LdaConstant), U8(14),
|
||||
B(Star), R(9),
|
||||
B(CallRuntime), U16(Runtime::kDefineClass), R(9), U8(3),
|
||||
B(Star), R(8),
|
||||
B(CreateClosure), U8(16), U8(17), U8(2),
|
||||
B(Star), R(7),
|
||||
B(LdaConstant), U8(13),
|
||||
B(Star), R(6),
|
||||
B(CallRuntime), U16(Runtime::kDefineClass), R(6), U8(3),
|
||||
B(Star), R(0),
|
||||
B(CreateClosure), U8(17), U8(18), U8(2),
|
||||
B(Star), R(7),
|
||||
B(StaNamedProperty), R(0), U8(5), U8(19),
|
||||
B(CreateClosure), U8(18), U8(21), U8(2),
|
||||
B(Star), R(9),
|
||||
B(CallProperty0), R(9), R(0), U8(22),
|
||||
B(Mov), R(0), R(5),
|
||||
/* 311 S> */ B(Ldar), R(2),
|
||||
/* 311 E> */ B(Construct), R(2), R(0), U8(0), U8(24),
|
||||
/* 320 S> */ B(Ldar), R(1),
|
||||
/* 320 E> */ B(Construct), R(1), R(0), U8(0), U8(26),
|
||||
/* 329 S> */ B(Ldar), R(0),
|
||||
/* 329 E> */ B(Construct), R(0), R(0), U8(0), U8(28),
|
||||
B(LdaUndefined),
|
||||
/* 338 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
FIXED_ARRAY_TYPE,
|
||||
FIXED_ARRAY_TYPE,
|
||||
SHARED_FUNCTION_INFO_TYPE,
|
||||
SHARED_FUNCTION_INFO_TYPE,
|
||||
SHARED_FUNCTION_INFO_TYPE,
|
||||
SYMBOL_TYPE,
|
||||
SHARED_FUNCTION_INFO_TYPE,
|
||||
FIXED_ARRAY_TYPE,
|
||||
FIXED_ARRAY_TYPE,
|
||||
SHARED_FUNCTION_INFO_TYPE,
|
||||
SHARED_FUNCTION_INFO_TYPE,
|
||||
SHARED_FUNCTION_INFO_TYPE,
|
||||
SHARED_FUNCTION_INFO_TYPE,
|
||||
FIXED_ARRAY_TYPE,
|
||||
FIXED_ARRAY_TYPE,
|
||||
SHARED_FUNCTION_INFO_TYPE,
|
||||
SHARED_FUNCTION_INFO_TYPE,
|
||||
SHARED_FUNCTION_INFO_TYPE,
|
||||
SHARED_FUNCTION_INFO_TYPE,
|
||||
]
|
||||
handlers: [
|
||||
]
|
||||
|
@ -17,7 +17,7 @@ snippet: "
|
||||
test = new B(1, 2, 3).constructor;
|
||||
})();
|
||||
"
|
||||
frame size: 4
|
||||
frame size: 5
|
||||
parameter count: 1
|
||||
bytecode array length: 19
|
||||
bytecodes: [
|
||||
@ -26,9 +26,9 @@ bytecodes: [
|
||||
B(Mov), R(closure), R(1),
|
||||
/* 93 E> */ B(StackCheck),
|
||||
/* 93 S> */ B(Ldar), R(1),
|
||||
B(GetSuperConstructor), R(3),
|
||||
B(GetSuperConstructor), R(4),
|
||||
B(Ldar), R(0),
|
||||
/* 93 E> */ B(ConstructWithSpread), R(3), R(2), U8(1), U8(0),
|
||||
/* 93 E> */ B(ConstructWithSpread), R(4), R(2), U8(1), U8(0),
|
||||
/* 93 S> */ B(Return),
|
||||
]
|
||||
constant pool: [
|
||||
@ -49,7 +49,7 @@ snippet: "
|
||||
test = new B(1, 2, 3).constructor;
|
||||
})();
|
||||
"
|
||||
frame size: 7
|
||||
frame size: 8
|
||||
parameter count: 1
|
||||
bytecode array length: 40
|
||||
bytecodes: [
|
||||
@ -59,12 +59,12 @@ bytecodes: [
|
||||
/* 128 E> */ B(StackCheck),
|
||||
B(Mov), R(2), R(3),
|
||||
/* 140 S> */ B(Ldar), R(closure),
|
||||
B(GetSuperConstructor), R(4),
|
||||
B(GetSuperConstructor), R(5),
|
||||
B(LdaSmi), I8(1),
|
||||
B(Star), R(5),
|
||||
B(Star), R(6),
|
||||
B(Ldar), R(0),
|
||||
B(Mov), R(2), R(6),
|
||||
/* 140 E> */ B(ConstructWithSpread), R(4), R(5), U8(2), U8(0),
|
||||
B(Mov), R(2), R(7),
|
||||
/* 140 E> */ B(ConstructWithSpread), R(5), R(6), U8(2), U8(0),
|
||||
B(Star), R(4),
|
||||
B(Ldar), R(this),
|
||||
/* 140 E> */ B(ThrowSuperAlreadyCalledIfNotHole),
|
||||
|
@ -44,6 +44,7 @@ class ProgramOptions final {
|
||||
top_level_(false),
|
||||
do_expressions_(false),
|
||||
async_iteration_(false),
|
||||
class_fields_(false),
|
||||
verbose_(false) {}
|
||||
|
||||
bool Validate() const;
|
||||
@ -63,6 +64,7 @@ class ProgramOptions final {
|
||||
bool top_level() const { return top_level_; }
|
||||
bool do_expressions() const { return do_expressions_; }
|
||||
bool async_iteration() const { return async_iteration_; }
|
||||
bool class_fields() const { return class_fields_; }
|
||||
bool verbose() const { return verbose_; }
|
||||
bool suppress_runtime_errors() const { return rebaseline_ && !verbose_; }
|
||||
std::vector<std::string> input_filenames() const { return input_filenames_; }
|
||||
@ -80,6 +82,7 @@ class ProgramOptions final {
|
||||
bool top_level_;
|
||||
bool do_expressions_;
|
||||
bool async_iteration_;
|
||||
bool class_fields_;
|
||||
bool verbose_;
|
||||
std::vector<std::string> input_filenames_;
|
||||
std::string output_filename_;
|
||||
@ -169,6 +172,8 @@ ProgramOptions ProgramOptions::FromCommandLine(int argc, char** argv) {
|
||||
options.do_expressions_ = true;
|
||||
} else if (strcmp(argv[i], "--async-iteration") == 0) {
|
||||
options.async_iteration_ = true;
|
||||
} else if (strcmp(argv[i], "--class-fields") == 0) {
|
||||
options.class_fields_ = true;
|
||||
} else if (strcmp(argv[i], "--verbose") == 0) {
|
||||
options.verbose_ = true;
|
||||
} else if (strncmp(argv[i], "--output=", 9) == 0) {
|
||||
@ -273,6 +278,8 @@ void ProgramOptions::UpdateFromHeader(std::istream& stream) {
|
||||
do_expressions_ = ParseBoolean(line.c_str() + 16);
|
||||
} else if (line.compare(0, 17, "async iteration: ") == 0) {
|
||||
async_iteration_ = ParseBoolean(line.c_str() + 17);
|
||||
} else if (line.compare(0, 14, "class fields: ") == 0) {
|
||||
class_fields_ = ParseBoolean(line.c_str() + 14);
|
||||
} else if (line == "---") {
|
||||
break;
|
||||
} else if (line.empty()) {
|
||||
@ -296,6 +303,7 @@ void ProgramOptions::PrintHeader(std::ostream& stream) const { // NOLINT
|
||||
if (top_level_) stream << "\ntop level: yes";
|
||||
if (do_expressions_) stream << "\ndo expressions: yes";
|
||||
if (async_iteration_) stream << "\nasync iteration: yes";
|
||||
if (class_fields_) stream << "\nclass fields: yes";
|
||||
|
||||
stream << "\n\n";
|
||||
}
|
||||
@ -400,6 +408,7 @@ void GenerateExpectationsFile(std::ostream& stream, // NOLINT
|
||||
|
||||
if (options.do_expressions()) i::FLAG_harmony_do_expressions = true;
|
||||
if (options.async_iteration()) i::FLAG_harmony_async_iteration = true;
|
||||
if (options.class_fields()) i::FLAG_harmony_class_fields = true;
|
||||
|
||||
stream << "#\n# Autogenerated by generate-bytecode-expectations.\n#\n\n";
|
||||
options.PrintHeader(stream);
|
||||
@ -409,6 +418,7 @@ void GenerateExpectationsFile(std::ostream& stream, // NOLINT
|
||||
|
||||
i::FLAG_harmony_do_expressions = false;
|
||||
i::FLAG_harmony_async_iteration = false;
|
||||
i::FLAG_harmony_class_fields = false;
|
||||
}
|
||||
|
||||
bool WriteExpectationsFile(const std::vector<std::string>& snippet_list,
|
||||
@ -456,6 +466,7 @@ void PrintUsage(const char* exec_path) {
|
||||
" --top-level Process top level code, not the top-level function.\n"
|
||||
" --do-expressions Enable harmony_do_expressions flag.\n"
|
||||
" --async-iteration Enable harmony_async_iteration flag.\n"
|
||||
" --class-fields Enable harmony_class_fields flag.\n"
|
||||
" --output=file.name\n"
|
||||
" Specify the output file. If not specified, output goes to "
|
||||
"stdout.\n"
|
||||
|
@ -2233,6 +2233,45 @@ TEST(ClassAndSuperClass) {
|
||||
LoadGolden("ClassAndSuperClass.golden")));
|
||||
}
|
||||
|
||||
TEST(ClassFields) {
|
||||
bool old_flag = i::FLAG_harmony_class_fields;
|
||||
i::FLAG_harmony_class_fields = true;
|
||||
InitializedIgnitionHandleScope scope;
|
||||
BytecodeExpectationsPrinter printer(CcTest::isolate());
|
||||
|
||||
const char* snippets[] = {
|
||||
"{\n"
|
||||
" class A extends class {} {\n"
|
||||
" a;\n"
|
||||
" static c;\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" class B extends class {} {\n"
|
||||
" a = 1;\n"
|
||||
" static c = 3;\n"
|
||||
" constructor() {\n"
|
||||
" super();\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" class C extends class {} {\n"
|
||||
" a = 1;\n"
|
||||
" static c = 3;\n"
|
||||
" constructor() {\n"
|
||||
" (() => super())();\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
" new A;\n"
|
||||
" new B;\n"
|
||||
" new C;\n"
|
||||
"}\n"};
|
||||
|
||||
CHECK(CompareTexts(BuildActual(printer, snippets),
|
||||
LoadGolden("ClassFields.golden")));
|
||||
i::FLAG_harmony_class_fields = old_flag;
|
||||
}
|
||||
|
||||
TEST(Generators) {
|
||||
InitializedIgnitionHandleScope scope;
|
||||
BytecodeExpectationsPrinter printer(CcTest::isolate());
|
||||
|
@ -31,6 +31,20 @@
|
||||
assertEquals(1, c.x);
|
||||
}
|
||||
|
||||
{
|
||||
function t() {
|
||||
class X {
|
||||
x = 1;
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
var x = new X;
|
||||
return x.x;
|
||||
}
|
||||
|
||||
assertEquals(1, t());
|
||||
}
|
||||
|
||||
{
|
||||
let x = 'a';
|
||||
class C {
|
||||
@ -313,3 +327,275 @@ x();
|
||||
assertThrows(() => { class X { [X] } let x = new X;});
|
||||
assertEquals(undefined, d[C]);
|
||||
}
|
||||
|
||||
{
|
||||
class B {
|
||||
a = 1;
|
||||
}
|
||||
|
||||
class C extends B {
|
||||
b = 2;
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
let c = new C;
|
||||
assertEquals(1, c.a);
|
||||
assertEquals(2, c.b);
|
||||
}
|
||||
|
||||
{
|
||||
var log = [];
|
||||
function addToLog(item) { log.push(item); }
|
||||
|
||||
class B {
|
||||
a = 1;
|
||||
constructor() {
|
||||
addToLog("base constructor");
|
||||
}
|
||||
}
|
||||
|
||||
function initF() {
|
||||
addToLog("init f");
|
||||
return 1;
|
||||
}
|
||||
|
||||
class C extends B {
|
||||
f = initF();
|
||||
|
||||
constructor() {
|
||||
addToLog("derived constructor");
|
||||
var t = () => {
|
||||
addToLog("t");
|
||||
if (1==-1) {
|
||||
super();
|
||||
} else {
|
||||
super();
|
||||
}
|
||||
}
|
||||
(() => {
|
||||
addToLog("anon");
|
||||
t();
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
let c = new C;
|
||||
assertEquals(1, c.f);
|
||||
assertEquals(1, c.a);
|
||||
assertEquals(["derived constructor","anon","t","base constructor","init f"],
|
||||
log);
|
||||
}
|
||||
|
||||
{
|
||||
class B {
|
||||
a = 1;
|
||||
returnA = () => this.a;
|
||||
}
|
||||
|
||||
class C extends B {
|
||||
c = this.a;
|
||||
d = 2;
|
||||
returnC = () => this.c;
|
||||
returnD = () => this.d;
|
||||
}
|
||||
|
||||
let c = new C;
|
||||
assertEquals(1, c.a);
|
||||
assertEquals(1, c.returnA());
|
||||
assertEquals(1, c.c);
|
||||
assertEquals(1, c.returnA());
|
||||
assertEquals(1, c.returnC());
|
||||
assertEquals(2, c.d);
|
||||
assertEquals(2, c.returnD());
|
||||
|
||||
let c2 = new C;
|
||||
assertNotEquals(c2.returnA, c.returnA);
|
||||
assertNotEquals(c2.returnC, c.returnC);
|
||||
assertNotEquals(c2.returnD, c.returnD);
|
||||
}
|
||||
|
||||
{
|
||||
let foo = undefined;
|
||||
class B {
|
||||
set d(x) {
|
||||
foo = x;
|
||||
}
|
||||
}
|
||||
|
||||
class C extends B {
|
||||
d = 2;
|
||||
}
|
||||
|
||||
let c = new C;
|
||||
assertEquals(undefined, foo);
|
||||
assertEquals(2, c.d);
|
||||
}
|
||||
|
||||
{
|
||||
class B {}
|
||||
class C extends B {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
c = 1;
|
||||
}
|
||||
|
||||
let c = new C;
|
||||
assertEquals(1, c.c);
|
||||
}
|
||||
|
||||
{
|
||||
class B {}
|
||||
class C extends B {
|
||||
constructor() {
|
||||
let t = () => {
|
||||
super();
|
||||
}
|
||||
t();
|
||||
}
|
||||
|
||||
c = 1;
|
||||
}
|
||||
|
||||
let c = new C;
|
||||
assertEquals(1, c.c);
|
||||
}
|
||||
|
||||
{
|
||||
let log = [];
|
||||
|
||||
class B {}
|
||||
|
||||
class C extends B {
|
||||
|
||||
x = (log.push(1), 1);
|
||||
|
||||
constructor() {
|
||||
let t = () => {
|
||||
class D extends B {
|
||||
|
||||
x = (log.push(2), 2);
|
||||
|
||||
constructor() {
|
||||
let p = () => {
|
||||
super();
|
||||
}
|
||||
|
||||
p();
|
||||
}
|
||||
}
|
||||
|
||||
let d = new D();
|
||||
assertEquals(2, d.x);
|
||||
super();
|
||||
}
|
||||
|
||||
t();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let c = new C;
|
||||
assertEquals(1, c.x);
|
||||
assertEquals([2, 1], log);
|
||||
}
|
||||
|
||||
{
|
||||
let log = [];
|
||||
class C1 extends class {} {
|
||||
x = log.push(1);
|
||||
constructor() {
|
||||
var t = () => super();
|
||||
super();
|
||||
t();
|
||||
}
|
||||
}
|
||||
|
||||
assertThrows(() => new C1, ReferenceError);
|
||||
assertEquals([1,1], log);
|
||||
|
||||
log = [];
|
||||
class C2 extends class {} {
|
||||
x = log.push(1);
|
||||
constructor() {
|
||||
var t = () => super();
|
||||
t();
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
assertThrows(() => new C2, ReferenceError);
|
||||
assertEquals([1,1], log);
|
||||
}
|
||||
|
||||
{
|
||||
class C1 extends class {} {
|
||||
x = 1
|
||||
constructor() {
|
||||
eval("super()");
|
||||
}
|
||||
}
|
||||
|
||||
let c = new C1;
|
||||
assertEquals(1, c.x);
|
||||
|
||||
class C2 extends class {} {
|
||||
x = 1
|
||||
constructor() {
|
||||
var t = () => {
|
||||
eval("super()");
|
||||
}
|
||||
t();
|
||||
}
|
||||
}
|
||||
|
||||
c = new C2;
|
||||
assertEquals(1, c.x);
|
||||
}
|
||||
|
||||
{
|
||||
class C {
|
||||
['x'] = 1;
|
||||
['y'] = 2;
|
||||
}
|
||||
|
||||
class C1 extends C {
|
||||
['x'] = 3;
|
||||
['z'] = 4;
|
||||
}
|
||||
|
||||
let c = new C1;
|
||||
assertEquals(3, c.x);
|
||||
assertEquals(2, c.y);
|
||||
assertEquals(4, c.z);
|
||||
}
|
||||
|
||||
{
|
||||
class X extends class {} {
|
||||
c = 1;
|
||||
|
||||
constructor() {
|
||||
let t = () => {
|
||||
|
||||
class P extends class {} {
|
||||
constructor() {
|
||||
let t = () => { super(); };
|
||||
t();
|
||||
}
|
||||
}
|
||||
|
||||
let p = new P;
|
||||
assertEquals(undefined, p.c);
|
||||
super();
|
||||
}
|
||||
|
||||
t();
|
||||
}
|
||||
}
|
||||
|
||||
let x = new X;
|
||||
assertEquals(1, x.c);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user