Commit d11a0648 authored by Daniel Clifford's avatar Daniel Clifford Committed by Commit Bot

[torque] Implement safe initialization of classes through hidden structs

Initialization of classes now happens atomically at the end of the
class constructor only once all of the values for the class' fields
have been fully computed. This makes Torque constructors completely
GC safe, e.g. hardened against allocations or exceptions in
constructors.

As part of this change, make the 'this' parameter for method calls
explicit rather than implicit.

Drive by: add validation to check for duplicate field declarations

Bug: v8:7793
Change-Id: I8b5e85980d6a103ef9fc3262b76f6514f36ebf88
Reviewed-on: https://chromium-review.googlesource.com/c/1411252
Commit-Queue: Daniel Clifford <danno@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58979}
parent f6549baf
......@@ -688,6 +688,10 @@ struct NameAndTypeExpression {
TypeExpression* type;
};
struct StructFieldExpression {
NameAndTypeExpression name_and_type;
};
struct ClassFieldExpression {
NameAndTypeExpression name_and_type;
bool weak;
......@@ -883,33 +887,32 @@ struct StructDeclaration : Declaration {
DEFINE_AST_NODE_LEAF_BOILERPLATE(StructDeclaration)
StructDeclaration(SourcePosition pos, std::string name,
std::vector<Declaration*> methods,
std::vector<NameAndTypeExpression> fields)
std::vector<StructFieldExpression> fields)
: Declaration(kKind, pos),
name(std::move(name)),
methods(std::move(methods)),
fields(std::move(fields)) {}
std::string name;
std::vector<Declaration*> methods;
std::vector<NameAndTypeExpression> fields;
std::vector<StructFieldExpression> fields;
};
struct ClassDeclaration : Declaration {
DEFINE_AST_NODE_LEAF_BOILERPLATE(ClassDeclaration)
ClassDeclaration(SourcePosition pos, std::string name, bool transient,
base::Optional<std::string> extends,
base::Optional<std::string> generates,
std::string super, base::Optional<std::string> generates,
std::vector<Declaration*> methods,
std::vector<ClassFieldExpression> fields)
: Declaration(kKind, pos),
name(std::move(name)),
transient(transient),
extends(std::move(extends)),
super(std::move(super)),
generates(std::move(generates)),
methods(std::move(methods)),
fields(std::move(fields)) {}
std::string name;
bool transient;
base::Optional<std::string> extends;
std::string super;
base::Optional<std::string> generates;
std::vector<Declaration*> methods;
std::vector<ClassFieldExpression> fields;
......
......@@ -326,7 +326,9 @@ class Method : public Macro {
DECLARE_DECLARABLE_BOILERPLATE(Method, Method);
bool ShouldBeInlined() const override {
return Macro::ShouldBeInlined() ||
signature().parameter_types.types[0]->IsStructType();
signature()
.parameter_types.types[signature().implicit_count]
->IsStructType();
}
AggregateType* aggregate_type() const { return aggregate_type_; }
......
......@@ -163,13 +163,6 @@ void DeclarationVisitor::Visit(GenericDeclaration* decl) {
Declarations::DeclareGeneric(decl->callable->name, decl);
}
static Statement* WrapBodyWithNoThrow(Statement* body, std::string reason) {
return MakeNode<ExpressionStatement>(MakeNode<TryLabelExpression>(
true, MakeNode<StatementExpression>(body),
MakeNode<LabelBlock>("_catch", ParameterList{},
MakeNode<DebugStatement>(reason, true))));
}
void DeclarationVisitor::Visit(SpecializationDeclaration* decl) {
if ((decl->body != nullptr) == decl->external) {
std::stringstream stream;
......@@ -241,21 +234,27 @@ void DeclarationVisitor::DeclareMethods(
// Declare the class' methods
IdentifierExpression* constructor_this = MakeNode<IdentifierExpression>(
std::vector<std::string>{}, kThisParameterName);
AggregateType* constructor_this_type =
container_type->IsStructType()
? container_type
: ClassType::cast(container_type)->struct_type();
for (auto declaration : methods) {
CurrentSourcePosition::Scope pos_scope(declaration->pos);
StandardDeclaration* standard_declaration =
StandardDeclaration::DynamicCast(declaration);
DCHECK(standard_declaration);
TorqueMacroDeclaration* method =
TorqueMacroDeclaration::DynamicCast(standard_declaration->callable);
Signature signature = MakeSignature(method->signature.get());
signature.parameter_names.insert(signature.parameter_names.begin(),
kThisParameterName);
signature.parameter_types.types.insert(
signature.parameter_types.types.begin(), container_type);
signature.implicit_count++;
signature.parameter_names.insert(
signature.parameter_names.begin() + signature.implicit_count,
kThisParameterName);
Statement* body = *(standard_declaration->body);
std::string method_name(method->name);
if (method->name == kConstructMethodName) {
signature.parameter_types.types.insert(
signature.parameter_types.types.begin() + signature.implicit_count,
constructor_this_type);
// Constructor
if (!signature.return_type->IsVoid()) {
ReportError("constructors musn't have a return type");
......@@ -264,15 +263,15 @@ void DeclarationVisitor::DeclareMethods(
ReportError("constructors musn't have labels");
}
method_name = kConstructMethodName;
signature.return_type = container_type;
ReturnStatement* return_statement = MakeNode<ReturnStatement>(
MakeNode<IdentifierExpression>(kThisParameterName));
body = MakeNode<BlockStatement>(
false, std::vector<Statement*>{body, return_statement});
body = WrapBodyWithNoThrow(body, "exception thrown from constructor");
Declarations::CreateMethod(constructor_this_type, method_name, signature,
false, body);
} else {
signature.parameter_types.types.insert(
signature.parameter_types.types.begin() + signature.implicit_count,
container_type);
Declarations::CreateMethod(container_type, method_name, signature, false,
body);
}
Declarations::CreateMethod(container_type, method_name, signature, false,
body);
}
if (container_type->Constructors().size() != 0) return;
......@@ -280,7 +279,7 @@ void DeclarationVisitor::DeclareMethods(
// Generate default constructor.
Signature constructor_signature;
constructor_signature.parameter_types.var_args = false;
constructor_signature.return_type = container_type;
constructor_signature.return_type = TypeOracle::GetVoidType();
std::vector<const AggregateType*> hierarchy = container_type->GetHierarchy();
std::vector<Statement*> statements;
......@@ -288,8 +287,7 @@ void DeclarationVisitor::DeclareMethods(
size_t parameter_number = 0;
constructor_signature.parameter_names.push_back(kThisParameterName);
constructor_signature.parameter_types.types.push_back(container_type);
constructor_signature.implicit_count = 1;
constructor_signature.parameter_types.types.push_back(constructor_this_type);
std::vector<Expression*> super_arguments;
for (auto current_type : hierarchy) {
for (auto& f : current_type->fields()) {
......@@ -302,13 +300,7 @@ void DeclarationVisitor::DeclareMethods(
if (container_type != current_type) {
super_arguments.push_back(MakeNode<IdentifierExpression>(
std::vector<std::string>{}, parameter_name));
} else if (container_type->IsClassType()) {
Statement* statement =
MakeNode<ExpressionStatement>(MakeNode<StoreObjectFieldExpression>(
constructor_this, f.name_and_type.name, value));
initializer_statements.push_back(statement);
} else {
DCHECK(container_type->IsStructType());
LocationExpression* location = MakeNode<FieldAccessExpression>(
constructor_this, f.name_and_type.name);
Statement* statement = MakeNode<ExpressionStatement>(
......@@ -321,26 +313,20 @@ void DeclarationVisitor::DeclareMethods(
if (hierarchy.size() > 1) {
IdentifierExpression* super_identifier = MakeNode<IdentifierExpression>(
std::vector<std::string>{}, kSuperMethodName);
Statement* super_call_statement =
Statement* statement =
MakeNode<ExpressionStatement>(MakeNode<CallMethodExpression>(
constructor_this, super_identifier, super_arguments,
std::vector<std::string>{}));
statements.push_back(super_call_statement);
statements.push_back(statement);
}
for (auto s : initializer_statements) {
statements.push_back(s);
}
statements.push_back(MakeNode<ReturnStatement>(MakeNode<IdentifierExpression>(
std::vector<std::string>{}, kThisParameterName)));
Statement* constructor_body = MakeNode<BlockStatement>(false, statements);
constructor_body = WrapBodyWithNoThrow(constructor_body,
"exception thrown from constructor");
Declarations::CreateMethod(container_type, kConstructMethodName,
Declarations::CreateMethod(constructor_this_type, kConstructMethodName,
constructor_signature, false, constructor_body);
}
......@@ -348,8 +334,11 @@ void DeclarationVisitor::Visit(StructDeclaration* decl) {
std::vector<Field> fields;
size_t offset = 0;
for (auto& field : decl->fields) {
const Type* field_type = Declarations::GetType(field.type);
fields.push_back({{field.name, field_type}, offset, false});
const Type* field_type = Declarations::GetType(field.name_and_type.type);
fields.push_back({field.name_and_type.type->pos,
{field.name_and_type.name, field_type},
offset,
false});
offset += LoweredSlotCount(field_type);
}
StructType* struct_type = Declarations::DeclareStruct(decl->name, fields);
......@@ -360,17 +349,14 @@ void DeclarationVisitor::Visit(ClassDeclaration* decl) {
// Compute the offset of the class' first member. If the class extends
// another class, it's the size of the extended class, otherwise zero.
size_t first_field_offset = 0;
if (decl->extends) {
const Type* super_type = Declarations::LookupType(*decl->extends);
if (super_type != TypeOracle::GetTaggedType()) {
const ClassType* super_class = ClassType::DynamicCast(super_type);
if (!super_class) {
ReportError(
"class \"", decl->name,
"\" must extend either Tagged or an already declared class");
}
first_field_offset = super_class->size();
const Type* super_type = Declarations::LookupType(decl->super);
if (super_type != TypeOracle::GetTaggedType()) {
const ClassType* super_class = ClassType::DynamicCast(super_type);
if (!super_class) {
ReportError("class \"", decl->name,
"\" must extend either Tagged or an already declared class");
}
first_field_offset = super_class->size();
}
// The generates clause must create a TNode<>
......@@ -385,7 +371,7 @@ void DeclarationVisitor::Visit(ClassDeclaration* decl) {
}
std::vector<Field> fields;
size_t offset = first_field_offset;
size_t class_offset = first_field_offset;
bool seen_strong = false;
bool seen_weak = false;
for (ClassFieldExpression& field : decl->fields) {
......@@ -412,13 +398,15 @@ void DeclarationVisitor::Visit(ClassDeclaration* decl) {
"field \"", field.name_and_type.name, "\" of class \"", decl->name,
"\" must be a subtype of Tagged (other types not yet supported)");
}
fields.push_back(
{{field.name_and_type.name, field_type}, offset, field.weak});
offset += kTaggedSize;
fields.push_back({field.name_and_type.type->pos,
{field.name_and_type.name, field_type},
class_offset,
field.weak});
class_offset += kTaggedSize;
}
auto new_class = Declarations::DeclareClass(
decl->extends, decl->name, decl->transient, generates, fields, offset);
super_type, decl->name, decl->transient, generates, fields, class_offset);
DeclareMethods(new_class, decl->methods);
// For each field, construct AST snippits that implement a CSA accessor
......
......@@ -174,16 +174,43 @@ StructType* Declarations::DeclareStruct(const std::string& name,
return new_type;
}
ClassType* Declarations::DeclareClass(base::Optional<std::string> parent,
ClassType* Declarations::DeclareClass(const Type* super_class,
const std::string& name, bool transient,
const std::string& generates,
std::vector<Field> fields, size_t size) {
const Type* parent_type = nullptr;
if (parent) {
parent_type = LookupType(QualifiedName{*parent});
std::vector<Field> this_struct_fields;
size_t struct_offset = 0;
const StructType* super_struct_type = nullptr;
// In order to ensure "atomicity" of object allocation, a class'
// constructors operate on a per-class internal struct rather than the class
// directly until the constructor has successfully completed and all class
// members are available. Create the appropriate struct type for use in the
// class' constructors, including a '_super' field in the struct that
// contains the values constructed by calls to super constructors.
if (super_class->IsClassType()) {
super_struct_type = ClassType::cast(super_class)->struct_type();
this_struct_fields.push_back(
{CurrentSourcePosition::Get(),
{kConstructorStructSuperFieldName, super_struct_type},
struct_offset,
false});
struct_offset += LoweredSlotCount(super_struct_type);
}
ClassType* new_type = TypeOracle::GetClassType(
parent_type, name, transient, generates, std::move(fields), size);
for (auto& field : fields) {
const Type* field_type = field.name_and_type.type;
this_struct_fields.push_back({field.pos,
{field.name_and_type.name, field_type},
struct_offset,
false});
struct_offset += LoweredSlotCount(field_type);
}
StructType* this_struct_type = DeclareStruct(
kClassConstructorThisStructPrefix + name, this_struct_fields);
ClassType* new_type =
TypeOracle::GetClassType(super_class, name, transient, generates,
std::move(fields), this_struct_type, size);
this_struct_type->SetDerivedFrom(new_type);
DeclareType(name, new_type, false);
return new_type;
}
......
......@@ -84,9 +84,8 @@ class Declarations {
static StructType* DeclareStruct(const std::string& name,
const std::vector<Field>& fields);
static ClassType* DeclareClass(base::Optional<std::string> parent,
const std::string& name, bool transient,
const std::string& generates,
static ClassType* DeclareClass(const Type* super, const std::string& name,
bool transient, const std::string& generates,
std::vector<Field> fields, size_t size);
static Macro* CreateMacro(std::string external_name,
......
......@@ -195,33 +195,35 @@ VisitResult ImplementationVisitor::InlineMacro(
const Type* return_type = macro->signature().return_type;
bool can_return = return_type != TypeOracle::GetNeverType();
CurrentConstructorInfo::Scope current_constructor;
if (macro->IsConstructor())
CurrentConstructorInfo::Get() = ConstructorInfo{0};
BlockBindings<LocalValue> parameter_bindings(&ValueBindingsManager::Get());
BlockBindings<LocalLabel> label_bindings(&LabelBindingsManager::Get());
DCHECK_EQ(macro->signature().parameter_names.size(),
arguments.size() + (this_reference ? 1 : 0));
DCHECK_EQ(this_reference.has_value(), macro->IsMethod());
{
size_t i = 0;
// Bind the this for methods. Methods that modify a struct-type "this" must
// only be called if the this is in a variable, in which case the
// LocalValue is non-const. Otherwise, the LocalValue used for the parameter
// binding is const, and thus read-only, which will cause errors if
// modified, e.g. when called by a struct method that sets the structs
// fields. This prevents using temporary struct values for anything other
// than read operations.
if (this_reference) {
DCHECK(macro->IsMethod());
LocalValue this_value = LocalValue{!this_reference->IsVariableAccess(),
this_reference->GetVisitResult()};
parameter_bindings.Add(kThisParameterName, this_value);
i++;
}
// Bind the this for methods. Methods that modify a struct-type "this" must
// only be called if the this is in a variable, in which case the
// LocalValue is non-const. Otherwise, the LocalValue used for the parameter
// binding is const, and thus read-only, which will cause errors if
// modified, e.g. when called by a struct method that sets the structs
// fields. This prevents using temporary struct values for anything other
// than read operations.
if (this_reference) {
DCHECK(macro->IsMethod());
LocalValue this_value = LocalValue{!this_reference->IsVariableAccess(),
this_reference->GetVisitResult()};
parameter_bindings.Add(kThisParameterName, this_value);
}
for (auto arg : arguments) {
const std::string& name = macro->parameter_names()[i++];
parameter_bindings.Add(name, LocalValue{true, arg});
}
size_t i = 0;
for (auto arg : arguments) {
if (this_reference && i == signature.implicit_count) i++;
const std::string& name = macro->parameter_names()[i++];
parameter_bindings.Add(name, LocalValue{true, arg});
}
DCHECK_EQ(label_blocks.size(), signature.labels.size());
......@@ -241,6 +243,16 @@ VisitResult ImplementationVisitor::InlineMacro(
SetReturnValue(VisitResult(return_type,
stack.TopRange(lowered_return_types.size())));
}
// The stack copy used to initialize the _macro_end block is only used
// as a template for the actual gotos generated by return statements. It
// doesn't correspond to any real return values, and thus shouldn't contain
// top types, because these would pollute actual return value types that get
// unioned with them for return statements, erroneously forcing them to top.
for (auto i = stack.begin(); i != stack.end(); ++i) {
if ((*i)->IsTopType()) {
*i = TopType::cast(*i)->source_type();
}
}
macro_end = assembler().NewBlock(std::move(stack));
macro_end_binding.emplace(&LabelBindingsManager::Get(), "_macro_end",
LocalLabel{macro_end, {return_type}});
......@@ -283,6 +295,11 @@ VisitResult ImplementationVisitor::InlineMacro(
}
void ImplementationVisitor::VisitMacroCommon(Macro* macro) {
// Do not generate code for inlined macros.
if (macro->ShouldBeInlined()) {
return;
}
CurrentCallable::Scope current_callable(macro);
const Signature& signature = macro->signature();
const Type* return_type = macro->signature().return_type;
......@@ -307,7 +324,6 @@ void ImplementationVisitor::VisitMacroCommon(Macro* macro) {
std::vector<VisitResult> arguments;
size_t i = 0;
base::Optional<LocationReference> this_reference;
if (Method* method = Method::DynamicCast(macro)) {
const Type* this_type = method->aggregate_type();
......@@ -319,10 +335,10 @@ void ImplementationVisitor::VisitMacroCommon(Macro* macro) {
// Mark the this as a temporary to prevent assignment to it.
this_reference =
LocationReference::Temporary(this_result, "this parameter");
++i;
}
for (; i < macro->signature().parameter_names.size(); ++i) {
for (size_t i = 0; i < macro->signature().parameter_names.size(); ++i) {
if (this_reference && i == macro->signature().implicit_count) continue;
const std::string& name = macro->parameter_names()[i];
std::string external_name = ExternalParameterName(name);
const Type* type = macro->signature().types()[i];
......@@ -389,19 +405,11 @@ void ImplementationVisitor::VisitMacroCommon(Macro* macro) {
void ImplementationVisitor::Visit(Macro* macro) {
if (macro->IsExternal()) return;
// Do not generate code for inlined macros.
if (macro->ShouldBeInlined()) return;
VisitMacroCommon(macro);
}
void ImplementationVisitor::Visit(Method* method) {
DCHECK(!method->IsExternal());
// Do not generate code for inlined methods, they have to be inlined in order
// to get reference semantics for the 'this' argument.
if (method->ShouldBeInlined()) return;
CurrentConstructorInfo::Scope current_constructor;
if (method->IsConstructor())
CurrentConstructorInfo::Get() = ConstructorInfo{0, false};
VisitMacroCommon(method);
}
......@@ -1110,6 +1118,29 @@ const Type* ImplementationVisitor::Visit(ForOfLoopStatement* stmt) {
return TypeOracle::GetVoidType();
}
VisitResult ImplementationVisitor::TemporaryUninitializedStruct(
const StructType* struct_type, const std::string& reason) {
StackRange range = assembler().TopRange(0);
for (const Field& f : struct_type->fields()) {
if (const StructType* struct_type =
StructType::DynamicCast(f.name_and_type.type)) {
range.Extend(
TemporaryUninitializedStruct(struct_type, reason).stack_range());
} else {
std::string descriptor = "unitialized field '" + f.name_and_type.name +
"' declared at " + PositionAsString(f.pos) +
" (" + reason + ")";
TypeVector lowered_types = LowerType(f.name_and_type.type);
for (const Type* type : lowered_types) {
assembler().Emit(PushUninitializedInstruction{
TypeOracle::GetTopType(descriptor, type)});
}
range.Extend(assembler().TopRange(lowered_types.size()));
}
}
return VisitResult(struct_type, range);
}
VisitResult ImplementationVisitor::Visit(TryLabelExpression* expr) {
size_t parameter_count = expr->label_block->parameters.names.size();
std::vector<VisitResult> parameters;
......@@ -1196,23 +1227,70 @@ VisitResult ImplementationVisitor::Visit(NewExpression* expr) {
ReportError("type for new expression must be a class, \"", *type,
"\" is not");
}
// In order to ensure "atomicity" of object allocation, a class' constructors
// operate on a per-class internal struct rather than the class directly until
// the constructor has successfully completed and all class members are
// available. Create the appropriate unitialized struct and pass it to the
// matching class constructor with the arguments that were passed to new{}
StructType* class_this_struct = class_type->struct_type();
VisitResult unitialized_struct = TemporaryUninitializedStruct(
class_this_struct,
"it's not set in the constructor for class " + class_type->name());
Arguments constructor_arguments;
for (auto p : expr->parameters) {
constructor_arguments.parameters.push_back(Visit(p));
}
LocationReference unitialized_struct_ref =
LocationReference::VariableAccess(unitialized_struct);
Callable* callable =
LookupConstructor(unitialized_struct_ref, constructor_arguments, {});
GenerateCall(callable, unitialized_struct_ref, constructor_arguments,
{class_type}, false);
VisitResult new_struct_result = unitialized_struct;
// Output the code to generate an unitialized object of the class size in the
// GC heap.
Arguments allocate_arguments;
allocate_arguments.parameters.push_back(VisitResult(
TypeOracle::GetConstInt31Type(), std::to_string(class_type->size())));
VisitResult allocate_result =
GenerateCall("%Allocate", allocate_arguments, {class_type}, false);
Arguments constructor_arguments;
for (auto p : expr->parameters) {
constructor_arguments.parameters.push_back(Visit(p));
DCHECK(allocate_result.IsOnStack());
// Fill in the fields of the newly allocated class by copying the values
// from the struct that was built by the constructor. So that the generaeted
// code is a bit more readable, assign the values from the first class
// member to the last, in order. To do this, first build a list of fields
// to assign to in reverse order by visiting the class heirarchy.
std::vector<std::pair<const Field*, VisitResult>> store_pairs;
const ClassType* current_class = class_type;
while (current_class != nullptr) {
auto& fields = current_class->fields();
for (auto i = fields.rbegin(); i != fields.rend(); ++i) {
store_pairs.push_back(std::make_pair(
&*i, ProjectStructField(new_struct_result, i->name_and_type.name)));
}
current_class = current_class->GetSuperClass();
if (current_class) {
new_struct_result = ProjectStructField(new_struct_result,
kConstructorStructSuperFieldName);
}
}
Callable* callable =
LookupCall({{}, kConstructMethodName}, class_type->Constructors(),
constructor_arguments, {});
return stack_scope.Yield(
GenerateCall(callable,
LocationReference::Temporary(allocate_result,
"unitialized object from new"),
constructor_arguments, {class_type}, false));
// Now that the reversed list of fields and the assignment VisitResults are
// available, emit the copies in reverse order of the reversed list to
// produce the class field assignments in the expected order.
for (auto i = store_pairs.rbegin(); i != store_pairs.rend(); ++i) {
assembler().Emit(
PeekInstruction(allocate_result.stack_range().begin(), class_type));
assembler().Emit(PeekInstruction(i->second.stack_range().begin(),
i->first->name_and_type.type));
assembler().Emit(
StoreObjectFieldInstruction(class_type, i->first->name_and_type.name));
}
return stack_scope.Yield(allocate_result);
}
const Type* ImplementationVisitor::Visit(BreakStatement* stmt) {
......@@ -1357,17 +1435,15 @@ void ImplementationVisitor::GenerateFunctionDeclaration(
namespace {
void FailCallableLookup(const std::string& reason, const QualifiedName& name,
const Arguments& arguments,
const TypeVector& parameter_types,
const std::vector<Binding<LocalLabel>*>& labels,
const std::vector<Signature>& candidates) {
std::stringstream stream;
stream << "\n"
<< reason << ": \n " << name << "("
<< arguments.parameters.GetTypeVector() << ")";
if (arguments.labels.size() != 0) {
stream << "\n" << reason << ": \n " << name << "(" << parameter_types << ")";
if (labels.size() != 0) {
stream << " labels ";
for (size_t i = 0; i < arguments.labels.size(); ++i) {
stream << arguments.labels[i]->name() << "("
<< arguments.labels[i]->parameter_types << ")";
for (size_t i = 0; i < labels.size(); ++i) {
stream << labels[i]->name() << "(" << labels[i]->parameter_types << ")";
}
}
stream << "\ncandidates are:";
......@@ -1416,11 +1492,12 @@ Block* ImplementationVisitor::LookupSimpleLabel(const std::string& name) {
}
template <class Container>
Callable* ImplementationVisitor::LookupCall(
Callable* ImplementationVisitor::LookupCallable(
const QualifiedName& name, const Container& declaration_container,
const Arguments& arguments, const TypeVector& specialization_types) {
const TypeVector& parameter_types,
const std::vector<Binding<LocalLabel>*>& labels,
const TypeVector& specialization_types) {
Callable* result = nullptr;
TypeVector parameter_types(arguments.parameters.GetTypeVector());
std::vector<Declarable*> overloads;
std::vector<Signature> overload_signatures;
......@@ -1443,7 +1520,7 @@ Callable* ImplementationVisitor::LookupCall(
std::vector<size_t> candidates;
for (size_t i = 0; i < overloads.size(); ++i) {
const Signature& signature = overload_signatures[i];
bool try_bool_context = arguments.labels.size() == 0 &&
bool try_bool_context = labels.size() == 0 &&
signature.return_type == TypeOracle::GetNeverType();
base::Optional<Binding<LocalLabel>*> true_label;
base::Optional<Binding<LocalLabel>*> false_label;
......@@ -1451,7 +1528,7 @@ Callable* ImplementationVisitor::LookupCall(
true_label = TryLookupLabel(kTrueLabelName);
false_label = TryLookupLabel(kFalseLabelName);
}
if (IsCompatibleSignature(signature, parameter_types, arguments.labels) ||
if (IsCompatibleSignature(signature, parameter_types, labels) ||
(true_label && false_label &&
IsCompatibleSignature(signature, parameter_types,
{*true_label, *false_label}))) {
......@@ -1465,7 +1542,7 @@ Callable* ImplementationVisitor::LookupCall(
ReportError(stream.str());
} else if (candidates.empty()) {
FailCallableLookup("cannot find suitable callable with name", name,
arguments, overload_signatures);
parameter_types, labels, overload_signatures);
}
auto is_better_candidate = [&](size_t a, size_t b) {
......@@ -1485,7 +1562,7 @@ Callable* ImplementationVisitor::LookupCall(
for (size_t i : candidates) {
candidate_signatures.push_back(overload_signatures[i]);
}
FailCallableLookup("ambiguous callable", name, arguments,
FailCallableLookup("ambiguous callable ", name, parameter_types, labels,
candidate_signatures);
}
}
......@@ -1513,6 +1590,27 @@ Callable* ImplementationVisitor::LookupCall(
return result;
}
template <class Container>
Callable* ImplementationVisitor::LookupCallable(
const QualifiedName& name, const Container& declaration_container,
const Arguments& arguments, const TypeVector& specialization_types) {
return LookupCallable(name, declaration_container,
arguments.parameters.GetTypeVector(), arguments.labels,
specialization_types);
}
Method* ImplementationVisitor::LookupMethod(
const std::string& name, LocationReference this_reference,
const Arguments& arguments, const TypeVector& specialization_types) {
TypeVector types(arguments.parameters.GetTypeVector());
types.insert(types.begin(), this_reference.GetVisitResult().type());
return Method::cast(
LookupCallable({{}, name},
AggregateType::cast(this_reference.GetVisitResult().type())
->Methods(name),
types, arguments.labels, specialization_types));
}
const Type* ImplementationVisitor::GetCommonType(const Type* left,
const Type* right) {
const Type* common_type;
......@@ -1545,31 +1643,19 @@ VisitResult ImplementationVisitor::Visit(StructExpression* decl) {
ReportError(s.str());
}
const StructType* struct_type = StructType::cast(raw_type);
if (struct_type->fields().size() != decl->expressions.size()) {
std::stringstream s;
s << "initializer count mismatch for struct " << decl->name << " (expected "
<< struct_type->fields().size() << ", found " << decl->expressions.size()
<< ")";
ReportError(s.str());
}
// Push uninitialized 'this'
TypeVector lowered_types = LowerType(struct_type);
for (const Type* type : lowered_types) {
assembler().Emit(PushUninitializedInstruction{TypeOracle::GetTopType(
"unitialized struct field at " + PositionAsString(decl->pos), type)});
}
VisitResult uninitialized_struct =
VisitResult(struct_type, assembler().TopRange(lowered_types.size()));
// Push unitialized 'this'
VisitResult uninitialized_struct = TemporaryUninitializedStruct(
struct_type,
"it's not set in the constructor for struct " + struct_type->name());
Arguments constructor_arguments;
for (auto p : decl->expressions) {
constructor_arguments.parameters.push_back(Visit(p));
}
Callable* callable =
LookupCall({{}, kConstructMethodName}, struct_type->Constructors(),
constructor_arguments, {});
return stack_scope.Yield(GenerateCall(
callable, LocationReference::VariableAccess(uninitialized_struct),
constructor_arguments, {}, false));
LocationReference this_ref =
LocationReference::VariableAccess(uninitialized_struct);
Callable* callable = LookupConstructor(this_ref, constructor_arguments, {});
GenerateCall(callable, this_ref, constructor_arguments, {}, false);
return stack_scope.Yield(uninitialized_struct);
}
LocationReference ImplementationVisitor::GetLocationReference(
......@@ -1615,11 +1701,6 @@ LocationReference ImplementationVisitor::GetLocationReference(
LocationReference ImplementationVisitor::GetLocationReference(
IdentifierExpression* expr) {
if (expr->namespace_qualification.empty()) {
if (expr->IsThis()) {
if (CurrentConstructorInfo::Get()) {
CurrentConstructorInfo::Get()->accessed_this = true;
}
}
if (base::Optional<Binding<LocalValue>*> value =
TryLookupLocalValue(expr->name)) {
if (expr->generic_arguments.size() != 0) {
......@@ -1797,6 +1878,21 @@ VisitResult ImplementationVisitor::GenerateCall(
std::vector<std::string> constexpr_arguments;
size_t current = 0;
for (; current < callable->signature().implicit_count; ++current) {
std::string implicit_name = callable->signature().parameter_names[current];
base::Optional<Binding<LocalValue>*> val =
TryLookupLocalValue(implicit_name);
if (!val) {
ReportError("implicit parameter '", implicit_name,
"' required for call to '", callable->ReadableName(),
"' is not defined");
}
AddCallParameter(callable, (*val)->value,
callable->signature().parameter_types.types[current],
&converted_arguments, &argument_range,
&constexpr_arguments);
}
if (this_reference) {
DCHECK(callable->IsMethod());
Method* method = Method::cast(callable);
......@@ -1806,7 +1902,8 @@ VisitResult ImplementationVisitor::GenerateCall(
if (method->ShouldBeInlined()) {
if (!this_value.type()->IsSubtypeOf(method->aggregate_type())) {
ReportError("this parameter must be a subtype of ",
*method->aggregate_type());
*method->aggregate_type(), " but it is of type ",
this_value.type());
}
} else {
AddCallParameter(callable, this_value, method->aggregate_type(),
......@@ -1816,21 +1913,6 @@ VisitResult ImplementationVisitor::GenerateCall(
++current;
}
for (; current < callable->signature().implicit_count; ++current) {
std::string implicit_name = callable->signature().parameter_names[current];
base::Optional<Binding<LocalValue>*> val =
TryLookupLocalValue(implicit_name);
if (!val) {
ReportError("implicit parameter '", implicit_name,
"' required for call to '", callable->ReadableName(),
"' is not defined");
}
AddCallParameter(callable, (*val)->value,
callable->signature().parameter_types.types[current],
&converted_arguments, &argument_range,
&constexpr_arguments);
}
for (auto arg : arguments.parameters) {
const Type* to_type = (current >= callable->signature().types().size())
? TypeOracle::GetObjectType()
......@@ -2016,8 +2098,8 @@ VisitResult ImplementationVisitor::GenerateCall(
const QualifiedName& callable_name, Arguments arguments,
const TypeVector& specialization_types, bool is_tailcall) {
Callable* callable =
LookupCall(callable_name, Declarations::Lookup(callable_name), arguments,
specialization_types);
LookupCallable(callable_name, Declarations::Lookup(callable_name),
arguments, specialization_types);
return GenerateCall(callable, base::nullopt, arguments, specialization_types,
is_tailcall);
}
......@@ -2050,17 +2132,35 @@ VisitResult ImplementationVisitor::Visit(CallMethodExpression* expr) {
std::string method_name = expr->method->name;
TypeVector specialization_types =
GetTypeVector(expr->method->generic_arguments);
LocationReference target = GetLocationReference(expr->target);
if (!target.IsVariableAccess()) {
VisitResult result = GenerateFetchFromLocation(target);
target = LocationReference::Temporary(result, "method target result");
}
const AggregateType* target_type =
AggregateType::DynamicCast(target.GetVisitResult().type());
if (!target_type) {
ReportError("target of method call not a struct or class type");
}
if (method_name == kConstructMethodName || method_name == kSuperMethodName) {
if (CurrentConstructorInfo::Get()) {
ConstructorInfo& info = *CurrentConstructorInfo::Get();
if (method_name == kSuperMethodName) {
if (info.accessed_this) {
ReportError("cannot call super after accessing \"this\"");
}
if (info.super_calls != 0) {
ReportError("\"super\" can only be called once from a constructor");
}
++info.super_calls;
CHECK(target_type->IsStructType());
base::Optional<const ClassType*> derived_from =
StructType::cast(target_type)->GetDerivedFrom();
if (!derived_from) {
ReportError("\"super\" can only be called from class constructors");
}
if ((*derived_from)->GetSuperClass() == nullptr) {
ReportError(
"\"super\" can only be called in constructors for derived "
"classes");
}
} else {
ReportError("cannot call a constructor from a constructor");
}
......@@ -2069,41 +2169,26 @@ VisitResult ImplementationVisitor::Visit(CallMethodExpression* expr) {
"cannot call a constructor or \"super\" from a non-constructor");
}
}
const AggregateType* target_type;
LocationReference target = GetLocationReference(expr->target);
if (target.IsVariableAccess()) {
target_type = AggregateType::DynamicCast(target.variable().type());
} else {
VisitResult result = GenerateFetchFromLocation(target);
target = LocationReference::Temporary(result, "method target result");
target_type = AggregateType::DynamicCast(result.type());
}
if (!target_type) {
ReportError("target of method call not a struct or class type");
}
for (Expression* arg : expr->arguments)
for (Expression* arg : expr->arguments) {
arguments.parameters.push_back(Visit(arg));
}
arguments.labels = LabelsFromIdentifiers(expr->labels);
TypeVector argument_types = arguments.parameters.GetTypeVector();
DCHECK_EQ(expr->method->namespace_qualification.size(), 0);
QualifiedName qualified_name = QualifiedName(method_name);
Callable* callable = nullptr;
if (method_name == kConstructMethodName) {
callable =
LookupCall(qualified_name, target_type->Constructors(), arguments, {});
callable = LookupConstructor(target, arguments, {});
} else if (method_name == kSuperMethodName) {
if (!target_type->IsClassType()) {
ReportError("cannot call super in struct constructor");
}
target_type = ClassType::cast(target_type)->GetSuperClass();
if (!target_type) {
ReportError("cannot call \"super\" on a class with no superclass");
}
callable = LookupCall(QualifiedName(kConstructMethodName),
target_type->Constructors(), arguments, {});
LocationReference super_this =
LocationReference::VariableAccess(ProjectStructField(
target.GetVisitResult(), kConstructorStructSuperFieldName));
callable = LookupConstructor(super_this, arguments, {});
VisitResult super_result =
GenerateCall(callable, super_this, arguments, {}, false);
return scope.Yield(super_result);
} else {
callable = LookupCall(qualified_name, target_type->Methods(method_name),
arguments, {});
callable = LookupMethod(method_name, target, arguments, {});
}
return scope.Yield(GenerateCall(callable, target, arguments, {}, false));
}
......
......@@ -212,6 +212,8 @@ class ImplementationVisitor : public FileVisitor {
VisitResult Visit(Expression* expr);
const Type* Visit(Statement* stmt);
VisitResult TemporaryUninitializedStruct(const StructType* struct_type,
const std::string& reason);
VisitResult Visit(StructExpression* decl);
LocationReference GetLocationReference(Expression* location);
......@@ -290,7 +292,6 @@ class ImplementationVisitor : public FileVisitor {
struct ConstructorInfo {
int super_calls;
bool accessed_this;
};
DECLARE_CONTEXTUAL_VARIABLE(ValueBindingsManager,
......@@ -396,11 +397,29 @@ class ImplementationVisitor : public FileVisitor {
Binding<LocalLabel>* LookupLabel(const std::string& name);
Block* LookupSimpleLabel(const std::string& name);
template <class Container>
Callable* LookupCall(const QualifiedName& name,
const Container& declaration_container,
Callable* LookupCallable(const QualifiedName& name,
const Container& declaration_container,
const TypeVector& types,
const std::vector<Binding<LocalLabel>*>& labels,
const TypeVector& specialization_types);
template <class Container>
Callable* LookupCallable(const QualifiedName& name,
const Container& declaration_container,
const Arguments& arguments,
const TypeVector& specialization_types);
Method* LookupMethod(const std::string& name, LocationReference target,
const Arguments& arguments,
const TypeVector& specialization_types);
Method* LookupConstructor(LocationReference target,
const Arguments& arguments,
const TypeVector& specialization_types) {
return LookupMethod(kConstructMethodName, target, arguments,
specialization_types);
}
const Type* GetCommonType(const Type* left, const Type* right);
VisitResult GenerateCopy(const VisitResult& to_copy);
......
......@@ -42,8 +42,10 @@ enum class ParseResultHolderBase::TypeId {
kOptionalLabelBlockPtr,
kNameAndTypeExpression,
kClassFieldExpression,
kStructFieldExpression,
kStdVectorOfNameAndTypeExpression,
kStdVectorOfClassFieldExpression,
kStdVectorOfStructFieldExpression,
kIncrementDecrementOperator,
kOptionalStdString,
kStdVectorOfStatementPtr,
......@@ -107,6 +109,10 @@ V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<ClassFieldExpression>::id =
ParseResultTypeId::kClassFieldExpression;
template <>
V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<StructFieldExpression>::id =
ParseResultTypeId::kStructFieldExpression;
template <>
V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<std::vector<NameAndTypeExpression>>::id =
ParseResultTypeId::kStdVectorOfNameAndTypeExpression;
......@@ -115,6 +121,10 @@ V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<std::vector<ClassFieldExpression>>::id =
ParseResultTypeId::kStdVectorOfClassFieldExpression;
template <>
V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<std::vector<StructFieldExpression>>::id =
ParseResultTypeId::kStdVectorOfStructFieldExpression;
template <>
V8_EXPORT_PRIVATE const ParseResultTypeId
ParseResultHolder<IncrementDecrementOperator>::id =
ParseResultTypeId::kIncrementDecrementOperator;
......@@ -581,7 +591,7 @@ base::Optional<ParseResult> MakeClassDeclaration(
if (!IsValidTypeName(name)) {
NamingConventionError("Type", name, "UpperCamelCase");
}
auto extends = child_results->NextAs<base::Optional<std::string>>();
auto extends = child_results->NextAs<std::string>();
auto generates = child_results->NextAs<base::Optional<std::string>>();
auto methods = child_results->NextAs<std::vector<Declaration*>>();
auto fields = child_results->NextAs<std::vector<ClassFieldExpression>>();
......@@ -623,7 +633,7 @@ base::Optional<ParseResult> MakeStructDeclaration(
ParseResultIterator* child_results) {
auto name = child_results->NextAs<std::string>();
auto methods = child_results->NextAs<std::vector<Declaration*>>();
auto fields = child_results->NextAs<std::vector<NameAndTypeExpression>>();
auto fields = child_results->NextAs<std::vector<StructFieldExpression>>();
Declaration* result = MakeNode<StructDeclaration>(
std::move(name), std::move(methods), std::move(fields));
return ParseResult{result};
......@@ -1119,6 +1129,13 @@ base::Optional<ParseResult> MakeClassField(ParseResultIterator* child_results) {
return ParseResult{ClassFieldExpression{{std::move(name), type}, weak}};
}
base::Optional<ParseResult> MakeStructField(
ParseResultIterator* child_results) {
auto name = child_results->NextAs<std::string>();
auto type = child_results->NextAs<TypeExpression*>();
return ParseResult{StructFieldExpression{{std::move(name), type}}};
}
base::Optional<ParseResult> ExtractAssignmentOperator(
ParseResultIterator* child_results) {
auto op = child_results->NextAs<std::string>();
......@@ -1309,6 +1326,9 @@ struct TorqueGrammar : Grammar {
Rule({CheckIf(Token("weak")), &identifier, Token(":"), &type, Token(";")},
MakeClassField)};
Symbol structField = {
Rule({&identifier, Token(":"), &type, Token(";")}, MakeStructField)};
// Result: ParameterList
Symbol parameterListNoVararg = {
Rule({optionalImplicitParameterList, Token("("),
......@@ -1589,7 +1609,7 @@ struct TorqueGrammar : Grammar {
&externalString, Token(";")},
MakeExternConstDeclaration),
Rule({CheckIf(Token("transient")), Token("class"), &identifier,
Optional<std::string>(Sequence({Token("extends"), &identifier})),
Sequence({Token("extends"), &identifier}),
Optional<std::string>(
Sequence({Token("generates"), &externalString})),
Token("{"), List<Declaration*>(&method),
......@@ -1597,8 +1617,7 @@ struct TorqueGrammar : Grammar {
MakeClassDeclaration),
Rule({Token("struct"), &identifier, Token("{"),
List<Declaration*>(&method),
List<NameAndTypeExpression>(Sequence({&nameAndType, Token(";")})),
Token("}")},
List<StructFieldExpression>(&structField), Token("}")},
MakeStructDeclaration),
Rule({CheckIf(Token("transient")), Token("type"), &identifier,
Optional<std::string>(Sequence({Token("extends"), &identifier})),
......
......@@ -38,9 +38,10 @@ class TypeOracle : public ContextualClass<TypeOracle> {
static ClassType* GetClassType(const Type* parent, const std::string& name,
bool transient, const std::string& generates,
const std::vector<Field>& fields,
size_t size) {
ClassType* result = new ClassType(parent, CurrentNamespace(), name,
transient, generates, fields, size);
StructType* this_struct, size_t size) {
ClassType* result =
new ClassType(parent, CurrentNamespace(), name, transient, generates,
fields, this_struct, size);
Get().struct_types_.push_back(std::unique_ptr<ClassType>(result));
return result;
}
......
......@@ -184,6 +184,38 @@ const Type* SubtractType(const Type* a, const Type* b) {
return TypeOracle::GetUnionType(result);
}
AggregateType::AggregateType(Kind kind, const Type* parent, Namespace* nspace,
const std::string& name,
const std::vector<Field>& fields)
: Type(kind, parent), namespace_(nspace), name_(name), fields_(fields) {
// Check the aggregate hierarchy and currently defined class for duplicate
// field declarations.
auto hierarchy = GetHierarchy();
std::map<std::string, const AggregateType*> field_names;
for (const AggregateType* aggregate_type : hierarchy) {
for (const Field& field : aggregate_type->fields()) {
const std::string& field_name = field.name_and_type.name;
auto i = field_names.find(field_name);
if (i != field_names.end()) {
CurrentSourcePosition::Scope current_source_position(field.pos);
std::string aggregate_type_name =
aggregate_type->IsClassType() ? "class" : "struct";
if (i->second == this) {
ReportError(aggregate_type_name, " '", name,
"' declares a field with the name '", field_name,
"' more than once");
} else {
ReportError(aggregate_type_name, " '", name,
"' declares a field with the name '", field_name,
"' that masks an inherited field from class '",
i->second->name(), "'");
}
}
field_names[field_name] = aggregate_type;
}
}
}
std::vector<const AggregateType*> AggregateType::GetHierarchy() {
std::vector<const AggregateType*> hierarchy;
const AggregateType* current_container_type = this;
......@@ -217,7 +249,7 @@ std::string StructType::GetGeneratedTypeName() const {
std::vector<Method*> AggregateType::Methods(const std::string& name) const {
std::vector<Method*> result;
std::copy_if(methods_.begin(), methods_.end(), std::back_inserter(result),
[&](Macro* macro) { return macro->ReadableName() == name; });
[name](Macro* macro) { return macro->ReadableName() == name; });
return result;
}
......@@ -343,19 +375,53 @@ bool operator<(const Type& a, const Type& b) {
return a.MangledName() < b.MangledName();
}
VisitResult ProjectStructField(VisitResult structure,
VisitResult ProjectStructField(const StructType* original_struct,
VisitResult structure,
const std::string& fieldname) {
DCHECK(structure.IsOnStack());
BottomOffset begin = structure.stack_range().begin();
// Check constructor this super classes for fields.
const StructType* type = StructType::cast(structure.type());
for (auto& field : type->fields()) {
auto& fields = type->fields();
for (auto& field : fields) {
BottomOffset end = begin + LoweredSlotCount(field.name_and_type.type);
if (field.name_and_type.name == fieldname) {
return VisitResult(field.name_and_type.type, StackRange{begin, end});
}
begin = end;
}
UNREACHABLE();
if (fields.size() > 0 &&
fields[0].name_and_type.name == kConstructorStructSuperFieldName) {
structure = ProjectStructField(original_struct, structure,
kConstructorStructSuperFieldName);
return ProjectStructField(original_struct, structure, fieldname);
} else {
base::Optional<const ClassType*> class_type =
original_struct->GetDerivedFrom();
if (original_struct == type) {
if (class_type) {
ReportError("class '", (*class_type)->name(),
"' doesn't contain a field '", fieldname, "'");
} else {
ReportError("struct '", original_struct->name(),
"' doesn't contain a field '", fieldname, "'");
}
} else {
DCHECK(class_type);
ReportError(
"class '", (*class_type)->name(),
"' or one of its derived-from classes doesn't contain a field '",
fieldname, "'");
}
}
}
VisitResult ProjectStructField(VisitResult structure,
const std::string& fieldname) {
DCHECK(structure.IsOnStack());
const StructType* type = StructType::cast(structure.type());
return ProjectStructField(type, structure, fieldname);
}
namespace {
......
......@@ -12,6 +12,7 @@
#include <vector>
#include "src/base/optional.h"
#include "src/torque/source-positions.h"
#include "src/torque/utils.h"
namespace v8 {
......@@ -43,6 +44,7 @@ static const char* const CONST_FLOAT64_TYPE_STRING = "constexpr float64";
class Macro;
class Method;
class StructType;
class ClassType;
class Value;
class Namespace;
......@@ -150,6 +152,7 @@ struct NameAndType {
std::ostream& operator<<(std::ostream& os, const NameAndType& name_and_type);
struct Field {
SourcePosition pos;
NameAndType name_and_type;
size_t offset;
bool is_weak;
......@@ -406,8 +409,7 @@ class AggregateType : public Type {
protected:
AggregateType(Kind kind, const Type* parent, Namespace* nspace,
const std::string& name, const std::vector<Field>& fields)
: Type(kind, parent), namespace_(nspace), name_(name), fields_(fields) {}
const std::string& name, const std::vector<Field>& fields);
private:
Namespace* namespace_;
......@@ -422,6 +424,13 @@ class StructType final : public AggregateType {
std::string ToExplicitString() const override;
std::string GetGeneratedTypeName() const override;
void SetDerivedFrom(const ClassType* derived_from) {
derived_from_ = derived_from;
}
base::Optional<const ClassType*> GetDerivedFrom() const {
return derived_from_;
}
private:
friend class TypeOracle;
StructType(Namespace* nspace, const std::string& name,
......@@ -429,6 +438,8 @@ class StructType final : public AggregateType {
: AggregateType(Kind::kStructType, nullptr, nspace, name, fields) {}
const std::string& GetStructName() const { return name(); }
base::Optional<const ClassType*> derived_from_;
};
class ClassType final : public AggregateType {
......@@ -441,7 +452,9 @@ class ClassType final : public AggregateType {
std::string GetGeneratedTNodeTypeName() const override;
bool IsTransient() const override { return transient_; }
size_t size() const { return size_; }
StructType* struct_type() const { return this_struct_; }
const ClassType* GetSuperClass() const {
if (parent() == nullptr) return nullptr;
return parent()->IsClassType() ? ClassType::DynamicCast(parent()) : nullptr;
}
......@@ -449,12 +462,15 @@ class ClassType final : public AggregateType {
friend class TypeOracle;
ClassType(const Type* parent, Namespace* nspace, const std::string& name,
bool transient, const std::string& generates,
const std::vector<Field>& fields, size_t size)
const std::vector<Field>& fields, StructType* this_struct,
size_t size)
: AggregateType(Kind::kClassType, parent, nspace, name, fields),
this_struct_(this_struct),
transient_(transient),
size_(size),
generates_(generates) {}
StructType* this_struct_;
bool transient_;
size_t size_;
const std::string generates_;
......
......@@ -272,6 +272,8 @@ constexpr int kTaggedSize = sizeof(void*);
static const char* const kConstructMethodName = "constructor";
static const char* const kSuperMethodName = "super";
static const char* const kConstructorStructSuperFieldName = "_super";
static const char* const kClassConstructorThisStructPrefix = "_ThisStruct";
// Erase elements of a container that has a constant-time erase function, like
// std::set or std::list. Calling this on std::vector would have quadratic
......
......@@ -736,10 +736,36 @@ namespace test {
c: int32;
}
struct TestCustomStructConstructor {
constructor(x: int32, y: Smi) {
this.a = x;
this.c = x;
this.b = y;
this.d = y;
}
a: int32;
b: Smi;
c: int32;
d: Smi;
}
macro TestStructConstructor(implicit context: Context)() {
let a: TestOuter = TestOuter{0, TestInner{0, 0}, 0};
// Test default constructor
let a: TestOuter = TestOuter{5, TestInner{6, 7}, 8};
assert(a.a == 5);
assert(a.b.x == 6);
assert(a.b.y == 7);
assert(a.c == 8);
a.b.x = 1;
assert(a.b.x == 1);
a.b.SetX(2);
assert(a.b.x == 2);
assert(a.b.GetX() == 2);
// Test custom constructor
let w: TestCustomStructConstructor = TestCustomStructConstructor{1, 2};
assert(w.a == 1);
assert(w.b == 2);
assert(w.c == 1);
assert(w.d == 2);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment