diff --git a/ets2panda/BUILD.gn b/ets2panda/BUILD.gn index 14a3b74899ba38ec3d58bc74ee972a6d2b95b551..95926a4a73a96b60442d7388e471b0719a958d95 100644 --- a/ets2panda/BUILD.gn +++ b/ets2panda/BUILD.gn @@ -171,6 +171,7 @@ libes2panda_sources = [ "compiler/lowering/ets/ambientLowering.cpp", "compiler/lowering/ets/bigintLowering.cpp", "compiler/lowering/ets/boxingForLocals.cpp", + "compiler/lowering/ets/capturedVariables.cpp", "compiler/lowering/ets/constStringToCharLowering.cpp", "compiler/lowering/ets/defaultParameterLowering.cpp", "compiler/lowering/ets/enumLowering.cpp", @@ -199,6 +200,7 @@ libes2panda_sources = [ "compiler/lowering/ets/unionLowering.cpp", "compiler/lowering/phase.cpp", "compiler/lowering/plugin_phase.cpp", + "compiler/lowering/resolveIdentifiers.cpp", "compiler/lowering/scopesInit/savedBindingsCtx.cpp", "compiler/lowering/scopesInit/scopesInitPhase.cpp", "compiler/lowering/util.cpp", diff --git a/ets2panda/CMakeLists.txt b/ets2panda/CMakeLists.txt index 270899a6b50bbf9379e0c090245d70ead6b8e745..e4cf1ea05575f117f6cb2f6c82e57a11634a72bf 100644 --- a/ets2panda/CMakeLists.txt +++ b/ets2panda/CMakeLists.txt @@ -165,6 +165,7 @@ set(ES2PANDA_LIB_SRC compiler/lowering/scopesInit/scopesInitPhase.cpp compiler/lowering/phase.cpp compiler/lowering/plugin_phase.cpp + compiler/lowering/resolveIdentifiers.cpp compiler/lowering/util.cpp compiler/lowering/ets/topLevelStmts/importExportDecls.cpp compiler/lowering/ets/topLevelStmts/globalClassHandler.cpp @@ -172,6 +173,7 @@ set(ES2PANDA_LIB_SRC compiler/lowering/ets/topLevelStmts/topLevelStmts.cpp compiler/lowering/ets/expressionLambdaLowering.cpp compiler/lowering/ets/boxingForLocals.cpp + compiler/lowering/ets/capturedVariables.cpp compiler/lowering/ets/lambdaLowering.cpp compiler/lowering/ets/spreadLowering.cpp compiler/lowering/ets/localClassLowering.cpp diff --git a/ets2panda/checker/ETSchecker.cpp b/ets2panda/checker/ETSchecker.cpp index 8556428fab368b58762f763e3be2ecde65e43204..df65144b1d9fe7fd74b501f8cc7c583cce9b18a1 100644 --- a/ets2panda/checker/ETSchecker.cpp +++ b/ets2panda/checker/ETSchecker.cpp @@ -138,26 +138,14 @@ void ETSChecker::InitializeBuiltin(varbinder::Variable *var, const util::StringV GetGlobalTypesHolder()->InitializeBuiltin(name, type); } -bool ETSChecker::StartChecker([[maybe_unused]] varbinder::VarBinder *varbinder, const CompilerOptions &options) +bool ETSChecker::StartChecker(varbinder::VarBinder *varbinder, const CompilerOptions &options) { Initialize(varbinder); - if (options.dumpAst) { - std::cout << Program()->Dump() << std::endl; - } - - if (options.opDumpAstOnlySilent) { - Program()->DumpSilent(); - return false; - } - if (options.parseOnly) { return false; } - varbinder->SetGenStdLib(options.compilationMode == CompilationMode::GEN_STD_LIB); - varbinder->IdentifierAnalysis(); - auto *etsBinder = varbinder->AsETSBinder(); InitializeBuiltins(etsBinder); diff --git a/ets2panda/checker/checkerContext.cpp b/ets2panda/checker/checkerContext.cpp index d69b16dcf07676a7da8febb6a24bb05007ba877b..2d4efb5686c2f7689195504933f598ddfff46c4b 100644 --- a/ets2panda/checker/checkerContext.cpp +++ b/ets2panda/checker/checkerContext.cpp @@ -30,6 +30,14 @@ CheckerContext::CheckerContext(Checker *checker, CheckerStatus newStatus, ETSObj { } +void CheckerContext::SetSmartCast(varbinder::Variable const *const variable, checker::Type *const smartType) noexcept +{ + // Just block captured and modified variables here instead of finding all their usage occurrences. + if (!variable->HasFlag(varbinder::VariableFlags::CAPTURED_MODIFIED)) { + smartCasts_.insert_or_assign(variable, smartType); + } +} + SmartCastTypes CheckerContext::CloneTestSmartCasts(bool const clearData) noexcept { if (testSmartCasts_.empty()) { diff --git a/ets2panda/checker/checkerContext.h b/ets2panda/checker/checkerContext.h index f50d37811a4ac2c5147783e34ae6e525968b1049..b97876e1febe25d52fd5dde5e2206f0c069e70fc 100644 --- a/ets2panda/checker/checkerContext.h +++ b/ets2panda/checker/checkerContext.h @@ -171,10 +171,7 @@ public: smartCasts_.erase(variable); } - void SetSmartCast(varbinder::Variable const *const variable, checker::Type *const smartType) noexcept - { - smartCasts_.insert_or_assign(variable, smartType); - } + void SetSmartCast(varbinder::Variable const *const variable, checker::Type *const smartType) noexcept; [[nodiscard]] checker::Type *GetSmartCast(varbinder::Variable const *const variable) const noexcept; [[nodiscard]] SmartCastArray CloneSmartCasts(bool clearData = false) noexcept; diff --git a/ets2panda/compiler/lowering/ets/capturedVariables.cpp b/ets2panda/compiler/lowering/ets/capturedVariables.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3f61d206841f6b7cd851a5e26ed56e8bdca5aefb --- /dev/null +++ b/ets2panda/compiler/lowering/ets/capturedVariables.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "capturedVariables.h" + +namespace ark::es2panda::compiler { + +static bool OnLeftSideOfAssignment(ir::Identifier const *const ident) noexcept +{ + return ident->Parent()->IsAssignmentExpression() && ident->Parent()->AsAssignmentExpression()->Left() == ident; +} + +static void AddScopes(ir::AstNode *node, std::set &scopes) noexcept +{ + if (node->Scope()->IsFunctionScope()) { + scopes.emplace(node->Scope()->AsFunctionScope()->ParamScope()); + } + if (node->Scope()->IsCatchScope()) { + scopes.emplace(node->Scope()->AsCatchScope()->ParamScope()); + } + if (node->Scope()->IsLoopScope()) { + scopes.emplace(node->Scope()->AsLoopScope()->DeclScope()); + } + scopes.emplace(node->Scope()); +} + +static varbinder::Variable *FindVariable(ir::Identifier *ident, std::set const &scopes) noexcept +{ + auto *var = ident->Variable(); + // NOTE! For some unknown reasons :) variables exist in scope collections but are not set to identifiers after + // 'varbinder->IdentifierAnalysis()' pass. Probably need to be investigated and fixed sometimes... + if (var == nullptr) { + // We start from the innermost scope! + for (auto it = scopes.crbegin(); it != scopes.crend(); ++it) { + auto res = (*it)->Find(ident->Name(), varbinder::ResolveBindingOptions::VARIABLES); + if (res.variable != nullptr) { + var = res.variable; + break; + } + } + } + + if (var != nullptr) { + auto *scope = var->GetScope(); + ASSERT(scope != nullptr); + // We are not interested in variables defined inside arrow function! + if (scopes.find(scope) != scopes.cend()) { + return nullptr; + } + } + + return var; +} + +static void FindModifiedCaptured(ir::ScriptFunction const *const scriptFunction, + std::set &variables) noexcept +{ + auto scopes = std::set {}; + bool inLambda = false; + + std::function walker = [&](ir::AstNode *node) -> void { + if (node->IsArrowFunctionExpression() || node->IsClassDeclaration()) { + auto savedWL = inLambda; + auto savedScopes = std::set {}; + std::swap(scopes, savedScopes); + + inLambda = true; + node->Iterate(walker); + + inLambda = savedWL; + std::swap(scopes, savedScopes); + savedScopes.clear(); + + return; + } + + if (inLambda && node->IsScopeBearer()) { + AddScopes(node, scopes); + } else if (inLambda && node->IsIdentifier() && OnLeftSideOfAssignment(node->AsIdentifier())) { + if (auto *var = FindVariable(node->AsIdentifier(), scopes); var != nullptr) { + variables.insert(var); + } + } + + node->Iterate(walker); + }; + + scriptFunction->Iterate(walker); +} + +static void HandleScriptFunction(ir::ScriptFunction const *const scriptFunction) noexcept +{ + auto variables = std::set {}; + FindModifiedCaptured(scriptFunction, variables); + + for (auto *variable : variables) { + variable->AddFlag(varbinder::VariableFlags::CAPTURED_MODIFIED); + } + + variables.clear(); +} + +bool CapturedVariables::Perform(public_lib::Context *ctx, parser::Program *program) +{ + if (ctx->config->options->CompilerOptions().compilationMode == CompilationMode::GEN_STD_LIB) { + for (auto &[_, ext_programs] : program->ExternalSources()) { + (void)_; + for (auto *extProg : ext_programs) { + Perform(ctx, extProg); + } + } + } + + std::function searchForFunctions = [&](ir::AstNode *ast) { + if (ast->IsScriptFunction()) { + HandleScriptFunction(ast->AsScriptFunction()); // no recursion + } else { + ast->Iterate(searchForFunctions); + } + }; + + program->Ast()->Iterate(searchForFunctions); + return true; +} +} // namespace ark::es2panda::compiler diff --git a/ets2panda/compiler/lowering/ets/capturedVariables.h b/ets2panda/compiler/lowering/ets/capturedVariables.h new file mode 100644 index 0000000000000000000000000000000000000000..539d4726250207cd84796026ffe3c5edaf5ab0ec --- /dev/null +++ b/ets2panda/compiler/lowering/ets/capturedVariables.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ES2PANDA_COMPILER_LOWERING_CAPTURED_VARIABLES_H +#define ES2PANDA_COMPILER_LOWERING_CAPTURED_VARIABLES_H + +#include "compiler/lowering/phase.h" + +namespace ark::es2panda::compiler { + +class CapturedVariables : public Phase { +public: + std::string_view Name() const override + { + return "CapturedVariables"; + } + + bool Perform(public_lib::Context *ctx, parser::Program *program) override; +}; + +} // namespace ark::es2panda::compiler + +#endif diff --git a/ets2panda/compiler/lowering/phase.cpp b/ets2panda/compiler/lowering/phase.cpp index 99fb42aae557c4579432b0f1957483ffb742968c..f0cb2a8ec2b9f2c84888715d9500dd38d70c83de 100644 --- a/ets2panda/compiler/lowering/phase.cpp +++ b/ets2panda/compiler/lowering/phase.cpp @@ -15,10 +15,10 @@ #include "phase.h" #include "checker/checker.h" -#include "compiler/core/ASTVerifier.h" #include "ets/ambientLowering.h" #include "ets/defaultParameterLowering.h" #include "lexer/token/sourceLocation.h" +#include "compiler/lowering/resolveIdentifiers.h" #include "compiler/lowering/checkerPhase.h" #include "compiler/lowering/ets/constStringToCharLowering.h" #include "compiler/lowering/ets/defaultParameterLowering.h" @@ -27,6 +27,7 @@ #include "compiler/lowering/ets/topLevelStmts/topLevelStmts.h" #include "compiler/lowering/ets/expressionLambdaLowering.h" #include "compiler/lowering/ets/boxingForLocals.h" +#include "compiler/lowering/ets/capturedVariables.h" #include "compiler/lowering/ets/lambdaLowering.h" #include "compiler/lowering/ets/spreadLowering.h" #include "compiler/lowering/ets/interfacePropertyDeclarations.h" @@ -52,14 +53,7 @@ namespace ark::es2panda::compiler { static CheckerPhase g_checkerPhase; - -std::vector GetTrivialPhaseList() -{ - return std::vector { - &g_checkerPhase, - }; -} - +static ResolveIdentifiers g_resolveIdentifiers {}; static AmbientLowering g_ambientLowering; static BigIntLowering g_bigintLowering; static StringConstructorLowering g_stringConstructorLowering; @@ -70,6 +64,7 @@ static SpreadConstructionPhase g_spreadConstructionPhase; static ExpressionLambdaConstructionPhase g_expressionLambdaConstructionPhase; static OpAssignmentLowering g_opAssignmentLowering; static BoxingForLocals g_boxingForLocals; +static CapturedVariables g_capturedVariables {}; static LambdaConversionPhase g_lambdaConversionPhase; static ObjectIndexLowering g_objectIndexLowering; static ObjectIteratorLowering g_objectIteratorLowering; @@ -117,6 +112,8 @@ std::vector GetETSPhaseList() &g_expressionLambdaConstructionPhase, &g_interfacePropDeclPhase, &g_enumLoweringPhase, + &g_resolveIdentifiers, + &g_capturedVariables, &g_checkerPhase, &g_spreadConstructionPhase, &g_pluginsAfterCheck, diff --git a/ets2panda/compiler/lowering/resolveIdentifiers.cpp b/ets2panda/compiler/lowering/resolveIdentifiers.cpp new file mode 100644 index 0000000000000000000000000000000000000000..944c828acbc383c329b187149e92de6bafc270b3 --- /dev/null +++ b/ets2panda/compiler/lowering/resolveIdentifiers.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "resolveIdentifiers.h" + +#include "varbinder/ETSBinder.h" + +namespace ark::es2panda::compiler { +bool ResolveIdentifiers::Perform(public_lib::Context *ctx, [[maybe_unused]] parser::Program *program) +{ + auto const &options = ctx->config->options->CompilerOptions(); + auto *varbinder = ctx->parserProgram->VarBinder()->AsETSBinder(); + + if (options.dumpAst) { + std::cout << varbinder->Program()->Dump() << std::endl; + } + + if (options.opDumpAstOnlySilent) { + varbinder->Program()->DumpSilent(); + return false; + } + + if (options.parseOnly) { + return false; + } + + varbinder->SetGenStdLib(options.compilationMode == CompilationMode::GEN_STD_LIB); + varbinder->IdentifierAnalysis(); + + return true; +} + +} // namespace ark::es2panda::compiler diff --git a/ets2panda/compiler/lowering/resolveIdentifiers.h b/ets2panda/compiler/lowering/resolveIdentifiers.h new file mode 100644 index 0000000000000000000000000000000000000000..d7f97078bfc115cc868b5fd62914dde3713fd5dc --- /dev/null +++ b/ets2panda/compiler/lowering/resolveIdentifiers.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ES2PANDA_COMPILER_LOWERING_RESOLVE_IDENT_H +#define ES2PANDA_COMPILER_LOWERING_RESOLVE_IDENT_H + +#include "compiler/lowering/phase.h" + +namespace ark::es2panda::compiler { +class ResolveIdentifiers : public Phase { +public: + std::string_view Name() const override + { + return "ResolveIdentifiers"; + } + + bool Perform(public_lib::Context *ctx, parser::Program *program) override; +}; +} // namespace ark::es2panda::compiler + +#endif diff --git a/ets2panda/test/parser/ets/SmartCast_n01-expected.txt b/ets2panda/test/parser/ets/SmartCast_n01-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..3a2505b99a0daeb072a8cb9d232e70dd819097ea --- /dev/null +++ b/ets2panda/test/parser/ets/SmartCast_n01-expected.txt @@ -0,0 +1,888 @@ +{ + "type": "Program", + "statements": [ + { + "type": "ClassDeclaration", + "definition": { + "id": { + "type": "Identifier", + "name": "ETSGLOBAL", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "superClass": null, + "implements": [], + "body": [ + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "_$init$_", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "kind": "method", + "accessibility": "public", + "static": true, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "_$init$_", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [], + "body": { + "type": "BlockStatement", + "statements": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "overloads": [], + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "main", + "decorators": [], + "loc": { + "start": { + "line": 16, + "column": 10 + }, + "end": { + "line": 16, + "column": 14 + } + } + }, + "kind": "method", + "accessibility": "public", + "static": true, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "main", + "decorators": [], + "loc": { + "start": { + "line": 16, + "column": 10 + }, + "end": { + "line": 16, + "column": 14 + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [], + "body": { + "type": "BlockStatement", + "statements": [ + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "resolve", + "typeAnnotation": { + "type": "ETSUnionType", + "types": [ + { + "type": "ETSFunctionType", + "params": [ + { + "type": "ETSParameterExpression", + "name": { + "type": "Identifier", + "name": "value", + "typeAnnotation": { + "type": "ETSTypeReference", + "part": { + "type": "ETSTypeReferencePart", + "name": { + "type": "Identifier", + "name": "string", + "decorators": [], + "loc": { + "start": { + "line": 17, + "column": 25 + }, + "end": { + "line": 17, + "column": 31 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 25 + }, + "end": { + "line": 17, + "column": 32 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 25 + }, + "end": { + "line": 17, + "column": 32 + } + } + }, + "decorators": [], + "loc": { + "start": { + "line": 17, + "column": 18 + }, + "end": { + "line": 17, + "column": 32 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 18 + }, + "end": { + "line": 17, + "column": 32 + } + } + } + ], + "returnType": { + "type": "ETSPrimitiveType", + "loc": { + "start": { + "line": 17, + "column": 36 + }, + "end": { + "line": 17, + "column": 40 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 16 + }, + "end": { + "line": 17, + "column": 40 + } + } + }, + { + "type": "ETSNullType", + "loc": { + "start": { + "line": 17, + "column": 44 + }, + "end": { + "line": 17, + "column": 48 + } + } + } + ], + "loc": { + "start": { + "line": 17, + "column": 16 + }, + "end": { + "line": 17, + "column": 48 + } + } + }, + "decorators": [], + "loc": { + "start": { + "line": 17, + "column": 7 + }, + "end": { + "line": 17, + "column": 14 + } + } + }, + "init": { + "type": "NullLiteral", + "value": null, + "loc": { + "start": { + "line": 17, + "column": 51 + }, + "end": { + "line": 17, + "column": 55 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 7 + }, + "end": { + "line": 17, + "column": 55 + } + } + } + ], + "kind": "let", + "loc": { + "start": { + "line": 17, + "column": 3 + }, + "end": { + "line": 17, + "column": 56 + } + } + }, + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "p", + "decorators": [], + "loc": { + "start": { + "line": 19, + "column": 7 + }, + "end": { + "line": 19, + "column": 8 + } + } + }, + "init": { + "type": "ETSNewClassInstanceExpression", + "typeReference": { + "type": "ETSTypeReference", + "part": { + "type": "ETSTypeReferencePart", + "name": { + "type": "Identifier", + "name": "Promise", + "decorators": [], + "loc": { + "start": { + "line": 19, + "column": 15 + }, + "end": { + "line": 19, + "column": 22 + } + } + }, + "typeParams": { + "type": "TSTypeParameterInstantiation", + "params": [ + { + "type": "ETSTypeReference", + "part": { + "type": "ETSTypeReferencePart", + "name": { + "type": "Identifier", + "name": "string", + "decorators": [], + "loc": { + "start": { + "line": 19, + "column": 23 + }, + "end": { + "line": 19, + "column": 29 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 23 + }, + "end": { + "line": 19, + "column": 30 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 23 + }, + "end": { + "line": 19, + "column": 30 + } + } + } + ], + "loc": { + "start": { + "line": 19, + "column": 22 + }, + "end": { + "line": 19, + "column": 30 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 15 + }, + "end": { + "line": 19, + "column": 31 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 15 + }, + "end": { + "line": 19, + "column": 31 + } + } + }, + "arguments": [ + { + "type": "ArrowFunctionExpression", + "function": { + "type": "ScriptFunction", + "id": null, + "generator": false, + "async": false, + "expression": false, + "params": [ + { + "type": "ETSParameterExpression", + "name": { + "type": "Identifier", + "name": "_resolve", + "typeAnnotation": { + "type": "ETSFunctionType", + "params": [ + { + "type": "ETSParameterExpression", + "name": { + "type": "Identifier", + "name": "value", + "typeAnnotation": { + "type": "ETSTypeReference", + "part": { + "type": "ETSTypeReferencePart", + "name": { + "type": "Identifier", + "name": "string", + "decorators": [], + "loc": { + "start": { + "line": 19, + "column": 50 + }, + "end": { + "line": 19, + "column": 56 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 50 + }, + "end": { + "line": 19, + "column": 57 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 50 + }, + "end": { + "line": 19, + "column": 57 + } + } + }, + "decorators": [], + "loc": { + "start": { + "line": 19, + "column": 43 + }, + "end": { + "line": 19, + "column": 57 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 43 + }, + "end": { + "line": 19, + "column": 57 + } + } + } + ], + "returnType": { + "type": "ETSPrimitiveType", + "loc": { + "start": { + "line": 19, + "column": 60 + }, + "end": { + "line": 19, + "column": 64 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 42 + }, + "end": { + "line": 19, + "column": 64 + } + } + }, + "decorators": [], + "loc": { + "start": { + "line": 19, + "column": 32 + }, + "end": { + "line": 19, + "column": 64 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 32 + }, + "end": { + "line": 19, + "column": 64 + } + } + } + ], + "returnType": { + "type": "ETSPrimitiveType", + "loc": { + "start": { + "line": 19, + "column": 67 + }, + "end": { + "line": 19, + "column": 71 + } + } + }, + "body": { + "type": "BlockStatement", + "statements": [ + { + "type": "ExpressionStatement", + "expression": { + "type": "AssignmentExpression", + "operator": "=", + "left": { + "type": "Identifier", + "name": "resolve", + "decorators": [], + "loc": { + "start": { + "line": 20, + "column": 5 + }, + "end": { + "line": 20, + "column": 12 + } + } + }, + "right": { + "type": "Identifier", + "name": "_resolve", + "decorators": [], + "loc": { + "start": { + "line": 20, + "column": 15 + }, + "end": { + "line": 20, + "column": 23 + } + } + }, + "loc": { + "start": { + "line": 20, + "column": 5 + }, + "end": { + "line": 20, + "column": 23 + } + } + }, + "loc": { + "start": { + "line": 20, + "column": 5 + }, + "end": { + "line": 20, + "column": 24 + } + } + } + ], + "loc": { + "start": { + "line": 19, + "column": 75 + }, + "end": { + "line": 21, + "column": 4 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 31 + }, + "end": { + "line": 21, + "column": 4 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 31 + }, + "end": { + "line": 21, + "column": 4 + } + } + } + ], + "loc": { + "start": { + "line": 19, + "column": 11 + }, + "end": { + "line": 21, + "column": 6 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 7 + }, + "end": { + "line": 21, + "column": 6 + } + } + } + ], + "kind": "let", + "loc": { + "start": { + "line": 19, + "column": 3 + }, + "end": { + "line": 21, + "column": 6 + } + } + }, + { + "type": "ExpressionStatement", + "expression": { + "type": "CallExpression", + "callee": { + "type": "Identifier", + "name": "resolve", + "decorators": [], + "loc": { + "start": { + "line": 23, + "column": 3 + }, + "end": { + "line": 23, + "column": 10 + } + } + }, + "arguments": [ + { + "type": "StringLiteral", + "value": "abc", + "loc": { + "start": { + "line": 23, + "column": 11 + }, + "end": { + "line": 23, + "column": 16 + } + } + } + ], + "optional": false, + "loc": { + "start": { + "line": 23, + "column": 3 + }, + "end": { + "line": 23, + "column": 17 + } + } + }, + "loc": { + "start": { + "line": 23, + "column": 3 + }, + "end": { + "line": 23, + "column": 18 + } + } + } + ], + "loc": { + "start": { + "line": 16, + "column": 17 + }, + "end": { + "line": 24, + "column": 2 + } + } + }, + "loc": { + "start": { + "line": 16, + "column": 14 + }, + "end": { + "line": 24, + "column": 2 + } + } + }, + "loc": { + "start": { + "line": 16, + "column": 14 + }, + "end": { + "line": 24, + "column": 2 + } + } + }, + "overloads": [], + "decorators": [], + "loc": { + "start": { + "line": 16, + "column": 1 + }, + "end": { + "line": 24, + "column": 2 + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 25, + "column": 1 + } + } +} +TypeError: This expression is not callable. [SmartCast_n01.ets:23:3] diff --git a/ets2panda/test/parser/ets/SmartCast_n01.ets b/ets2panda/test/parser/ets/SmartCast_n01.ets new file mode 100644 index 0000000000000000000000000000000000000000..0dbdfb678015d70baf47e5ebfaa2ce3b0a1e7d6a --- /dev/null +++ b/ets2panda/test/parser/ets/SmartCast_n01.ets @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function main() { + let resolve: ((value: string) => void) | null = null; + + let p = new Promise((_resolve: (value: string)=> void): void => { + resolve = _resolve; + }); + + resolve("abc"); // no smart cast! +} diff --git a/ets2panda/test/parser/ets/SmartCast_n02-expected.txt b/ets2panda/test/parser/ets/SmartCast_n02-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..1f238339ed821be49652efd3144e2636601eaaab --- /dev/null +++ b/ets2panda/test/parser/ets/SmartCast_n02-expected.txt @@ -0,0 +1,789 @@ +{ + "type": "Program", + "statements": [ + { + "type": "ClassDeclaration", + "definition": { + "id": { + "type": "Identifier", + "name": "ETSGLOBAL", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "superClass": null, + "implements": [], + "body": [ + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "_$init$_", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "kind": "method", + "accessibility": "public", + "static": true, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "_$init$_", + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [], + "body": { + "type": "BlockStatement", + "statements": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "overloads": [], + "decorators": [], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "main", + "decorators": [], + "loc": { + "start": { + "line": 16, + "column": 10 + }, + "end": { + "line": 16, + "column": 14 + } + } + }, + "kind": "method", + "accessibility": "public", + "static": true, + "optional": false, + "computed": false, + "value": { + "type": "FunctionExpression", + "function": { + "type": "ScriptFunction", + "id": { + "type": "Identifier", + "name": "main", + "decorators": [], + "loc": { + "start": { + "line": 16, + "column": 10 + }, + "end": { + "line": 16, + "column": 14 + } + } + }, + "generator": false, + "async": false, + "expression": false, + "params": [], + "body": { + "type": "BlockStatement", + "statements": [ + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "x", + "typeAnnotation": { + "type": "ETSUnionType", + "types": [ + { + "type": "ETSTypeReference", + "part": { + "type": "ETSTypeReferencePart", + "name": { + "type": "Identifier", + "name": "Number", + "decorators": [], + "loc": { + "start": { + "line": 17, + "column": 10 + }, + "end": { + "line": 17, + "column": 16 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 10 + }, + "end": { + "line": 17, + "column": 17 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 10 + }, + "end": { + "line": 17, + "column": 17 + } + } + }, + { + "type": "ETSTypeReference", + "part": { + "type": "ETSTypeReferencePart", + "name": { + "type": "Identifier", + "name": "String", + "decorators": [], + "loc": { + "start": { + "line": 17, + "column": 17 + }, + "end": { + "line": 17, + "column": 23 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 17 + }, + "end": { + "line": 17, + "column": 24 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 17 + }, + "end": { + "line": 17, + "column": 24 + } + } + }, + { + "type": "ETSUndefinedType", + "loc": { + "start": { + "line": 17, + "column": 24 + }, + "end": { + "line": 17, + "column": 33 + } + } + } + ], + "loc": { + "start": { + "line": 17, + "column": 10 + }, + "end": { + "line": 17, + "column": 33 + } + } + }, + "decorators": [], + "loc": { + "start": { + "line": 17, + "column": 7 + }, + "end": { + "line": 17, + "column": 8 + } + } + }, + "init": { + "type": "StringLiteral", + "value": "test1", + "loc": { + "start": { + "line": 17, + "column": 36 + }, + "end": { + "line": 17, + "column": 43 + } + } + }, + "loc": { + "start": { + "line": 17, + "column": 7 + }, + "end": { + "line": 17, + "column": 43 + } + } + } + ], + "kind": "let", + "loc": { + "start": { + "line": 17, + "column": 3 + }, + "end": { + "line": 17, + "column": 44 + } + } + }, + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "lam", + "typeAnnotation": { + "type": "ETSFunctionType", + "params": [], + "returnType": { + "type": "ETSPrimitiveType", + "loc": { + "start": { + "line": 19, + "column": 18 + }, + "end": { + "line": 19, + "column": 22 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 12 + }, + "end": { + "line": 19, + "column": 22 + } + } + }, + "decorators": [], + "loc": { + "start": { + "line": 19, + "column": 7 + }, + "end": { + "line": 19, + "column": 10 + } + } + }, + "init": { + "type": "ArrowFunctionExpression", + "function": { + "type": "ScriptFunction", + "id": null, + "generator": false, + "async": false, + "expression": false, + "params": [], + "body": { + "type": "BlockStatement", + "statements": [ + { + "type": "ExpressionStatement", + "expression": { + "type": "AssignmentExpression", + "operator": "=", + "left": { + "type": "Identifier", + "name": "x", + "decorators": [], + "loc": { + "start": { + "line": 20, + "column": 6 + }, + "end": { + "line": 20, + "column": 7 + } + } + }, + "right": { + "type": "NumberLiteral", + "value": 7, + "loc": { + "start": { + "line": 20, + "column": 10 + }, + "end": { + "line": 20, + "column": 11 + } + } + }, + "loc": { + "start": { + "line": 20, + "column": 6 + }, + "end": { + "line": 20, + "column": 11 + } + } + }, + "loc": { + "start": { + "line": 20, + "column": 6 + }, + "end": { + "line": 20, + "column": 12 + } + } + } + ], + "loc": { + "start": { + "line": 19, + "column": 31 + }, + "end": { + "line": 21, + "column": 4 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 25 + }, + "end": { + "line": 21, + "column": 4 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 25 + }, + "end": { + "line": 21, + "column": 4 + } + } + }, + "loc": { + "start": { + "line": 19, + "column": 7 + }, + "end": { + "line": 21, + "column": 4 + } + } + } + ], + "kind": "let", + "loc": { + "start": { + "line": 19, + "column": 3 + }, + "end": { + "line": 21, + "column": 5 + } + } + }, + { + "type": "IfStatement", + "test": { + "type": "BinaryExpression", + "operator": "instanceof", + "left": { + "type": "Identifier", + "name": "x", + "decorators": [], + "loc": { + "start": { + "line": 23, + "column": 7 + }, + "end": { + "line": 23, + "column": 8 + } + } + }, + "right": { + "type": "ETSTypeReference", + "part": { + "type": "ETSTypeReferencePart", + "name": { + "type": "Identifier", + "name": "string", + "decorators": [], + "loc": { + "start": { + "line": 23, + "column": 20 + }, + "end": { + "line": 23, + "column": 26 + } + } + }, + "loc": { + "start": { + "line": 23, + "column": 20 + }, + "end": { + "line": 23, + "column": 27 + } + } + }, + "loc": { + "start": { + "line": 23, + "column": 20 + }, + "end": { + "line": 23, + "column": 27 + } + } + }, + "loc": { + "start": { + "line": 23, + "column": 7 + }, + "end": { + "line": 23, + "column": 27 + } + } + }, + "consequent": { + "type": "BlockStatement", + "statements": [ + { + "type": "VariableDeclaration", + "declarations": [ + { + "type": "VariableDeclarator", + "id": { + "type": "Identifier", + "name": "tmp", + "typeAnnotation": { + "type": "ETSTypeReference", + "part": { + "type": "ETSTypeReferencePart", + "name": { + "type": "Identifier", + "name": "string", + "decorators": [], + "loc": { + "start": { + "line": 24, + "column": 14 + }, + "end": { + "line": 24, + "column": 20 + } + } + }, + "loc": { + "start": { + "line": 24, + "column": 14 + }, + "end": { + "line": 24, + "column": 22 + } + } + }, + "loc": { + "start": { + "line": 24, + "column": 14 + }, + "end": { + "line": 24, + "column": 22 + } + } + }, + "decorators": [], + "loc": { + "start": { + "line": 24, + "column": 9 + }, + "end": { + "line": 24, + "column": 12 + } + } + }, + "init": { + "type": "Identifier", + "name": "x", + "decorators": [], + "loc": { + "start": { + "line": 24, + "column": 23 + }, + "end": { + "line": 24, + "column": 24 + } + } + }, + "loc": { + "start": { + "line": 24, + "column": 9 + }, + "end": { + "line": 24, + "column": 24 + } + } + } + ], + "kind": "let", + "loc": { + "start": { + "line": 24, + "column": 5 + }, + "end": { + "line": 24, + "column": 25 + } + } + } + ], + "loc": { + "start": { + "line": 23, + "column": 28 + }, + "end": { + "line": 25, + "column": 4 + } + } + }, + "alternate": null, + "loc": { + "start": { + "line": 23, + "column": 3 + }, + "end": { + "line": 25, + "column": 4 + } + } + } + ], + "loc": { + "start": { + "line": 16, + "column": 17 + }, + "end": { + "line": 26, + "column": 2 + } + } + }, + "loc": { + "start": { + "line": 16, + "column": 14 + }, + "end": { + "line": 26, + "column": 2 + } + } + }, + "loc": { + "start": { + "line": 16, + "column": 14 + }, + "end": { + "line": 26, + "column": 2 + } + } + }, + "overloads": [], + "decorators": [], + "loc": { + "start": { + "line": 16, + "column": 1 + }, + "end": { + "line": 26, + "column": 2 + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + }, + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 1, + "column": 1 + } + } + } + ], + "loc": { + "start": { + "line": 1, + "column": 1 + }, + "end": { + "line": 27, + "column": 1 + } + } +} +TypeError: Type 'Double|String|undefined' cannot be assigned to type 'String' [SmartCast_n02.ets:24:23] diff --git a/ets2panda/test/parser/ets/SmartCast_n02.ets b/ets2panda/test/parser/ets/SmartCast_n02.ets new file mode 100644 index 0000000000000000000000000000000000000000..0fc05483c0393716027615c97649ded6c0dd7830 --- /dev/null +++ b/ets2panda/test/parser/ets/SmartCast_n02.ets @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function main() { + let x: Number|String|undefined = "test1"; + + let lam: () => void = () => { + x = 7; + }; + + if (x instanceof string) { + let tmp: string = x; // no smart cast! + } +} diff --git a/ets2panda/test/runtime/ets/SmartCast_12.ets b/ets2panda/test/runtime/ets/SmartCast_12.ets new file mode 100644 index 0000000000000000000000000000000000000000..70e68b6b3feb80b9e6410db995a5bc0378c3dbc5 --- /dev/null +++ b/ets2panda/test/runtime/ets/SmartCast_12.ets @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function main() { + let resolve: ((value: string) => void) | null = null; + + let p = new Promise((_resolve: (value: string)=> void): void => { + resolve = _resolve; + }); + + resolve!("abc"); // no smart cast! + + let x: Number|String|undefined = "test1"; + let y: Number|String|undefined = "test2" + let z: Number|String|undefined = 7 + + let lam: () => void = () => { + let y: Number|String|undefined = 2; // hides outer declaration! + x = z; + let tmp: number = y; // smart cast is used + assert(tmp == 2); + }; + + if (x instanceof string) { + let tmp: string = x as string; // no smart cast! + assert(tmp == "test1"); + } + + lam(); + + assert(x == 7); + + if (y instanceof string) { + let tmp: string = y; // smart cast is used + assert(tmp == "test2"); + } + + let w: number = z; // smart cast is used + assert(w == 7) +} diff --git a/ets2panda/varbinder/variableFlags.h b/ets2panda/varbinder/variableFlags.h index b4dabdd82e9e212e661fd16b4998bdf3ce3aa3e4..9ec50fa1fe201cbf913bc9d9a8485710c9ae0bad 100644 --- a/ets2panda/varbinder/variableFlags.h +++ b/ets2panda/varbinder/variableFlags.h @@ -155,6 +155,7 @@ enum class VariableFlags : uint64_t { LEXICAL_BOUND = 1U << 30U, BUILTIN_TYPE = 1ULL << 31ULL, + CAPTURED_MODIFIED = 1ULL << 32ULL, HOIST_VAR = HOIST | VAR, CLASS_OR_INTERFACE = CLASS | INTERFACE,