diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index f91ee6ca74baf1c27f552ac3a85e504a908deba4..2fcdd2c921e958605fb8bd9f26d7d73736519dde 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -23,13 +23,20 @@ #include "ir/base/catchClause.h" #include "ir/base/classProperty.h" #include "ir/base/classStaticBlock.h" +#include "ir/base/property.h" +#include "ir/base/scriptFunction.h" #include "ir/expressions/identifier.h" -#include "ir/expressions/objectExpression.h" #include "ir/expressions/arrayExpression.h" +#include "ir/expressions/assignmentExpression.h" +#include "ir/expressions/literals/stringLiteral.h" +#include "ir/expressions/memberExpression.h" +#include "ir/expressions/objectExpression.h" #include "ir/statements/blockStatement.h" #include "ir/statements/returnStatement.h" +#include "ir/statements/variableDeclaration.h" +#include "ir/statements/variableDeclarator.h" +#include "ir/statements/whileStatement.h" #include "util/helpers.h" - namespace panda::es2panda::checker { ETSChecker *ETSAnalyzer::GetETSChecker() const @@ -150,9 +157,8 @@ checker::Type *ETSAnalyzer::Check(ir::ScriptFunction *node) const UNREACHABLE(); } -checker::Type *ETSAnalyzer::Check(ir::SpreadElement *expr) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::SpreadElement *expr) const { - (void)expr; UNREACHABLE(); } @@ -289,11 +295,50 @@ checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSWildcardType *node) co { UNREACHABLE(); } + // compile methods for EXPRESSIONS in alphabetical order + +checker::Type *ETSAnalyzer::GetPreferredType(ir::ArrayExpression *expr) const +{ + return expr->preferred_type_; +} + checker::Type *ETSAnalyzer::Check(ir::ArrayExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + if (!expr->Elements().empty()) { + if (expr->preferred_type_ == nullptr) { + expr->preferred_type_ = expr->Elements()[0]->Check(checker); + } + + for (auto *element : expr->Elements()) { + if (element->IsArrayExpression() && expr->preferred_type_->IsETSArrayType()) { + element->AsArrayExpression()->SetPreferredType(expr->preferred_type_->AsETSArrayType()->ElementType()); + } + if (element->IsObjectExpression()) { + element->AsObjectExpression()->SetPreferredType(expr->preferred_type_); + } + + checker::Type *element_type = element->Check(checker); + checker::AssignmentContext(checker->Relation(), element, element_type, expr->preferred_type_, + element->Start(), + {"Array element type '", element_type, "' is not assignable to explicit type '", + GetPreferredType(expr), "'"}); + } + } + + if (expr->preferred_type_ == nullptr) { + checker->ThrowTypeError("Can't resolve array type", expr->Start()); + } + + expr->SetTsType(checker->CreateETSArrayType(expr->preferred_type_)); + auto array_type = expr->TsType()->AsETSArrayType(); + checker->CreateBuiltinArraySignature(array_type, array_type->Rank()); + return expr->TsType(); } checker::Type *ETSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const @@ -366,8 +411,76 @@ checker::Type *ETSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + auto *left_type = expr->Left()->Check(checker); + if (expr->Left()->IsMemberExpression() && + expr->Left()->AsMemberExpression()->Object()->TsType()->IsETSArrayType() && + expr->Left()->AsMemberExpression()->Property()->IsIdentifier() && + expr->Left()->AsMemberExpression()->Property()->AsIdentifier()->Name().Is("length")) { + checker->ThrowTypeError("Setting the length of an array is not permitted", expr->Left()->Start()); + } + + if (expr->Left()->IsIdentifier()) { + expr->target_ = expr->Left()->AsIdentifier()->Variable(); + } else { + expr->target_ = expr->Left()->AsMemberExpression()->PropVar(); + } + + if (expr->target_ != nullptr) { + checker->ValidateUnaryOperatorOperand(expr->target_); + } + + checker::Type *source_type {}; + ir::Expression *relation_node = expr->Right(); + switch (expr->OperatorType()) { + case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL: + case lexer::TokenType::PUNCTUATOR_EXPONENTIATION_EQUAL: + case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL: + case lexer::TokenType::PUNCTUATOR_MOD_EQUAL: + case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL: + case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL: + case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL: + case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL: + case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL: + case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL: + case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: + case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: { + std::tie(std::ignore, expr->operation_type_) = checker->CheckBinaryOperator( + expr->Left(), expr->Right(), expr, expr->OperatorType(), expr->Start(), true); + + auto unboxed_left = checker->ETSBuiltinTypeAsPrimitiveType(left_type); + source_type = unboxed_left == nullptr ? left_type : unboxed_left; + + relation_node = expr; + break; + } + case lexer::TokenType::PUNCTUATOR_SUBSTITUTION: { + if (left_type->IsETSArrayType() && expr->Right()->IsArrayExpression()) { + expr->Right()->AsArrayExpression()->SetPreferredType(left_type->AsETSArrayType()->ElementType()); + } + if (expr->Right()->IsObjectExpression()) { + expr->Right()->AsObjectExpression()->SetPreferredType(left_type); + } + + source_type = expr->Right()->Check(checker); + break; + } + default: { + UNREACHABLE(); + break; + } + } + + checker::AssignmentContext(checker->Relation(), relation_node, source_type, left_type, expr->Right()->Start(), + {"Initializers type is not assignable to the target type"}); + + expr->SetTsType(expr->Left()->TsType()); + return expr->TsType(); } checker::Type *ETSAnalyzer::Check(ir::AwaitExpression *expr) const @@ -441,11 +554,94 @@ checker::Type *ETSAnalyzer::Check(ir::NewExpression *expr) const (void)expr; UNREACHABLE(); } +checker::Type *ETSAnalyzer::PreferredType(ir::ObjectExpression *expr) const +{ + return expr->preferred_type_; +} checker::Type *ETSAnalyzer::Check(ir::ObjectExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + if (expr->PreferredType() == nullptr) { + checker->ThrowTypeError({"need to specify target type for class composite"}, expr->Start()); + } + if (!expr->PreferredType()->IsETSObjectType()) { + checker->ThrowTypeError({"target type for class composite needs to be an object type"}, expr->Start()); + } + + if (expr->PreferredType()->IsETSDynamicType()) { + for (ir::Expression *prop_expr : expr->Properties()) { + ASSERT(prop_expr->IsProperty()); + ir::Property *prop = prop_expr->AsProperty(); + ir::Expression *value = prop->Value(); + value->Check(checker); + ASSERT(value->TsType()); + } + + expr->SetTsType(expr->PreferredType()); + return expr->PreferredType(); + } + + checker::ETSObjectType *obj_type = expr->PreferredType()->AsETSObjectType(); + if (obj_type->HasObjectFlag(checker::ETSObjectFlags::ABSTRACT | checker::ETSObjectFlags::INTERFACE)) { + checker->ThrowTypeError({"target type for class composite ", obj_type->Name(), " is not instantiable"}, + expr->Start()); + } + + bool have_empty_constructor = false; + for (checker::Signature *sig : obj_type->ConstructSignatures()) { + if (sig->Params().empty()) { + have_empty_constructor = true; + checker->ValidateSignatureAccessibility(obj_type, sig, expr->Start()); + break; + } + } + if (!have_empty_constructor) { + checker->ThrowTypeError({"type ", obj_type->Name(), " has no parameterless constructor"}, expr->Start()); + } + + for (ir::Expression *prop_expr : expr->Properties()) { + ASSERT(prop_expr->IsProperty()); + ir::Property *prop = prop_expr->AsProperty(); + ir::Expression *key = prop->Key(); + ir::Expression *value = prop->Value(); + + util::StringView pname; + if (key->IsStringLiteral()) { + pname = key->AsStringLiteral()->Str(); + } else if (key->IsIdentifier()) { + pname = key->AsIdentifier()->Name(); + } else { + checker->ThrowTypeError({"key in class composite should be either identifier or string literal"}, + expr->Start()); + } + varbinder::LocalVariable *lv = obj_type->GetProperty( + pname, checker::PropertySearchFlags::SEARCH_INSTANCE_FIELD | checker::PropertySearchFlags::SEARCH_IN_BASE); + if (lv == nullptr) { + checker->ThrowTypeError({"type ", obj_type->Name(), " has no property named ", pname}, prop_expr->Start()); + } + checker->ValidatePropertyAccess(lv, obj_type, prop_expr->Start()); + if (lv->HasFlag(varbinder::VariableFlags::READONLY)) { + checker->ThrowTypeError({"cannot assign to readonly property ", pname}, prop_expr->Start()); + } + + auto *prop_type = checker->GetTypeOfVariable(lv); + key->SetTsType(prop_type); + + if (value->IsObjectExpression()) { + value->AsObjectExpression()->SetPreferredType(prop_type); + } + value->SetTsType(value->Check(checker)); + checker::AssignmentContext(checker->Relation(), value, value->TsType(), prop_type, value->Start(), + {"value type is not assignable to the property type"}); + } + + expr->SetTsType(obj_type); + return obj_type; } checker::Type *ETSAnalyzer::Check(ir::OmittedExpression *expr) const @@ -454,9 +650,8 @@ checker::Type *ETSAnalyzer::Check(ir::OmittedExpression *expr) const UNREACHABLE(); } -checker::Type *ETSAnalyzer::Check(ir::OpaqueTypeNode *expr) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::OpaqueTypeNode *expr) const { - (void)expr; UNREACHABLE(); } @@ -502,9 +697,8 @@ checker::Type *ETSAnalyzer::Check(ir::UpdateExpression *expr) const UNREACHABLE(); } -checker::Type *ETSAnalyzer::Check(ir::YieldExpression *expr) const +checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::YieldExpression *expr) const { - (void)expr; UNREACHABLE(); } // compile methods for LITERAL EXPRESSIONS in alphabetical order @@ -932,8 +1126,18 @@ checker::Type *ETSAnalyzer::Check(ir::TryStatement *st) const checker::Type *ETSAnalyzer::Check(ir::VariableDeclarator *st) const { - (void)st; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + ASSERT(st->Id()->IsIdentifier()); + ir::ModifierFlags flags = ir::ModifierFlags::NONE; + + if (st->Id()->Parent()->Parent()->AsVariableDeclaration()->Kind() == + ir::VariableDeclaration::VariableDeclarationKind::CONST) { + flags |= ir::ModifierFlags::CONST; + } + + st->SetTsType(checker->CheckVariableDeclaration(st->Id()->AsIdentifier(), + st->Id()->AsIdentifier()->TypeAnnotation(), st->Init(), flags)); + return st->TsType(); } checker::Type *ETSAnalyzer::Check(ir::VariableDeclaration *st) const @@ -944,8 +1148,13 @@ checker::Type *ETSAnalyzer::Check(ir::VariableDeclaration *st) const checker::Type *ETSAnalyzer::Check(ir::WhileStatement *st) const { - (void)st; - UNREACHABLE(); + ETSChecker *checker = GetETSChecker(); + checker::ScopeContext scope_ctx(checker, st->Scope()); + + checker->CheckTruthinessOfType(st->Test()); + + st->Body()->Check(checker); + return nullptr; } // from ts folder checker::Type *ETSAnalyzer::Check(ir::TSAnyKeyword *node) const diff --git a/ets2panda/checker/ETSAnalyzer.h b/ets2panda/checker/ETSAnalyzer.h index de2b57cc58f1221ba182913a895318b90abe91fd..227380e0f2f6854156866c79379a06d8382804d2 100644 --- a/ets2panda/checker/ETSAnalyzer.h +++ b/ets2panda/checker/ETSAnalyzer.h @@ -34,6 +34,8 @@ public: virtual checker::Type *Check(ir::nodeType *node) const override; AST_NODE_REINTERPRET_MAPPING(DECLARE_ETSANALYZER_CHECK_METHOD) #undef DECLARE_ETSANALYZER_CHECK_METHOD + checker::Type *PreferredType(ir::ObjectExpression *expr) const; + checker::Type *GetPreferredType(ir::ArrayExpression *expr) const; private: ETSChecker *GetETSChecker() const; diff --git a/ets2panda/checker/TSAnalyzer.cpp b/ets2panda/checker/TSAnalyzer.cpp index d3e96e821ffd6bb149a2337acaa909ed200d7dca..840be10d76a6f62774d062143a839117c48151a6 100644 --- a/ets2panda/checker/TSAnalyzer.cpp +++ b/ets2panda/checker/TSAnalyzer.cpp @@ -102,9 +102,8 @@ checker::Type *TSAnalyzer::Check(ir::ScriptFunction *node) const UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::SpreadElement *expr) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::SpreadElement *expr) const { - (void)expr; UNREACHABLE(); } @@ -222,10 +221,129 @@ checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ETSWildcardType *node) con UNREACHABLE(); } // compile methods for EXPRESSIONS in alphabetical order + +static void GetSpreadElementType(checker::TSChecker *checker, checker::Type *spread_type, + ArenaVector &element_types, const lexer::SourcePosition &loc) +{ + bool in_const_context = checker->HasStatus(checker::CheckerStatus::IN_CONST_CONTEXT); + + if (spread_type->IsObjectType() && spread_type->AsObjectType()->IsTupleType()) { + ArenaVector tuple_element_types(checker->Allocator()->Adapter()); + checker::TupleType *spread_tuple = spread_type->AsObjectType()->AsTupleType(); + + for (auto *it : spread_tuple->Properties()) { + if (in_const_context) { + element_types.push_back(it->TsType()); + continue; + } + + tuple_element_types.push_back(it->TsType()); + } + + if (in_const_context) { + return; + } + + element_types.push_back(checker->CreateUnionType(std::move(tuple_element_types))); + return; + } + + if (spread_type->IsUnionType()) { + ArenaVector spread_types(checker->Allocator()->Adapter()); + bool throw_error = false; + + for (auto *type : spread_type->AsUnionType()->ConstituentTypes()) { + if (type->IsArrayType()) { + spread_types.push_back(type->AsArrayType()->ElementType()); + continue; + } + + if (type->IsObjectType() && type->AsObjectType()->IsTupleType()) { + checker::TupleType *tuple = type->AsObjectType()->AsTupleType(); + + for (auto *it : tuple->Properties()) { + spread_types.push_back(it->TsType()); + } + + continue; + } + + throw_error = true; + break; + } + + if (!throw_error) { + element_types.push_back(checker->CreateUnionType(std::move(spread_types))); + return; + } + } + + checker->ThrowTypeError( + {"Type '", spread_type, "' must have a '[Symbol.iterator]()' method that returns an iterator."}, loc); +} + checker::Type *TSAnalyzer::Check(ir::ArrayExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + ArenaVector element_types(checker->Allocator()->Adapter()); + ArenaVector element_flags(checker->Allocator()->Adapter()); + bool in_const_context = checker->HasStatus(checker::CheckerStatus::IN_CONST_CONTEXT); + bool create_tuple = checker->HasStatus(checker::CheckerStatus::FORCE_TUPLE); + + for (auto *it : expr->Elements()) { + if (it->IsSpreadElement()) { + checker::Type *spread_type = it->AsSpreadElement()->Argument()->Check(checker); + + if (spread_type->IsArrayType()) { + element_types.push_back(in_const_context ? spread_type : spread_type->AsArrayType()->ElementType()); + element_flags.push_back(checker::ElementFlags::VARIADIC); + continue; + } + + GetSpreadElementType(checker, spread_type, element_types, it->Start()); + element_flags.push_back(checker::ElementFlags::REST); + continue; + } + + checker::Type *element_type = it->Check(checker); + + if (!in_const_context) { + element_type = checker->GetBaseTypeOfLiteralType(element_type); + } + + element_flags.push_back(checker::ElementFlags::REQUIRED); + element_types.push_back(element_type); + } + + if (in_const_context || create_tuple) { + checker::ObjectDescriptor *desc = checker->Allocator()->New(checker->Allocator()); + uint32_t index = 0; + + for (auto it = element_types.begin(); it != element_types.end(); it++, index++) { + util::StringView member_index = util::Helpers::ToStringView(checker->Allocator(), index); + varbinder::LocalVariable *tuple_member = varbinder::Scope::CreateVar( + checker->Allocator(), member_index, varbinder::VariableFlags::PROPERTY, nullptr); + + if (in_const_context) { + tuple_member->AddFlag(varbinder::VariableFlags::READONLY); + } + + tuple_member->SetTsType(*it); + desc->properties.push_back(tuple_member); + } + + return checker->CreateTupleType(desc, std::move(element_flags), checker::ElementFlags::REQUIRED, index, index, + in_const_context); + } + + checker::Type *array_element_type = nullptr; + if (element_types.empty()) { + array_element_type = checker->GlobalAnyType(); + } else { + array_element_type = checker->CreateUnionType(std::move(element_types)); + } + + return checker->Allocator()->New(array_element_type); } checker::Type *TSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const @@ -263,8 +381,73 @@ checker::Type *TSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const checker::Type *TSAnalyzer::Check(ir::AssignmentExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + if (expr->Left()->IsArrayPattern()) { + auto saved_context = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); + auto destructuring_context = + checker::ArrayDestructuringContext(checker, expr->Left(), true, true, nullptr, expr->Right()); + destructuring_context.Start(); + return destructuring_context.InferredType(); + } + + if (expr->Left()->IsObjectPattern()) { + auto saved_context = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); + auto destructuring_context = + checker::ObjectDestructuringContext(checker, expr->Left(), true, true, nullptr, expr->Right()); + destructuring_context.Start(); + return destructuring_context.InferredType(); + } + + if (expr->Left()->IsIdentifier() && expr->Left()->AsIdentifier()->Variable() != nullptr && + expr->Left()->AsIdentifier()->Variable()->Declaration()->IsConstDecl()) { + checker->ThrowTypeError( + {"Cannot assign to ", expr->Left()->AsIdentifier()->Name(), " because it is a constant."}, + expr->Left()->Start()); + } + + auto *left_type = expr->Left()->Check(checker); + + if (left_type->HasTypeFlag(checker::TypeFlag::READONLY)) { + checker->ThrowTypeError("Cannot assign to this property because it is readonly.", expr->Left()->Start()); + } + + if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION) { + checker->ElaborateElementwise(left_type, expr->Right(), expr->Left()->Start()); + return checker->CheckTypeCached(expr->Right()); + } + + auto *right_type = expr->Right()->Check(checker); + + switch (expr->OperatorType()) { + case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL: + case lexer::TokenType::PUNCTUATOR_EXPONENTIATION_EQUAL: + case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL: + case lexer::TokenType::PUNCTUATOR_MOD_EQUAL: + case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL: + case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL: + case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL: + case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL: + case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL: + case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL: + case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: { + return checker->CheckBinaryOperator(left_type, right_type, expr->Left(), expr->Right(), expr, + expr->OperatorType()); + } + case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: { + return checker->CheckPlusOperator(left_type, right_type, expr->Left(), expr->Right(), expr, + expr->OperatorType()); + } + case lexer::TokenType::PUNCTUATOR_SUBSTITUTION: { + checker->CheckAssignmentOperator(expr->OperatorType(), expr->Left(), left_type, right_type); + return right_type; + } + default: { + UNREACHABLE(); + break; + } + } + + return nullptr; } checker::Type *TSAnalyzer::Check(ir::AwaitExpression *expr) const @@ -338,11 +521,170 @@ checker::Type *TSAnalyzer::Check(ir::NewExpression *expr) const (void)expr; UNREACHABLE(); } +static const util::StringView &GetPropertyName(const ir::Expression *key) +{ + if (key->IsIdentifier()) { + return key->AsIdentifier()->Name(); + } + + if (key->IsStringLiteral()) { + return key->AsStringLiteral()->Str(); + } + + ASSERT(key->IsNumberLiteral()); + return key->AsNumberLiteral()->Str(); +} + +static varbinder::VariableFlags GetFlagsForProperty(const ir::Property *prop) +{ + if (!prop->IsMethod()) { + return varbinder::VariableFlags::PROPERTY; + } + + varbinder::VariableFlags prop_flags = varbinder::VariableFlags::METHOD; + + if (prop->IsAccessor() && prop->Kind() == ir::PropertyKind::GET) { + prop_flags |= varbinder::VariableFlags::READONLY; + } + + return prop_flags; +} + +static checker::Type *GetTypeForProperty(ir::Property *prop, checker::TSChecker *checker) +{ + if (prop->IsAccessor()) { + checker::Type *func_type = prop->Value()->Check(checker); + + if (prop->Kind() == ir::PropertyKind::SET) { + return checker->GlobalAnyType(); + } + + ASSERT(func_type->IsObjectType() && func_type->AsObjectType()->IsFunctionType()); + return func_type->AsObjectType()->CallSignatures()[0]->ReturnType(); + } + + if (prop->IsShorthand()) { + return prop->Key()->Check(checker); + } + + return prop->Value()->Check(checker); +} checker::Type *TSAnalyzer::Check(ir::ObjectExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + + checker::ObjectDescriptor *desc = checker->Allocator()->New(checker->Allocator()); + std::unordered_map all_properties_map; + bool in_const_context = checker->HasStatus(checker::CheckerStatus::IN_CONST_CONTEXT); + ArenaVector computed_number_prop_types(checker->Allocator()->Adapter()); + ArenaVector computed_string_prop_types(checker->Allocator()->Adapter()); + bool has_computed_number_property = false; + bool has_computed_string_property = false; + bool seen_spread = false; + + for (auto *it : expr->Properties()) { + if (it->IsProperty()) { + auto *prop = it->AsProperty(); + + if (prop->IsComputed()) { + checker::Type *computed_name_type = checker->CheckComputedPropertyName(prop->Key()); + + if (computed_name_type->IsNumberType()) { + has_computed_number_property = true; + computed_number_prop_types.push_back(prop->Value()->Check(checker)); + continue; + } + + if (computed_name_type->IsStringType()) { + has_computed_string_property = true; + computed_string_prop_types.push_back(prop->Value()->Check(checker)); + continue; + } + } + + checker::Type *prop_type = GetTypeForProperty(prop, checker); + varbinder::VariableFlags flags = GetFlagsForProperty(prop); + const util::StringView &prop_name = GetPropertyName(prop->Key()); + + auto *member_var = varbinder::Scope::CreateVar(checker->Allocator(), prop_name, flags, it); + + if (in_const_context) { + member_var->AddFlag(varbinder::VariableFlags::READONLY); + } else { + prop_type = checker->GetBaseTypeOfLiteralType(prop_type); + } + + member_var->SetTsType(prop_type); + + if (prop->Key()->IsNumberLiteral()) { + member_var->AddFlag(varbinder::VariableFlags::NUMERIC_NAME); + } + + varbinder::LocalVariable *found_member = desc->FindProperty(prop_name); + all_properties_map.insert({prop_name, it->Start()}); + + if (found_member != nullptr) { + found_member->SetTsType(prop_type); + continue; + } + + desc->properties.push_back(member_var); + continue; + } + + ASSERT(it->IsSpreadElement()); + + checker::Type *const spread_type = it->AsSpreadElement()->Argument()->Check(checker); + seen_spread = true; + + // NOTE: aszilagyi. handle union of object types + if (!spread_type->IsObjectType()) { + checker->ThrowTypeError("Spread types may only be created from object types.", it->Start()); + } + + for (auto *spread_prop : spread_type->AsObjectType()->Properties()) { + auto found = all_properties_map.find(spread_prop->Name()); + if (found != all_properties_map.end()) { + checker->ThrowTypeError( + {found->first, " is specified more than once, so this usage will be overwritten."}, found->second); + } + + varbinder::LocalVariable *found_member = desc->FindProperty(spread_prop->Name()); + + if (found_member != nullptr) { + found_member->SetTsType(spread_prop->TsType()); + continue; + } + + desc->properties.push_back(spread_prop); + } + } + + if (!seen_spread && (has_computed_number_property || has_computed_string_property)) { + for (auto *it : desc->properties) { + computed_string_prop_types.push_back(it->TsType()); + + if (has_computed_number_property && it->HasFlag(varbinder::VariableFlags::NUMERIC_NAME)) { + computed_number_prop_types.push_back(it->TsType()); + } + } + + if (has_computed_number_property) { + desc->number_index_info = checker->Allocator()->New( + checker->CreateUnionType(std::move(computed_number_prop_types)), "x", in_const_context); + } + + if (has_computed_string_property) { + desc->string_index_info = checker->Allocator()->New( + checker->CreateUnionType(std::move(computed_string_prop_types)), "x", in_const_context); + } + } + + checker::Type *return_type = checker->Allocator()->New(desc); + return_type->AsObjectType()->AddObjectFlag(checker::ObjectFlags::RESOLVED_MEMBERS | + checker::ObjectFlags::CHECK_EXCESS_PROPS); + return return_type; } checker::Type *TSAnalyzer::Check(ir::OmittedExpression *expr) const @@ -351,10 +693,9 @@ checker::Type *TSAnalyzer::Check(ir::OmittedExpression *expr) const UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::OpaqueTypeNode *expr) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::OpaqueTypeNode *expr) const { - (void)expr; - UNREACHABLE(); + return expr->TsType(); } checker::Type *TSAnalyzer::Check(ir::SequenceExpression *expr) const @@ -399,10 +740,11 @@ checker::Type *TSAnalyzer::Check(ir::UpdateExpression *expr) const UNREACHABLE(); } -checker::Type *TSAnalyzer::Check(ir::YieldExpression *expr) const +checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::YieldExpression *expr) const { - (void)expr; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + // NOTE: aszilagyi. + return checker->GlobalAnyType(); } // compile methods for LITERAL EXPRESSIONS in alphabetical order checker::Type *TSAnalyzer::Check(ir::BigIntLiteral *expr) const @@ -647,10 +989,93 @@ checker::Type *TSAnalyzer::Check(ir::TryStatement *st) const UNREACHABLE(); } +static void CheckSimpleVariableDeclaration(checker::TSChecker *checker, ir::VariableDeclarator *declarator) +{ + varbinder::Variable *const binding_var = declarator->Id()->AsIdentifier()->Variable(); + checker::Type *previous_type = binding_var->TsType(); + auto *const type_annotation = declarator->Id()->AsIdentifier()->TypeAnnotation(); + auto *const initializer = declarator->Init(); + const bool is_const = declarator->Parent()->AsVariableDeclaration()->Kind() == + ir::VariableDeclaration::VariableDeclarationKind::CONST; + + if (is_const) { + checker->AddStatus(checker::CheckerStatus::IN_CONST_CONTEXT); + } + + if (type_annotation != nullptr) { + type_annotation->Check(checker); + } + + if (type_annotation != nullptr && initializer != nullptr) { + checker::Type *const annotation_type = type_annotation->GetType(checker); + checker->ElaborateElementwise(annotation_type, initializer, declarator->Id()->Start()); + binding_var->SetTsType(annotation_type); + } else if (type_annotation != nullptr) { + binding_var->SetTsType(type_annotation->GetType(checker)); + } else if (initializer != nullptr) { + checker::Type *initializer_type = checker->CheckTypeCached(initializer); + + if (!is_const) { + initializer_type = checker->GetBaseTypeOfLiteralType(initializer_type); + } + + if (initializer_type->IsNullType()) { + checker->ThrowTypeError( + {"Cannot infer type for variable '", declarator->Id()->AsIdentifier()->Name(), "'."}, + declarator->Id()->Start()); + } + + binding_var->SetTsType(initializer_type); + } else { + checker->ThrowTypeError({"Variable ", declarator->Id()->AsIdentifier()->Name(), " implicitly has an any type."}, + declarator->Id()->Start()); + } + + if (previous_type != nullptr) { + checker->IsTypeIdenticalTo(binding_var->TsType(), previous_type, + {"Subsequent variable declaration must have the same type. Variable '", + binding_var->Name(), "' must be of type '", previous_type, "', but here has type '", + binding_var->TsType(), "'."}, + declarator->Id()->Start()); + } + + checker->RemoveStatus(checker::CheckerStatus::IN_CONST_CONTEXT); +} + checker::Type *TSAnalyzer::Check(ir::VariableDeclarator *st) const { - (void)st; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + + if (st->TsType() == st->CHECKED) { + return nullptr; + } + + if (st->Id()->IsIdentifier()) { + CheckSimpleVariableDeclaration(checker, st); + st->SetTsType(st->CHECKED); + return nullptr; + } + + if (st->Id()->IsArrayPattern()) { + auto context = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); + checker::ArrayDestructuringContext(checker, st->Id(), false, + st->Id()->AsArrayPattern()->TypeAnnotation() == nullptr, + st->Id()->AsArrayPattern()->TypeAnnotation(), st->Init()) + .Start(); + + st->SetTsType(st->CHECKED); + return nullptr; + } + + ASSERT(st->Id()->IsObjectPattern()); + auto context = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); + checker::ObjectDestructuringContext(checker, st->Id(), false, + st->Id()->AsObjectPattern()->TypeAnnotation() == nullptr, + st->Id()->AsObjectPattern()->TypeAnnotation(), st->Init()) + .Start(); + + st->SetTsType(st->CHECKED); + return nullptr; } checker::Type *TSAnalyzer::Check(ir::VariableDeclaration *st) const @@ -661,8 +1086,14 @@ checker::Type *TSAnalyzer::Check(ir::VariableDeclaration *st) const checker::Type *TSAnalyzer::Check(ir::WhileStatement *st) const { - (void)st; - UNREACHABLE(); + TSChecker *checker = GetTSChecker(); + checker::ScopeContext scope_ctx(checker, st->Scope()); + + checker::Type *test_type = st->Test()->Check(checker); + checker->CheckTruthinessOfType(test_type, st->Test()->Start()); + + st->Body()->Check(checker); + return nullptr; } // from ts folder checker::Type *TSAnalyzer::Check(ir::TSAnyKeyword *node) const diff --git a/ets2panda/compiler/core/ETSCompiler.cpp b/ets2panda/compiler/core/ETSCompiler.cpp index 46dd096ae82d9c812db8af28704dee330951bdcd..68ef93b628ed5cdfd27d2d9313b5d349c0ad001a 100644 --- a/ets2panda/compiler/core/ETSCompiler.cpp +++ b/ets2panda/compiler/core/ETSCompiler.cpp @@ -20,7 +20,6 @@ #include "compiler/base/lreference.h" #include "compiler/core/ETSGen.h" #include "compiler/function/functionBuilder.h" - namespace panda::es2panda::compiler { ETSGen *ETSCompiler::GetETSGen() const @@ -110,9 +109,8 @@ void ETSCompiler::Compile(const ir::ScriptFunction *node) const UNREACHABLE(); } -void ETSCompiler::Compile(const ir::SpreadElement *expr) const +void ETSCompiler::Compile([[maybe_unused]] const ir::SpreadElement *expr) const { - (void)expr; UNREACHABLE(); } @@ -236,8 +234,34 @@ void ETSCompiler::Compile([[maybe_unused]] const ir::ETSWildcardType *node) cons // compile methods for EXPRESSIONS in alphabetical order void ETSCompiler::Compile(const ir::ArrayExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + const compiler::RegScope rs(etsg); + + const auto arr = etsg->AllocReg(); + const auto dim = etsg->AllocReg(); + + const compiler::TargetTypeContext ttctx(etsg, etsg->Checker()->GlobalIntType()); + etsg->LoadAccumulatorInt(expr, static_cast(expr->Elements().size())); + etsg->StoreAccumulator(expr, dim); + etsg->NewArray(expr, arr, dim, expr->TsType()); + + const auto index_reg = etsg->AllocReg(); + for (std::uint32_t i = 0; i < expr->Elements().size(); ++i) { + const auto *const expression = expr->Elements()[i]; + etsg->LoadAccumulatorInt(expr, i); + etsg->StoreAccumulator(expr, index_reg); + + const compiler::TargetTypeContext ttctx2(etsg, expr->preferred_type_); + if (!etsg->TryLoadConstantExpression(expression)) { + expression->Compile(etsg); + } + + etsg->ApplyConversion(expression, nullptr); + etsg->ApplyConversion(expression); + etsg->StoreArrayElement(expr, arr, index_reg, expr->TsType()->AsETSArrayType()->ElementType()); + } + + etsg->LoadAccumulator(expr, arr); } void ETSCompiler::Compile(const ir::ArrowFunctionExpression *expr) const @@ -264,8 +288,20 @@ void ETSCompiler::Compile(const ir::ArrowFunctionExpression *expr) const void ETSCompiler::Compile(const ir::AssignmentExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + // All other operations are handled in OpAssignmentLowering + ASSERT(expr->OperatorType() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION); + compiler::RegScope rs(etsg); + auto lref = compiler::ETSLReference::Create(etsg, expr->Left(), false); + auto ttctx = compiler::TargetTypeContext(etsg, expr->TsType()); + + if (expr->Right()->IsNullLiteral()) { + etsg->LoadAccumulatorNull(expr, expr->Left()->TsType()); + } else { + expr->Right()->Compile(etsg); + etsg->ApplyConversion(expr->Right(), expr->TsType()); + } + lref.SetValue(); } void ETSCompiler::Compile(const ir::AwaitExpression *expr) const @@ -342,8 +378,59 @@ void ETSCompiler::Compile(const ir::NewExpression *expr) const void ETSCompiler::Compile(const ir::ObjectExpression *expr) const { - (void)expr; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + compiler::RegScope rs {etsg}; + checker::ETSObjectType const *obj_type = expr->TsType()->AsETSObjectType(); + compiler::VReg obj_reg = etsg->AllocReg(); + if (expr->TsType()->IsETSDynamicType()) { + auto *signature_info = etsg->Allocator()->New(etsg->Allocator()); + auto *create_obj_sig = etsg->Allocator()->New( + signature_info, nullptr, compiler::Signatures::BUILTIN_JSRUNTIME_CREATE_OBJECT); + compiler::VReg dummy_reg = compiler::VReg::RegStart(); + etsg->CallDynamic(expr, dummy_reg, dummy_reg, create_obj_sig, + ArenaVector(etsg->Allocator()->Adapter())); + } else { + checker::Signature *empty_sig = nullptr; + for (checker::Signature *sig : obj_type->ConstructSignatures()) { + if (sig->Params().empty()) { + empty_sig = sig; + break; + } + } + if (empty_sig == nullptr) { // Would have already thrown in the checker. + UNREACHABLE(); + } + etsg->InitObject(expr, empty_sig, ArenaVector(etsg->Allocator()->Adapter())); + } + etsg->SetAccumulatorType(expr->TsType()); + etsg->StoreAccumulator(expr, obj_reg); + + for (ir::Expression *prop_expr : expr->Properties()) { + ASSERT(prop_expr->IsProperty()); + ir::Property *prop = prop_expr->AsProperty(); + ir::Expression *key = prop->Key(); + ir::Expression *value = prop->Value(); + + util::StringView pname; + if (key->IsStringLiteral()) { + pname = key->AsStringLiteral()->Str(); + } else if (key->IsIdentifier()) { + pname = key->AsIdentifier()->Name(); + } else { + UNREACHABLE(); + } + + value->Compile(etsg); + etsg->ApplyConversion(value, key->TsType()); + if (expr->TsType()->IsETSDynamicType()) { + etsg->StorePropertyDynamic(expr, value->TsType(), obj_reg, pname, + expr->TsType()->AsETSDynamicType()->Language()); + } else { + etsg->StoreProperty(expr, key->TsType(), obj_reg, pname); + } + } + + etsg->LoadAccumulator(expr, obj_reg); } void ETSCompiler::Compile(const ir::OmittedExpression *expr) const @@ -352,9 +439,8 @@ void ETSCompiler::Compile(const ir::OmittedExpression *expr) const UNREACHABLE(); } -void ETSCompiler::Compile(const ir::OpaqueTypeNode *node) const +void ETSCompiler::Compile([[maybe_unused]] const ir::OpaqueTypeNode *node) const { - (void)node; UNREACHABLE(); } @@ -400,9 +486,8 @@ void ETSCompiler::Compile(const ir::UpdateExpression *expr) const UNREACHABLE(); } -void ETSCompiler::Compile(const ir::YieldExpression *expr) const +void ETSCompiler::Compile([[maybe_unused]] const ir::YieldExpression *expr) const { - (void)expr; UNREACHABLE(); } // compile methods for LITERAL EXPRESSIONS in alphabetical order @@ -662,8 +747,27 @@ void ETSCompiler::Compile(const ir::TryStatement *st) const void ETSCompiler::Compile(const ir::VariableDeclarator *st) const { - (void)st; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + auto lref = compiler::ETSLReference::Create(etsg, st->Id(), true); + auto ttctx = compiler::TargetTypeContext(etsg, st->TsType()); + + if (st->Id()->AsIdentifier()->Variable()->HasFlag(varbinder::VariableFlags::BOXED)) { + etsg->EmitLocalBoxCtor(st->Id()); + etsg->StoreAccumulator(st, lref.Variable()->AsLocalVariable()->Vreg()); + etsg->SetAccumulatorType(lref.Variable()->TsType()); + } + + if (st->Init() != nullptr) { + if (!etsg->TryLoadConstantExpression(st->Init())) { + st->Init()->Compile(etsg); + etsg->ApplyConversion(st->Init(), nullptr); + } + } else { + etsg->LoadDefaultValue(st, st->Id()->AsIdentifier()->Variable()->TsType()); + } + + etsg->ApplyConversion(st, st->TsType()); + lref.SetValue(); } void ETSCompiler::Compile(const ir::VariableDeclaration *st) const @@ -672,10 +776,28 @@ void ETSCompiler::Compile(const ir::VariableDeclaration *st) const UNREACHABLE(); } +template +void CompileImpl(const ir::WhileStatement *while_stmt, [[maybe_unused]] CodeGen *cg) +{ + compiler::LabelTarget label_target(cg); + + cg->SetLabel(while_stmt, label_target.ContinueTarget()); + compiler::Condition::Compile(cg, while_stmt->Test(), label_target.BreakTarget()); + + { + compiler::LocalRegScope reg_scope(cg, while_stmt->Scope()); + compiler::LabelContext label_ctx(cg, label_target); + while_stmt->Body()->Compile(cg); + } + + cg->Branch(while_stmt, label_target.ContinueTarget()); + cg->SetLabel(while_stmt, label_target.BreakTarget()); +} + void ETSCompiler::Compile(const ir::WhileStatement *st) const { - (void)st; - UNREACHABLE(); + ETSGen *etsg = GetETSGen(); + CompileImpl(st, etsg); } // from ts folder void ETSCompiler::Compile(const ir::TSAnyKeyword *node) const diff --git a/ets2panda/compiler/core/JSCompiler.cpp b/ets2panda/compiler/core/JSCompiler.cpp index 10a894d364243fe8b74d0bd55d96c5d90df2551c..a28c96e2f78d9a89dcb12200619138b80cd0171d 100644 --- a/ets2panda/compiler/core/JSCompiler.cpp +++ b/ets2panda/compiler/core/JSCompiler.cpp @@ -19,6 +19,7 @@ #include "compiler/base/lreference.h" #include "compiler/core/pandagen.h" #include "compiler/function/functionBuilder.h" +#include "util/bitset.h" #include "util/helpers.h" namespace panda::es2panda::compiler { @@ -431,9 +432,8 @@ void JSCompiler::Compile(const ir::ScriptFunction *node) const UNREACHABLE(); } -void JSCompiler::Compile(const ir::SpreadElement *expr) const +void JSCompiler::Compile([[maybe_unused]] const ir::SpreadElement *expr) const { - (void)expr; UNREACHABLE(); } @@ -554,8 +554,11 @@ void JSCompiler::Compile([[maybe_unused]] const ir::ETSWildcardType *expr) const // JSCompiler::compile methods for EXPRESSIONS in alphabetical order void JSCompiler::Compile(const ir::ArrayExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + compiler::RegScope rs(pg); + compiler::VReg array_obj = pg->AllocReg(); + + pg->CreateArray(expr, expr->Elements(), array_obj); } void JSCompiler::Compile(const ir::ArrowFunctionExpression *expr) const @@ -566,8 +569,29 @@ void JSCompiler::Compile(const ir::ArrowFunctionExpression *expr) const void JSCompiler::Compile(const ir::AssignmentExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + compiler::RegScope rs(pg); + auto lref = compiler::JSLReference::Create(pg, expr->Left(), false); + + if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND_EQUAL || + expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_OR_EQUAL) { + compiler::PandaGen::Unimplemented(); + } + + if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION) { + expr->Right()->Compile(pg); + lref.SetValue(); + return; + } + + compiler::VReg lhs_reg = pg->AllocReg(); + + lref.GetValue(); + pg->StoreAccumulator(expr->Left(), lhs_reg); + expr->Right()->Compile(pg); + pg->Binary(expr, expr->OperatorType(), lhs_reg); + + lref.SetValue(); } void JSCompiler::Compile(const ir::AwaitExpression *expr) const @@ -644,13 +668,204 @@ void JSCompiler::Compile(const ir::NewExpression *expr) const void JSCompiler::Compile(const ir::ObjectExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + if (expr->Properties().empty()) { + pg->CreateEmptyObject(expr); + return; + } + + util::BitSet compiled(expr->Properties().size()); + CompileStaticProperties(pg, &compiled, expr); + + if (compiled.Any(false)) { + CompileRemainingProperties(pg, &compiled, expr); + } } -void JSCompiler::Compile(const ir::OpaqueTypeNode *node) const +static compiler::Literal CreateLiteral(const ir::Property *prop, util::BitSet *compiled, size_t prop_index) +{ + compiler::Literal lit = util::Helpers::ToConstantLiteral(prop->Value()); + if (!lit.IsInvalid()) { + compiled->Set(prop_index); + return lit; + } + + if (prop->Kind() != ir::PropertyKind::INIT) { + ASSERT(prop->IsAccessor()); + return compiler::Literal::AccessorLiteral(); + } + + if (!prop->Value()->IsFunctionExpression()) { + return compiler::Literal::NullLiteral(); + } + + const ir::ScriptFunction *method = prop->Value()->AsFunctionExpression()->Function(); + + compiler::LiteralTag tag = compiler::LiteralTag::METHOD; + + if (method->IsGenerator()) { + tag = compiler::LiteralTag::GENERATOR_METHOD; + + if (method->IsAsyncFunc()) { + tag = compiler::LiteralTag::ASYNC_GENERATOR_METHOD; + } + } + + compiled->Set(prop_index); + return compiler::Literal(tag, method->Scope()->InternalName()); +} + +static bool IsLiteralBufferCompatible(const ir::Expression *expr) +{ + if (expr->IsSpreadElement()) { + return false; + } + + const ir::Property *prop = expr->AsProperty(); + if (prop->Value()->IsFunctionExpression() && !prop->Value()->AsFunctionExpression()->Function()->IsMethod()) { + return false; + } + + return util::Helpers::IsConstantPropertyKey(prop->Key(), prop->IsComputed()) && + prop->Kind() != ir::PropertyKind::PROTO; +} + +void JSCompiler::CompileStaticProperties(compiler::PandaGen *pg, util::BitSet *compiled, + const ir::ObjectExpression *expr) const +{ + bool has_method = false; + bool seen_computed = false; + compiler::LiteralBuffer buf; + std::unordered_map prop_name_map; + + for (size_t i = 0; i < expr->Properties().size(); i++) { + if (!IsLiteralBufferCompatible(expr->Properties()[i])) { + seen_computed = true; + continue; + } + + const ir::Property *prop = expr->Properties()[i]->AsProperty(); + + util::StringView name = util::Helpers::LiteralToPropName(prop->Key()); + size_t buffer_pos = buf.size(); + auto res = prop_name_map.insert({name, buffer_pos}); + if (res.second) { + if (seen_computed) { + break; + } + + buf.emplace_back(name); + buf.emplace_back(); + } else { + buffer_pos = res.first->second; + } + + compiler::Literal lit = CreateLiteral(prop, compiled, i); + if (lit.IsTagMethod()) { + has_method = true; + } + + buf[buffer_pos + 1] = std::move(lit); + } + + if (buf.empty()) { + pg->CreateEmptyObject(expr); + return; + } + + uint32_t buf_idx = pg->AddLiteralBuffer(std::move(buf)); + + if (has_method) { + pg->CreateObjectHavingMethod(expr, buf_idx); + } else { + pg->CreateObjectWithBuffer(expr, buf_idx); + } +} + +void JSCompiler::CompileRemainingProperties(compiler::PandaGen *pg, const util::BitSet *compiled, + const ir::ObjectExpression *expr) const +{ + compiler::RegScope rs(pg); + compiler::VReg obj_reg = pg->AllocReg(); + + pg->StoreAccumulator(expr, obj_reg); + + for (size_t i = 0; i < expr->Properties().size(); i++) { + if (compiled->Test(i)) { + continue; + } + + compiler::RegScope prs(pg); + + if (expr->Properties()[i]->IsSpreadElement()) { + compiler::VReg src_obj = pg->AllocReg(); + auto const *const spread = expr->Properties()[i]->AsSpreadElement(); + + spread->Argument()->Compile(pg); + pg->StoreAccumulator(spread, src_obj); + + pg->CopyDataProperties(spread, obj_reg, src_obj); + continue; + } + + const ir::Property *prop = expr->Properties()[i]->AsProperty(); + + switch (prop->Kind()) { + case ir::PropertyKind::GET: + case ir::PropertyKind::SET: { + compiler::VReg key = pg->LoadPropertyKey(prop->Key(), prop->IsComputed()); + + compiler::VReg undef = pg->AllocReg(); + pg->LoadConst(expr, compiler::Constant::JS_UNDEFINED); + pg->StoreAccumulator(expr, undef); + + compiler::VReg getter = undef; + compiler::VReg setter = undef; + + compiler::VReg accessor = pg->AllocReg(); + pg->LoadAccumulator(prop->Value(), obj_reg); + prop->Value()->Compile(pg); + pg->StoreAccumulator(prop->Value(), accessor); + + if (prop->Kind() == ir::PropertyKind::GET) { + getter = accessor; + } else { + setter = accessor; + } + + pg->DefineGetterSetterByValue(expr, obj_reg, key, getter, setter, prop->IsComputed()); + break; + } + case ir::PropertyKind::INIT: { + compiler::Operand key = pg->ToOwnPropertyKey(prop->Key(), prop->IsComputed()); + + if (prop->IsMethod()) { + pg->LoadAccumulator(prop->Value(), obj_reg); + } + + prop->Value()->Compile(pg); + pg->StoreOwnProperty(expr, obj_reg, key); + break; + } + case ir::PropertyKind::PROTO: { + prop->Value()->Compile(pg); + compiler::VReg proto = pg->AllocReg(); + pg->StoreAccumulator(expr, proto); + + pg->SetObjectWithProto(expr, proto, obj_reg); + break; + } + default: { + UNREACHABLE(); + } + } + } + + pg->LoadAccumulator(expr, obj_reg); +} + +void JSCompiler::Compile([[maybe_unused]] const ir::OpaqueTypeNode *node) const { - (void)node; UNREACHABLE(); } @@ -704,9 +919,23 @@ void JSCompiler::Compile(const ir::UpdateExpression *expr) const void JSCompiler::Compile(const ir::YieldExpression *expr) const { - (void)expr; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + compiler::RegScope rs(pg); + + if (expr->Argument() != nullptr) { + expr->Argument()->Compile(pg); + } else { + pg->LoadConst(expr, compiler::Constant::JS_UNDEFINED); + } + + if (expr->HasDelegate()) { + ASSERT(expr->Argument()); + pg->FuncBuilder()->YieldStar(expr); + } else { + pg->FuncBuilder()->Yield(expr); + } } + // Compile methods for LITERAL EXPRESSIONS in alphabetical order void JSCompiler::Compile(const ir::BigIntLiteral *expr) const { @@ -947,8 +1176,22 @@ void JSCompiler::Compile(const ir::TryStatement *st) const void JSCompiler::Compile(const ir::VariableDeclarator *st) const { - (void)st; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + auto lref = compiler::JSLReference::Create(pg, st->Id(), true); + const ir::VariableDeclaration *decl = st->Parent()->AsVariableDeclaration(); + + if (st->Init() != nullptr) { + st->Init()->Compile(pg); + } else { + if (decl->Kind() == ir::VariableDeclaration::VariableDeclarationKind::VAR) { + return; + } + if (decl->Kind() == ir::VariableDeclaration::VariableDeclarationKind::LET && !decl->Parent()->IsCatchClause()) { + pg->LoadConst(st, compiler::Constant::JS_UNDEFINED); + } + } + + lref.SetValue(); } void JSCompiler::Compile(const ir::VariableDeclaration *st) const @@ -957,11 +1200,30 @@ void JSCompiler::Compile(const ir::VariableDeclaration *st) const UNREACHABLE(); } +template +void CompileImpl(const ir::WhileStatement *while_stmt, [[maybe_unused]] CodeGen *cg) +{ + compiler::LabelTarget label_target(cg); + + cg->SetLabel(while_stmt, label_target.ContinueTarget()); + compiler::Condition::Compile(cg, while_stmt->Test(), label_target.BreakTarget()); + + { + compiler::LocalRegScope reg_scope(cg, while_stmt->Scope()); + compiler::LabelContext label_ctx(cg, label_target); + while_stmt->Body()->Compile(cg); + } + + cg->Branch(while_stmt, label_target.ContinueTarget()); + cg->SetLabel(while_stmt, label_target.BreakTarget()); +} + void JSCompiler::Compile(const ir::WhileStatement *st) const { - (void)st; - UNREACHABLE(); + PandaGen *pg = GetPandaGen(); + CompileImpl(st, pg); } + // from ts folder void JSCompiler::Compile(const ir::TSAnyKeyword *node) const { diff --git a/ets2panda/compiler/core/JSCompiler.h b/ets2panda/compiler/core/JSCompiler.h index 9d506416504cc8298c4bffcde4682922cd26ab9c..f0fd56738f9f3f1c46f74c63e5016119dcaa4dce 100644 --- a/ets2panda/compiler/core/JSCompiler.h +++ b/ets2panda/compiler/core/JSCompiler.h @@ -17,6 +17,7 @@ #define ES2PANDA_COMPILER_CORE_JSCOMPILER_H #include "compiler/core/ASTCompiler.h" +#include "util/bitset.h" namespace panda::es2panda::ir { class AstNode; @@ -36,6 +37,10 @@ public: #define DECLARE_JSCOMPILER_COMPILE_METHOD(_, __, nodeType, ___) void Compile(const ir::nodeType *node) const override; AST_NODE_REINTERPRET_MAPPING(DECLARE_JSCOMPILER_COMPILE_METHOD) #undef DECLARE_JSCOMPILER_COMPILE_METHOD + void CompileStaticProperties(compiler::PandaGen *pg, util::BitSet *compiled, + const ir::ObjectExpression *expr) const; + void CompileRemainingProperties(compiler::PandaGen *pg, const util::BitSet *compiled, + const ir::ObjectExpression *expr) const; private: PandaGen *GetPandaGen() const; diff --git a/ets2panda/compiler/core/codeGen.h b/ets2panda/compiler/core/codeGen.h index e06a982cad260779b94b10d71f4f1b60e4b24c2d..87e6faa6099d853f43618214d80b169243350414 100644 --- a/ets2panda/compiler/core/codeGen.h +++ b/ets2panda/compiler/core/codeGen.h @@ -20,7 +20,6 @@ #include "compiler/core/ASTCompiler.h" #include "compiler/core/regAllocator.h" #include "compiler/core/regScope.h" - namespace panda::es2panda::compiler { class CatchTable; class DynamicContext; diff --git a/ets2panda/compiler/core/compilerImpl.cpp b/ets2panda/compiler/core/compilerImpl.cpp index 14bc8b7fbdbde505a5043aef2087f4e2f417a36d..372fe4478556c59c556007046410d48a6a3e9f18 100644 --- a/ets2panda/compiler/core/compilerImpl.cpp +++ b/ets2panda/compiler/core/compilerImpl.cpp @@ -15,6 +15,8 @@ #include "compilerImpl.h" +#include "checker/ETSAnalyzer.h" +#include "checker/TSAnalyzer.h" #include "compiler/core/compilerContext.h" #include "compiler/core/compileQueue.h" #include "compiler/core/compilerImpl.h" @@ -35,9 +37,7 @@ #include "varbinder/ASBinder.h" #include "varbinder/TSBinder.h" #include "varbinder/ETSBinder.h" -#include "checker/TSAnalyzer.h" #include "checker/TSchecker.h" -#include "checker/ETSAnalyzer.h" #include "checker/ETSchecker.h" #include "checker/ASchecker.h" #include "checker/JSchecker.h" diff --git a/ets2panda/ir/base/spreadElement.cpp b/ets2panda/ir/base/spreadElement.cpp index 1f995a1f1160a8f03d86f2b8c0199817629b6394..7056605028836e6a521aaf0c3590b3ace00f69a6 100644 --- a/ets2panda/ir/base/spreadElement.cpp +++ b/ets2panda/ir/base/spreadElement.cpp @@ -16,6 +16,9 @@ #include "spreadElement.h" #include "es2panda.h" +#include "checker/TSchecker.h" +#include "compiler/core/pandagen.h" +#include "compiler/core/ETSGen.h" #include "ir/astDump.h" #include "ir/base/decorator.h" #include "ir/typeNode.h" @@ -138,15 +141,22 @@ void SpreadElement::Dump(ir::AstDumper *dumper) const {"typeAnnotation", AstDumper::Optional(TypeAnnotation())}}); } -void SpreadElement::Compile([[maybe_unused]] compiler::PandaGen *pg) const {} +void SpreadElement::Compile([[maybe_unused]] compiler::PandaGen *pg) const +{ + pg->GetAstCompiler()->Compile(this); +} +void SpreadElement::Compile([[maybe_unused]] compiler::ETSGen *etsg) const +{ + etsg->GetAstCompiler()->Compile(this); +} checker::Type *SpreadElement::Check([[maybe_unused]] checker::TSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *SpreadElement::Check([[maybe_unused]] checker::ETSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ets2panda/ir/base/spreadElement.h b/ets2panda/ir/base/spreadElement.h index 037cc59ca40d90174d64c54e4380f659c96ae2b5..7a43822d19439245c226b813a1ca9126f96bef43 100644 --- a/ets2panda/ir/base/spreadElement.h +++ b/ets2panda/ir/base/spreadElement.h @@ -78,6 +78,7 @@ public: void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; + void Compile([[maybe_unused]] compiler::ETSGen *etsg) const override; checker::Type *Check([[maybe_unused]] checker::TSChecker *checker) override; checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; diff --git a/ets2panda/ir/expressions/arrayExpression.cpp b/ets2panda/ir/expressions/arrayExpression.cpp index c3c36bef08d178a73a9bc23e6ce871535cb73f3e..135f245e1eb02e04e029a2a47a214686c1d79441 100644 --- a/ets2panda/ir/expressions/arrayExpression.cpp +++ b/ets2panda/ir/expressions/arrayExpression.cpp @@ -208,164 +208,17 @@ void ArrayExpression::Dump(ir::AstDumper *dumper) const void ArrayExpression::Compile(compiler::PandaGen *pg) const { - compiler::RegScope rs(pg); - compiler::VReg array_obj = pg->AllocReg(); - - pg->CreateArray(this, elements_, array_obj); + pg->GetAstCompiler()->Compile(this); } void ArrayExpression::Compile(compiler::ETSGen *const etsg) const { - const compiler::RegScope rs(etsg); - - const auto arr = etsg->AllocReg(); - const auto dim = etsg->AllocReg(); - - const compiler::TargetTypeContext ttctx(etsg, etsg->Checker()->GlobalIntType()); - etsg->LoadAccumulatorInt(this, static_cast(elements_.size())); - etsg->StoreAccumulator(this, dim); - etsg->NewArray(this, arr, dim, TsType()); - - const auto index_reg = etsg->AllocReg(); - for (std::uint32_t i = 0; i < elements_.size(); ++i) { - const auto *const expr = elements_[i]; - etsg->LoadAccumulatorInt(this, i); - etsg->StoreAccumulator(this, index_reg); - - const compiler::TargetTypeContext ttctx2(etsg, preferred_type_); - if (!etsg->TryLoadConstantExpression(expr)) { - expr->Compile(etsg); - } - - etsg->ApplyConversion(expr, nullptr); - etsg->ApplyConversion(expr); - etsg->StoreArrayElement(this, arr, index_reg, TsType()->AsETSArrayType()->ElementType()); - } - - etsg->LoadAccumulator(this, arr); -} - -void GetSpreadElementType(checker::TSChecker *checker, checker::Type *spread_type, - ArenaVector &element_types, const lexer::SourcePosition &loc) -{ - bool in_const_context = checker->HasStatus(checker::CheckerStatus::IN_CONST_CONTEXT); - - if (spread_type->IsObjectType() && spread_type->AsObjectType()->IsTupleType()) { - ArenaVector tuple_element_types(checker->Allocator()->Adapter()); - checker::TupleType *spread_tuple = spread_type->AsObjectType()->AsTupleType(); - - for (auto *it : spread_tuple->Properties()) { - if (in_const_context) { - element_types.push_back(it->TsType()); - continue; - } - - tuple_element_types.push_back(it->TsType()); - } - - if (in_const_context) { - return; - } - - element_types.push_back(checker->CreateUnionType(std::move(tuple_element_types))); - return; - } - - if (spread_type->IsUnionType()) { - ArenaVector spread_types(checker->Allocator()->Adapter()); - bool throw_error = false; - - for (auto *type : spread_type->AsUnionType()->ConstituentTypes()) { - if (type->IsArrayType()) { - spread_types.push_back(type->AsArrayType()->ElementType()); - continue; - } - - if (type->IsObjectType() && type->AsObjectType()->IsTupleType()) { - checker::TupleType *tuple = type->AsObjectType()->AsTupleType(); - - for (auto *it : tuple->Properties()) { - spread_types.push_back(it->TsType()); - } - - continue; - } - - throw_error = true; - break; - } - - if (!throw_error) { - element_types.push_back(checker->CreateUnionType(std::move(spread_types))); - return; - } - } - - checker->ThrowTypeError( - {"Type '", spread_type, "' must have a '[Symbol.iterator]()' method that returns an iterator."}, loc); + etsg->GetAstCompiler()->Compile(this); } checker::Type *ArrayExpression::Check(checker::TSChecker *checker) { - ArenaVector element_types(checker->Allocator()->Adapter()); - ArenaVector element_flags(checker->Allocator()->Adapter()); - bool in_const_context = checker->HasStatus(checker::CheckerStatus::IN_CONST_CONTEXT); - bool create_tuple = checker->HasStatus(checker::CheckerStatus::FORCE_TUPLE); - - for (auto *it : elements_) { - if (it->IsSpreadElement()) { - checker::Type *spread_type = it->AsSpreadElement()->Argument()->Check(checker); - - if (spread_type->IsArrayType()) { - element_types.push_back(in_const_context ? spread_type : spread_type->AsArrayType()->ElementType()); - element_flags.push_back(checker::ElementFlags::VARIADIC); - continue; - } - - GetSpreadElementType(checker, spread_type, element_types, it->Start()); - element_flags.push_back(checker::ElementFlags::REST); - continue; - } - - checker::Type *element_type = it->Check(checker); - - if (!in_const_context) { - element_type = checker->GetBaseTypeOfLiteralType(element_type); - } - - element_flags.push_back(checker::ElementFlags::REQUIRED); - element_types.push_back(element_type); - } - - if (in_const_context || create_tuple) { - checker::ObjectDescriptor *desc = checker->Allocator()->New(checker->Allocator()); - uint32_t index = 0; - - for (auto it = element_types.begin(); it != element_types.end(); it++, index++) { - util::StringView member_index = util::Helpers::ToStringView(checker->Allocator(), index); - varbinder::LocalVariable *tuple_member = varbinder::Scope::CreateVar( - checker->Allocator(), member_index, varbinder::VariableFlags::PROPERTY, nullptr); - - if (in_const_context) { - tuple_member->AddFlag(varbinder::VariableFlags::READONLY); - } - - tuple_member->SetTsType(*it); - desc->properties.push_back(tuple_member); - } - - return checker->CreateTupleType(desc, std::move(element_flags), checker::ElementFlags::REQUIRED, index, index, - in_const_context); - } - - checker::Type *array_element_type = nullptr; - if (element_types.empty()) { - array_element_type = checker->GlobalAnyType(); - } else { - array_element_type = checker->CreateUnionType(std::move(element_types)); - } - - return checker->Allocator()->New(array_element_type); + return checker->GetAnalyzer()->Check(this); } checker::Type *ArrayExpression::CheckPattern(checker::TSChecker *checker) @@ -481,37 +334,6 @@ checker::Type *ArrayExpression::CheckPattern(checker::TSChecker *checker) checker::Type *ArrayExpression::Check(checker::ETSChecker *checker) { - if (TsType() != nullptr) { - return TsType(); - } - - if (!elements_.empty()) { - if (preferred_type_ == nullptr) { - preferred_type_ = elements_[0]->Check(checker); - } - - for (auto *element : elements_) { - if (element->IsArrayExpression() && preferred_type_->IsETSArrayType()) { - element->AsArrayExpression()->SetPreferredType(preferred_type_->AsETSArrayType()->ElementType()); - } - if (element->IsObjectExpression()) { - element->AsObjectExpression()->SetPreferredType(preferred_type_); - } - - checker::Type *element_type = element->Check(checker); - checker::AssignmentContext(checker->Relation(), element, element_type, preferred_type_, element->Start(), - {"Array element type '", element_type, "' is not assignable to explicit type '", - GetPreferredType(), "'"}); - } - } - - if (preferred_type_ == nullptr) { - checker->ThrowTypeError("Can't resolve array type", Start()); - } - - SetTsType(checker->CreateETSArrayType(preferred_type_)); - auto array_type = TsType()->AsETSArrayType(); - checker->CreateBuiltinArraySignature(array_type, array_type->Rank()); - return TsType(); + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ets2panda/ir/expressions/arrayExpression.h b/ets2panda/ir/expressions/arrayExpression.h index 5848b480b54f641968a51e5abaddad04fa2a09ed..b2eae8de5d4047cf4bf2c3c0b29f801513b9249f 100644 --- a/ets2panda/ir/expressions/arrayExpression.h +++ b/ets2panda/ir/expressions/arrayExpression.h @@ -19,6 +19,12 @@ #include "ir/expression.h" #include "ir/validationInfo.h" +namespace panda::es2panda::checker { +class ETSAnalyzer; +} // namespace panda::es2panda::checker +namespace panda::es2panda::compiler { +class ETSCompiler; +} // namespace panda::es2panda::compiler namespace panda::es2panda::ir { class ArrayExpression : public AnnotatedExpression { private: @@ -48,6 +54,10 @@ public: explicit ArrayExpression(Tag tag, ArrayExpression const &other, ArenaAllocator *allocator); + // TODO (vivienvoros): these friend relationships can be removed once there are getters for private fields + friend class checker::ETSAnalyzer; + friend class compiler::ETSCompiler; + [[nodiscard]] const ArenaVector &Elements() const noexcept { return elements_; diff --git a/ets2panda/ir/expressions/assignmentExpression.cpp b/ets2panda/ir/expressions/assignmentExpression.cpp index a6ba7d6ee0528ee4b948d9be668fe3a275f21027..868277145be21b2ff2675afc16d5cba9a57df897 100644 --- a/ets2panda/ir/expressions/assignmentExpression.cpp +++ b/ets2panda/ir/expressions/assignmentExpression.cpp @@ -121,45 +121,12 @@ void AssignmentExpression::Dump(ir::AstDumper *dumper) const void AssignmentExpression::Compile(compiler::PandaGen *pg) const { - compiler::RegScope rs(pg); - auto lref = compiler::JSLReference::Create(pg, left_, false); - - if (operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND_EQUAL || - operator_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR_EQUAL) { - compiler::PandaGen::Unimplemented(); - } - - if (operator_ == lexer::TokenType::PUNCTUATOR_SUBSTITUTION) { - right_->Compile(pg); - lref.SetValue(); - return; - } - - compiler::VReg lhs_reg = pg->AllocReg(); - - lref.GetValue(); - pg->StoreAccumulator(left_, lhs_reg); - right_->Compile(pg); - pg->Binary(this, operator_, lhs_reg); - - lref.SetValue(); + pg->GetAstCompiler()->Compile(this); } void AssignmentExpression::Compile(compiler::ETSGen *etsg) const { - // All other operations are handled in OpAssignmentLowering - ASSERT(operator_ == lexer::TokenType::PUNCTUATOR_SUBSTITUTION); - compiler::RegScope rs(etsg); - auto lref = compiler::ETSLReference::Create(etsg, left_, false); - auto ttctx = compiler::TargetTypeContext(etsg, TsType()); - - if (right_->IsNullLiteral()) { - etsg->LoadAccumulatorNull(this, left_->TsType()); - } else { - right_->Compile(etsg); - etsg->ApplyConversion(right_, TsType()); - } - lref.SetValue(); + etsg->GetAstCompiler()->Compile(this); } void AssignmentExpression::CompilePattern(compiler::PandaGen *pg) const @@ -172,138 +139,12 @@ void AssignmentExpression::CompilePattern(compiler::PandaGen *pg) const checker::Type *AssignmentExpression::Check(checker::TSChecker *checker) { - if (left_->IsArrayPattern()) { - auto saved_context = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); - auto destructuring_context = checker::ArrayDestructuringContext(checker, left_, true, true, nullptr, right_); - destructuring_context.Start(); - return destructuring_context.InferredType(); - } - - if (left_->IsObjectPattern()) { - auto saved_context = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); - auto destructuring_context = checker::ObjectDestructuringContext(checker, left_, true, true, nullptr, right_); - destructuring_context.Start(); - return destructuring_context.InferredType(); - } - - if (left_->IsIdentifier() && left_->AsIdentifier()->Variable() != nullptr && - left_->AsIdentifier()->Variable()->Declaration()->IsConstDecl()) { - checker->ThrowTypeError({"Cannot assign to ", left_->AsIdentifier()->Name(), " because it is a constant."}, - left_->Start()); - } - - auto *left_type = left_->Check(checker); - - if (left_type->HasTypeFlag(checker::TypeFlag::READONLY)) { - checker->ThrowTypeError("Cannot assign to this property because it is readonly.", left_->Start()); - } - - if (operator_ == lexer::TokenType::PUNCTUATOR_SUBSTITUTION) { - checker->ElaborateElementwise(left_type, right_, left_->Start()); - return checker->CheckTypeCached(right_); - } - - auto *right_type = right_->Check(checker); - - switch (operator_) { - case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL: - case lexer::TokenType::PUNCTUATOR_EXPONENTIATION_EQUAL: - case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL: - case lexer::TokenType::PUNCTUATOR_MOD_EQUAL: - case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL: - case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL: - case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL: - case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL: - case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL: - case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL: - case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: { - return checker->CheckBinaryOperator(left_type, right_type, left_, right_, this, operator_); - } - case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: { - return checker->CheckPlusOperator(left_type, right_type, left_, right_, this, operator_); - } - case lexer::TokenType::PUNCTUATOR_SUBSTITUTION: { - checker->CheckAssignmentOperator(operator_, left_, left_type, right_type); - return right_type; - } - default: { - UNREACHABLE(); - break; - } - } - - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *AssignmentExpression::Check([[maybe_unused]] checker::ETSChecker *checker) { - if (TsType() != nullptr) { - return TsType(); - } - - auto *left_type = left_->Check(checker); - if (left_->IsMemberExpression() && left_->AsMemberExpression()->Object()->TsType()->IsETSArrayType() && - left_->AsMemberExpression()->Property()->IsIdentifier() && - left_->AsMemberExpression()->Property()->AsIdentifier()->Name().Is("length")) { - checker->ThrowTypeError("Setting the length of an array is not permitted", left_->Start()); - } - - if (left_->IsIdentifier()) { - target_ = left_->AsIdentifier()->Variable(); - } else { - target_ = left_->AsMemberExpression()->PropVar(); - } - - if (target_ != nullptr) { - checker->ValidateUnaryOperatorOperand(target_); - } - - checker::Type *source_type {}; - ir::Expression *relation_node = right_; - switch (operator_) { - case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL: - case lexer::TokenType::PUNCTUATOR_EXPONENTIATION_EQUAL: - case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL: - case lexer::TokenType::PUNCTUATOR_MOD_EQUAL: - case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL: - case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL: - case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL: - case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL: - case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL: - case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL: - case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: - case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: { - std::tie(std::ignore, operation_type_) = - checker->CheckBinaryOperator(left_, right_, this, operator_, Start(), true); - - auto unboxed_left = checker->ETSBuiltinTypeAsPrimitiveType(left_type); - source_type = unboxed_left == nullptr ? left_type : unboxed_left; - - relation_node = this; - break; - } - case lexer::TokenType::PUNCTUATOR_SUBSTITUTION: { - if (left_type->IsETSArrayType() && right_->IsArrayExpression()) { - right_->AsArrayExpression()->SetPreferredType(left_type->AsETSArrayType()->ElementType()); - } - if (right_->IsObjectExpression()) { - right_->AsObjectExpression()->SetPreferredType(left_type); - } - - source_type = right_->Check(checker); - break; - } - default: { - UNREACHABLE(); - break; - } - } - - checker::AssignmentContext(checker->Relation(), relation_node, source_type, left_type, right_->Start(), - {"Initializers type is not assignable to the target type"}); - - SetTsType(left_->TsType()); - return TsType(); + return checker->GetAnalyzer()->Check(this); } AssignmentExpression::AssignmentExpression([[maybe_unused]] Tag const tag, AssignmentExpression const &other, diff --git a/ets2panda/ir/expressions/assignmentExpression.h b/ets2panda/ir/expressions/assignmentExpression.h index 5f14a1c1cff1321beae8fb22326bc89d9c25353a..7087ba01d16c1d07817322bb316c9faebd631aad 100644 --- a/ets2panda/ir/expressions/assignmentExpression.h +++ b/ets2panda/ir/expressions/assignmentExpression.h @@ -18,7 +18,9 @@ #include "ir/expression.h" #include "lexer/token/tokenType.h" - +namespace panda::es2panda::checker { +class ETSAnalyzer; +} // namespace panda::es2panda::checker namespace panda::es2panda::ir { class AssignmentExpression : public Expression { private: @@ -45,6 +47,9 @@ public: explicit AssignmentExpression(Tag tag, AssignmentExpression const &other, Expression *left, Expression *right); + // TODO (vivienvoros): these friend relationships can be removed once there are getters for private fields + friend class checker::ETSAnalyzer; + [[nodiscard]] const Expression *Left() const noexcept { return left_; diff --git a/ets2panda/ir/expressions/objectExpression.cpp b/ets2panda/ir/expressions/objectExpression.cpp index f3b4b4ebff09da14eb37d8fd2e2cedca2209ae1e..6379361b99dfb569f2df68c54d87fa82fba93c37 100644 --- a/ets2panda/ir/expressions/objectExpression.cpp +++ b/ets2panda/ir/expressions/objectExpression.cpp @@ -231,199 +231,9 @@ void ObjectExpression::Dump(ir::AstDumper *dumper) const {"optional", AstDumper::Optional(optional_)}}); } -static compiler::Literal CreateLiteral(const ir::Property *prop, util::BitSet *compiled, size_t prop_index) -{ - compiler::Literal lit = util::Helpers::ToConstantLiteral(prop->Value()); - if (!lit.IsInvalid()) { - compiled->Set(prop_index); - return lit; - } - - if (prop->Kind() != ir::PropertyKind::INIT) { - ASSERT(prop->IsAccessor()); - return compiler::Literal::AccessorLiteral(); - } - - if (!prop->Value()->IsFunctionExpression()) { - return compiler::Literal::NullLiteral(); - } - - const ir::ScriptFunction *method = prop->Value()->AsFunctionExpression()->Function(); - - compiler::LiteralTag tag = compiler::LiteralTag::METHOD; - - if (method->IsGenerator()) { - tag = compiler::LiteralTag::GENERATOR_METHOD; - - if (method->IsAsyncFunc()) { - tag = compiler::LiteralTag::ASYNC_GENERATOR_METHOD; - } - } - - compiled->Set(prop_index); - return compiler::Literal(tag, method->Scope()->InternalName()); -} - -static bool IsLiteralBufferCompatible(const Expression *expr) -{ - if (expr->IsSpreadElement()) { - return false; - } - - const ir::Property *prop = expr->AsProperty(); - if (prop->Value()->IsFunctionExpression() && !prop->Value()->AsFunctionExpression()->Function()->IsMethod()) { - return false; - } - - return util::Helpers::IsConstantPropertyKey(prop->Key(), prop->IsComputed()) && - prop->Kind() != ir::PropertyKind::PROTO; -} - -void ObjectExpression::CompileStaticProperties(compiler::PandaGen *pg, util::BitSet *compiled) const -{ - bool has_method = false; - bool seen_computed = false; - compiler::LiteralBuffer buf; - std::unordered_map prop_name_map; - - for (size_t i = 0; i < properties_.size(); i++) { - if (!IsLiteralBufferCompatible(properties_[i])) { - seen_computed = true; - continue; - } - - const ir::Property *prop = properties_[i]->AsProperty(); - - util::StringView name = util::Helpers::LiteralToPropName(prop->Key()); - size_t buffer_pos = buf.size(); - auto res = prop_name_map.insert({name, buffer_pos}); - if (res.second) { - if (seen_computed) { - break; - } - - buf.emplace_back(name); - buf.emplace_back(); - } else { - buffer_pos = res.first->second; - } - - compiler::Literal lit = CreateLiteral(prop, compiled, i); - if (lit.IsTagMethod()) { - has_method = true; - } - - buf[buffer_pos + 1] = std::move(lit); - } - - if (buf.empty()) { - pg->CreateEmptyObject(this); - return; - } - - uint32_t buf_idx = pg->AddLiteralBuffer(std::move(buf)); - - if (has_method) { - pg->CreateObjectHavingMethod(this, buf_idx); - } else { - pg->CreateObjectWithBuffer(this, buf_idx); - } -} - -void ObjectExpression::CompileRemainingProperties(compiler::PandaGen *pg, const util::BitSet *compiled) const -{ - compiler::RegScope rs(pg); - compiler::VReg obj_reg = pg->AllocReg(); - - pg->StoreAccumulator(this, obj_reg); - - for (size_t i = 0; i < properties_.size(); i++) { - if (compiled->Test(i)) { - continue; - } - - compiler::RegScope prs(pg); - - if (properties_[i]->IsSpreadElement()) { - compiler::VReg src_obj = pg->AllocReg(); - auto const *const spread = properties_[i]->AsSpreadElement(); - - spread->Argument()->Compile(pg); - pg->StoreAccumulator(spread, src_obj); - - pg->CopyDataProperties(spread, obj_reg, src_obj); - continue; - } - - const ir::Property *prop = properties_[i]->AsProperty(); - - switch (prop->Kind()) { - case ir::PropertyKind::GET: - case ir::PropertyKind::SET: { - compiler::VReg key = pg->LoadPropertyKey(prop->Key(), prop->IsComputed()); - - compiler::VReg undef = pg->AllocReg(); - pg->LoadConst(this, compiler::Constant::JS_UNDEFINED); - pg->StoreAccumulator(this, undef); - - compiler::VReg getter = undef; - compiler::VReg setter = undef; - - compiler::VReg accessor = pg->AllocReg(); - pg->LoadAccumulator(prop->Value(), obj_reg); - prop->Value()->Compile(pg); - pg->StoreAccumulator(prop->Value(), accessor); - - if (prop->Kind() == ir::PropertyKind::GET) { - getter = accessor; - } else { - setter = accessor; - } - - pg->DefineGetterSetterByValue(this, obj_reg, key, getter, setter, prop->IsComputed()); - break; - } - case ir::PropertyKind::INIT: { - compiler::Operand key = pg->ToOwnPropertyKey(prop->Key(), prop->IsComputed()); - - if (prop->IsMethod()) { - pg->LoadAccumulator(prop->Value(), obj_reg); - } - - prop->Value()->Compile(pg); - pg->StoreOwnProperty(this, obj_reg, key); - break; - } - case ir::PropertyKind::PROTO: { - prop->Value()->Compile(pg); - compiler::VReg proto = pg->AllocReg(); - pg->StoreAccumulator(this, proto); - - pg->SetObjectWithProto(this, proto, obj_reg); - break; - } - default: { - UNREACHABLE(); - } - } - } - - pg->LoadAccumulator(this, obj_reg); -} - void ObjectExpression::Compile([[maybe_unused]] compiler::PandaGen *pg) const { - if (properties_.empty()) { - pg->CreateEmptyObject(this); - return; - } - - util::BitSet compiled(properties_.size()); - CompileStaticProperties(pg, &compiled); - - if (compiled.Any(false)) { - CompileRemainingProperties(pg, &compiled); - } + pg->GetAstCompiler()->Compile(this); } checker::Type *ObjectExpression::CheckPattern(checker::TSChecker *checker) @@ -561,305 +371,18 @@ checker::Type *ObjectExpression::CheckPattern(checker::TSChecker *checker) return return_type; } -const util::StringView &GetPropertyName(const ir::Expression *key) -{ - if (key->IsIdentifier()) { - return key->AsIdentifier()->Name(); - } - - if (key->IsStringLiteral()) { - return key->AsStringLiteral()->Str(); - } - - ASSERT(key->IsNumberLiteral()); - return key->AsNumberLiteral()->Str(); -} - -varbinder::VariableFlags GetFlagsForProperty(const ir::Property *prop) -{ - if (!prop->IsMethod()) { - return varbinder::VariableFlags::PROPERTY; - } - - varbinder::VariableFlags prop_flags = varbinder::VariableFlags::METHOD; - - if (prop->IsAccessor() && prop->Kind() == PropertyKind::GET) { - prop_flags |= varbinder::VariableFlags::READONLY; - } - - return prop_flags; -} - -checker::Type *GetTypeForProperty(ir::Property *prop, checker::TSChecker *checker) -{ - if (prop->IsAccessor()) { - checker::Type *func_type = prop->Value()->Check(checker); - - if (prop->Kind() == PropertyKind::SET) { - return checker->GlobalAnyType(); - } - - ASSERT(func_type->IsObjectType() && func_type->AsObjectType()->IsFunctionType()); - return func_type->AsObjectType()->CallSignatures()[0]->ReturnType(); - } - - if (prop->IsShorthand()) { - return prop->Key()->Check(checker); - } - - return prop->Value()->Check(checker); -} - checker::Type *ObjectExpression::Check(checker::TSChecker *checker) { - checker::ObjectDescriptor *desc = checker->Allocator()->New(checker->Allocator()); - std::unordered_map all_properties_map; - bool in_const_context = checker->HasStatus(checker::CheckerStatus::IN_CONST_CONTEXT); - ArenaVector computed_number_prop_types(checker->Allocator()->Adapter()); - ArenaVector computed_string_prop_types(checker->Allocator()->Adapter()); - bool has_computed_number_property = false; - bool has_computed_string_property = false; - bool seen_spread = false; - - for (auto *it : properties_) { - if (it->IsProperty()) { - auto *prop = it->AsProperty(); - - if (prop->IsComputed()) { - checker::Type *computed_name_type = checker->CheckComputedPropertyName(prop->Key()); - - if (computed_name_type->IsNumberType()) { - has_computed_number_property = true; - computed_number_prop_types.push_back(prop->Value()->Check(checker)); - continue; - } - - if (computed_name_type->IsStringType()) { - has_computed_string_property = true; - computed_string_prop_types.push_back(prop->Value()->Check(checker)); - continue; - } - } - - checker::Type *prop_type = GetTypeForProperty(prop, checker); - varbinder::VariableFlags flags = GetFlagsForProperty(prop); - const util::StringView &prop_name = GetPropertyName(prop->Key()); - - auto *member_var = varbinder::Scope::CreateVar(checker->Allocator(), prop_name, flags, it); - - if (in_const_context) { - member_var->AddFlag(varbinder::VariableFlags::READONLY); - } else { - prop_type = checker->GetBaseTypeOfLiteralType(prop_type); - } - - member_var->SetTsType(prop_type); - - if (prop->Key()->IsNumberLiteral()) { - member_var->AddFlag(varbinder::VariableFlags::NUMERIC_NAME); - } - - varbinder::LocalVariable *found_member = desc->FindProperty(prop_name); - all_properties_map.insert({prop_name, it->Start()}); - - if (found_member != nullptr) { - found_member->SetTsType(prop_type); - continue; - } - - desc->properties.push_back(member_var); - continue; - } - - ASSERT(it->IsSpreadElement()); - - checker::Type *const spread_type = it->AsSpreadElement()->Argument()->Check(checker); - seen_spread = true; - - // NOTE: aszilagyi. handle union of object types - if (!spread_type->IsObjectType()) { - checker->ThrowTypeError("Spread types may only be created from object types.", it->Start()); - } - - for (auto *spread_prop : spread_type->AsObjectType()->Properties()) { - auto found = all_properties_map.find(spread_prop->Name()); - if (found != all_properties_map.end()) { - checker->ThrowTypeError( - {found->first, " is specified more than once, so this usage will be overwritten."}, found->second); - } - - varbinder::LocalVariable *found_member = desc->FindProperty(spread_prop->Name()); - - if (found_member != nullptr) { - found_member->SetTsType(spread_prop->TsType()); - continue; - } - - desc->properties.push_back(spread_prop); - } - } - - if (!seen_spread && (has_computed_number_property || has_computed_string_property)) { - for (auto *it : desc->properties) { - computed_string_prop_types.push_back(it->TsType()); - - if (has_computed_number_property && it->HasFlag(varbinder::VariableFlags::NUMERIC_NAME)) { - computed_number_prop_types.push_back(it->TsType()); - } - } - - if (has_computed_number_property) { - desc->number_index_info = checker->Allocator()->New( - checker->CreateUnionType(std::move(computed_number_prop_types)), "x", in_const_context); - } - - if (has_computed_string_property) { - desc->string_index_info = checker->Allocator()->New( - checker->CreateUnionType(std::move(computed_string_prop_types)), "x", in_const_context); - } - } - - checker::Type *return_type = checker->Allocator()->New(desc); - return_type->AsObjectType()->AddObjectFlag(checker::ObjectFlags::RESOLVED_MEMBERS | - checker::ObjectFlags::CHECK_EXCESS_PROPS); - return return_type; + return checker->GetAnalyzer()->Check(this); } void ObjectExpression::Compile(compiler::ETSGen *etsg) const { - compiler::RegScope rs {etsg}; - checker::ETSObjectType const *obj_type = TsType()->AsETSObjectType(); - compiler::VReg obj_reg = etsg->AllocReg(); - if (TsType()->IsETSDynamicType()) { - auto *signature_info = etsg->Allocator()->New(etsg->Allocator()); - auto *create_obj_sig = etsg->Allocator()->New( - signature_info, nullptr, compiler::Signatures::BUILTIN_JSRUNTIME_CREATE_OBJECT); - compiler::VReg dummy_reg = compiler::VReg::RegStart(); - etsg->CallDynamic(this, dummy_reg, dummy_reg, create_obj_sig, - ArenaVector(etsg->Allocator()->Adapter())); - } else { - checker::Signature *empty_sig = nullptr; - for (checker::Signature *sig : obj_type->ConstructSignatures()) { - if (sig->Params().empty()) { - empty_sig = sig; - break; - } - } - if (empty_sig == nullptr) { // Would have already thrown in the checker. - UNREACHABLE(); - } - etsg->InitObject(this, empty_sig, ArenaVector(etsg->Allocator()->Adapter())); - } - etsg->SetAccumulatorType(TsType()); - etsg->StoreAccumulator(this, obj_reg); - - for (Expression *prop_expr : Properties()) { - ASSERT(prop_expr->IsProperty()); - Property *prop = prop_expr->AsProperty(); - Expression *key = prop->Key(); - Expression *value = prop->Value(); - - util::StringView pname; - if (key->IsStringLiteral()) { - pname = key->AsStringLiteral()->Str(); - } else if (key->IsIdentifier()) { - pname = key->AsIdentifier()->Name(); - } else { - UNREACHABLE(); - } - - value->Compile(etsg); - etsg->ApplyConversion(value, key->TsType()); - if (TsType()->IsETSDynamicType()) { - etsg->StorePropertyDynamic(this, value->TsType(), obj_reg, pname, TsType()->AsETSDynamicType()->Language()); - } else { - etsg->StoreProperty(this, key->TsType(), obj_reg, pname); - } - } - - etsg->LoadAccumulator(this, obj_reg); + etsg->GetAstCompiler()->Compile(this); } checker::Type *ObjectExpression::Check(checker::ETSChecker *checker) { - if (TsType() != nullptr) { - return TsType(); - } - - if (PreferredType() == nullptr) { - checker->ThrowTypeError({"need to specify target type for class composite"}, Start()); - } - if (!PreferredType()->IsETSObjectType()) { - checker->ThrowTypeError({"target type for class composite needs to be an object type"}, Start()); - } - - if (PreferredType()->IsETSDynamicType()) { - for (Expression *prop_expr : Properties()) { - ASSERT(prop_expr->IsProperty()); - Property *prop = prop_expr->AsProperty(); - Expression *value = prop->Value(); - value->Check(checker); - ASSERT(value->TsType()); - } - - SetTsType(PreferredType()); - return PreferredType(); - } - - checker::ETSObjectType *obj_type = PreferredType()->AsETSObjectType(); - if (obj_type->HasObjectFlag(checker::ETSObjectFlags::ABSTRACT | checker::ETSObjectFlags::INTERFACE)) { - checker->ThrowTypeError({"target type for class composite ", obj_type->Name(), " is not instantiable"}, - Start()); - } - - bool have_empty_constructor = false; - for (checker::Signature *sig : obj_type->ConstructSignatures()) { - if (sig->Params().empty()) { - have_empty_constructor = true; - checker->ValidateSignatureAccessibility(obj_type, sig, Start()); - break; - } - } - if (!have_empty_constructor) { - checker->ThrowTypeError({"type ", obj_type->Name(), " has no parameterless constructor"}, Start()); - } - - for (Expression *prop_expr : Properties()) { - ASSERT(prop_expr->IsProperty()); - Property *prop = prop_expr->AsProperty(); - Expression *key = prop->Key(); - Expression *value = prop->Value(); - - util::StringView pname; - if (key->IsStringLiteral()) { - pname = key->AsStringLiteral()->Str(); - } else if (key->IsIdentifier()) { - pname = key->AsIdentifier()->Name(); - } else { - checker->ThrowTypeError({"key in class composite should be either identifier or string literal"}, Start()); - } - varbinder::LocalVariable *lv = obj_type->GetProperty( - pname, checker::PropertySearchFlags::SEARCH_INSTANCE_FIELD | checker::PropertySearchFlags::SEARCH_IN_BASE); - if (lv == nullptr) { - checker->ThrowTypeError({"type ", obj_type->Name(), " has no property named ", pname}, prop_expr->Start()); - } - checker->ValidatePropertyAccess(lv, obj_type, prop_expr->Start()); - if (lv->HasFlag(varbinder::VariableFlags::READONLY)) { - checker->ThrowTypeError({"cannot assign to readonly property ", pname}, prop_expr->Start()); - } - - auto *prop_type = checker->GetTypeOfVariable(lv); - key->SetTsType(prop_type); - - if (value->IsObjectExpression()) { - value->AsObjectExpression()->SetPreferredType(prop_type); - } - value->SetTsType(value->Check(checker)); - checker::AssignmentContext(checker->Relation(), value, value->TsType(), prop_type, value->Start(), - {"value type is not assignable to the property type"}); - } - - SetTsType(obj_type); - return obj_type; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ets2panda/ir/expressions/objectExpression.h b/ets2panda/ir/expressions/objectExpression.h index b258313d0b93de38a1d3b55eab3cae03dfbc5123..6db977774e16600fffde0b65e58940bd58153ee2 100644 --- a/ets2panda/ir/expressions/objectExpression.h +++ b/ets2panda/ir/expressions/objectExpression.h @@ -19,7 +19,9 @@ #include "varbinder/variable.h" #include "ir/expression.h" #include "ir/validationInfo.h" - +namespace panda::es2panda::checker { +class ETSAnalyzer; +} // namespace panda::es2panda::checker namespace panda::es2panda::util { class BitSet; } // namespace panda::es2panda::util @@ -44,9 +46,11 @@ public: trailing_comma_(trailing_comma) { } - explicit ObjectExpression(Tag tag, ObjectExpression const &other, ArenaAllocator *allocator); + // TODO (vivienvoros): these friend relationships can be removed once there are getters for private fields + friend class checker::ETSAnalyzer; + [[nodiscard]] const ArenaVector &Properties() const noexcept { return properties_; @@ -100,9 +104,6 @@ public: checker::Type *CheckPattern(checker::TSChecker *checker); private: - void CompileStaticProperties(compiler::PandaGen *pg, util::BitSet *compiled) const; - void CompileRemainingProperties(compiler::PandaGen *pg, const util::BitSet *compiled) const; - ArenaVector decorators_; ArenaVector properties_; checker::Type *preferred_type_ {}; diff --git a/ets2panda/ir/expressions/yieldExpression.cpp b/ets2panda/ir/expressions/yieldExpression.cpp index 4aadc7a54582364feea1d450fdf0c06c3361bad6..9b597c88da942406f1bd3b0b498fea44125f1a6d 100644 --- a/ets2panda/ir/expressions/yieldExpression.cpp +++ b/ets2panda/ir/expressions/yieldExpression.cpp @@ -15,11 +15,11 @@ #include "yieldExpression.h" +#include "compiler/core/ETSGen.h" #include "compiler/core/pandagen.h" #include "compiler/function/generatorFunctionBuilder.h" #include "checker/TSchecker.h" #include "ir/astDump.h" - namespace panda::es2panda::ir { void YieldExpression::TransformChildren(const NodeTransformer &cb) { @@ -42,31 +42,22 @@ void YieldExpression::Dump(ir::AstDumper *dumper) const void YieldExpression::Compile([[maybe_unused]] compiler::PandaGen *pg) const { - compiler::RegScope rs(pg); - - if (argument_ != nullptr) { - argument_->Compile(pg); - } else { - pg->LoadConst(this, compiler::Constant::JS_UNDEFINED); - } + pg->GetAstCompiler()->Compile(this); +} - if (delegate_) { - ASSERT(argument_); - pg->FuncBuilder()->YieldStar(this); - } else { - pg->FuncBuilder()->Yield(this); - } +void YieldExpression::Compile(compiler::ETSGen *etsg) const +{ + etsg->GetAstCompiler()->Compile(this); } checker::Type *YieldExpression::Check([[maybe_unused]] checker::TSChecker *checker) { - // NOTE: aszilagyi. - return checker->GlobalAnyType(); + return checker->GetAnalyzer()->Check(this); } checker::Type *YieldExpression::Check([[maybe_unused]] checker::ETSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } // NOLINTNEXTLINE(google-default-arguments) diff --git a/ets2panda/ir/expressions/yieldExpression.h b/ets2panda/ir/expressions/yieldExpression.h index 9e8e85608b77f19cfcdf8a788394cb66af1fa9de..2d2116c3deeb4238fbc4de64666f8337db5e4c5d 100644 --- a/ets2panda/ir/expressions/yieldExpression.h +++ b/ets2panda/ir/expressions/yieldExpression.h @@ -53,6 +53,7 @@ public: void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; void Compile(compiler::PandaGen *pg) const override; + void Compile(compiler::ETSGen *etsg) const override; checker::Type *Check(checker::TSChecker *checker) override; checker::Type *Check([[maybe_unused]] checker::ETSChecker *checker) override; diff --git a/ets2panda/ir/opaqueTypeNode.cpp b/ets2panda/ir/opaqueTypeNode.cpp index caa23c61331bc4f76205925f84116c44f3979c7b..b09b065cb4cba3e35869a2cb44a6855fa5e8fb3b 100644 --- a/ets2panda/ir/opaqueTypeNode.cpp +++ b/ets2panda/ir/opaqueTypeNode.cpp @@ -14,8 +14,10 @@ */ #include "opaqueTypeNode.h" +#include "checker/TSchecker.h" +#include "compiler/core/ETSGen.h" +#include "compiler/core/pandagen.h" #include "ir/astDump.h" - namespace panda::es2panda::ir { void OpaqueTypeNode::TransformChildren([[maybe_unused]] const NodeTransformer &cb) {} @@ -26,16 +28,19 @@ void OpaqueTypeNode::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "OpaqueType"}}); } -void OpaqueTypeNode::Compile([[maybe_unused]] compiler::PandaGen *pg) const {} +void OpaqueTypeNode::Compile([[maybe_unused]] compiler::PandaGen *pg) const +{ + pg->GetAstCompiler()->Compile(this); +} void OpaqueTypeNode::Compile([[maybe_unused]] compiler::ETSGen *etsg) const { - UNREACHABLE(); + etsg->GetAstCompiler()->Compile(this); } checker::Type *OpaqueTypeNode::Check([[maybe_unused]] checker::TSChecker *checker) { - return TsType(); + return checker->GetAnalyzer()->Check(this); } checker::Type *OpaqueTypeNode::GetType([[maybe_unused]] checker::TSChecker *checker) @@ -50,6 +55,6 @@ checker::Type *OpaqueTypeNode::GetType([[maybe_unused]] checker::ETSChecker *che checker::Type *OpaqueTypeNode::Check([[maybe_unused]] checker::ETSChecker *checker) { - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ets2panda/ir/statements/returnStatement.h b/ets2panda/ir/statements/returnStatement.h index cacaf713a77d7b33281721073dea6680f01c6cff..91cf30cc42076cc1cc2a41cc4cc6a1270a7912a7 100644 --- a/ets2panda/ir/statements/returnStatement.h +++ b/ets2panda/ir/statements/returnStatement.h @@ -26,6 +26,11 @@ namespace panda::es2panda::compiler { class ETSCompiler; } // namespace panda::es2panda::compiler +namespace panda::es2panda::compiler { +class JSCompiler; +class ETSCompiler; +} // namespace panda::es2panda::compiler + namespace panda::es2panda::ir { class ReturnStatement : public Statement { public: diff --git a/ets2panda/ir/statements/variableDeclarator.cpp b/ets2panda/ir/statements/variableDeclarator.cpp index 3303096080170efbee1901e1d37ad1e4c66c9e44..3089bfd1cb17ff13b661abd5392bbed2c91555b8 100644 --- a/ets2panda/ir/statements/variableDeclarator.cpp +++ b/ets2panda/ir/statements/variableDeclarator.cpp @@ -58,144 +58,21 @@ void VariableDeclarator::Dump(ir::AstDumper *dumper) const void VariableDeclarator::Compile([[maybe_unused]] compiler::PandaGen *pg) const { - auto lref = compiler::JSLReference::Create(pg, id_, true); - const ir::VariableDeclaration *decl = parent_->AsVariableDeclaration(); - - if (init_ != nullptr) { - init_->Compile(pg); - } else { - if (decl->Kind() == ir::VariableDeclaration::VariableDeclarationKind::VAR) { - return; - } - if (decl->Kind() == ir::VariableDeclaration::VariableDeclarationKind::LET && !decl->Parent()->IsCatchClause()) { - pg->LoadConst(this, compiler::Constant::JS_UNDEFINED); - } - } - - lref.SetValue(); + pg->GetAstCompiler()->Compile(this); } void VariableDeclarator::Compile(compiler::ETSGen *etsg) const { - auto lref = compiler::ETSLReference::Create(etsg, id_, true); - auto ttctx = compiler::TargetTypeContext(etsg, TsType()); - - if (id_->AsIdentifier()->Variable()->HasFlag(varbinder::VariableFlags::BOXED)) { - etsg->EmitLocalBoxCtor(id_); - etsg->StoreAccumulator(this, lref.Variable()->AsLocalVariable()->Vreg()); - etsg->SetAccumulatorType(lref.Variable()->TsType()); - } - - if (init_ != nullptr) { - if (!etsg->TryLoadConstantExpression(init_)) { - init_->Compile(etsg); - etsg->ApplyConversion(init_, nullptr); - } - } else { - etsg->LoadDefaultValue(this, id_->AsIdentifier()->Variable()->TsType()); - } - - etsg->ApplyConversion(this, TsType()); - lref.SetValue(); -} - -static void CheckSimpleVariableDeclaration(checker::TSChecker *checker, ir::VariableDeclarator *declarator) -{ - varbinder::Variable *const binding_var = declarator->Id()->AsIdentifier()->Variable(); - checker::Type *previous_type = binding_var->TsType(); - auto *const type_annotation = declarator->Id()->AsIdentifier()->TypeAnnotation(); - auto *const initializer = declarator->Init(); - const bool is_const = declarator->Parent()->AsVariableDeclaration()->Kind() == - ir::VariableDeclaration::VariableDeclarationKind::CONST; - - if (is_const) { - checker->AddStatus(checker::CheckerStatus::IN_CONST_CONTEXT); - } - - if (type_annotation != nullptr) { - type_annotation->Check(checker); - } - - if (type_annotation != nullptr && initializer != nullptr) { - checker::Type *const annotation_type = type_annotation->GetType(checker); - checker->ElaborateElementwise(annotation_type, initializer, declarator->Id()->Start()); - binding_var->SetTsType(annotation_type); - } else if (type_annotation != nullptr) { - binding_var->SetTsType(type_annotation->GetType(checker)); - } else if (initializer != nullptr) { - checker::Type *initializer_type = checker->CheckTypeCached(initializer); - - if (!is_const) { - initializer_type = checker->GetBaseTypeOfLiteralType(initializer_type); - } - - if (initializer_type->IsNullType()) { - checker->ThrowTypeError( - {"Cannot infer type for variable '", declarator->Id()->AsIdentifier()->Name(), "'."}, - declarator->Id()->Start()); - } - - binding_var->SetTsType(initializer_type); - } else { - checker->ThrowTypeError({"Variable ", declarator->Id()->AsIdentifier()->Name(), " implicitly has an any type."}, - declarator->Id()->Start()); - } - - if (previous_type != nullptr) { - checker->IsTypeIdenticalTo(binding_var->TsType(), previous_type, - {"Subsequent variable declaration must have the same type. Variable '", - binding_var->Name(), "' must be of type '", previous_type, "', but here has type '", - binding_var->TsType(), "'."}, - declarator->Id()->Start()); - } - - checker->RemoveStatus(checker::CheckerStatus::IN_CONST_CONTEXT); + etsg->GetAstCompiler()->Compile(this); } checker::Type *VariableDeclarator::Check([[maybe_unused]] checker::TSChecker *checker) { - if (TsType() == CHECKED) { - return nullptr; - } - - if (id_->IsIdentifier()) { - CheckSimpleVariableDeclaration(checker, this); - SetTsType(CHECKED); - return nullptr; - } - - if (id_->IsArrayPattern()) { - auto context = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); - checker::ArrayDestructuringContext(checker, id_, false, id_->AsArrayPattern()->TypeAnnotation() == nullptr, - id_->AsArrayPattern()->TypeAnnotation(), init_) - .Start(); - - SetTsType(CHECKED); - return nullptr; - } - - ASSERT(id_->IsObjectPattern()); - auto context = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE); - checker::ObjectDestructuringContext(checker, id_, false, id_->AsObjectPattern()->TypeAnnotation() == nullptr, - id_->AsObjectPattern()->TypeAnnotation(), init_) - .Start(); - - SetTsType(CHECKED); - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *VariableDeclarator::Check(checker::ETSChecker *checker) { - ASSERT(id_->IsIdentifier()); - ir::ModifierFlags flags = ir::ModifierFlags::NONE; - - if (id_->Parent()->Parent()->AsVariableDeclaration()->Kind() == - ir::VariableDeclaration::VariableDeclarationKind::CONST) { - flags |= ir::ModifierFlags::CONST; - } - - SetTsType( - checker->CheckVariableDeclaration(id_->AsIdentifier(), id_->AsIdentifier()->TypeAnnotation(), init_, flags)); - return TsType(); + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir diff --git a/ets2panda/ir/statements/variableDeclarator.h b/ets2panda/ir/statements/variableDeclarator.h index 1b1234ff3e01b1c93e834971ac0b4f050ebe183a..e6cdbc698cc833086cbe40b287a0c76efe532ada 100644 --- a/ets2panda/ir/statements/variableDeclarator.h +++ b/ets2panda/ir/statements/variableDeclarator.h @@ -18,7 +18,9 @@ #include "ir/expression.h" #include "ir/statement.h" - +namespace panda::es2panda::checker { +class TSAnalyzer; +} // namespace panda::es2panda::checker namespace panda::es2panda::ir { class Expression; @@ -31,6 +33,9 @@ public: { } + // TODO (vivienvoros): these friend relationships can be removed once there are getters for private fields + friend class checker::TSAnalyzer; + Expression *Init() { return init_; diff --git a/ets2panda/ir/statements/whileStatement.cpp b/ets2panda/ir/statements/whileStatement.cpp index 7accd4a83125afb9bf8a9d94db396cc4822dcc6d..5624fd56d4d6aae09ac5f7bc615a6706952b3fda 100644 --- a/ets2panda/ir/statements/whileStatement.cpp +++ b/ets2panda/ir/statements/whileStatement.cpp @@ -43,52 +43,23 @@ void WhileStatement::Dump(ir::AstDumper *dumper) const dumper->Add({{"type", "WhileStatement"}, {"test", test_}, {"body", body_}}); } -template -void CompileImpl(const WhileStatement *while_stmt, [[maybe_unused]] CodeGen *cg) -{ - compiler::LabelTarget label_target(cg); - - cg->SetLabel(while_stmt, label_target.ContinueTarget()); - compiler::Condition::Compile(cg, while_stmt->Test(), label_target.BreakTarget()); - - { - compiler::LocalRegScope reg_scope(cg, while_stmt->Scope()); - compiler::LabelContext label_ctx(cg, label_target); - while_stmt->Body()->Compile(cg); - } - - cg->Branch(while_stmt, label_target.ContinueTarget()); - cg->SetLabel(while_stmt, label_target.BreakTarget()); -} - void WhileStatement::Compile([[maybe_unused]] compiler::PandaGen *pg) const { - CompileImpl(this, pg); + pg->GetAstCompiler()->Compile(this); } void WhileStatement::Compile([[maybe_unused]] compiler::ETSGen *etsg) const { - CompileImpl(this, etsg); + etsg->GetAstCompiler()->Compile(this); } checker::Type *WhileStatement::Check([[maybe_unused]] checker::TSChecker *checker) { - checker::ScopeContext scope_ctx(checker, Scope()); - - checker::Type *test_type = Test()->Check(checker); - checker->CheckTruthinessOfType(test_type, Test()->Start()); - - Body()->Check(checker); - return nullptr; + return checker->GetAnalyzer()->Check(this); } checker::Type *WhileStatement::Check([[maybe_unused]] checker::ETSChecker *checker) { - checker::ScopeContext scope_ctx(checker, Scope()); - - checker->CheckTruthinessOfType(Test()); - - Body()->Check(checker); - return nullptr; + return checker->GetAnalyzer()->Check(this); } } // namespace panda::es2panda::ir