From 34edc8fa464fccdf93054ca9c020fb159ad61fed Mon Sep 17 00:00:00 2001 From: Vivien Voros Date: Thu, 28 Sep 2023 17:30:57 +0200 Subject: [PATCH] Move Compile and Check Logic from ASTNode classes Compile logic is moved to JSCompiler and ETSCompiler classes. Check logic with their helper functions is moved to TSAnalyzer and ETSAnalyzer classes. SpreadElement, ObjectExpression, ArrayExpression, OpaqueTypeNode, YieldExpression, WhileStatement, VariableDeclarator. Linked Internal issue: 13840 Change-Id: I50a489f80a203b0b69db944e36aa93392e753047 Signed-off-by: Vivien Voros --- ets2panda/checker/ETSAnalyzer.cpp | 245 ++++++++- ets2panda/checker/ETSAnalyzer.h | 2 + ets2panda/checker/TSAnalyzer.cpp | 467 ++++++++++++++++- ets2panda/compiler/core/ETSCompiler.cpp | 156 +++++- ets2panda/compiler/core/JSCompiler.cpp | 294 ++++++++++- ets2panda/compiler/core/JSCompiler.h | 5 + ets2panda/compiler/core/codeGen.h | 1 - ets2panda/compiler/core/compilerImpl.cpp | 4 +- ets2panda/ir/base/spreadElement.cpp | 16 +- ets2panda/ir/base/spreadElement.h | 1 + ets2panda/ir/expressions/arrayExpression.cpp | 186 +------ ets2panda/ir/expressions/arrayExpression.h | 10 + .../ir/expressions/assignmentExpression.cpp | 167 +----- .../ir/expressions/assignmentExpression.h | 7 +- ets2panda/ir/expressions/objectExpression.cpp | 485 +----------------- ets2panda/ir/expressions/objectExpression.h | 11 +- ets2panda/ir/expressions/yieldExpression.cpp | 25 +- ets2panda/ir/expressions/yieldExpression.h | 1 + ets2panda/ir/opaqueTypeNode.cpp | 15 +- ets2panda/ir/statements/returnStatement.h | 5 + .../ir/statements/variableDeclarator.cpp | 131 +---- ets2panda/ir/statements/variableDeclarator.h | 7 +- ets2panda/ir/statements/whileStatement.cpp | 37 +- 23 files changed, 1188 insertions(+), 1090 deletions(-) diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index f91ee6ca74..2fcdd2c921 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 de2b57cc58..227380e0f2 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 d3e96e821f..840be10d76 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 46dd096ae8..68ef93b628 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 10a894d364..a28c96e2f7 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 9d50641650..f0fd56738f 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 e06a982cad..87e6faa609 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 14bc8b7fbd..372fe44785 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 1f995a1f11..7056605028 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 037cc59ca4..7a43822d19 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 c3c36bef08..135f245e1e 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 5848b480b5..b2eae8de5d 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 a6ba7d6ee0..868277145b 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 5f14a1c1cf..7087ba01d1 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 f3b4b4ebff..6379361b99 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 b258313d0b..6db977774e 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 4aadc7a545..9b597c88da 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 9e8e85608b..2d2116c3de 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 caa23c6133..b09b065cb4 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 cacaf713a7..91cf30cc42 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 3303096080..3089bfd1cb 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 1b1234ff3e..e6cdbc698c 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 7accd4a831..5624fd56d4 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 -- Gitee