diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..c56fd371787938afd247f3eeda8d5751ed2a1bc8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +test/__pycache__/ +test/runner/__pycache__/ +test/runner/plugins/ets/__pycache__/ +test/runner/plugins/hermes/__pycache__/ +test/runner/plugins/parser/__pycache__/ +test/runner/plugins/test262/__pycache__/ diff --git a/BUILD.gn b/BUILD.gn index 714417c1643cde5d2652383bd22dcecc36f4186a..60b4520b5398c2c5d9d83e48892ed9495bf2394c 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -25,8 +25,14 @@ config("libes2panda_public_config") { libes2panda_sources = [ "es2panda.cpp", + "binder/ASBinder.cpp", + "binder/TSBinder.cpp", + "binder/TypedBinder.cpp", + "binder/ETSBinder.cpp", + "binder/JSBinder.cpp", "binder/binder.cpp", "binder/declaration.cpp", + "binder/recordTable.cpp", "binder/scope.cpp", "binder/variable.cpp", "compiler/base/catchTable.cpp", @@ -38,26 +44,36 @@ libes2panda_sources = [ "compiler/base/literals.cpp", "compiler/base/lreference.cpp", "compiler/base/optionalChain.cpp", + "compiler/core/codeGen.cpp", + "compiler/core/compileJob.cpp", "compiler/core/compileQueue.cpp", "compiler/core/compilerContext.cpp", "compiler/core/compilerImpl.cpp", "compiler/core/dynamicContext.cpp", "compiler/core/emitter.cpp", + "compiler/core/JSemitter.cpp", "compiler/core/envScope.cpp", "compiler/core/function.cpp", - "compiler/core/inlineCache.cpp", "compiler/core/labelTarget.cpp", "compiler/core/moduleContext.cpp", "compiler/core/pandagen.cpp", + "compiler/core/programElement.cpp", "compiler/core/regAllocator.cpp", "compiler/core/regScope.cpp", + "compiler/core/regSpiller.cpp", + "compiler/core/ETSemitter.cpp", + "compiler/core/ETSGen.cpp", + "compiler/core/ETSfunction.cpp", "compiler/core/switchBuilder.cpp", + "compiler/core/targetTypeContext.cpp", + "compiler/core/vReg.cpp", "compiler/debugger/debuginfoDumper.cpp", "compiler/function/asyncFunctionBuilder.cpp", "compiler/function/asyncGeneratorFunctionBuilder.cpp", "compiler/function/functionBuilder.cpp", "compiler/function/generatorFunctionBuilder.cpp", "ir/astDump.cpp", + "ir/astNode.cpp", "ir/irnode.cpp", "ir/base/catchClause.cpp", "ir/base/classElement.cpp", @@ -71,6 +87,10 @@ libes2panda_sources = [ "ir/base/scriptFunction.cpp", "ir/base/spreadElement.cpp", "ir/base/templateElement.cpp", + "ir/base/tsIndexSignature.cpp", + "ir/base/tsMethodSignature.cpp", + "ir/base/tsPropertySignature.cpp", + "ir/base/tsSignatureDeclaration.cpp", "ir/expression.cpp", "ir/expressions/arrayExpression.cpp", "ir/expressions/arrowFunctionExpression.cpp", @@ -88,6 +108,7 @@ libes2panda_sources = [ "ir/expressions/literal.cpp", "ir/expressions/literals/bigIntLiteral.cpp", "ir/expressions/literals/booleanLiteral.cpp", + "ir/expressions/literals/charLiteral.cpp", "ir/expressions/literals/nullLiteral.cpp", "ir/expressions/literals/numberLiteral.cpp", "ir/expressions/literals/regExpLiteral.cpp", @@ -113,11 +134,13 @@ libes2panda_sources = [ "ir/module/importNamespaceSpecifier.cpp", "ir/module/importSpecifier.cpp", "ir/statement.cpp", + "ir/statements/assertStatement.cpp", "ir/statements/blockStatement.cpp", "ir/statements/breakStatement.cpp", "ir/statements/classDeclaration.cpp", "ir/statements/continueStatement.cpp", "ir/statements/debuggerStatement.cpp", + "ir/statements/deferStatement.cpp", "ir/statements/doWhileStatement.cpp", "ir/statements/emptyStatement.cpp", "ir/statements/expressionStatement.cpp", @@ -128,9 +151,11 @@ libes2panda_sources = [ "ir/statements/ifStatement.cpp", "ir/statements/labelledStatement.cpp", "ir/statements/loopStatement.cpp", + "ir/statements/panicStatement.cpp", "ir/statements/returnStatement.cpp", "ir/statements/switchCaseStatement.cpp", "ir/statements/switchStatement.cpp", + "ir/statements/trapStatement.cpp", "ir/statements/throwStatement.cpp", "ir/statements/tryStatement.cpp", "ir/statements/variableDeclaration.cpp", @@ -152,7 +177,6 @@ libes2panda_sources = [ "ir/ts/tsFunctionType.cpp", "ir/ts/tsImportEqualsDeclaration.cpp", "ir/ts/tsImportType.cpp", - "ir/ts/tsIndexSignature.cpp", "ir/ts/tsIndexedAccessType.cpp", "ir/ts/tsInferType.cpp", "ir/ts/tsInterfaceBody.cpp", @@ -161,7 +185,6 @@ libes2panda_sources = [ "ir/ts/tsIntersectionType.cpp", "ir/ts/tsLiteralType.cpp", "ir/ts/tsMappedType.cpp", - "ir/ts/tsMethodSignature.cpp", "ir/ts/tsModuleBlock.cpp", "ir/ts/tsModuleDeclaration.cpp", "ir/ts/tsNamedTupleMember.cpp", @@ -172,9 +195,7 @@ libes2panda_sources = [ "ir/ts/tsObjectKeyword.cpp", "ir/ts/tsParameterProperty.cpp", "ir/ts/tsParenthesizedType.cpp", - "ir/ts/tsPropertySignature.cpp", "ir/ts/tsQualifiedName.cpp", - "ir/ts/tsSignatureDeclaration.cpp", "ir/ts/tsStringKeyword.cpp", "ir/ts/tsThisType.cpp", "ir/ts/tsTupleType.cpp", @@ -192,61 +213,118 @@ libes2panda_sources = [ "ir/ts/tsUnionType.cpp", "ir/ts/tsUnknownKeyword.cpp", "ir/ts/tsVoidKeyword.cpp", + "ir/ets/etsClassLiteral.cpp", + "ir/ets/etsFunctionType.cpp", + "ir/ets/etsMethodReferenceExpression.cpp", + "ir/ets/etsNewArrayInstanceExpression.cpp", + "ir/ets/etsNewClassInstanceExpression.cpp", + "ir/ets/etsNewMultiDimArrayInstanceExpression.cpp", + "ir/ets/etsPackageDeclaration.cpp", + "ir/ets/etsPrimitiveType.cpp", + "ir/ets/etsScript.cpp", + "ir/ets/etsTryExpression.cpp", + "ir/ets/etsTypeReference.cpp", + "ir/ets/etsTypeReferencePart.cpp", + "ir/ets/etsWildcardType.cpp", + "lexer/ASLexer.cpp", + "lexer/keywords.cpp", "lexer/keywordsUtil.cpp", "lexer/lexer.cpp", + "lexer/ETSLexer.cpp", + "lexer/TSLexer.cpp", "lexer/regexp/regexp.cpp", + "lexer/token/number.cpp", "lexer/token/sourceLocation.cpp", "lexer/token/token.cpp", "parser/context/classPrivateContext.cpp", "parser/context/parserContext.cpp", "parser/expressionParser.cpp", + "parser/ASparser.cpp", + "parser/JSparser.cpp", "parser/parserImpl.cpp", + "parser/ETSparser.cpp", + "parser/TSparser.cpp", + "parser/TypedParser.cpp", "parser/program/program.cpp", "parser/statementParser.cpp", - "typescript/checker.cpp", - "typescript/core/binaryLikeExpression.cpp", - "typescript/core/checkerContext.cpp", - "typescript/core/destructuringContext.cpp", - "typescript/core/function.cpp", - "typescript/core/helpers.cpp", - "typescript/core/object.cpp", - "typescript/core/typeCreation.cpp", - "typescript/core/typeElaborationContext.cpp", - "typescript/core/typeRelation.cpp", - "typescript/core/util.cpp", - "typescript/types/anyType.cpp", - "typescript/types/arrayType.cpp", - "typescript/types/bigintLiteralType.cpp", - "typescript/types/bigintType.cpp", - "typescript/types/booleanLiteralType.cpp", - "typescript/types/booleanType.cpp", - "typescript/types/constructorType.cpp", - "typescript/types/enumLiteralType.cpp", - "typescript/types/enumType.cpp", - "typescript/types/functionType.cpp", - "typescript/types/globalTypesHolder.cpp", - "typescript/types/indexInfo.cpp", - "typescript/types/interfaceType.cpp", - "typescript/types/neverType.cpp", - "typescript/types/nonPrimitiveType.cpp", - "typescript/types/nullType.cpp", - "typescript/types/numberLiteralType.cpp", - "typescript/types/numberType.cpp", - "typescript/types/objectDescriptor.cpp", - "typescript/types/objectLiteralType.cpp", - "typescript/types/objectType.cpp", - "typescript/types/signature.cpp", - "typescript/types/stringLiteralType.cpp", - "typescript/types/stringType.cpp", - "typescript/types/tupleType.cpp", - "typescript/types/type.cpp", - "typescript/types/typeParameter.cpp", - "typescript/types/typeReference.cpp", - "typescript/types/typeRelation.cpp", - "typescript/types/undefinedType.cpp", - "typescript/types/unionType.cpp", - "typescript/types/unknownType.cpp", - "typescript/types/voidType.cpp", + "checker/checker.cpp", + "checker/checkerContext.cpp", + "checker/ETSchecker.cpp", + "checker/TSchecker.cpp", + "checker/ASchecker.cpp", + "checker/JSchecker.cpp", + "checker/ets/aliveAnalyzer.cpp", + "checker/ets/arithmetic.cpp", + "checker/ets/baseAnalyzer.cpp", + "checker/ets/boxingConverter.cpp", + "checker/ets/function.cpp", + "checker/ets/helpers.cpp", + "checker/ets/narrowingConverter.cpp", + "checker/ets/narrowingWideningConverter.cpp", + "checker/ets/object.cpp", + "checker/ets/primitiveWrappers.cpp", + "checker/ets/typeConverter.cpp", + "checker/ets/typeCreation.cpp", + "checker/ets/typeRelationContext.cpp", + "checker/ets/unboxingConverter.cpp", + "checker/ets/wideningConverter.cpp", + "checker/ts/binaryLikeExpression.cpp", + "checker/ts/destructuringContext.cpp", + "checker/ts/function.cpp", + "checker/ts/helpers.cpp", + "checker/ts/object.cpp", + "checker/ts/typeCreation.cpp", + "checker/ts/typeElaborationContext.cpp", + "checker/ts/util.cpp", + "checker/types/signature.cpp", + "checker/types/type.cpp", + "checker/types/typeRelation.cpp", + "checker/types/globalTypesHolder.cpp", + "checker/types/ets/byteType.cpp", + "checker/types/ets/charType.cpp", + "checker/types/ets/doubleType.cpp", + "checker/types/ets/floatType.cpp", + "checker/types/ets/intType.cpp", + "checker/types/ets/longType.cpp", + "checker/types/ets/shortType.cpp", + "checker/types/ets/etsArrayType.cpp", + "checker/types/ets/etsBooleanType.cpp", + "checker/types/ets/etsFunctionType.cpp", + "checker/types/ets/etsObjectType.cpp", + "checker/types/ets/etsStringType.cpp", + "checker/types/ets/etsTypeParameter.cpp", + "checker/types/ets/etsTypeReference.cpp", + "checker/types/ets/etsVoidType.cpp", + "checker/types/ets/wildcardType.cpp", + "checker/types/ts/anyType.cpp", + "checker/types/ts/arrayType.cpp", + "checker/types/ts/bigintLiteralType.cpp", + "checker/types/ts/bigintType.cpp", + "checker/types/ts/booleanLiteralType.cpp", + "checker/types/ts/booleanType.cpp", + "checker/types/ts/constructorType.cpp", + "checker/types/ts/enumLiteralType.cpp", + "checker/types/ts/enumType.cpp", + "checker/types/ts/functionType.cpp", + "checker/types/ts/indexInfo.cpp", + "checker/types/ts/interfaceType.cpp", + "checker/types/ts/neverType.cpp", + "checker/types/ts/nonPrimitiveType.cpp", + "checker/types/ts/nullType.cpp", + "checker/types/ts/numberLiteralType.cpp", + "checker/types/ts/numberType.cpp", + "checker/types/ts/objectDescriptor.cpp", + "checker/types/ts/objectLiteralType.cpp", + "checker/types/ts/objectType.cpp", + "checker/types/ts/stringLiteralType.cpp", + "checker/types/ts/stringType.cpp", + "checker/types/ts/tupleType.cpp", + "checker/types/ts/typeParameter.cpp", + "checker/types/ts/typeReference.cpp", + "checker/types/ts/undefinedType.cpp", + "checker/types/ts/unionType.cpp", + "checker/types/ts/unknownType.cpp", + "checker/types/ts/voidType.cpp", "util/bitset.cpp", "util/helpers.cpp", "util/ustring.cpp", @@ -277,8 +355,8 @@ ohos_shared_library("libes2panda") { deps = [ ":isa_gen_es2panda_isa_h", ":isa_gen_es2panda_formats_h", - ":es2panda_lexer_keywords_h", - ":es2panda_lexer_keywords_map_h", + ":gen_es2panda_lexer_keywords_h", + ":gen_es2panda_compiler_signatures_h", "$ark_root/assembler:libarkassembler", "$ark_root/libpandabase:libarkbase", "$ark_root/libpandafile:libarkfile", @@ -299,8 +377,8 @@ ohos_static_library("libes2panda_frontend_static") { deps = [ ":isa_gen_es2panda_isa_h", ":isa_gen_es2panda_formats_h", - ":es2panda_lexer_keywords_h", - ":es2panda_lexer_keywords_map_h", + ":gen_es2panda_lexer_keywords_h", + ":gen_es2panda_compiler_signatures_h", "$ark_root/assembler:libarkassembler_frontend_static", "$ark_root/libpandabase:libarkbase_frontend_static", "$ark_root/libpandafile:libarkfile_frontend_static", @@ -318,20 +396,26 @@ ark_isa_gen("isa_gen_es2panda") { destination = "$target_gen_dir/generated" } -action("es2panda_lexer_keywords_h") { - script = "lexer/scripts/keywords.rb" - outputs = [ "$target_gen_dir/generated/keywords.h" ] - args = [ - rebase_path("lexer/templates/keywords.h.erb", root_build_dir), - rebase_path("$target_gen_dir/generated/keywords.h", root_build_dir), +ark_gen("gen_es2panda_lexer") { + data = "lexer/scripts/keywords.yaml" + template_files = [ + "keywords.h.erb", + ] + sources = "lexer/templates" + destination = "$target_gen_dir/generated" + requires = [ + "lexer/scripts/keywords.rb", ] } -action("es2panda_lexer_keywords_map_h") { - script = "lexer/scripts/keywords.rb" - outputs = [ "$target_gen_dir/generated/keywordsMap.h" ] - args = [ - rebase_path("lexer/templates/keywordsMap.h.erb", root_build_dir), - rebase_path("$target_gen_dir/generated/keywordsMap.h", root_build_dir), +ark_gen("gen_es2panda_compiler") { + data = "compiler/scripts/signatures.yaml" + template_files = [ + "signatures.h.erb", + ] + sources = "compiler/templates" + destination = "$target_gen_dir/generated" + requires = [ + "compiler/scripts/signatures.rb", ] } diff --git a/CMakeLists.txt b/CMakeLists.txt index 3113e256fd9fb475c49ec6cbdf259d02c3e48897..5fea467294d8000897b78d2dba8a5c1e14a55244 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(GENERATED_DIR ${OUTPUT_DIR}/generated) set(GENERATED_STAMP ${OUTPUT_DIR}/gen_dir.stamp) -add_custom_target(es2panda-gen) +file(MAKE_DIRECTORY "${GENERATED_DIR}") add_custom_command( OUTPUT ${GENERATED_STAMP} @@ -40,35 +40,36 @@ panda_isa_gen( EXTRA_DEPENDENCIES ${GENERATED_STAMP} ) -add_dependencies(es2panda-gen isa_gen_es2panda) - -function(gen_keywords TEMPLATE OUT_DIR) - set(TEMPLATE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/lexer/templates/${TEMPLATE}) - - string(REGEX REPLACE "\.erb$" "" NAME ${TEMPLATE}) - string(REPLACE "\." "_" CUSTOM_TARGET ${NAME}) - string(REPLACE "/" "_" CUSTOM_TARGET ${CUSTOM_TARGET}) - set(CUSTOM_TARGET "panda_es2panda_parser_gen_${CUSTOM_TARGET}") - - set(OUT_FILE ${OUT_DIR}/${NAME}) - set(GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/lexer/scripts/keywords.rb) - - add_custom_command(OUTPUT ${OUT_FILE} - COMMAND ruby ${GENERATOR} ${TEMPLATE_FILE} ${OUT_FILE} - DEPENDS ${GENERATED_STAMP} ${GENERATOR} ${TEMPLATE_FILE} - ) - - add_custom_target(${CUSTOM_TARGET} DEPENDS ${OUT_FILE}) - add_dependencies(es2panda-gen ${CUSTOM_TARGET}) -endfunction() +panda_gen( + DATA ${CMAKE_CURRENT_SOURCE_DIR}/lexer/scripts/keywords.yaml + TARGET_NAME es2panda_keywords + TEMPLATES keywords.h.erb + REQUIRES + ${CMAKE_CURRENT_SOURCE_DIR}/lexer/scripts/keywords.rb + SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/lexer/templates + DESTINATION ${GENERATED_DIR} +) -gen_keywords(keywords.h.erb ${GENERATED_DIR}) -gen_keywords(keywordsMap.h.erb ${GENERATED_DIR}) +panda_gen( + DATA ${CMAKE_CURRENT_SOURCE_DIR}/compiler/scripts/signatures.yaml + TARGET_NAME es2panda_signatures + TEMPLATES signatures.h.erb + REQUIRES + ${CMAKE_CURRENT_SOURCE_DIR}/compiler/scripts/signatures.rb + SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/compiler/templates + DESTINATION ${GENERATED_DIR} +) set(ES2PANDA_LIB_SRC es2panda.cpp + binder/ASBinder.cpp + binder/TSBinder.cpp + binder/TypedBinder.cpp + binder/ETSBinder.cpp + binder/JSBinder.cpp binder/binder.cpp binder/declaration.cpp + binder/recordTable.cpp binder/scope.cpp binder/variable.cpp compiler/base/catchTable.cpp @@ -80,26 +81,36 @@ set(ES2PANDA_LIB_SRC compiler/base/literals.cpp compiler/base/lreference.cpp compiler/base/optionalChain.cpp + compiler/core/codeGen.cpp + compiler/core/compileJob.cpp compiler/core/compileQueue.cpp compiler/core/compilerContext.cpp compiler/core/compilerImpl.cpp compiler/core/dynamicContext.cpp compiler/core/emitter.cpp + compiler/core/JSemitter.cpp compiler/core/envScope.cpp compiler/core/function.cpp - compiler/core/inlineCache.cpp compiler/core/labelTarget.cpp compiler/core/moduleContext.cpp compiler/core/pandagen.cpp + compiler/core/programElement.cpp compiler/core/regAllocator.cpp compiler/core/regScope.cpp + compiler/core/regSpiller.cpp + compiler/core/ETSemitter.cpp + compiler/core/ETSGen.cpp + compiler/core/ETSfunction.cpp compiler/core/switchBuilder.cpp + compiler/core/targetTypeContext.cpp + compiler/core/vReg.cpp compiler/debugger/debuginfoDumper.cpp compiler/function/asyncFunctionBuilder.cpp compiler/function/asyncGeneratorFunctionBuilder.cpp compiler/function/functionBuilder.cpp compiler/function/generatorFunctionBuilder.cpp ir/astDump.cpp + ir/astNode.cpp ir/irnode.cpp ir/base/catchClause.cpp ir/base/classElement.cpp @@ -113,6 +124,10 @@ set(ES2PANDA_LIB_SRC ir/base/scriptFunction.cpp ir/base/spreadElement.cpp ir/base/templateElement.cpp + ir/base/tsIndexSignature.cpp + ir/base/tsMethodSignature.cpp + ir/base/tsPropertySignature.cpp + ir/base/tsSignatureDeclaration.cpp ir/expression.cpp ir/expressions/arrayExpression.cpp ir/expressions/arrowFunctionExpression.cpp @@ -130,6 +145,7 @@ set(ES2PANDA_LIB_SRC ir/expressions/literal.cpp ir/expressions/literals/bigIntLiteral.cpp ir/expressions/literals/booleanLiteral.cpp + ir/expressions/literals/charLiteral.cpp ir/expressions/literals/nullLiteral.cpp ir/expressions/literals/numberLiteral.cpp ir/expressions/literals/regExpLiteral.cpp @@ -155,11 +171,13 @@ set(ES2PANDA_LIB_SRC ir/module/importNamespaceSpecifier.cpp ir/module/importSpecifier.cpp ir/statement.cpp + ir/statements/assertStatement.cpp ir/statements/blockStatement.cpp ir/statements/breakStatement.cpp ir/statements/classDeclaration.cpp ir/statements/continueStatement.cpp ir/statements/debuggerStatement.cpp + ir/statements/deferStatement.cpp ir/statements/doWhileStatement.cpp ir/statements/emptyStatement.cpp ir/statements/expressionStatement.cpp @@ -170,16 +188,31 @@ set(ES2PANDA_LIB_SRC ir/statements/ifStatement.cpp ir/statements/labelledStatement.cpp ir/statements/loopStatement.cpp + ir/statements/panicStatement.cpp ir/statements/returnStatement.cpp ir/statements/switchCaseStatement.cpp ir/statements/switchStatement.cpp ir/statements/throwStatement.cpp + ir/statements/trapStatement.cpp ir/statements/tryStatement.cpp ir/statements/variableDeclaration.cpp ir/statements/variableDeclarator.cpp ir/statements/whileStatement.cpp ir/as/namedType.cpp ir/as/prefixAssertionExpression.cpp + ir/ets/etsClassLiteral.cpp + ir/ets/etsFunctionType.cpp + ir/ets/etsMethodReferenceExpression.cpp + ir/ets/etsNewArrayInstanceExpression.cpp + ir/ets/etsNewClassInstanceExpression.cpp + ir/ets/etsNewMultiDimArrayInstanceExpression.cpp + ir/ets/etsPackageDeclaration.cpp + ir/ets/etsPrimitiveType.cpp + ir/ets/etsScript.cpp + ir/ets/etsTryExpression.cpp + ir/ets/etsTypeReference.cpp + ir/ets/etsTypeReferencePart.cpp + ir/ets/etsWildcardType.cpp ir/ts/tsAnyKeyword.cpp ir/ts/tsArrayType.cpp ir/ts/tsAsExpression.cpp @@ -194,7 +227,6 @@ set(ES2PANDA_LIB_SRC ir/ts/tsFunctionType.cpp ir/ts/tsImportEqualsDeclaration.cpp ir/ts/tsImportType.cpp - ir/ts/tsIndexSignature.cpp ir/ts/tsIndexedAccessType.cpp ir/ts/tsInferType.cpp ir/ts/tsInterfaceBody.cpp @@ -203,7 +235,6 @@ set(ES2PANDA_LIB_SRC ir/ts/tsIntersectionType.cpp ir/ts/tsLiteralType.cpp ir/ts/tsMappedType.cpp - ir/ts/tsMethodSignature.cpp ir/ts/tsModuleBlock.cpp ir/ts/tsModuleDeclaration.cpp ir/ts/tsNamedTupleMember.cpp @@ -214,9 +245,7 @@ set(ES2PANDA_LIB_SRC ir/ts/tsObjectKeyword.cpp ir/ts/tsParameterProperty.cpp ir/ts/tsParenthesizedType.cpp - ir/ts/tsPropertySignature.cpp ir/ts/tsQualifiedName.cpp - ir/ts/tsSignatureDeclaration.cpp ir/ts/tsStringKeyword.cpp ir/ts/tsThisType.cpp ir/ts/tsTupleType.cpp @@ -234,68 +263,112 @@ set(ES2PANDA_LIB_SRC ir/ts/tsUnionType.cpp ir/ts/tsUnknownKeyword.cpp ir/ts/tsVoidKeyword.cpp + lexer/ASLexer.cpp + lexer/keywords.cpp lexer/keywordsUtil.cpp lexer/lexer.cpp + lexer/ETSLexer.cpp + lexer/TSLexer.cpp lexer/regexp/regexp.cpp + lexer/token/number.cpp lexer/token/sourceLocation.cpp lexer/token/token.cpp parser/context/classPrivateContext.cpp parser/context/parserContext.cpp parser/expressionParser.cpp + parser/ASparser.cpp + parser/JSparser.cpp parser/parserImpl.cpp + parser/ETSparser.cpp + parser/TSparser.cpp + parser/TypedParser.cpp parser/program/program.cpp parser/statementParser.cpp - typescript/checker.cpp - typescript/core/binaryLikeExpression.cpp - typescript/core/checkerContext.cpp - typescript/core/destructuringContext.cpp - typescript/core/function.cpp - typescript/core/helpers.cpp - typescript/core/object.cpp - typescript/core/typeCreation.cpp - typescript/core/typeElaborationContext.cpp - typescript/core/typeRelation.cpp - typescript/core/util.cpp - typescript/types/anyType.cpp - typescript/types/arrayType.cpp - typescript/types/bigintLiteralType.cpp - typescript/types/bigintType.cpp - typescript/types/booleanLiteralType.cpp - typescript/types/booleanType.cpp - typescript/types/constructorType.cpp - typescript/types/enumLiteralType.cpp - typescript/types/enumType.cpp - typescript/types/functionType.cpp - typescript/types/globalTypesHolder.cpp - typescript/types/indexInfo.cpp - typescript/types/interfaceType.cpp - typescript/types/neverType.cpp - typescript/types/nonPrimitiveType.cpp - typescript/types/nullType.cpp - typescript/types/numberLiteralType.cpp - typescript/types/numberType.cpp - typescript/types/objectDescriptor.cpp - typescript/types/objectLiteralType.cpp - typescript/types/objectType.cpp - typescript/types/signature.cpp - typescript/types/stringLiteralType.cpp - typescript/types/stringType.cpp - typescript/types/tupleType.cpp - typescript/types/type.cpp - typescript/types/typeParameter.cpp - typescript/types/typeReference.cpp - typescript/types/typeRelation.cpp - typescript/types/undefinedType.cpp - typescript/types/unionType.cpp - typescript/types/unknownType.cpp - typescript/types/voidType.cpp + checker/checker.cpp + checker/checkerContext.cpp + checker/ETSchecker.cpp + checker/TSchecker.cpp + checker/ASchecker.cpp + checker/JSchecker.cpp + checker/ets/aliveAnalyzer.cpp + checker/ets/arithmetic.cpp + checker/ets/baseAnalyzer.cpp + checker/ets/boxingConverter.cpp + checker/ets/function.cpp + checker/ets/helpers.cpp + checker/ets/narrowingConverter.cpp + checker/ets/narrowingWideningConverter.cpp + checker/ets/object.cpp + checker/ets/primitiveWrappers.cpp + checker/ets/typeConverter.cpp + checker/ets/typeCreation.cpp + checker/ets/typeRelationContext.cpp + checker/ets/unboxingConverter.cpp + checker/ets/wideningConverter.cpp + checker/ts/binaryLikeExpression.cpp + checker/ts/destructuringContext.cpp + checker/ts/function.cpp + checker/ts/helpers.cpp + checker/ts/object.cpp + checker/ts/typeCreation.cpp + checker/ts/typeElaborationContext.cpp + checker/ts/util.cpp + checker/types/signature.cpp + checker/types/type.cpp + checker/types/typeRelation.cpp + checker/types/globalTypesHolder.cpp + checker/types/ets/byteType.cpp + checker/types/ets/charType.cpp + checker/types/ets/doubleType.cpp + checker/types/ets/floatType.cpp + checker/types/ets/intType.cpp + checker/types/ets/longType.cpp + checker/types/ets/shortType.cpp + checker/types/ets/etsArrayType.cpp + checker/types/ets/etsBooleanType.cpp + checker/types/ets/etsFunctionType.cpp + checker/types/ets/etsObjectType.cpp + checker/types/ets/etsStringType.cpp + checker/types/ets/etsTypeParameter.cpp + checker/types/ets/etsTypeReference.cpp + checker/types/ets/etsVoidType.cpp + checker/types/ets/wildcardType.cpp + checker/types/ts/anyType.cpp + checker/types/ts/arrayType.cpp + checker/types/ts/bigintLiteralType.cpp + checker/types/ts/bigintType.cpp + checker/types/ts/booleanLiteralType.cpp + checker/types/ts/booleanType.cpp + checker/types/ts/constructorType.cpp + checker/types/ts/enumLiteralType.cpp + checker/types/ts/enumType.cpp + checker/types/ts/functionType.cpp + checker/types/ts/indexInfo.cpp + checker/types/ts/interfaceType.cpp + checker/types/ts/neverType.cpp + checker/types/ts/nonPrimitiveType.cpp + checker/types/ts/nullType.cpp + checker/types/ts/numberLiteralType.cpp + checker/types/ts/numberType.cpp + checker/types/ts/objectDescriptor.cpp + checker/types/ts/objectLiteralType.cpp + checker/types/ts/objectType.cpp + checker/types/ts/stringLiteralType.cpp + checker/types/ts/stringType.cpp + checker/types/ts/tupleType.cpp + checker/types/ts/typeParameter.cpp + checker/types/ts/typeReference.cpp + checker/types/ts/undefinedType.cpp + checker/types/ts/unionType.cpp + checker/types/ts/unknownType.cpp + checker/types/ts/voidType.cpp util/bitset.cpp util/helpers.cpp util/ustring.cpp ) add_library(es2panda-lib ${PANDA_DEFAULT_LIB_TYPE} ${ES2PANDA_LIB_SRC}) -add_dependencies(es2panda-lib es2panda-gen) +add_dependencies(es2panda-lib isa_gen_es2panda es2panda_keywords es2panda_signatures) set(ICU_INCLUDE_DIRS ${PANDA_THIRD_PARTY_SOURCES_DIR}/icu/icu4c/source/common @@ -318,10 +391,19 @@ if(POLICY CMP0079) cmake_policy(SET CMP0079 NEW) endif() -target_link_libraries(es2panda-lib - PUBLIC arkbase hmicuuc.z - PRIVATE arkassembler -) +if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.1) OR + (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0)) + target_link_libraries(es2panda-lib + PUBLIC arkbase hmicuuc.z stdc++fs + PRIVATE arkassembler + ) +else() + target_link_libraries(es2panda-lib + PUBLIC arkbase hmicuuc.z + PRIVATE arkassembler + ) +endif() + if (PANDA_FUZZILLI) target_compile_options(es2panda-lib diff --git a/README.md b/README.md index 1ae69d9bc615a6bd74dcd3ecf9ea2e786862a74f..43a3d5500db85b11cc4cf8d434e6e7b577e043c8 100644 --- a/README.md +++ b/README.md @@ -25,38 +25,19 @@ es2panda [OPTIONS] [input file] -- [arguments] ## Running the tests ```sh -pip install tqdm dataclasses python-dotenv +pip install tqdm ``` ```sh -python3 test/runner/runner.py [OPTIONS] [build_directory] +python3 test/runner.py [OPTIONS] [build_directory] ``` ### Optional arguments -#### Test sets - `--regression`: Run regression tests - - `--test262`: Run test262. To run tests from test262 set specify environment variables `TEST262_REVISION` and `TEST262_URL` in the `.env` file. - - `--hermes`: Run Hermes runtime tests. To run tests from hermes set specify environment variables `HERMES_REVISION` and `HERMES_URL` in the `.env` file. - -#### Extra arguments + - `--test262`: Run test262 - `--no-progress`: Don't show progress bar - - `--verbose`: Generates more detailed output - -Other options are described at starter.py file -#### Tail arguments +### Tail arguments - `build_directory`: Path to panda build directory -### Execution time report -It is possible to collect statistics how long separate tests work. In the result report tests are grouped by execution time. -The grouping edges are set in seconds in the environment variable `TIME_EDGES`. For example the value `1 5 10` specifies -4 groups - less than 1 second, from 1 second to 5 seconds, from 5 seconds to 10 seconds and from 10 seconds and more. -For the last group the report contains real durations. - - Specify the option `--time-report` - - Set an environment variable in the `.env` file in the format `TIME_EDGES="1 5 10"` - - After test run the short report will be output to the console - - And full report will be created at the path `test/time_report.txt` - ### Skip list -Skip list for the runtime: - - test262: `test/test262skiplist.txt, test/test262skiplist-long.txt` - - hermes: `test/hermes-excluded.txt, test/hermes-excluded-aot-inline-full.txt`. +Skip list for the runtime: `test/test262skiplist.txt, test/test262skiplist-long.txt`. diff --git a/aot/main.cpp b/aot/main.cpp index 778d36a769bf0efb6d4c64bc301afb63555d1c52..51c8339d7b187c77d70117b0a632ddc1ca0234d4 100644 --- a/aot/main.cpp +++ b/aot/main.cpp @@ -63,13 +63,14 @@ static int GenerateProgram(panda::pandasm::Program *prog, const Options *options panda::Logger::InitializeStdLogging(Logger::LevelFromString(options->LogLevel()), componentMask); if (!panda::pandasm::AsmEmitter::Emit(options->CompilerOutput(), *prog, statp, mapsp, true)) { + std::cerr << "Failed to emit binary data: " << panda::pandasm::AsmEmitter::GetLastError() << std::endl; return 1; } panda::bytecodeopt::OPTIONS.SetOptLevel(options->OptLevel()); // Set default value instead of maximum set in panda::bytecodeopt::SetCompilerOptions() panda::compiler::OPTIONS.SetCompilerMaxBytecodeSize(panda::compiler::OPTIONS.GetCompilerMaxBytecodeSize()); - panda::bytecodeopt::OptimizeBytecode(prog, mapsp, options->CompilerOutput(), true, true); + panda::bytecodeopt::OptimizeBytecode(prog, mapsp, options->CompilerOutput(), options->IsDynamic(), true); } #endif @@ -83,6 +84,7 @@ static int GenerateProgram(panda::pandasm::Program *prog, const Options *options } if (!panda::pandasm::AsmEmitter::Emit(options->CompilerOutput(), *prog, statp, mapsp, true)) { + std::cerr << "Failed to emit binary data: " << panda::pandasm::AsmEmitter::GetLastError() << std::endl; return 1; } @@ -118,10 +120,19 @@ int Run(int argc, const char **argv) return 1; } + std::string_view sourceFile; + std::string_view parserInput; + if (options->CompilerOptions().genStdLib) { + sourceFile = "etsstdlib.ets"; + parserInput = ""; + } else { + sourceFile = options->SourceFile(); + parserInput = options->ParserInput(); + } es2panda::Compiler compiler(options->Extension(), options->ThreadCount()); - es2panda::SourceFile input(options->SourceFile(), options->ParserInput(), options->ParseModule()); + es2panda::SourceFile input(sourceFile, parserInput, options->ParseModule()); - auto *program = compiler.Compile(input, options->CompilerOptions()); + auto program = std::unique_ptr {compiler.Compile(input, options->CompilerOptions())}; if (program == nullptr) { const auto &err = compiler.GetError(); @@ -131,15 +142,13 @@ int Run(int argc, const char **argv) } std::cout << err.TypeString() << ": " << err.Message(); - std::cout << " [" << options->SourceFile() << ":" << err.Line() << ":" << err.Col() << "]" << std::endl; + std::cout << " [" << (err.File().empty() ? options->SourceFile() : err.File()) << ":" << err.Line() << ":" + << err.Col() << "]" << std::endl; return err.ErrorCode(); } - GenerateProgram(program, options.get()); - delete program; - - return 0; + return GenerateProgram(program.get(), options.get()); } } // namespace panda::es2panda::aot diff --git a/aot/options.cpp b/aot/options.cpp index fbce88d98b691b953203466560151bd1d307e1c3..053713541f2bd471ef83574558beb549c8a6fb16 100644 --- a/aot/options.cpp +++ b/aot/options.cpp @@ -48,7 +48,7 @@ bool Options::Parse(int argc, const char **argv) // parser panda::PandArg inputExtension("extension", "js", - "Parse the input as the given extension (options: js | ts | as)"); + "Parse the input as the given extension (options: js | ts | as | ets)"); panda::PandArg opModule("module", false, "Parse the input as module"); panda::PandArg opParseOnly("parse-only", false, "Parse the input only"); panda::PandArg opDumpAst("dump-ast", false, "Dump the parsed AST"); @@ -64,6 +64,8 @@ bool Options::Parse(int argc, const char **argv) panda::PandArg opSizeStat("dump-size-stat", false, "Dump size statistics"); panda::PandArg outputFile("output", "", "Compiler binary output (.abc)"); panda::PandArg logLevel("log-level", "error", "Log-level"); + panda::PandArg stdLib("stdlib", "", "Path to standard library"); + panda::PandArg genStdLib("gen-stdlib", false, "Gen standard library"); // tail arguments panda::PandArg inputFile("input", "", "input file"); @@ -83,6 +85,8 @@ bool Options::Parse(int argc, const char **argv) argparser_->Add(&inputExtension); argparser_->Add(&outputFile); argparser_->Add(&logLevel); + argparser_->Add(&stdLib); + argparser_->Add(&genStdLib); argparser_->PushBackTail(&inputFile); argparser_->EnableTail(); @@ -137,8 +141,10 @@ bool Options::Parse(int argc, const char **argv) extension_ = es2panda::ScriptExtension::TS; } else if (extension == "as") { extension_ = es2panda::ScriptExtension::AS; + } else if (extension == "ets") { + extension_ = es2panda::ScriptExtension::ETS; } else { - errorMsg_ = "Invalid extension (available options: js, ts, as)"; + errorMsg_ = "Invalid extension (available options: js, ts, as, ets)"; return false; } } @@ -163,6 +169,8 @@ bool Options::Parse(int argc, const char **argv) compilerOptions_.dumpDebugInfo = opDumpDebugInfo.GetValue(); compilerOptions_.isDebug = opDebugInfo.GetValue(); compilerOptions_.parseOnly = opParseOnly.GetValue(); + compilerOptions_.stdLib_ = stdLib.GetValue(); + compilerOptions_.genStdLib = genStdLib.GetValue(); return true; } diff --git a/aot/options.h b/aot/options.h index a3d7d2f0ac44981ca68b0021ca6cc048f351b1eb..d463599cd00569348dc59ceb1d2e51532c04564e 100644 --- a/aot/options.h +++ b/aot/options.h @@ -18,7 +18,6 @@ #include "plugins/ecmascript/es2panda/es2panda.h" -#include #include #include #include @@ -30,10 +29,10 @@ class PandaArg; namespace panda::es2panda::aot { enum class OptionFlags : uint32_t { - DEFAULT = 0x0U, - PARSE_ONLY = 0x1U, - PARSE_MODULE = 0x2U, - SIZE_STAT = 0x4U, + DEFAULT = 0U, + PARSE_ONLY = 1U << 0U, + PARSE_MODULE = 1U << 1U, + SIZE_STAT = 1U << 2U, }; inline std::underlying_type_t operator&(OptionFlags a, OptionFlags b) @@ -119,6 +118,11 @@ public: return (options_ & OptionFlags::SIZE_STAT) != 0; } + bool IsDynamic() const + { + return extension_ != es2panda::ScriptExtension::ETS; + } + private: es2panda::ScriptExtension extension_ {es2panda::ScriptExtension::JS}; es2panda::CompilerOptions compilerOptions_ {}; diff --git a/binder/ASBinder.cpp b/binder/ASBinder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8a58d1c3e7dbad3a0c7c0240500a98935482e3ad --- /dev/null +++ b/binder/ASBinder.cpp @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2021 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 "ASBinder.h" + +namespace panda::es2panda::binder { +} // namespace panda::es2panda::binder diff --git a/binder/ASBinder.h b/binder/ASBinder.h new file mode 100644 index 0000000000000000000000000000000000000000..ac6c8b22ec9131397f9e5ddee1698e714c907080 --- /dev/null +++ b/binder/ASBinder.h @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2021 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_BINDER_AS_BINDER_H +#define ES2PANDA_BINDER_AS_BINDER_H + +#include "plugins/ecmascript/es2panda/binder/binder.h" + +namespace panda::es2panda::binder { +class ASBinder : public Binder { +public: + explicit ASBinder(ArenaAllocator *allocator) : Binder(allocator) {} + + NO_COPY_SEMANTIC(ASBinder); + NO_MOVE_SEMANTIC(ASBinder); + ~ASBinder() = default; + + ScriptExtension Extension() const override + { + return ScriptExtension::AS; + } + + ResolveBindingOptions BindingOptions() const override + { + return ResolveBindingOptions::BINDINGS; + } + + void IdentifierAnalysis() override {} + +private: +}; +} // namespace panda::es2panda::binder + +#endif diff --git a/binder/ETSBinder.cpp b/binder/ETSBinder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dbc39a9e23146148188861ed2b78086edc4d7975 --- /dev/null +++ b/binder/ETSBinder.cpp @@ -0,0 +1,740 @@ +/** + * Copyright (c) 2021 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 "ETSBinder.h" + +#include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" +#include "plugins/ecmascript/es2panda/ir/expressions/thisExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/memberExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/callExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/functionExpression.h" +#include "plugins/ecmascript/es2panda/ir/base/methodDefinition.h" +#include "plugins/ecmascript/es2panda/ir/base/scriptFunction.h" +#include "plugins/ecmascript/es2panda/ir/base/classElement.h" +#include "plugins/ecmascript/es2panda/ir/base/classDefinition.h" +#include "plugins/ecmascript/es2panda/ir/base/classProperty.h" +#include "plugins/ecmascript/es2panda/ir/base/classStaticBlock.h" +#include "plugins/ecmascript/es2panda/ir/statements/blockStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/classDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/statements/variableDeclarator.h" +#include "plugins/ecmascript/es2panda/ir/statements/functionDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/statements/returnStatement.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsPrimitiveType.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsTypeReferencePart.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsMethodReferenceExpression.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsNewClassInstanceExpression.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsTypeReference.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsFunctionType.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsScript.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsTypeParameterDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsTypeParameterInstantiation.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsClassImplements.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsEnumDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsEnumMember.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceHeritage.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceBody.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsFunctionType.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsQualifiedName.h" +#include "plugins/ecmascript/es2panda/ir/module/importDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/module/importSpecifier.h" +#include "plugins/ecmascript/es2panda/parser/program/program.h" +#include "plugins/ecmascript/es2panda/util/helpers.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/types.h" + +namespace panda::es2panda::binder { + +void ETSBinder::IdentifierAnalysis() +{ + ASSERT(Program()->Ast()); + ASSERT(GetScope() == TopScope()); + ASSERT(VarScope() == TopScope()); + + recordTable_->SetProgram(Program()); + globalRecordTable_.SetClassDefinition(Program()->GlobalClass()); + externalRecordTable_.insert({Program(), &globalRecordTable_}); + + BuildProgram(); + + ASSERT(globalRecordTable_.ClassDefinition() == Program()->GlobalClass()); +} + +void ETSBinder::LookupTypeArgumentReferences(ir::ETSTypeReference *typeRef) +{ + auto *iter = typeRef->Part(); + + while (iter != nullptr) { + if (iter->TypeParams() == nullptr) { + iter = iter->Previous(); + continue; + } + + for (auto *it : iter->TypeParams()->Params()) { + if (!it->IsETSTypeReference()) { + continue; + } + + ResolveReference(it); + } + + iter = iter->Previous(); + } +} + +void ETSBinder::LookupTypeReference(ir::Identifier *ident) +{ + const auto &name = ident->Name(); + auto *iter = GetScope(); + + while (iter != nullptr) { + ScopeFindResult res = iter->Find(name, ResolveBindingOptions::DECLARATION); + + if (res.variable == nullptr) { + break; + } + + switch (res.variable->Declaration()->Node()->Type()) { + case ir::AstNodeType::TS_INTERFACE_DECLARATION: + case ir::AstNodeType::CLASS_DECLARATION: + case ir::AstNodeType::CLASS_DEFINITION: + case ir::AstNodeType::TS_TYPE_PARAMETER: + case ir::AstNodeType::TS_ENUM_DECLARATION: { + ident->SetVariable(res.variable); + return; + } + default: { + iter = iter->Parent(); + } + } + } + + ThrowUnresolvableType(ident->Start(), name); +} + +void ETSBinder::LookupIdentReference(ir::Identifier *ident) +{ + const auto &name = ident->Name(); + ScopeFindResult res = GetScope()->Find(name, ResolveBindingOptions::ALL); + if (res.level != 0) { + ASSERT(res.variable != nullptr); + + auto *outerFunction = GetScope()->EnclosingVariableScope()->Node(); + + if ((!outerFunction->IsScriptFunction() || !outerFunction->AsScriptFunction()->IsArrow()) && + !res.variable->IsGlobalVariable() && res.level > 1) { + ThrowInvalidCapture(ident->Start(), name); + } + + res.variable->SetLexical(res.scope); + } + + if (res.variable == nullptr) { + return; + } + + if (ident->IsReference() && res.variable->Declaration()->IsLetOrConstDecl() && + !res.variable->HasFlag(VariableFlags::INITIALIZED)) { + ThrowTDZ(ident->Start(), name); + } + + ident->SetVariable(res.variable); +} + +void ETSBinder::BuildClassProperty(const ir::ClassProperty *prop) +{ + ResolveReferences(prop); +} + +void ETSBinder::InitializeInterfaceIdent(ir::TSInterfaceDeclaration *decl) +{ + ScopeFindResult res = GetScope()->Find(decl->Id()->Name()); + + ASSERT(res.variable && res.variable->Declaration()->IsInterfaceDecl()); + res.variable->AddFlag(VariableFlags::INITIALIZED); + decl->Id()->SetVariable(res.variable); +} + +void ETSBinder::ResolveEnumDeclaration(ir::TSEnumDeclaration *enumDecl) +{ + auto bctx = BoundContext(recordTable_, enumDecl); + auto enumScopeCtx = LexicalScope::Enter(this, enumDecl->Scope()); + + for (auto *member : enumDecl->Members()) { + ResolveReference(member); + } +} + +void ETSBinder::ResolveInterfaceDeclaration(ir::TSInterfaceDeclaration *decl) +{ + auto bctx = BoundContext(recordTable_, decl); + + for (auto *extend : decl->Extends()) { + ResolveReference(extend); + } + + auto scopeCtx = LexicalScope::Enter(this, decl->Scope()->AsClassScope()); + + for (auto *stmt : decl->Body()->Body()) { + if (!stmt->IsClassProperty()) { + continue; + } + + ResolveReference(stmt); + + auto fieldVar = ResolvePropertyReference(stmt->AsClassProperty(), decl->Scope()->AsClassScope()) + ->FindLocal(stmt->AsClassProperty()->Id()->Name()); + fieldVar->AddFlag(VariableFlags::INITIALIZED); + } + + for (auto *stmt : decl->Body()->Body()) { + if (stmt->IsClassProperty()) { + continue; + } + ResolveReference(stmt); + } +} + +void ETSBinder::BuildInterfaceDeclaration(ir::TSInterfaceDeclaration *decl) +{ + if (decl->TypeParams() != nullptr) { + auto typeParamScopeCtx = LexicalScope::Enter(this, decl->TypeParams()->Scope()); + ResolveReferences(decl->TypeParams()); + ResolveInterfaceDeclaration(decl); + return; + } + + ResolveInterfaceDeclaration(decl); +} + +void ETSBinder::BuildMethodDefinition(ir::MethodDefinition *methodDef) +{ + if (methodDef->Function()->TypeParams() != nullptr) { + auto scopeCtx = LexicalScope::Enter(this, methodDef->Function()->TypeParams()->Scope()); + ResolveReferences(methodDef->Function()->TypeParams()); + ResolveMethodDefinition(methodDef); + return; + } + + ResolveMethodDefinition(methodDef); +} + +void ETSBinder::ResolveMethodDefinition(ir::MethodDefinition *methodDef) +{ + auto *func = methodDef->Function(); + ResolveReferences(methodDef); + + if (recordTable_->InterfaceDeclaration() != nullptr || methodDef->IsStatic() || func->IsStaticBlock()) { + return; + } + + auto paramScopeCtx = LexicalScope::Enter(this, func->Scope()->ParamScope()); + auto *thisParam = AddMandatoryParam(MANDATORY_PARAM_THIS); + thisParam->Declaration()->BindNode(thisParam_); +} + +void ETSBinder::BuildMemberExpression(ir::MemberExpression *memberExpr) +{ + ResolveReference(memberExpr->Object()); + + if (memberExpr->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS) { + ResolveReference(memberExpr->Property()); + } +} + +void ETSBinder::BuildClassDefinition(ir::ClassDefinition *classDef) +{ + auto bctx = BoundContext(recordTable_, classDef); + + if (classDef->TypeParams() != nullptr) { + auto scopeCtx = LexicalScope::Enter(this, classDef->TypeParams()->Scope()); + ResolveReferences(classDef->TypeParams()); + BuildClassDefinitionImpl(classDef); + return; + } + + BuildClassDefinitionImpl(classDef); +} + +LocalScope *ETSBinder::ResolvePropertyReference(ir::ClassProperty *prop, ClassScope *scope) +{ + ResolveReferences(prop); + + if (prop->IsStatic()) { + return scope->StaticFieldScope(); + } + + return scope->InstanceFieldScope(); +} + +void ETSBinder::BuildClassDefinitionImpl(ir::ClassDefinition *classDef) +{ + auto classCtx = LexicalScope::Enter(this, classDef->Scope()->AsClassScope()); + + if (classDef->Super() != nullptr) { + ResolveReference(classDef->Super()); + } + + for (auto *impl : classDef->Implements()) { + ResolveReference(impl); + } + + for (auto *stmt : classDef->Body()) { + if (!stmt->IsClassProperty()) { + continue; + } + + auto fieldVar = ResolvePropertyReference(stmt->AsClassProperty(), classDef->Scope()->AsClassScope()) + ->FindLocal(stmt->AsClassProperty()->Id()->Name()); + fieldVar->AddFlag(VariableFlags::INITIALIZED); + if (fieldVar->Declaration()->IsConstDecl() && stmt->AsClassProperty()->Value() == nullptr) { + fieldVar->AddFlag(VariableFlags::EXPLICIT_INIT_REQUIRED); + } + } + + for (auto *stmt : classDef->Body()) { + if (stmt->IsClassProperty()) { + continue; + } + ResolveReference(stmt); + } +} + +void ETSBinder::AddLambdaFunctionThisParam(ir::ScriptFunction *func) +{ + auto paramScopeCtx = LexicalScope::Enter(this, func->Scope()->ParamScope()); + auto *thisParam = AddMandatoryParam(MANDATORY_PARAM_THIS); + thisParam->Declaration()->BindNode(thisParam_); + Functions().push_back(func->Scope()); +} + +void ETSBinder::AddInvokeFunctionThisParam(ir::ScriptFunction *func) +{ + auto paramScopeCtx = LexicalScope::Enter(this, func->Scope()->ParamScope()); + auto *thisParam = AddMandatoryParam(MANDATORY_PARAM_THIS); + thisParam->Declaration()->BindNode(thisParam_); +} + +void ETSBinder::BuildMethodReferenceExpression(ir::ETSMethodReferenceExpression *methodRef) +{ + ResolveReferences(methodRef); + + auto bctx = BoundContext(recordTable_, methodRef->LambdaObject()); + AddLambdaFunctionThisParam(methodRef->LambdaObject()->Body()[1]->AsMethodDefinition()->Function()); + AddLambdaFunctionThisParam(methodRef->LambdaObject()->Body()[2]->AsMethodDefinition()->Function()); + + LambdaObjects().push_back(methodRef); +} + +void ETSBinder::BuildFunctionType(ir::ETSFunctionType *funcType) +{ + auto bctx = BoundContext(recordTable_, funcType->FunctionalInterface()); + + auto *invokeFunc = funcType->FunctionalInterface()->Body()->Body()[0]->AsMethodDefinition()->Function(); + auto *funcScope = invokeFunc->Scope(); + funcScope->BindName(recordTable_->RecordName()); + AddInvokeFunctionThisParam(invokeFunc); + + recordTable_->Signatures().push_back(funcScope); + FunctionalInterfaces().push_back(funcType); +} + +void ETSBinder::HandleCustomNodes(ir::AstNode *childNode) +{ + switch (childNode->Type()) { + case ir::AstNodeType::ETS_TYPE_REFERENCE: { + auto *typeRef = childNode->AsETSTypeReference(); + ASSERT(typeRef->BaseName()->IsReference()); + LookupTypeReference(typeRef->BaseName()); + LookupTypeArgumentReferences(typeRef); + break; + } + case ir::AstNodeType::TS_INTERFACE_DECLARATION: { + BuildInterfaceDeclaration(childNode->AsTSInterfaceDeclaration()); + break; + } + case ir::AstNodeType::TS_ENUM_DECLARATION: { + ResolveEnumDeclaration(childNode->AsTSEnumDeclaration()); + break; + } + case ir::AstNodeType::EXPORT_NAMED_DECLARATION: { + break; + } + case ir::AstNodeType::IMPORT_DECLARATION: { + BuildImportDeclaration(childNode->AsImportDeclaration()); + break; + } + case ir::AstNodeType::MEMBER_EXPRESSION: { + BuildMemberExpression(childNode->AsMemberExpression()); + break; + } + case ir::AstNodeType::METHOD_DEFINITION: { + BuildMethodDefinition(childNode->AsMethodDefinition()); + break; + } + case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: { + BuildETSNewClassInstanceExpression(childNode->AsETSNewClassInstanceExpression()); + break; + } + case ir::AstNodeType::ETS_METHOD_REFERENCE_EXPRESSION: { + BuildMethodReferenceExpression(childNode->AsETSMethodReferenceExpression()); + break; + } + case ir::AstNodeType::ETS_FUNCTION_TYPE: { + BuildFunctionType(childNode->AsETSFunctionType()); + break; + } + case ir::AstNodeType::DEFER_STATEMENT: { + GetScope()->AddFlag(ScopeFlags::DEFER_STMT); + ResolveReferences(childNode); + break; + } + default: { + ResolveReferences(childNode); + break; + } + } +} + +bool ETSBinder::BuildInternalName(ir::ScriptFunction *scriptFunc) +{ + auto *funcScope = scriptFunc->Scope(); + funcScope->BindName(recordTable_->RecordName()); + bool isExternal = recordTable_->IsExternal(); + + bool compilable = scriptFunc->Body() != nullptr && !isExternal; + + if (!compilable) { + recordTable_->Signatures().push_back(funcScope); + } + + if (isExternal) { + scriptFunc->AddFlag(ir::ScriptFunctionFlags::EXTERNAL); + } + + return compilable; +} + +void ETSBinder::BuildFunctionName(const ir::ScriptFunction *func) const +{ + auto *funcScope = func->Scope(); + + std::stringstream ss; + ss << funcScope->Name() << compiler::Signatures::METHOD_SEPARATOR; + + const auto *signature = func->Signature(); + + if (func->IsStaticBlock()) { + ss << compiler::Signatures::CCTOR; + } else { + if (func->IsConstructor()) { + ss << compiler::Signatures::CTOR; + } else { + ss << util::Helpers::FunctionName(Allocator(), func); + } + } + + signature->ToAssemblerType(ss); + + util::UString internalName(ss.str(), Allocator()); + funcScope->BindInternalName(internalName.View()); +} + +void ETSBinder::FormLambdaName(util::UString &name, const util::StringView &signature, bool addSeparator) +{ + if (addSeparator) { + name.Append(compiler::Signatures::LAMBDA_SEPARATOR); + } + + std::string replaced = std::string(signature.Utf8()); + std::replace(replaced.begin(), replaced.end(), '.', '-'); + std::replace(replaced.begin(), replaced.end(), ':', '-'); + std::replace(replaced.begin(), replaced.end(), ';', '-'); + replaced.append("0"); + name.Append(replaced); +} + +void ETSBinder::BuildLambdaObjectName(ir::ETSMethodReferenceExpression *methodRef) +{ + auto *lambdaClass = methodRef->LambdaObject(); + + util::UString lambdaObjectName(lambdaClass->Ident()->Name(), Allocator()); + FormLambdaName(lambdaObjectName, methodRef->ComputedSignature()->InternalName()); + lambdaClass->Ident()->SetName(lambdaObjectName.View()); + lambdaClass->SetInternalName(lambdaClass->Ident()->Name()); + + util::StringView assemblerName(lambdaClass->Ident()->Name()); + auto *program = static_cast(methodRef->GetTopStatement())->Program(); + util::StringView prefix = program->GetPackageName(); + prefix = program->GetPackageName(); + + if (!prefix.Empty()) { + util::UString fullPath(prefix, Allocator()); + fullPath.Append('.'); + fullPath.Append(assemblerName); + assemblerName = fullPath.View(); + } + + checker::ETSObjectType *lambdaObject = lambdaClass->TsType()->AsETSObjectType(); + lambdaObject->SetName(lambdaClass->Ident()->Name()); + lambdaObject->SetAssemblerName(assemblerName); + + auto *ctorFunc = lambdaClass->Body()[1]->AsMethodDefinition()->Function(); + auto *ctorFuncScope = ctorFunc->Scope(); + ctorFuncScope->BindName(lambdaClass->Ident()->Name()); + + util::UString ctorInternalName(ctorFuncScope->Name(), Allocator()); + ctorInternalName.Append(compiler::Signatures::METHOD_SEPARATOR); + ctorInternalName.Append(compiler::Signatures::CTOR); + std::stringstream ctorSignatureSs; + ctorFunc->Signature()->ToAssemblerType(ctorSignatureSs); + ctorInternalName.Append(ctorSignatureSs.str()); + ctorFuncScope->BindInternalName(ctorInternalName.View()); + + auto *invokeFunc = lambdaClass->Body()[2]->AsMethodDefinition()->Function(); + auto *invokeFuncScope = invokeFunc->Scope(); + invokeFuncScope->BindName(lambdaClass->Ident()->Name()); + + util::UString invokeInternalName(invokeFuncScope->Name(), Allocator()); + invokeInternalName.Append(compiler::Signatures::METHOD_SEPARATOR); + invokeInternalName.Append(invokeFunc->Id()->Name()); + std::stringstream invokeSinatureSs; + invokeFunc->Signature()->ToAssemblerType(invokeSinatureSs); + invokeInternalName.Append(invokeSinatureSs.str()); + invokeFuncScope->BindInternalName(invokeInternalName.View()); +} + +void ETSBinder::BuildFunctionalInterfaceName(ir::ETSFunctionType *funcType) +{ + auto *functionalInterface = funcType->FunctionalInterface(); + auto *invokeFunc = functionalInterface->Body()->Body()[0]->AsMethodDefinition()->Function(); + util::UString functionalInterfaceName(functionalInterface->Id()->Name(), Allocator()); + std::stringstream ss; + invokeFunc->Signature()->ToAssemblerType(ss); + util::StringView signatureName(ss.str()); + FormLambdaName(functionalInterfaceName, signatureName, false); + functionalInterface->Id()->SetName(functionalInterfaceName.View()); + functionalInterface->SetInternalName(functionalInterface->Id()->Name()); + + util::StringView assemblerName(functionalInterface->Id()->Name()); + auto *program = static_cast(funcType->GetTopStatement())->Program(); + util::StringView prefix = program->GetPackageName(); + prefix = program->GetPackageName(); + + if (!prefix.Empty()) { + util::UString fullPath(prefix, Allocator()); + fullPath.Append('.'); + fullPath.Append(assemblerName); + assemblerName = fullPath.View(); + } + + checker::ETSObjectType *functionalInterfaceType = functionalInterface->TsType()->AsETSObjectType(); + functionalInterfaceType->SetName(functionalInterface->Id()->Name()); + functionalInterfaceType->SetAssemblerName(assemblerName); + + auto *invokeFuncScope = invokeFunc->Scope(); + invokeFuncScope->BindName(functionalInterface->Id()->Name()); + + util::UString invokeInternalName(invokeFuncScope->Name(), Allocator()); + invokeInternalName.Append(compiler::Signatures::METHOD_SEPARATOR); + invokeInternalName.Append(invokeFunc->Id()->Name()); + std::stringstream invokeSinatureSs; + invokeFunc->Signature()->ToAssemblerType(invokeSinatureSs); + invokeInternalName.Append(invokeSinatureSs.str()); + invokeFuncScope->BindInternalName(invokeInternalName.View()); +} + +void ETSBinder::InitImplicitThisParam() +{ + thisParam_ = Allocator()->New("this", Allocator()); +} + +void ETSBinder::BuildProgram() +{ + for (auto &[_, extPrograms] : Program()->ExternalSources()) { + (void)_; + for (auto *extProg : extPrograms) { + BuildExternalProgram(extProg); + } + } + + for (auto *defaultImport : defaultImports_) { + BuildImportDeclaration(defaultImport); + } + + auto &stmts = Program()->Ast()->Statements(); + const auto etsGlobal = std::find_if(stmts.begin(), stmts.end(), [](const ir::Statement *stmt) { + return stmt->IsClassDeclaration() && + stmt->AsClassDeclaration()->Definition()->Ident()->Name().Is(compiler::Signatures::ETS_GLOBAL); + }); + + if (etsGlobal != stmts.end()) { + const auto begin = stmts.begin(); + const size_t index = std::distance(begin, etsGlobal); + std::rotate(begin, begin + index, begin + index + 1); + } + + for (auto *stmt : stmts) { + ResolveReference(stmt); + } +} + +void ETSBinder::BuildExternalProgram(parser::Program *extProgram) +{ + auto *savedProgram = Program(); + auto *savedrecordTable = recordTable_; + auto *savedTopScope_ = TopScope(); + + auto flags = Program()->Binder()->IsGenStdLib() ? RecordTableFlags::NONE : RecordTableFlags::EXTERNAL; + auto *extRecordTable = Allocator()->New(Allocator(), extProgram, flags); + externalRecordTable_.insert({extProgram, extRecordTable}); + + ResetTopScope(extProgram->GlobalScope()); + recordTable_ = extRecordTable; + SetProgram(extProgram); + + BuildProgram(); + + SetProgram(savedProgram); + recordTable_ = savedrecordTable; + ResetTopScope(savedTopScope_); +} + +void ETSBinder::BuildETSNewClassInstanceExpression(ir::ETSNewClassInstanceExpression *classInstance) +{ + BoundContext boundCtx(recordTable_, classInstance->ClassDefinition()); + ResolveReference(classInstance->GetTypeRef()); + + for (auto *arg : classInstance->GetArguments()) { + ResolveReference(arg); + } + + if (classInstance->ClassDefinition() == nullptr) { + return; + } + + ResolveReference(classInstance->ClassDefinition()); +} + +void ETSBinder::BuildImportDeclaration(ir::ImportDeclaration *decl) +{ + auto *specifier = decl->Specifiers().front()->AsImportSpecifier(); + + if (!specifier->Imported()->IsTSQualifiedName()) { + return; + } + + auto *importName = specifier->Imported()->AsTSQualifiedName(); + util::StringView fullPath = importName->ToString(Allocator()); + + if (*fullPath.Utf8().rbegin() == Binder::STAR_IMPORT.front()) { + HandleStarImport(importName, fullPath); + return; + } + + util::StringView packageName = importName->BaseToString(Allocator()); + + if (packageName == Program()->GetPackageName()) { + return; + } + + util::StringView imported = importName->ResolveLeftMostQualifiedName()->Right()->Name(); + + auto res = globalRecordTable_.Program()->ExternalSources().find(packageName); + + if (res == globalRecordTable_.Program()->ExternalSources().end()) { + ThrowError(importName->Start(), "Cannot find package."); + } + + ASSERT(!res->second.empty()); + auto &packageNameSpace = res->second.front()->GlobalScope()->Bindings(); + auto bindingRes = packageNameSpace.find(imported); + + if (bindingRes == packageNameSpace.end()) { + ThrowError(importName->Start(), "Cannot find import"); + } + + auto *var = bindingRes->second; + util::StringView localName = imported; + + if (specifier->Local() != nullptr) { + localName = specifier->Local()->Name(); + } + + if (!TopScope()->Bindings().insert({localName, var}).second) { + ThrowError(importName->Start(), "Import already exists."); + } +} + +void ETSBinder::HandleStarImport(ir::TSQualifiedName *importName, util::StringView fullPath) +{ + // Cut ".*" + util::StringView packageName = fullPath.Substr(0, fullPath.Length() - 2); + + if (packageName == Program()->GetPackageName()) { + return; + } + + auto res = globalRecordTable_.Program()->ExternalSources().find(packageName); + + if (res == globalRecordTable_.Program()->ExternalSources().end()) { + ThrowError(importName->Start(), "Cannot find package."); + } + + ASSERT(!res->second.empty()); + auto *packageGlobalScope = res->second.front()->GlobalScope(); + + for (auto [bindingName, var] : packageGlobalScope->Bindings()) { + if (bindingName.Is(compiler::Signatures::ETS_GLOBAL)) { + auto *classDef = var->Declaration()->Node()->AsClassDeclaration()->Definition(); + ImportGlobalProperties(classDef); + continue; + } + + auto insRes = TopScope()->Bindings().insert({bindingName, var}); + if (!insRes.second) { + if (insRes.first->second != var) { + ThrowError(importName->Start(), "Import already exists."); + } + } + } +} + +void ETSBinder::ImportGlobalProperties(ir::ClassDefinition *classDef) +{ + auto scopeCtx = LexicalScope::Enter(this, classDef->Scope()->AsClassScope()); + + for (auto *prop : classDef->Body()) { + auto *classElement = prop->AsClassElement(); + + if (classElement->IsClassStaticBlock()) { + continue; + } + + ASSERT(classElement->IsStatic()); + auto &name = classElement->Id()->Name(); + auto *var = scopeCtx.GetScope()->FindLocal(name, ResolveBindingOptions::ALL); + ASSERT(var); + + auto insRes = TopScope()->Bindings().insert({name, var}); + if (!insRes.second) { + if (insRes.first->second != var) { + ThrowError(classElement->Id()->Start(), "Import already exists."); + } + } + } +} + +} // namespace panda::es2panda::binder diff --git a/binder/ETSBinder.h b/binder/ETSBinder.h new file mode 100644 index 0000000000000000000000000000000000000000..edcb922f6069e819ba74c484f34ea6491acea76a --- /dev/null +++ b/binder/ETSBinder.h @@ -0,0 +1,140 @@ +/** + * Copyright (c) 2021 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_BINDER_ETS_BINDER_H +#define ES2PANDA_BINDER_ETS_BINDER_H + +#include "plugins/ecmascript/es2panda/binder/TypedBinder.h" +#include "plugins/ecmascript/es2panda/binder/recordTable.h" + +namespace panda::es2panda::binder { +class ETSBinder : public TypedBinder { +public: + explicit ETSBinder(ArenaAllocator *allocator) + : TypedBinder(allocator), + globalRecordTable_(allocator, Program(), RecordTableFlags::NONE), + recordTable_(&globalRecordTable_), + externalRecordTable_(allocator->Adapter()), + defaultImports_(allocator->Adapter()) + { + InitImplicitThisParam(); + } + + NO_COPY_SEMANTIC(ETSBinder); + NO_MOVE_SEMANTIC(ETSBinder); + ~ETSBinder() = default; + + ScriptExtension Extension() const override + { + return ScriptExtension::ETS; + } + + ResolveBindingOptions BindingOptions() const override + { + return ResolveBindingOptions::BINDINGS; + } + + RecordTable *GetRecordTable() + { + return recordTable_; + } + + const RecordTable *GetRecordTable() const + { + return recordTable_; + } + + RecordTable *GetGlobalRecordTable() + { + return &globalRecordTable_; + } + + const RecordTable *GetGlobalRecordTable() const + { + return &globalRecordTable_; + } + + ArenaUnorderedMap &GetExternalRecordTable() + { + return externalRecordTable_; + } + + const ArenaUnorderedMap &GetExternalRecordTable() const + { + return externalRecordTable_; + } + + void HandleCustomNodes(ir::AstNode *childNode) override; + + void IdentifierAnalysis() override; + void BuildClassDefinition(ir::ClassDefinition *classDef) override; + void BuildClassProperty(const ir::ClassProperty *prop) override; + void LookupIdentReference(ir::Identifier *ident) override; + bool BuildInternalName(ir::ScriptFunction *scriptFunc) override; + + void LookupTypeReference(ir::Identifier *ident); + void LookupTypeArgumentReferences(ir::ETSTypeReference *typeRef); + void BuildInterfaceDeclaration(ir::TSInterfaceDeclaration *decl); + void BuildMemberExpression(ir::MemberExpression *memberExpr); + void BuildMethodDefinition(ir::MethodDefinition *methodDef); + void BuildImportDeclaration(ir::ImportDeclaration *decl); + void BuildETSNewClassInstanceExpression(ir::ETSNewClassInstanceExpression *classInstance); + void BuildMethodReferenceExpression(ir::ETSMethodReferenceExpression *methodRef); + void BuildFunctionType(ir::ETSFunctionType *funcType); + + void ResolveInterfaceDeclaration(ir::TSInterfaceDeclaration *decl); + void ResolveMethodDefinition(ir::MethodDefinition *methodDef); + LocalScope *ResolvePropertyReference(ir::ClassProperty *prop, ClassScope *scope); + void ResolveEnumDeclaration(ir::TSEnumDeclaration *enumDecl); + void InitializeInterfaceIdent(ir::TSInterfaceDeclaration *decl); + void BuildExternalProgram(parser::Program *extProgram); + void BuildProgram(); + + void BuildFunctionName(const ir::ScriptFunction *func) const; + void AddLambdaFunctionThisParam(ir::ScriptFunction *func); + void AddInvokeFunctionThisParam(ir::ScriptFunction *func); + void BuildLambdaObjectName(ir::ETSMethodReferenceExpression *methodRef); + void FormLambdaName(util::UString &name, const util::StringView &signature, bool addSeparator = true); + void BuildFunctionalInterfaceName(ir::ETSFunctionType *funcType); + + void SetDefaultImports(ArenaVector defaultImports) + { + defaultImports_ = std::move(defaultImports); + } + + static constexpr std::string_view DEFAULT_IMPORT_SOURCE_FILE = ".ets"; + static constexpr std::string_view DEFAULT_IMPORT_SOURCE = R"( +import std.containers.*; +import std.core.*; +import std.math.*; +import std.time.*; +)"; + +private: + void BuildClassDefinitionImpl(ir::ClassDefinition *classDef); + void InitImplicitThisParam(); + void HandleStarImport(ir::TSQualifiedName *importName, util::StringView fullPath); + void ImportGlobalProperties(ir::ClassDefinition *classDef); + + RecordTable globalRecordTable_; + RecordTable *recordTable_; + ArenaUnorderedMap externalRecordTable_; + ArenaVector defaultImports_; + ir::Identifier *thisParam_ {}; +}; + +} // namespace panda::es2panda::binder + +#endif diff --git a/binder/JSBinder.cpp b/binder/JSBinder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1e1ead59219b90db78133fad3421ceccaaf4bb64 --- /dev/null +++ b/binder/JSBinder.cpp @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2021 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 "JSBinder.h" + +namespace panda::es2panda::binder { +} // namespace panda::es2panda::binder diff --git a/binder/JSBinder.h b/binder/JSBinder.h new file mode 100644 index 0000000000000000000000000000000000000000..af3178f4fbccaf2d1330a058d0febb4911288d1e --- /dev/null +++ b/binder/JSBinder.h @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2021 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_BINDER_JS_BINDER_H +#define ES2PANDA_BINDER_JS_BINDER_H + +#include "plugins/ecmascript/es2panda/binder/binder.h" + +namespace panda::es2panda::binder { +class JSBinder : public Binder { +public: + explicit JSBinder(ArenaAllocator *allocator) : Binder(allocator) {} + + NO_COPY_SEMANTIC(JSBinder); + NO_MOVE_SEMANTIC(JSBinder); + ~JSBinder() = default; + +private: +}; +} // namespace panda::es2panda::binder + +#endif diff --git a/binder/TSBinder.cpp b/binder/TSBinder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c33d7d0ca6f55160ab73d04845703d0295e57ef7 --- /dev/null +++ b/binder/TSBinder.cpp @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2021 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 "TSBinder.h" + +namespace panda::es2panda::binder { +} // namespace panda::es2panda::binder diff --git a/binder/TSBinder.h b/binder/TSBinder.h new file mode 100644 index 0000000000000000000000000000000000000000..30b11582990cd14984f62e002bbe0bd89ba0909c --- /dev/null +++ b/binder/TSBinder.h @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2021 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_BINDER_TS_BINDER_H +#define ES2PANDA_BINDER_TS_BINDER_H + +#include "plugins/ecmascript/es2panda/binder/TypedBinder.h" + +namespace panda::es2panda::binder { +class TSBinder : public TypedBinder { +public: + explicit TSBinder(ArenaAllocator *allocator) : TypedBinder(allocator) {} + + NO_COPY_SEMANTIC(TSBinder); + NO_MOVE_SEMANTIC(TSBinder); + ~TSBinder() = default; + + ScriptExtension Extension() const override + { + return ScriptExtension::TS; + } + + ResolveBindingOptions BindingOptions() const override + { + return ResolveBindingOptions::ALL; + } + +protected: +}; +} // namespace panda::es2panda::binder + +#endif diff --git a/binder/TypedBinder.cpp b/binder/TypedBinder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..97078df08e8b0f4addd235029cd18f2d0d3a5c02 --- /dev/null +++ b/binder/TypedBinder.cpp @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2021 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 "TypedBinder.h" +#include "plugins/ecmascript/es2panda/ir/base/tsSignatureDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/base/tsMethodSignature.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsFunctionType.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsConstructorType.h" + +namespace panda::es2panda::binder { + +void TypedBinder::BuildSignatureDeclarationBaseParams(ir::AstNode *typeNode) +{ + if (typeNode == nullptr) { + return; + } + + Scope *scope = nullptr; + + switch (typeNode->Type()) { + case ir::AstNodeType::TS_FUNCTION_TYPE: { + scope = typeNode->AsTSFunctionType()->Scope(); + break; + } + case ir::AstNodeType::TS_CONSTRUCTOR_TYPE: { + scope = typeNode->AsTSConstructorType()->Scope(); + break; + } + case ir::AstNodeType::TS_SIGNATURE_DECLARATION: { + scope = typeNode->AsTSSignatureDeclaration()->Scope(); + break; + } + case ir::AstNodeType::TS_METHOD_SIGNATURE: { + scope = typeNode->AsTSMethodSignature()->Scope(); + break; + } + default: { + ResolveReference(typeNode); + return; + } + } + + ASSERT(scope && scope->IsFunctionParamScope()); + + auto scopeCtx = LexicalScope::Enter(this, scope->AsFunctionParamScope()); + ResolveReferences(typeNode); +} + +void TypedBinder::HandleCustomNodes(ir::AstNode *childNode) +{ + switch (childNode->Type()) { + case ir::AstNodeType::TS_FUNCTION_TYPE: + case ir::AstNodeType::TS_CONSTRUCTOR_TYPE: + case ir::AstNodeType::TS_METHOD_SIGNATURE: + case ir::AstNodeType::TS_SIGNATURE_DECLARATION: { + BuildSignatureDeclarationBaseParams(childNode); + break; + } + default: { + ResolveReferences(childNode); + break; + } + } +} +} // namespace panda::es2panda::binder diff --git a/binder/TypedBinder.h b/binder/TypedBinder.h new file mode 100644 index 0000000000000000000000000000000000000000..468e611eb0de76c4017d6e565405955a49bd405c --- /dev/null +++ b/binder/TypedBinder.h @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2021 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_BINDER_TYPED_BINDER_H +#define ES2PANDA_BINDER_TYPED_BINDER_H + +#include "plugins/ecmascript/es2panda/binder/binder.h" + +namespace panda::es2panda::binder { +class TypedBinder : public Binder { +public: + explicit TypedBinder(ArenaAllocator *allocator) : Binder(allocator) {} + + NO_COPY_SEMANTIC(TypedBinder); + NO_MOVE_SEMANTIC(TypedBinder); + ~TypedBinder() = default; + +protected: + void HandleCustomNodes(ir::AstNode *childNode) override; + void BuildSignatureDeclarationBaseParams([[maybe_unused]] ir::AstNode *typeNode) override; +}; +} // namespace panda::es2panda::binder + +#endif diff --git a/binder/binder.cpp b/binder/binder.cpp index 682ac0684997b8beac4ad591ad58846268574111..07bb950a379888c7b4ff6e49161288a837b651d7 100644 --- a/binder/binder.cpp +++ b/binder/binder.cpp @@ -16,6 +16,7 @@ #include "binder.h" #include "plugins/ecmascript/es2panda/binder/privateBinding.h" +#include "plugins/ecmascript/es2panda/parser/program/program.h" #include "plugins/ecmascript/es2panda/util/helpers.h" #include "plugins/ecmascript/es2panda/binder/scope.h" #include "plugins/ecmascript/es2panda/binder/tsBinding.h" @@ -24,12 +25,15 @@ #include "plugins/ecmascript/es2panda/ir/astNode.h" #include "plugins/ecmascript/es2panda/ir/base/catchClause.h" #include "plugins/ecmascript/es2panda/ir/base/classDefinition.h" +#include "plugins/ecmascript/es2panda/ir/base/classProperty.h" +#include "plugins/ecmascript/es2panda/ir/base/classStaticBlock.h" #include "plugins/ecmascript/es2panda/ir/base/methodDefinition.h" #include "plugins/ecmascript/es2panda/ir/base/property.h" #include "plugins/ecmascript/es2panda/ir/base/scriptFunction.h" #include "plugins/ecmascript/es2panda/ir/base/spreadElement.h" #include "plugins/ecmascript/es2panda/ir/expressions/arrayExpression.h" #include "plugins/ecmascript/es2panda/ir/expressions/assignmentExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/memberExpression.h" #include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" #include "plugins/ecmascript/es2panda/ir/expressions/objectExpression.h" #include "plugins/ecmascript/es2panda/ir/statements/blockStatement.h" @@ -42,10 +46,17 @@ #include "plugins/ecmascript/es2panda/ir/statements/variableDeclaration.h" #include "plugins/ecmascript/es2panda/ir/statements/variableDeclarator.h" #include "plugins/ecmascript/es2panda/ir/statements/whileStatement.h" +#include "plugins/ecmascript/es2panda/ir/module/exportNamedDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/module/importDeclaration.h" #include "plugins/ecmascript/es2panda/ir/ts/tsFunctionType.h" #include "plugins/ecmascript/es2panda/ir/ts/tsConstructorType.h" -#include "plugins/ecmascript/es2panda/ir/ts/tsSignatureDeclaration.h" -#include "plugins/ecmascript/es2panda/ir/ts/tsMethodSignature.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsTypeParameterDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsTypeReference.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsNewClassInstanceExpression.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsTypeReference.h" +#include "plugins/ecmascript/es2panda/ir/base/tsSignatureDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/base/tsMethodSignature.h" namespace panda::es2panda::binder { void Binder::InitTopScope() @@ -60,36 +71,67 @@ void Binder::InitTopScope() varScope_ = topScope_; } -ParameterDecl *Binder::AddParamDecl(const ir::AstNode *param) +std::tuple Binder::AddParamDecl(ir::AstNode *param) { ASSERT(scope_->IsFunctionParamScope() || scope_->IsCatchParamScope()); - auto [decl, node] = static_cast(scope_)->AddParamDecl(Allocator(), param); + auto [decl, node, var] = static_cast(scope_)->AddParamDecl(Allocator(), param); if (node == nullptr) { - return decl; + return {decl, var}; } ThrowRedeclaration(node->Start(), decl->Name()); } -void Binder::ThrowRedeclaration(const lexer::SourcePosition &pos, const util::StringView &name) +void Binder::ThrowRedeclaration(const lexer::SourcePosition &pos, const util::StringView &name) const { - lexer::LineIndex index(program_->SourceCode()); - lexer::SourceLocation loc = index.GetLocation(pos); - std::stringstream ss; ss << "Variable '" << name << "' has already been declared."; - throw Error(ErrorType::SYNTAX, ss.str(), loc.line, loc.col); + ThrowError(pos, ss.str()); +} + +void Binder::ThrowUnresolvableVariable(const lexer::SourcePosition &pos, const util::StringView &name) const +{ + std::stringstream ss; + ss << "Cannot find variable '" << name << "'."; + ThrowError(pos, ss.str()); +} + +void Binder::ThrowUnresolvableType(const lexer::SourcePosition &pos, const util::StringView &name) const +{ + std::stringstream ss; + ss << "Cannot find type '" << name << "'."; + ThrowError(pos, ss.str()); +} + +void Binder::ThrowTDZ(const lexer::SourcePosition &pos, const util::StringView &name) const +{ + std::stringstream ss; + ss << "Variable '" << name << "' is accessed before it's initialization."; + ThrowError(pos, ss.str()); +} + +void Binder::ThrowInvalidCapture(const lexer::SourcePosition &pos, const util::StringView &name) const +{ + std::stringstream ss; + ss << "Cannot capture variable'" << name << "'."; + ThrowError(pos, ss.str()); } void Binder::ThrowPrivateFieldMismatch(const lexer::SourcePosition &pos, const util::StringView &name) const +{ + std::stringstream ss; + ss << "Private field '" << name << "' must be declared in an enclosing class"; + + ThrowError(pos, ss.str()); +} + +void Binder::ThrowError(const lexer::SourcePosition &pos, const std::string_view &msg) const { lexer::LineIndex index(program_->SourceCode()); lexer::SourceLocation loc = index.GetLocation(pos); - std::stringstream ss; - ss << "Private field '" << name << "' must be declared in an enclosing class"; - throw Error(ErrorType::SYNTAX, ss.str(), loc.line, loc.col); + throw Error(ErrorType::SYNTAX, program_->SourceFile().Utf8(), msg, loc.line, loc.col); } void Binder::IdentifierAnalysis() @@ -98,11 +140,10 @@ void Binder::IdentifierAnalysis() ASSERT(scope_ == topScope_); ASSERT(varScope_ == topScope_); - if (program_->Extension() == ScriptExtension::AS) { - return; - } + functionScopes_.push_back(topScope_); + topScope_->BindName(MAIN); + topScope_->BindInternalName(BuildFunctionName(MAIN, 0)); - BuildFunction(topScope_, "main"); topScope_->CheckDirectEval(compilerCtx_); ResolveReferences(program_->Ast()); @@ -150,7 +191,7 @@ void Binder::InstantiateArguments() scope->Bindings().insert({argumentsVariable->Name(), argumentsVariable}); } - scope->AsVariableScope()->AddFlag(VariableScopeFlags::USE_ARGS); + scope->AddFlag(ScopeFlags::USE_ARGS); break; } @@ -167,7 +208,7 @@ void Binder::PropagateDirectEval() const VariableScope *scope = iter->IsFunctionParamScope() ? iter->AsFunctionParamScope()->GetFunctionScope() : iter->EnclosingVariableScope(); - scope->AddFlag(VariableScopeFlags::NO_REG_STORE); + scope->AddFlag(ScopeFlags::NO_REG_STORE); iter = iter->Parent(); } while (iter != nullptr); } @@ -197,16 +238,20 @@ void Binder::InstantiatePrivateContext(const ir::Identifier *ident) const void Binder::LookupIdentReference(ir::Identifier *ident) { + if (!ident->IsReference()) { + return; + } + if (ident->Name().Is(FUNCTION_ARGUMENTS)) { InstantiateArguments(); } - if (ident->IsPrivate()) { + if (ident->IsPrivateIdent()) { InstantiatePrivateContext(ident); return; } - ScopeFindResult res = scope_->Find(ident->Name(), bindingOptions_); + ScopeFindResult res = scope_->Find(ident->Name(), BindingOptions()); if (res.level != 0) { ASSERT(res.variable); res.variable->SetLexical(res.scope); @@ -223,32 +268,29 @@ void Binder::LookupIdentReference(ir::Identifier *ident) ident->SetVariable(res.variable); } -void Binder::BuildFunction(FunctionScope *funcScope, util::StringView name) +util::StringView Binder::BuildFunctionName(util::StringView name, uint32_t idx) { - uint32_t idx = functionScopes_.size(); - functionScopes_.push_back(funcScope); - std::stringstream ss; ss << "func_" << name << "_" << std::to_string(idx); util::UString internalName(ss.str(), Allocator()); - funcScope->BindName(name, internalName.View()); + return internalName.View(); } -void Binder::BuildScriptFunction(Scope *outerScope, const ir::ScriptFunction *scriptFunc) +bool Binder::BuildInternalName(ir::ScriptFunction *scriptFunc) { - if (scriptFunc->IsArrow()) { - VariableScope *outerVarScope = outerScope->EnclosingVariableScope(); - outerVarScope->AddFlag(VariableScopeFlags::INNER_ARROW); - } + auto *funcScope = scriptFunc->Scope(); + auto name = util::Helpers::FunctionName(Allocator(), scriptFunc); - BuildFunction(scope_->AsFunctionScope(), util::Helpers::FunctionName(Allocator(), scriptFunc)); + uint32_t idx = functionScopes_.size(); + funcScope->BindName(name); + funcScope->BindInternalName(BuildFunctionName(name, idx)); + + return !scriptFunc->IsOverload(); } -void Binder::BuildVarDeclaratorId(const ir::AstNode *parent, ir::AstNode *childNode) +void Binder::BuildVarDeclaratorId(ir::AstNode *childNode) { - childNode->SetParent(parent); - switch (childNode->Type()) { case ir::AstNodeType::IDENTIFIER: { auto *ident = childNode->AsIdentifier(); @@ -259,12 +301,8 @@ void Binder::BuildVarDeclaratorId(const ir::AstNode *parent, ir::AstNode *childN } auto *variable = scope_->FindLocal(name); - - if (Program()->Extension() == ScriptExtension::TS) { - ident->SetVariable(variable); - BuildTSSignatureDeclarationBaseParams(ident->TypeAnnotation()); - } - + ident->SetVariable(variable); + BuildSignatureDeclarationBaseParams(ident->TypeAnnotation()); variable->AddFlag(VariableFlags::INITIALIZED); break; } @@ -272,34 +310,34 @@ void Binder::BuildVarDeclaratorId(const ir::AstNode *parent, ir::AstNode *childN auto *objPattern = childNode->AsObjectPattern(); for (auto *prop : objPattern->Properties()) { - BuildVarDeclaratorId(childNode, prop); + BuildVarDeclaratorId(prop); } - BuildTSSignatureDeclarationBaseParams(objPattern->TypeAnnotation()); + BuildSignatureDeclarationBaseParams(objPattern->TypeAnnotation()); break; } case ir::AstNodeType::ARRAY_PATTERN: { auto *arrayPattern = childNode->AsArrayPattern(); for (auto *element : childNode->AsArrayPattern()->Elements()) { - BuildVarDeclaratorId(childNode, element); + BuildVarDeclaratorId(element); } - BuildTSSignatureDeclarationBaseParams(arrayPattern->TypeAnnotation()); + BuildSignatureDeclarationBaseParams(arrayPattern->TypeAnnotation()); break; } case ir::AstNodeType::ASSIGNMENT_PATTERN: { - ResolveReference(childNode, childNode->AsAssignmentPattern()->Right()); - BuildVarDeclaratorId(childNode, childNode->AsAssignmentPattern()->Left()); + ResolveReference(childNode->AsAssignmentPattern()->Right()); + BuildVarDeclaratorId(childNode->AsAssignmentPattern()->Left()); break; } case ir::AstNodeType::PROPERTY: { - ResolveReference(childNode, childNode->AsProperty()->Key()); - BuildVarDeclaratorId(childNode, childNode->AsProperty()->Value()); + ResolveReference(childNode->AsProperty()->Key()); + BuildVarDeclaratorId(childNode->AsProperty()->Value()); break; } case ir::AstNodeType::REST_ELEMENT: { - BuildVarDeclaratorId(childNode, childNode->AsRestElement()->Argument()); + BuildVarDeclaratorId(childNode->AsRestElement()->Argument()); break; } default: @@ -307,86 +345,67 @@ void Binder::BuildVarDeclaratorId(const ir::AstNode *parent, ir::AstNode *childN } } -void Binder::BuildTSSignatureDeclarationBaseParams(const ir::AstNode *typeNode) +void Binder::BuildVarDeclarator(ir::VariableDeclarator *varDecl) { - if (typeNode == nullptr) { + if (varDecl->Parent()->AsVariableDeclaration()->Kind() == ir::VariableDeclaration::VariableDeclarationKind::VAR) { + ResolveReferences(varDecl); return; } - Scope *scope = nullptr; - - switch (typeNode->Type()) { - case ir::AstNodeType::TS_FUNCTION_TYPE: { - scope = typeNode->AsTSFunctionType()->Scope(); - break; - } - case ir::AstNodeType::TS_CONSTRUCTOR_TYPE: { - scope = typeNode->AsTSConstructorType()->Scope(); - break; - } - case ir::AstNodeType::TS_SIGNATURE_DECLARATION: { - scope = typeNode->AsTSSignatureDeclaration()->Scope(); - break; - } - case ir::AstNodeType::TS_METHOD_SIGNATURE: { - scope = typeNode->AsTSMethodSignature()->Scope(); - break; - } - default: { - ResolveReferences(typeNode); - return; - } + if (varDecl->Init()) { + ResolveReference(varDecl->Init()); } - ASSERT(scope && scope->IsFunctionParamScope()); + BuildVarDeclaratorId(varDecl->Id()); +} + +void Binder::BuildClassProperty(const ir::ClassProperty *prop) +{ + const ir::ScriptFunction *ctor = util::Helpers::GetContainingConstructor(prop); + auto scopeCtx = LexicalScope::Enter(this, ctor->Scope()); - auto scopeCtx = LexicalScope::Enter(this, scope->AsFunctionParamScope()); - ResolveReferences(typeNode); + ResolveReferences(prop); } -void Binder::BuildVarDeclarator(ir::VariableDeclarator *varDecl) +void Binder::InitializeClassBinding(ir::ClassDefinition *classDef) { - if (varDecl->Parent()->AsVariableDeclaration()->Kind() == ir::VariableDeclaration::VariableDeclarationKind::VAR) { - ResolveReferences(varDecl); - return; - } + ScopeFindResult res = scope_->Find(classDef->Ident()->Name()); - if (varDecl->Init() != nullptr) { - ResolveReference(varDecl, varDecl->Init()); - } + ASSERT(res.variable && res.variable->Declaration()->IsLetDecl()); + res.variable->AddFlag(VariableFlags::INITIALIZED); +} + +void Binder::InitializeClassIdent(ir::ClassDefinition *classDef) +{ + ScopeFindResult res = scope_->Find(classDef->Ident()->Name()); - BuildVarDeclaratorId(varDecl, varDecl->Id()); + ASSERT(res.variable && res.variable->Declaration()->IsConstDecl()); + res.variable->AddFlag(VariableFlags::INITIALIZED); } void Binder::BuildClassDefinition(ir::ClassDefinition *classDef) { if (classDef->Parent()->IsClassDeclaration()) { - ScopeFindResult res = scope_->Find(classDef->Ident()->Name()); - - ASSERT(res.variable && res.variable->Declaration()->IsLetDecl()); - res.variable->AddFlag(VariableFlags::INITIALIZED); + InitializeClassBinding(classDef); } auto scopeCtx = LexicalScope::Enter(this, classDef->Scope()); if (classDef->Super() != nullptr) { - ResolveReference(classDef, classDef->Super()); + ResolveReference(classDef->Super()); } Variable *variable = scope_->FindLocal(classDef->PrivateId()); variable->AddFlag(VariableFlags::INITIALIZED); if (classDef->Ident() != nullptr) { - ScopeFindResult res = scope_->Find(classDef->Ident()->Name()); - - ASSERT(res.variable && res.variable->Declaration()->IsConstDecl()); - res.variable->AddFlag(VariableFlags::INITIALIZED); + InitializeClassIdent(classDef); } - ResolveReference(classDef, classDef->Ctor()); + ResolveReference(classDef->Ctor()); for (auto *stmt : classDef->Body()) { - ResolveReference(classDef, stmt); + ResolveReference(stmt); } } @@ -397,35 +416,35 @@ void Binder::BuildForUpdateLoop(ir::ForUpdateStatement *forUpdateStmt) auto declScopeCtx = LexicalScope::Enter(this, loopScope->DeclScope()); if (forUpdateStmt->Init() != nullptr) { - ResolveReference(forUpdateStmt, forUpdateStmt->Init()); + ResolveReference(forUpdateStmt->Init()); } if (forUpdateStmt->Update() != nullptr) { - ResolveReference(forUpdateStmt, forUpdateStmt->Update()); + ResolveReference(forUpdateStmt->Update()); } auto loopCtx = LexicalScope::Enter(this, loopScope); if (forUpdateStmt->Test() != nullptr) { - ResolveReference(forUpdateStmt, forUpdateStmt->Test()); + ResolveReference(forUpdateStmt->Test()); } - ResolveReference(forUpdateStmt, forUpdateStmt->Body()); + ResolveReference(forUpdateStmt->Body()); loopCtx.GetScope()->ConvertToVariableScope(Allocator()); } -void Binder::BuildForInOfLoop(const ir::Statement *parent, binder::LoopScope *loopScope, ir::AstNode *left, - ir::Expression *right, ir::Statement *body) +void Binder::BuildForInOfLoop(binder::LoopScope *loopScope, ir::AstNode *left, ir::Expression *right, + ir::Statement *body) { auto declScopeCtx = LexicalScope::Enter(this, loopScope->DeclScope()); - ResolveReference(parent, right); - ResolveReference(parent, left); + ResolveReference(right); + ResolveReference(left); auto loopCtx = LexicalScope::Enter(this, loopScope); - ResolveReference(parent, body); + ResolveReference(body); loopCtx.GetScope()->ConvertToVariableScope(Allocator()); } @@ -433,81 +452,98 @@ void Binder::BuildCatchClause(ir::CatchClause *catchClauseStmt) { if (catchClauseStmt->Param() != nullptr) { auto paramScopeCtx = LexicalScope::Enter(this, catchClauseStmt->Scope()->ParamScope()); - ResolveReference(catchClauseStmt, catchClauseStmt->Param()); + ResolveReference(catchClauseStmt->Param()); } auto scopeCtx = LexicalScope::Enter(this, catchClauseStmt->Scope()); - ResolveReference(catchClauseStmt, catchClauseStmt->Body()); + ResolveReference(catchClauseStmt->Body()); +} + +void Binder::AddCompilableFunction(ir::ScriptFunction *func) +{ + if (func->IsArrow()) { + VariableScope *outerVarScope = scope_->EnclosingVariableScope(); + outerVarScope->AddFlag(ScopeFlags::INNER_ARROW); + } + + AddCompilableFunctionScope(func->Scope()); +} + +void Binder::AddCompilableFunctionScope(binder::FunctionScope *funcScope) +{ + functionScopes_.push_back(funcScope); } -void Binder::ResolveReference(const ir::AstNode *parent, ir::AstNode *childNode) +void Binder::VisitScriptFunction(ir::ScriptFunction *func) { - childNode->SetParent(parent); + auto *funcScope = func->Scope(); + { + auto paramScopeCtx = LexicalScope::Enter(this, funcScope->ParamScope()); + + for (auto *param : func->Params()) { + ResolveReference(param); + } + } + + if (func->ReturnTypeAnnotation()) { + ResolveReference(func->ReturnTypeAnnotation()); + } + + if (!BuildInternalName(func)) { + return; + } + + AddCompilableFunction(func); + + auto scopeCtx = LexicalScope::Enter(this, funcScope); + + if (func->Body()) { + ResolveReference(func->Body()); + } +} + +void Binder::VisitScriptFunctionWithPotentialTypeParams(ir::ScriptFunction *func) +{ + if (func->TypeParams()) { + auto typeParamScopeCtx = LexicalScope::Enter(this, func->TypeParams()->Scope()); + VisitScriptFunction(func); + return; + } + + VisitScriptFunction(func); +} + +void Binder::ResolveReference(ir::AstNode *childNode) +{ switch (childNode->Type()) { case ir::AstNodeType::IDENTIFIER: { auto *ident = childNode->AsIdentifier(); - if (ident->IsReference()) { - LookupIdentReference(ident); - } - + LookupIdentReference(ident); ResolveReferences(childNode); break; } case ir::AstNodeType::SUPER_EXPRESSION: { VariableScope *varScope = scope_->EnclosingVariableScope(); - varScope->AddFlag(VariableScopeFlags::USE_SUPER); - + varScope->AddFlag(ScopeFlags::USE_SUPER); ResolveReferences(childNode); break; } case ir::AstNodeType::SCRIPT_FUNCTION: { - auto *scriptFunc = childNode->AsScriptFunction(); - auto *funcScope = scriptFunc->Scope(); - - auto *outerScope = scope_; - - { - auto paramScopeCtx = LexicalScope::Enter(this, funcScope->ParamScope()); - - for (auto *param : scriptFunc->Params()) { - ResolveReference(scriptFunc, param); - } - } - - if (Program()->Extension() == ScriptExtension::TS) { - if (scriptFunc->ReturnTypeAnnotation() != nullptr) { - ResolveReference(scriptFunc, scriptFunc->ReturnTypeAnnotation()); - } - - if (scriptFunc->IsOverload()) { - break; - } - } - - auto scopeCtx = LexicalScope::Enter(this, funcScope); - - BuildScriptFunction(outerScope, scriptFunc); - - ResolveReference(scriptFunc, scriptFunc->Body()); + VisitScriptFunctionWithPotentialTypeParams(childNode->AsScriptFunction()); break; } case ir::AstNodeType::VARIABLE_DECLARATOR: { BuildVarDeclarator(childNode->AsVariableDeclarator()); - break; } case ir::AstNodeType::CLASS_DEFINITION: { BuildClassDefinition(childNode->AsClassDefinition()); - break; } case ir::AstNodeType::CLASS_PROPERTY: { - const ir::ScriptFunction *ctor = util::Helpers::GetContainingConstructor(childNode->AsClassProperty()); - auto scopeCtx = LexicalScope::Enter(this, ctor->Scope()); - - ResolveReferences(childNode); + BuildClassProperty(childNode->AsClassProperty()); break; } case ir::AstNodeType::BLOCK_STATEMENT: { @@ -527,18 +563,18 @@ void Binder::ResolveReference(const ir::AstNode *parent, ir::AstNode *childNode) { auto loopScopeCtx = LexicalScope::Enter(this, doWhileStatement->Scope()); - ResolveReference(doWhileStatement, doWhileStatement->Body()); + ResolveReference(doWhileStatement->Body()); } - ResolveReference(doWhileStatement, doWhileStatement->Test()); + ResolveReference(doWhileStatement->Test()); break; } case ir::AstNodeType::WHILE_STATEMENT: { auto *whileStatement = childNode->AsWhileStatement(); - ResolveReference(whileStatement, whileStatement->Test()); + ResolveReference(whileStatement->Test()); auto loopScopeCtx = LexicalScope::Enter(this, whileStatement->Scope()); - ResolveReference(whileStatement, whileStatement->Body()); + ResolveReference(whileStatement->Body()); break; } @@ -548,36 +584,29 @@ void Binder::ResolveReference(const ir::AstNode *parent, ir::AstNode *childNode) } case ir::AstNodeType::FOR_IN_STATEMENT: { auto *forInStmt = childNode->AsForInStatement(); - BuildForInOfLoop(forInStmt, forInStmt->Scope(), forInStmt->Left(), forInStmt->Right(), forInStmt->Body()); + BuildForInOfLoop(forInStmt->Scope(), forInStmt->Left(), forInStmt->Right(), forInStmt->Body()); break; } case ir::AstNodeType::FOR_OF_STATEMENT: { auto *forOfStmt = childNode->AsForOfStatement(); - BuildForInOfLoop(forOfStmt, forOfStmt->Scope(), forOfStmt->Left(), forOfStmt->Right(), forOfStmt->Body()); + BuildForInOfLoop(forOfStmt->Scope(), forOfStmt->Left(), forOfStmt->Right(), forOfStmt->Body()); break; } case ir::AstNodeType::CATCH_CLAUSE: { BuildCatchClause(childNode->AsCatchClause()); break; } - // TypeScript specific part - case ir::AstNodeType::TS_FUNCTION_TYPE: - case ir::AstNodeType::TS_CONSTRUCTOR_TYPE: - case ir::AstNodeType::TS_METHOD_SIGNATURE: - case ir::AstNodeType::TS_SIGNATURE_DECLARATION: { - BuildTSSignatureDeclarationBaseParams(childNode); - break; - } default: { - ResolveReferences(childNode); + HandleCustomNodes(childNode); break; } } } + void Binder::ResolveReferences(const ir::AstNode *parent) { - parent->Iterate([this, parent](auto *childNode) { ResolveReference(parent, childNode); }); + parent->Iterate([this](auto *childNode) { ResolveReference(childNode); }); } LocalVariable *Binder::AddMandatoryParam(const std::string_view &name) @@ -601,7 +630,7 @@ void Binder::LookUpMandatoryReferences(const FunctionScope *funcScope, bool need LookupReference(MANDATORY_PARAM_NEW_TARGET); LookupReference(MANDATORY_PARAM_THIS); - if (funcScope->HasFlag(VariableScopeFlags::USE_ARGS)) { + if (funcScope->HasFlag(ScopeFlags::USE_ARGS)) { LookupReference(FUNCTION_ARGUMENTS); } @@ -649,9 +678,9 @@ void Binder::AddMandatoryParams() bool lexicalFunctionObject {}; if (ctor != nullptr && util::Helpers::GetClassDefiniton(ctor)->Super() != nullptr && - funcScope->HasFlag(VariableScopeFlags::USE_SUPER)) { - ASSERT(ctor->Scope()->HasFlag(VariableScopeFlags::INNER_ARROW)); - ctor->Scope()->AddFlag(VariableScopeFlags::SET_LEXICAL_FUNCTION); + funcScope->HasFlag(ScopeFlags::USE_SUPER)) { + ASSERT(ctor->Scope()->HasFlag(ScopeFlags::INNER_ARROW)); + ctor->Scope()->AddFlag(ScopeFlags::SET_LEXICAL_FUNCTION); lexicalFunctionObject = true; AddMandatoryParams(CTOR_ARROW_MANDATORY_PARAMS); } else { diff --git a/binder/binder.h b/binder/binder.h index 2f67336d1130a1a975d1fefdd3f5fa055d1919b4..08adfded969a25c8a576ea447da953324fcb8116 100644 --- a/binder/binder.h +++ b/binder/binder.h @@ -20,7 +20,11 @@ #include "plugins/ecmascript/es2panda/binder/variableFlags.h" #include "plugins/ecmascript/es2panda/lexer/token/sourceLocation.h" #include "macros.h" -#include "plugins/ecmascript/es2panda/parser/program/program.h" + +namespace panda::es2panda::parser { +class Program; +enum class ScriptKind; +} // namespace panda::es2panda::parser namespace panda::es2panda::ir { class AstNode; @@ -34,28 +38,28 @@ class ScriptFunction; class Statement; class VariableDeclarator; class TSFunctionType; +class ThisExpression; +class MemberExpression; +class ClassStaticBlock; } // namespace panda::es2panda::ir namespace panda::es2panda::binder { class Binder { public: - explicit Binder(parser::Program *program, ScriptExtension extension) - : program_(program), functionScopes_(Allocator()->Adapter()) + explicit Binder(ArenaAllocator *allocator) + : allocator_(allocator), + functionScopes_(allocator_->Adapter()), + lambdaObjects_(allocator_->Adapter()), + functionalInterfaces_(allocator_->Adapter()) { - if (extension == ScriptExtension::TS) { - bindingOptions_ = ResolveBindingOptions::ALL; - return; - } - - bindingOptions_ = ResolveBindingOptions::BINDINGS; } NO_COPY_SEMANTIC(Binder); - DEFAULT_MOVE_SEMANTIC(Binder); + NO_MOVE_SEMANTIC(Binder); ~Binder() = default; void InitTopScope(); - void IdentifierAnalysis(); + virtual void IdentifierAnalysis(); template T *AddDecl(const lexer::SourcePosition &pos, Args &&...args); @@ -63,7 +67,26 @@ public: template T *AddTsDecl(const lexer::SourcePosition &pos, Args &&...args); - ParameterDecl *AddParamDecl(const ir::AstNode *param); + template + std::tuple NewVarDecl(const lexer::SourcePosition &pos, Args &&...args); + + std::tuple AddParamDecl(ir::AstNode *param); + + void SetProgram(parser::Program *program) + { + program_ = program; + } + + parser::Program *Program() + { + return program_; + } + + const parser::Program *Program() const + { + ASSERT(program_); + return program_; + } void SetCompilerContext(compiler::CompilerContext *compilerContext) { @@ -77,17 +100,46 @@ public: return compilerCtx_; } + void SetGenStdLib(bool genStdLib) + { + genStdLib_ = genStdLib; + } + + bool IsGenStdLib() + { + return genStdLib_; + } + Scope *GetScope() const { return scope_; } + void ResetTopScope(GlobalScope *topScope) + { + ASSERT(topScope_ == scope_); + topScope_ = topScope; + varScope_ = topScope_; + scope_ = topScope_; + } + GlobalScope *TopScope() const { return topScope_; } - [[noreturn]] void ThrowRedeclaration(const lexer::SourcePosition &pos, const util::StringView &name); + VariableScope *VarScope() const + { + return varScope_; + } + + [[noreturn]] void ThrowPrivateFieldMismatch(const lexer::SourcePosition &pos, const util::StringView &name) const; + [[noreturn]] void ThrowRedeclaration(const lexer::SourcePosition &pos, const util::StringView &name) const; + [[noreturn]] void ThrowUnresolvableVariable(const lexer::SourcePosition &pos, const util::StringView &name) const; + [[noreturn]] void ThrowUnresolvableType(const lexer::SourcePosition &pos, const util::StringView &name) const; + [[noreturn]] void ThrowTDZ(const lexer::SourcePosition &pos, const util::StringView &name) const; + [[noreturn]] void ThrowInvalidCapture(const lexer::SourcePosition &pos, const util::StringView &name) const; + [[noreturn]] void ThrowError(const lexer::SourcePosition &pos, const std::string_view &msg) const; void PropagateDirectEval() const; @@ -96,7 +148,7 @@ public: inline ArenaAllocator *Allocator() const { - return program_->Allocator(); + return allocator_; } const ArenaVector &Functions() const @@ -104,14 +156,39 @@ public: return functionScopes_; } - ArenaVector Functions() + ArenaVector &Functions() { return functionScopes_; } - const parser::Program *Program() const + const ArenaVector &LambdaObjects() const { - return program_; + return lambdaObjects_; + } + + ArenaVector &LambdaObjects() + { + return lambdaObjects_; + } + + const ArenaVector &FunctionalInterfaces() const + { + return functionalInterfaces_; + } + + ArenaVector &FunctionalInterfaces() + { + return functionalInterfaces_; + } + + virtual ScriptExtension Extension() const + { + return ScriptExtension::JS; + } + + virtual ResolveBindingOptions BindingOptions() const + { + return ResolveBindingOptions::BINDINGS; } static constexpr std::string_view FUNCTION_ARGUMENTS = "arguments"; @@ -127,9 +204,11 @@ public: static constexpr std::string_view LEXICAL_MANDATORY_PARAM_THIS = "!t"; static constexpr std::string_view LEXICAL_CONTEXT_PARAM = "=eval"; + static constexpr std::string_view MAIN = "main"; static constexpr uint32_t LEXICAL_CONTEXT_PARAM_REG = MANDATORY_PARAMS_NUMBER; + static constexpr std::string_view STAR_IMPORT = "*"; -private: +protected: template using MandatoryParams = std::array; @@ -150,33 +229,48 @@ private: template void AddMandatoryParams(const MandatoryParams ¶ms); void AddMandatoryParams(); - void BuildFunction(FunctionScope *funcScope, util::StringView name); - void BuildScriptFunction(Scope *outerScope, const ir::ScriptFunction *scriptFunc); - void BuildClassDefinition(ir::ClassDefinition *classDef); void LookupReference(const util::StringView &name); void InstantiateArguments(); void InstantiatePrivateContext(const ir::Identifier *ident) const; void BuildVarDeclarator(ir::VariableDeclarator *varDecl); - void BuildVarDeclaratorId(const ir::AstNode *parent, ir::AstNode *childNode); + void BuildVarDeclaratorId(ir::AstNode *childNode); void BuildForUpdateLoop(ir::ForUpdateStatement *forUpdateStmt); - void BuildForInOfLoop(const ir::Statement *parent, binder::LoopScope *loopScope, ir::AstNode *left, - ir::Expression *right, ir::Statement *body); + void BuildForInOfLoop(binder::LoopScope *loopScope, ir::AstNode *left, ir::Expression *right, ir::Statement *body); void BuildCatchClause(ir::CatchClause *catchClauseStmt); - void LookupIdentReference(ir::Identifier *ident); - void ResolveReference(const ir::AstNode *parent, ir::AstNode *childNode); + void ResolveReference(ir::AstNode *childNode); void ResolveReferences(const ir::AstNode *parent); + void VisitScriptFunctionWithPotentialTypeParams(ir::ScriptFunction *func); + void VisitScriptFunction(ir::ScriptFunction *func); + util::StringView BuildFunctionName(util::StringView name, uint32_t idx); - // TypeScript specific functions - void BuildTSSignatureDeclarationBaseParams(const ir::AstNode *typeNode); - [[noreturn]] void ThrowPrivateFieldMismatch(const lexer::SourcePosition &pos, const util::StringView &name) const; + void AddCompilableFunction(ir::ScriptFunction *func); + void AddCompilableFunctionScope(binder::FunctionScope *funcScope); + void InitializeClassBinding(ir::ClassDefinition *classDef); + void InitializeClassIdent(ir::ClassDefinition *classDef); + + virtual void LookupIdentReference(ir::Identifier *ident); + virtual void HandleCustomNodes(ir::AstNode *childNode) + { + ResolveReferences(childNode); + } + virtual void BuildSignatureDeclarationBaseParams([[maybe_unused]] ir::AstNode *typeNode) {}; + virtual void BuildClassDefinition(ir::ClassDefinition *classDef); + virtual void BuildClassProperty(const ir::ClassProperty *prop); + virtual bool BuildInternalName(ir::ScriptFunction *scriptFunc); + +private: parser::Program *program_ {}; + ArenaAllocator *allocator_ {}; compiler::CompilerContext *compilerCtx_ {}; GlobalScope *topScope_ {}; Scope *scope_ {}; VariableScope *varScope_ {}; ArenaVector functionScopes_; - ResolveBindingOptions bindingOptions_; + ArenaVector lambdaObjects_; + ArenaVector functionalInterfaces_; + ResolveBindingOptions bindingOptions_ {}; + bool genStdLib_ {false}; }; template @@ -201,12 +295,13 @@ public: binder_->varScope_ = prevVarScope_; } - [[nodiscard]] static LexicalScope Enter(Binder *binder, T *scope) + [[nodiscard]] static LexicalScope Enter(Binder *binder, T *scope, bool checkEval = true) { LexicalScope lexScope(scope, binder); - if (binder->Program()->Extension() == ScriptExtension::TS) { + if (!checkEval || binder->Extension() == ScriptExtension::TS) { return lexScope; } + // NOLINTNEXTLINE(readability-braces-around-statements) if constexpr (std::is_same_v) { binder->varScope_ = scope->GetFunctionScope(); @@ -221,7 +316,7 @@ public: binder->varScope_ = scope; binder->varScope_->CheckDirectEval(binder->compilerCtx_); } - // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation,bugprone-suspicious-semicolon) + // NOLINTNEXTLINE(readability-braces-around-statements,readability-misleading-indentation) } else if constexpr (std::is_same_v) { if (scope->IsLoopDeclarationScope()) { binder->varScope_ = scope; @@ -266,7 +361,7 @@ T *Binder::AddTsDecl(const lexer::SourcePosition &pos, Args &&...args) { T *decl = Allocator()->New(std::forward(args)...); - if (scope_->AddTsDecl(Allocator(), decl, program_->Extension())) { + if (scope_->AddTsDecl(Allocator(), decl, Extension()) != nullptr) { return decl; } @@ -278,12 +373,25 @@ T *Binder::AddDecl(const lexer::SourcePosition &pos, Args &&...args) { T *decl = Allocator()->New(std::forward(args)...); - if (scope_->AddDecl(Allocator(), decl, program_->Extension())) { + if (scope_->AddDecl(Allocator(), decl, Extension()) != nullptr) { return decl; } ThrowRedeclaration(pos, decl->Name()); } + +template +std::tuple Binder::NewVarDecl(const lexer::SourcePosition &pos, Args &&...args) +{ + T *decl = Allocator()->New(std::forward(args)...); + binder::Variable *var = scope_->AddDecl(Allocator(), decl, Extension()); + + if (var != nullptr) { + return {decl, var}; + } + + ThrowRedeclaration(pos, decl->Name()); +} } // namespace panda::es2panda::binder #endif diff --git a/binder/declaration.h b/binder/declaration.h index ba4cee19e180526d5fec68c269da965fe19b9beb..90cf8831b38e76870f1f51458cdffb58aec83c1e 100644 --- a/binder/declaration.h +++ b/binder/declaration.h @@ -49,6 +49,11 @@ public: return name_; } + ir::AstNode *Node() + { + return node_; + } + const ir::AstNode *Node() const { return node_; @@ -73,7 +78,7 @@ public: DECLARATION_KINDS(DECLARE_CHECKS_CASTS) #undef DECLARE_CHECKS_CASTS - void BindNode(const ir::AstNode *node) + void BindNode(ir::AstNode *node) { node_ = node; } @@ -83,12 +88,18 @@ public: return IsLetDecl() || IsConstDecl(); } + bool PossibleTDZ() const + { + return IsLetOrConstDecl() || IsParameterDecl(); + } + protected: explicit Decl(util::StringView name) : name_(name) {} + explicit Decl(util::StringView name, ir::AstNode *declNode) : name_(name), node_(declNode) {} // NOLINTBEGIN(misc-non-private-member-variables-in-classes) util::StringView name_; - const ir::AstNode *node_ {}; + ir::AstNode *node_ {}; // NOLINTEND(misc-non-private-member-variables-in-classes) }; @@ -100,6 +111,11 @@ public: { } + explicit MultiDecl(ArenaAllocator *allocator, util::StringView name, ir::AstNode *declNode) + : Decl(name, declNode), declarations_(allocator->Adapter()) + { + } + const ArenaVector &Decls() const { return declarations_; @@ -117,6 +133,10 @@ private: class EnumLiteralDecl : public Decl { public: explicit EnumLiteralDecl(util::StringView name, bool isConst) : Decl(name), isConst_(isConst) {} + explicit EnumLiteralDecl(util::StringView name, ir::AstNode *declNode, bool isConst) + : Decl(name, declNode), isConst_(isConst) + { + } DeclType Type() const override { @@ -146,6 +166,10 @@ private: class InterfaceDecl : public MultiDecl { public: explicit InterfaceDecl(ArenaAllocator *allocator, util::StringView name) : MultiDecl(allocator, name) {} + explicit InterfaceDecl(ArenaAllocator *allocator, util::StringView name, ir::AstNode *declNode) + : MultiDecl(allocator, name, declNode) + { + } DeclType Type() const override { @@ -153,9 +177,20 @@ public: } }; +class ClassDecl : public Decl { +public: + explicit ClassDecl(util::StringView name) : Decl(name) {} + explicit ClassDecl(util::StringView name, ir::AstNode *node) : Decl(name, node) {} + + DeclType Type() const override + { + return DeclType::CLASS; + } +}; + class FunctionDecl : public MultiDecl { public: - explicit FunctionDecl(ArenaAllocator *allocator, util::StringView name, const ir::AstNode *node) + explicit FunctionDecl(ArenaAllocator *allocator, util::StringView name, ir::AstNode *node) : MultiDecl(allocator, name) { node_ = node; @@ -169,7 +204,7 @@ public: class TypeParameterDecl : public Decl { public: - explicit TypeParameterDecl(util::StringView name, const ir::AstNode *node); + explicit TypeParameterDecl(util::StringView name) : Decl(name) {} DeclType Type() const override { @@ -240,6 +275,7 @@ public: class LetDecl : public Decl { public: explicit LetDecl(util::StringView name) : Decl(name) {} + explicit LetDecl(util::StringView name, ir::AstNode *declNode) : Decl(name, declNode) {} DeclType Type() const override { @@ -250,6 +286,7 @@ public: class ConstDecl : public Decl { public: explicit ConstDecl(util::StringView name) : Decl(name) {} + explicit ConstDecl(util::StringView name, ir::AstNode *declNode) : Decl(name, declNode) {} DeclType Type() const override { @@ -274,7 +311,7 @@ public: { } - explicit ImportDecl(util::StringView importName, util::StringView localName, const ir::AstNode *node) + explicit ImportDecl(util::StringView importName, util::StringView localName, ir::AstNode *node) : Decl(localName), importName_(importName) { BindNode(node); @@ -306,7 +343,7 @@ public: { } - explicit ExportDecl(util::StringView exportName, util::StringView localName, const ir::AstNode *node) + explicit ExportDecl(util::StringView exportName, util::StringView localName, ir::AstNode *node) : Decl(localName), exportName_(exportName) { BindNode(node); diff --git a/binder/recordTable.cpp b/binder/recordTable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..862bb2131fb037e2a5058fe05c9270d6332d11af --- /dev/null +++ b/binder/recordTable.cpp @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2021 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 "recordTable.h" +#include "plugins/ecmascript/es2panda/parser/program/program.h" +#include "plugins/ecmascript/es2panda/binder/ETSBinder.h" +#include "plugins/ecmascript/es2panda/ir/base/classDefinition.h" +#include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsEnumDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceDeclaration.h" +#include "generated/signatures.h" + +namespace panda::es2panda::binder { +BoundContext::BoundContext(RecordTable *recordTable, ir::ClassDefinition *classDef) + : prev_(recordTable->boundCtx_), recordTable_(recordTable), savedRecord_(recordTable->record_) +{ + if (classDef == nullptr || !recordTable_->classDefinitions_.insert(classDef).second) { + return; + } + + recordTable_->boundCtx_ = this; + recordTable_->record_ = classDef; + recordIdent_ = classDef->Ident(); + classDef->SetInternalName(FormRecordName()); +} + +BoundContext::BoundContext(RecordTable *recordTable, ir::TSInterfaceDeclaration *interfaceDecl) + : prev_(recordTable->boundCtx_), recordTable_(recordTable), savedRecord_(recordTable->record_) +{ + if (interfaceDecl == nullptr || !recordTable_->interfaceDeclarations_.insert(interfaceDecl).second) { + return; + } + + recordTable_->boundCtx_ = this; + recordTable_->record_ = interfaceDecl; + recordIdent_ = interfaceDecl->Id(); + interfaceDecl->SetInternalName(FormRecordName()); +} + +BoundContext::BoundContext(RecordTable *recordTable, ir::TSEnumDeclaration *enumDecl) + : prev_(recordTable->boundCtx_), recordTable_(recordTable), savedRecord_(recordTable->record_) +{ + if (enumDecl == nullptr || !recordTable_->enumDeclarations_.insert(enumDecl).second) { + return; + } + + recordTable_->boundCtx_ = this; + recordTable_->record_ = enumDecl; + recordIdent_ = enumDecl->Key(); + enumDecl->SetInternalName(FormRecordName()); +} + +BoundContext::~BoundContext() +{ + recordTable_->record_ = savedRecord_; + recordTable_->boundCtx_ = prev_; +} + +util::StringView BoundContext::FormRecordName() const +{ + const auto &packageName = recordTable_->program_->GetPackageName(); + if (prev_ == nullptr) { + if (packageName.Empty()) { + return recordIdent_->Name(); + } + + util::UString recordName(recordTable_->program_->Allocator()); + recordName.Append(packageName); + recordName.Append(compiler::Signatures::METHOD_SEPARATOR); + recordName.Append(recordIdent_->Name()); + return recordName.View(); + } + + util::UString recordName(recordTable_->program_->Allocator()); + recordName.Append(prev_->FormRecordName()); + recordName.Append(compiler::Signatures::METHOD_SEPARATOR); + recordName.Append(recordIdent_->Name()); + return recordName.View(); +} + +util::StringView RecordTable::RecordName() const +{ + if (std::holds_alternative(record_)) { + return std::get(record_)->InternalName(); + } + if (std::holds_alternative(record_)) { + return std::get(record_)->Id()->Name(); + } + + ASSERT(std::holds_alternative(record_)); + return std::get(record_)->InternalName(); +} + +} // namespace panda::es2panda::binder diff --git a/binder/recordTable.h b/binder/recordTable.h new file mode 100644 index 0000000000000000000000000000000000000000..c5b184b7f0fbbb07a5e6e8f67d4b0bcf00d903f3 --- /dev/null +++ b/binder/recordTable.h @@ -0,0 +1,216 @@ +/** + * Copyright (c) 2021 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_recordTable_RECORD_TABLE_H +#define ES2PANDA_recordTable_RECORD_TABLE_H + +#include "macros.h" +#include "utils/arena_containers.h" +#include "plugins/ecmascript/es2panda/util/ustring.h" +#include "plugins/ecmascript/es2panda/util/enumbitops.h" + +namespace panda::es2panda::parser { +class Program; +} // namespace panda::es2panda::parser + +namespace panda::es2panda::checker { +class Signature; +} // namespace panda::es2panda::checker + +namespace panda::es2panda::ir { +class ClassDefinition; +class TSInterfaceDeclaration; +class TSEnumDeclaration; +class Identifier; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::binder { +class FunctionScope; +class BoundContext; + +enum class RecordTableFlags : uint32_t { + NONE = 0U, + EXTERNAL = 1U << 0U, +}; + +DEFINE_BITOPS(RecordTableFlags) + +class RecordTable { +public: + explicit RecordTable(ArenaAllocator *allocator, parser::Program *program, RecordTableFlags flags) + : classDefinitions_(allocator->Adapter()), + lambdaObjects_(allocator->Adapter()), + interfaceDeclarations_(allocator->Adapter()), + enumDeclarations_(allocator->Adapter()), + signatures_(allocator->Adapter()), + program_(program), + flags_(flags) + { + } + + NO_COPY_SEMANTIC(RecordTable); + NO_MOVE_SEMANTIC(RecordTable); + + ~RecordTable() = default; + + bool IsExternal() const + { + return (flags_ & RecordTableFlags::EXTERNAL) != 0; + } + + ArenaUnorderedSet &ClassDefinitions() + { + return classDefinitions_; + } + + const ArenaUnorderedSet &ClassDefinitions() const + { + return classDefinitions_; + } + + ArenaUnorderedSet &LambdaObjects() + { + return lambdaObjects_; + } + + const ArenaUnorderedSet &LambdaObjects() const + { + return lambdaObjects_; + } + + ArenaUnorderedSet &InterfaceDeclarations() + { + return interfaceDeclarations_; + } + + const ArenaUnorderedSet &InterfaceDeclarations() const + { + return interfaceDeclarations_; + } + + ArenaUnorderedSet &EnumDeclaration() + { + return enumDeclarations_; + } + + const ArenaUnorderedSet &EnumDeclaration() const + { + return enumDeclarations_; + } + + ArenaVector &Signatures() + { + return signatures_; + } + + const ArenaVector &Signatures() const + { + return signatures_; + } + + void SetClassDefinition(ir::ClassDefinition *classDefinition) + { + record_ = classDefinition; + } + + ir::ClassDefinition *ClassDefinition() + { + return std::holds_alternative(record_) ? std::get(record_) + : nullptr; + } + + const ir::ClassDefinition *ClassDefinition() const + { + return std::holds_alternative(record_) ? std::get(record_) + : nullptr; + } + + void SetInterfaceDeclaration(ir::TSInterfaceDeclaration *interfaceDeclaration) + { + record_ = interfaceDeclaration; + } + + ir::TSInterfaceDeclaration *InterfaceDeclaration() + { + return std::holds_alternative(record_) + ? std::get(record_) + : nullptr; + } + + const ir::TSInterfaceDeclaration *InterfaceDeclaration() const + { + return std::holds_alternative(record_) + ? std::get(record_) + : nullptr; + } + + void SetProgram(parser::Program *program) + { + program_ = program; + } + + parser::Program *Program() + { + return program_; + } + + const parser::Program *Program() const + { + return program_; + } + + util::StringView RecordName() const; + +private: + friend class BoundContext; + using RecordHolder = + std::variant; + + ArenaUnorderedSet classDefinitions_; + ArenaUnorderedSet lambdaObjects_; + ArenaUnorderedSet interfaceDeclarations_; + ArenaUnorderedSet enumDeclarations_; + ArenaVector signatures_; + RecordHolder record_ {nullptr}; + parser::Program *program_ {}; + BoundContext *boundCtx_ {}; + RecordTableFlags flags_ {}; +}; + +class BoundContext { +public: + explicit BoundContext(RecordTable *recordTable, ir::ClassDefinition *classDef); + explicit BoundContext(RecordTable *recordTable, ir::TSInterfaceDeclaration *interfaceDecl); + explicit BoundContext(RecordTable *recordTable, ir::TSEnumDeclaration *enumDecl); + ~BoundContext(); + + NO_COPY_SEMANTIC(BoundContext); + NO_MOVE_SEMANTIC(BoundContext); + + void *operator new(size_t) = delete; + void *operator new[](size_t) = delete; + + util::StringView FormRecordName() const; + +private: + BoundContext *prev_; + RecordTable *recordTable_; + RecordTable::RecordHolder savedRecord_ {nullptr}; + ir::Identifier *recordIdent_; +}; + +} // namespace panda::es2panda::binder + +#endif diff --git a/binder/scope.cpp b/binder/scope.cpp index 2c8fbad997d3e74a091816c23022c34846318d46..327b6acb09f845a554dc5779dbf230c5d5e4599c 100644 --- a/binder/scope.cpp +++ b/binder/scope.cpp @@ -22,12 +22,19 @@ #include "plugins/ecmascript/es2panda/binder/variableFlags.h" #include "plugins/ecmascript/es2panda/ir/astNode.h" #include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" +#include "plugins/ecmascript/es2panda/ir/statements/classDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/base/classDefinition.h" +#include "plugins/ecmascript/es2panda/ir/base/scriptFunction.h" +#include "plugins/ecmascript/es2panda/ir/base/classProperty.h" +#include "plugins/ecmascript/es2panda/ir/base/methodDefinition.h" #include "plugins/ecmascript/es2panda/ir/module/exportAllDeclaration.h" #include "plugins/ecmascript/es2panda/ir/module/exportNamedDeclaration.h" #include "plugins/ecmascript/es2panda/ir/module/exportSpecifier.h" #include "plugins/ecmascript/es2panda/ir/module/importDeclaration.h" #include "plugins/ecmascript/es2panda/ir/expressions/literals/stringLiteral.h" #include "plugins/ecmascript/es2panda/ir/expressions/literals/booleanLiteral.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsEnumDeclaration.h" #include "plugins/ecmascript/es2panda/compiler/base/literals.h" #include "plugins/ecmascript/es2panda/compiler/core/compilerContext.h" #include "macros.h" @@ -67,6 +74,7 @@ const VariableScope *Scope::EnclosingVariableScope() const return nullptr; } +// NOLINTNEXTLINE(google-default-arguments) Variable *Scope::FindLocal(const util::StringView &name, ResolveBindingOptions options) const { if ((options & ResolveBindingOptions::INTERFACES) != 0) { @@ -166,39 +174,44 @@ std::tuple Scope::IterateShadowedVariables(const util::StringView return {iter, false}; } -bool Scope::AddLocal(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) +Variable *Scope::AddLocal(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) { - VariableFlags flags = VariableFlags::NONE; + VariableFlags flags = VariableFlags::LEXICAL; switch (newDecl->Type()) { case DeclType::VAR: { auto [scope, shadowed] = IterateShadowedVariables( newDecl->Name(), [](const Variable *v) { return !v->HasFlag(VariableFlags::VAR); }); if (shadowed) { - return false; + return nullptr; } VariableFlags varFlags = VariableFlags::HOIST_VAR | VariableFlags::LEXICAL_VAR; if (scope->IsGlobalScope()) { - scope->Bindings().insert({newDecl->Name(), allocator->New(newDecl, varFlags)}); - } else { - scope->PropagateBinding(allocator, newDecl->Name(), newDecl, varFlags); + return scope->Bindings() + .insert({newDecl->Name(), allocator->New(newDecl, varFlags)}) + .first->second; } - return true; + return scope->PropagateBinding(allocator, newDecl->Name(), newDecl, varFlags); } case DeclType::ENUM: { - bindings_.insert({newDecl->Name(), allocator->New(newDecl, false)}); - return true; + return bindings_.insert({newDecl->Name(), allocator->New(newDecl, false)}).first->second; } case DeclType::ENUM_LITERAL: { - bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::ENUM_LITERAL)}); - return true; + return bindings_ + .insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::ENUM_LITERAL)}) + .first->second; } case DeclType::INTERFACE: { - bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::INTERFACE)}); - return true; + return bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::INTERFACE)}) + .first->second; + } + case DeclType::TYPE_PARAMETER: { + return bindings_ + .insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::TYPE_PARAMETER)}) + .first->second; } case DeclType::FUNC: { flags = VariableFlags::HOIST; @@ -206,7 +219,7 @@ bool Scope::AddLocal(ArenaAllocator *allocator, Variable *currentVariable, Decl } default: { if (currentVariable != nullptr) { - return false; + return nullptr; } auto [_, shadowed] = IterateShadowedVariables( @@ -214,11 +227,10 @@ bool Scope::AddLocal(ArenaAllocator *allocator, Variable *currentVariable, Decl (void)_; if (shadowed) { - return false; + return nullptr; } - bindings_.insert({newDecl->Name(), allocator->New(newDecl, flags)}); - return true; + return bindings_.insert({newDecl->Name(), allocator->New(newDecl, flags)}).first->second; } } } @@ -227,7 +239,7 @@ void VariableScope::CheckDirectEval(compiler::CompilerContext *compilerCtx) { ASSERT(compilerCtx); - if (!HasFlag(VariableScopeFlags::NO_REG_STORE) || bindings_.empty()) { + if (!HasFlag(ScopeFlags::NO_REG_STORE) || bindings_.empty()) { evalBindings_ = compiler::INVALID_LITERAL_BUFFER_ID; return; } @@ -276,52 +288,53 @@ void VariableScope::CheckDirectEval(compiler::CompilerContext *compilerCtx) evalBindings_ = compilerCtx->AddContextLiteral(std::move(literals)); } -bool ParamScope::AddParam(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, VariableFlags flags) +Variable *ParamScope::AddParam(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, VariableFlags flags) { ASSERT(newDecl->IsParameterDecl()); if (currentVariable != nullptr) { - return false; + return nullptr; } auto *param = allocator->New(newDecl, flags); params_.push_back(param); bindings_.insert({newDecl->Name(), param}); - return true; + return param; } -std::tuple ParamScope::AddParamDecl(ArenaAllocator *allocator, - const ir::AstNode *param) +std::tuple ParamScope::AddParamDecl(ArenaAllocator *allocator, + ir::AstNode *param) { const auto [name, pattern] = util::Helpers::ParamName(allocator, param, params_.size()); auto *decl = NewDecl(allocator, name); + auto *var = AddParam(allocator, FindLocal(name), decl, VariableFlags::VAR); - if (!AddParam(allocator, FindLocal(name), decl, VariableFlags::VAR)) { - return {decl, param}; + if (var == nullptr) { + return {decl, param, nullptr}; } if (!pattern) { decl->BindNode(param); - return {decl, nullptr}; + return {decl, nullptr, var}; } - std::vector bindings = util::Helpers::CollectBindingNames(param); + std::vector bindings = util::Helpers::CollectBindingNames(param); - for (const auto *binding : bindings) { + for (auto *binding : bindings) { auto *varDecl = NewDecl(allocator, binding->Name()); varDecl->BindNode(binding); if (FindLocal(varDecl->Name()) != nullptr) { - return {decl, binding}; + return {decl, binding, nullptr}; } auto *paramVar = allocator->New(varDecl, VariableFlags::VAR); bindings_.insert({varDecl->Name(), paramVar}); } - return {decl, nullptr}; + return {decl, nullptr, var}; } void FunctionParamScope::BindName(ArenaAllocator *allocator, util::StringView name) @@ -332,15 +345,15 @@ void FunctionParamScope::BindName(ArenaAllocator *allocator, util::StringView na } } -bool FunctionParamScope::AddBinding([[maybe_unused]] ArenaAllocator *allocator, - [[maybe_unused]] Variable *currentVariable, [[maybe_unused]] Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) +Variable *FunctionParamScope::AddBinding([[maybe_unused]] ArenaAllocator *allocator, + [[maybe_unused]] Variable *currentVariable, [[maybe_unused]] Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) { UNREACHABLE(); } -bool FunctionScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) +Variable *FunctionScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) { switch (newDecl->Type()) { case DeclType::VAR: { @@ -350,8 +363,7 @@ bool FunctionScope::AddBinding(ArenaAllocator *allocator, Variable *currentVaria return AddFunction(allocator, currentVariable, newDecl, extension); } case DeclType::ENUM: { - bindings_.insert({newDecl->Name(), allocator->New(newDecl, false)}); - return true; + return bindings_.insert({newDecl->Name(), allocator->New(newDecl, false)}).first->second; } case DeclType::ENUM_LITERAL: { return AddTSBinding(allocator, currentVariable, newDecl, VariableFlags::ENUM_LITERAL); @@ -365,8 +377,8 @@ bool FunctionScope::AddBinding(ArenaAllocator *allocator, Variable *currentVaria } } -bool GlobalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) +Variable *GlobalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) { switch (newDecl->Type()) { case DeclType::VAR: { @@ -376,8 +388,7 @@ bool GlobalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariabl return AddFunction(allocator, currentVariable, newDecl, extension); } case DeclType::ENUM: { - bindings_.insert({newDecl->Name(), allocator->New(newDecl, false)}); - return true; + return bindings_.insert({newDecl->Name(), allocator->New(newDecl, false)}).first->second; } case DeclType::ENUM_LITERAL: { return AddTSBinding(allocator, currentVariable, newDecl, VariableFlags::ENUM_LITERAL); @@ -389,14 +400,12 @@ bool GlobalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariabl return AddLexical(allocator, currentVariable, newDecl); } } - - return true; } // ModuleScope -bool ModuleScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) +Variable *ModuleScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) { switch (newDecl->Type()) { case DeclType::VAR: { @@ -406,8 +415,7 @@ bool ModuleScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariabl return AddFunction(allocator, currentVariable, newDecl, extension); } case DeclType::ENUM: { - bindings_.insert({newDecl->Name(), allocator->New(newDecl, false)}); - return true; + return bindings_.insert({newDecl->Name(), allocator->New(newDecl, false)}).first->second; } case DeclType::ENUM_LITERAL: { return AddTSBinding(allocator, currentVariable, newDecl, VariableFlags::ENUM_LITERAL); @@ -419,7 +427,7 @@ bool ModuleScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariabl return AddImport(allocator, currentVariable, newDecl); } case DeclType::EXPORT: { - return true; + return allocator->New(newDecl, VariableFlags::NONE); } default: { return AddLexical(allocator, currentVariable, newDecl); @@ -427,7 +435,7 @@ bool ModuleScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariabl } } -void ModuleScope::AddImportDecl(const ir::ImportDeclaration *importDecl, ImportDeclList &&decls) +void ModuleScope::AddImportDecl(ir::ImportDeclaration *importDecl, ImportDeclList &&decls) { auto res = imports_.emplace_back(importDecl, decls); @@ -436,7 +444,7 @@ void ModuleScope::AddImportDecl(const ir::ImportDeclaration *importDecl, ImportD } } -void ModuleScope::AddExportDecl(const ir::AstNode *exportDecl, ExportDecl *decl) +void ModuleScope::AddExportDecl(ir::AstNode *exportDecl, ExportDecl *decl) { decl->BindNode(exportDecl); @@ -446,7 +454,7 @@ void ModuleScope::AddExportDecl(const ir::AstNode *exportDecl, ExportDecl *decl) AddExportDecl(exportDecl, std::move(decls)); } -void ModuleScope::AddExportDecl(const ir::AstNode *exportDecl, ExportDeclList &&decls) +void ModuleScope::AddExportDecl(ir::AstNode *exportDecl, ExportDeclList &&decls) { auto res = exports_.emplace_back(exportDecl, decls); @@ -455,21 +463,21 @@ void ModuleScope::AddExportDecl(const ir::AstNode *exportDecl, ExportDeclList && } } -bool ModuleScope::AddImport(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl) +Variable *ModuleScope::AddImport(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl) { if (currentVariable != nullptr && currentVariable->Declaration()->Type() != DeclType::VAR) { - return false; + return nullptr; } if (newDecl->Node()->IsImportNamespaceSpecifier()) { - bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::READONLY)}); - } else { - auto *variable = allocator->New(newDecl, VariableFlags::NONE); - variable->ExoticName() = newDecl->AsImportDecl()->ImportName(); - bindings_.insert({newDecl->Name(), variable}); + return bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::READONLY)}) + .first->second; } - return true; + auto *variable = allocator->New(newDecl, VariableFlags::NONE); + variable->ExoticName() = newDecl->AsImportDecl()->ImportName(); + bindings_.insert({newDecl->Name(), variable}); + return variable; } bool ModuleScope::ExportAnalysis() @@ -522,12 +530,123 @@ bool ModuleScope::ExportAnalysis() // LocalScope -bool LocalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) +Variable *LocalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) { return AddLocal(allocator, currentVariable, newDecl, extension); } +// NOLINTNEXTLINE(google-default-arguments) +Variable *ClassScope::FindLocal(const util::StringView &name, ResolveBindingOptions options) const +{ + if ((options & ResolveBindingOptions::VARIABLES) != 0) { + auto found = instanceFieldScope_->Bindings().find(name); + if (found != bindings_.end()) { + return found->second; + } + } + + if ((options & ResolveBindingOptions::STATIC_VARIABLES) != 0) { + auto found = staticFieldScope_->Bindings().find(name); + if (found != bindings_.end()) { + return found->second; + } + } + + if ((options & ResolveBindingOptions::DECLARATION) != 0) { + auto found = instanceDeclScope_->Bindings().find(name); + if (found != bindings_.end()) { + return found->second; + } + } + + if ((options & ResolveBindingOptions::STATIC_DECLARATION) != 0) { + auto found = staticDeclScope_->Bindings().find(name); + if (found != bindings_.end()) { + return found->second; + } + } + + if ((options & ResolveBindingOptions::METHODS) != 0) { + auto found = instanceMethodScope_->Bindings().find(name); + if (found != bindings_.end()) { + return found->second; + } + } + + if ((options & ResolveBindingOptions::STATIC_METHODS) != 0) { + auto found = staticMethodScope_->Bindings().find(name); + if (found != bindings_.end()) { + return found->second; + } + } + + return nullptr; +} + +Variable *ClassScope::AddBinding(ArenaAllocator *allocator, [[maybe_unused]] Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) +{ + VariableFlags flags = VariableFlags::NONE; + bool isStatic = newDecl->Node()->IsStatic(); + ir::Identifier *ident {}; + LocalScope *targetScope {}; + + if (isStatic) { + flags |= VariableFlags::STATIC; + } + + switch (newDecl->Type()) { + case DeclType::CONST: + case DeclType::LET: { + targetScope = isStatic ? staticFieldScope_ : instanceFieldScope_; + ident = newDecl->Node()->AsClassProperty()->Id(); + flags |= VariableFlags::PROPERTY; + break; + } + case DeclType::INTERFACE: { + targetScope = isStatic ? staticDeclScope_ : instanceDeclScope_; + ident = newDecl->Node()->AsTSInterfaceDeclaration()->Id(); + flags |= VariableFlags::INTERFACE; + break; + } + case DeclType::CLASS: { + targetScope = isStatic ? staticDeclScope_ : instanceDeclScope_; + ident = newDecl->Node()->AsClassDefinition()->Ident(); + flags |= VariableFlags::CLASS; + break; + } + case DeclType::ENUM_LITERAL: { + targetScope = isStatic ? staticDeclScope_ : instanceDeclScope_; + ident = newDecl->Node()->AsTSEnumDeclaration()->Key(); + flags |= VariableFlags::ENUM_LITERAL; + break; + } + default: { + UNREACHABLE(); + break; + } + } + + if (FindLocal(newDecl->Name(), ResolveBindingOptions::ALL) != nullptr) { + return nullptr; + } + + auto *var = targetScope->AddBinding(allocator, nullptr, newDecl, extension); + + if (!var) { + return nullptr; + } + + var->AddFlag(flags); + + if (ident) { + ident->SetVariable(var); + } + + return var; +} + void LoopDeclarationScope::ConvertToVariableScope(ArenaAllocator *allocator) { if (NeedLexEnv()) { @@ -580,17 +699,17 @@ void LoopScope::ConvertToVariableScope(ArenaAllocator *allocator) } } -bool CatchParamScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) +Variable *CatchParamScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) { return AddParam(allocator, currentVariable, newDecl, VariableFlags::INITIALIZED); } -bool CatchScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) +Variable *CatchScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) { if (!newDecl->IsVarDecl() && (paramScope_->FindLocal(newDecl->Name()) != nullptr)) { - return false; + return nullptr; } return AddLocal(allocator, currentVariable, newDecl, extension); diff --git a/binder/scope.h b/binder/scope.h index 9beb2fcc0640436b2d853651e379bb6103966334..d0b903131f5b4fc9df455fe9ec1569696b67f57f 100644 --- a/binder/scope.h +++ b/binder/scope.h @@ -18,7 +18,7 @@ #include "plugins/ecmascript/es2panda/binder/declaration.h" #include "plugins/ecmascript/es2panda/binder/variable.h" -#include "plugins/ecmascript/es2panda/parser/program/program.h" +#include "plugins/ecmascript/es2panda/es2panda.h" #include "plugins/ecmascript/es2panda/util/enumbitops.h" #include "plugins/ecmascript/es2panda/util/ustring.h" @@ -127,11 +127,31 @@ public: const VariableScope *EnclosingVariableScope() const; + void AddFlag(ScopeFlags flag) + { + flags_ |= flag; + } + + void ClearFlag(ScopeFlags flag) + { + flags_ &= ~flag; + } + + bool HasFlag(ScopeFlags flag) const + { + return (flags_ & flag) != 0; + } + const ArenaVector &Decls() const { return decls_; } + void SetParent(Scope *parent) + { + parent_ = parent; + } + Scope *Parent() { return parent_; @@ -162,23 +182,28 @@ public: endIns_ = ins; } + ir::AstNode *Node() + { + return node_; + } + const ir::AstNode *Node() const { return node_; } - void BindNode(const ir::AstNode *node) + void BindNode(ir::AstNode *node) { node_ = node; } - bool AddDecl(ArenaAllocator *allocator, Decl *decl, [[maybe_unused]] ScriptExtension extension) + Variable *AddDecl(ArenaAllocator *allocator, Decl *decl, [[maybe_unused]] ScriptExtension extension) { decls_.push_back(decl); return AddBinding(allocator, FindLocal(decl->Name()), decl, extension); } - bool AddTsDecl(ArenaAllocator *allocator, Decl *decl, [[maybe_unused]] ScriptExtension extension) + Variable *AddTsDecl(ArenaAllocator *allocator, Decl *decl, [[maybe_unused]] ScriptExtension extension) { decls_.push_back(decl); return AddBinding(allocator, FindLocal(decl->Name(), ResolveBindingOptions::ALL), decl, extension); @@ -192,10 +217,10 @@ public: template static VariableType *CreateVar(ArenaAllocator *allocator, util::StringView name, VariableFlags flags, - const ir::AstNode *node); + ir::AstNode *node); template - void PropagateBinding(ArenaAllocator *allocator, util::StringView name, Args &&...args); + Variable *PropagateBinding(ArenaAllocator *allocator, util::StringView name, Args &&...args); VariableMap &Bindings() { @@ -207,11 +232,12 @@ public: return bindings_; } - virtual bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) = 0; + virtual Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) = 0; - Variable *FindLocal(const util::StringView &name, - ResolveBindingOptions options = ResolveBindingOptions::BINDINGS) const; + // NOLINTNEXTLINE(google-default-arguments) + virtual Variable *FindLocal(const util::StringView &name, + ResolveBindingOptions options = ResolveBindingOptions::BINDINGS) const; ScopeFindResult Find(const util::StringView &name, ResolveBindingOptions options = ResolveBindingOptions::BINDINGS) const; @@ -224,6 +250,11 @@ protected: { } + explicit Scope(ArenaAllocator *allocator, Scope *parent, ScopeFlags flags) + : parent_(parent), decls_(allocator->Adapter()), bindings_(allocator->Adapter()), flags_(flags) + { + } + /** * @return true - if the variable is shadowed * false - otherwise @@ -236,14 +267,15 @@ protected: */ std::tuple IterateShadowedVariables(const util::StringView &name, const VariableVisitior &visitor); - bool AddLocal(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension); + Variable *AddLocal(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension); // NOLINTBEGIN(misc-non-private-member-variables-in-classes) Scope *parent_ {}; ArenaVector decls_; VariableMap bindings_; - const ir::AstNode *node_ {}; + ir::AstNode *node_ {}; + ScopeFlags flags_ {}; const compiler::IRNode *startIns_ {}; const compiler::IRNode *endIns_ {}; // NOLINTEND(misc-non-private-member-variables-in-classes) @@ -255,21 +287,6 @@ public: NO_COPY_SEMANTIC(VariableScope); NO_MOVE_SEMANTIC(VariableScope); - void AddFlag(VariableScopeFlags flag) - { - flags_ |= flag; - } - - void ClearFlag(VariableScopeFlags flag) - { - flags_ &= ~flag; - } - - bool HasFlag(VariableScopeFlags flag) const - { - return (flags_ & flag) != 0; - } - uint32_t NextSlot() { return slotIndex_++; @@ -296,20 +313,19 @@ protected: explicit VariableScope(ArenaAllocator *allocator, Scope *parent) : Scope(allocator, parent) {} template - bool AddVar(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl); + Variable *AddVar(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl); template - bool AddFunction(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension); + Variable *AddFunction(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension); template - bool AddTSBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, VariableFlags flags); + Variable *AddTSBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, VariableFlags flags); template - bool AddLexical(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl); + Variable *AddLexical(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl); // NOLINTBEGIN(misc-non-private-member-variables-in-classes) - VariableScopeFlags flags_ {}; uint32_t evalBindings_ {}; uint32_t slotIndex_ {}; // NOLINTEND(misc-non-private-member-variables-in-classes) @@ -332,7 +348,7 @@ public: return params_; } - std::tuple AddParamDecl(ArenaAllocator *allocator, const ir::AstNode *param); + std::tuple AddParamDecl(ArenaAllocator *allocator, ir::AstNode *param); protected: explicit ParamScope(ArenaAllocator *allocator, Scope *parent) @@ -340,7 +356,7 @@ protected: { } - bool AddParam(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, VariableFlags flags); + Variable *AddParam(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, VariableFlags flags); // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) ArenaVector params_; @@ -374,8 +390,8 @@ public: return ScopeType::FUNCTION_PARAM; } - bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) override; + Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override; friend class FunctionScope; template @@ -429,9 +445,13 @@ public: return ScopeType::FUNCTION; } - void BindName(util::StringView name, util::StringView internalName) + void BindName(util::StringView name) { name_ = name; + } + + void BindInternalName(util::StringView internalName) + { internalName_ = internalName; } @@ -445,8 +465,8 @@ public: return internalName_; } - bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) override; + Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override; private: util::StringView name_ {}; @@ -456,14 +476,115 @@ private: class LocalScope : public Scope { public: explicit LocalScope(ArenaAllocator *allocator, Scope *parent) : Scope(allocator, parent) {} + explicit LocalScope(ArenaAllocator *allocator, Scope *parent, ScopeFlags flags) : Scope(allocator, parent, flags) {} ScopeType Type() const override { return ScopeType::LOCAL; } - bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) override; + Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override; +}; + +class ClassScope : public LocalScope { +public: + explicit ClassScope(ArenaAllocator *allocator, Scope *parent) + : LocalScope(allocator, parent), + staticDeclScope_(allocator->New(allocator, this, ScopeFlags::STATIC_DECL_SCOPE)), + staticFieldScope_(allocator->New(allocator, staticDeclScope_, ScopeFlags::STATIC_FIELD_SCOPE)), + staticMethodScope_(allocator->New(allocator, staticFieldScope_, ScopeFlags::STATIC_METHOD_SCOPE)), + instanceDeclScope_(allocator->New(allocator, staticMethodScope_, ScopeFlags::DECL_SCOPE)), + instanceFieldScope_(allocator->New(allocator, instanceDeclScope_, ScopeFlags::FIELD_SCOPE)), + instanceMethodScope_(allocator->New(allocator, instanceFieldScope_, ScopeFlags::METHOD_SCOPE)) + { + } + + ScopeType Type() const override + { + return ScopeType::CLASS; + } + + LocalScope *StaticDeclScope() + { + return staticDeclScope_; + } + + const LocalScope *StaticDeclScope() const + { + return staticDeclScope_; + } + + LocalScope *StaticFieldScope() + { + return staticFieldScope_; + } + + const LocalScope *StaticFieldScope() const + { + return staticFieldScope_; + } + + LocalScope *StaticMethodScope() + { + return staticMethodScope_; + } + + const LocalScope *StaticMethodScope() const + { + return staticMethodScope_; + } + + LocalScope *InstanceFieldScope() + { + return instanceFieldScope_; + } + + const LocalScope *InstanceFieldScope() const + { + return instanceFieldScope_; + } + + LocalScope *InstanceMethodScope() + { + return instanceMethodScope_; + } + + const LocalScope *InstanceMethodScope() const + { + return instanceMethodScope_; + } + + LocalScope *InstanceDeclScope() + { + return instanceDeclScope_; + } + + const LocalScope *InstanceDeclScope() const + { + return instanceDeclScope_; + } + + uint32_t GetAndIncrementAnonymousClassIdx() const + { + return anonymousClassIdx_++; + } + + // NOLINTNEXTLINE(google-default-arguments) + Variable *FindLocal(const util::StringView &name, + ResolveBindingOptions options = ResolveBindingOptions::BINDINGS) const override; + + Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override; + +private: + LocalScope *staticDeclScope_; + LocalScope *staticFieldScope_; + LocalScope *staticMethodScope_; + LocalScope *instanceDeclScope_; + LocalScope *instanceFieldScope_; + LocalScope *instanceMethodScope_; + mutable uint32_t anonymousClassIdx_ {1}; }; class CatchParamScope : public ParamScope { @@ -475,8 +596,8 @@ public: return ScopeType::CATCH_PARAM; } - bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) override; + Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override; friend class CatchScope; }; @@ -490,8 +611,8 @@ public: return ScopeType::CATCH; } - bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) override; + Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override; }; class LoopScope; @@ -505,8 +626,8 @@ public: return loopType_; } - bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) override + Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override { return AddLocal(allocator, currentVariable, newDecl, extension); } @@ -551,8 +672,8 @@ public: void ConvertToVariableScope(ArenaAllocator *allocator); - bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) override + Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override { return AddLocal(allocator, currentVariable, newDecl, extension); } @@ -578,8 +699,8 @@ public: return ScopeType::GLOBAL; } - bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) override; + Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override; }; class ModuleScope : public GlobalScope { @@ -604,12 +725,12 @@ public: return ScopeType::MODULE; } - const ModuleEntry &Imports() const + const ModuleEntry &Imports() const { return imports_; } - const ModuleEntry &Exports() const + const ModuleEntry &Exports() const { return exports_; } @@ -619,98 +740,90 @@ public: return localExports_; } - void AddImportDecl(const ir::ImportDeclaration *importDecl, ImportDeclList &&decls); + void AddImportDecl(ir::ImportDeclaration *importDecl, ImportDeclList &&decls); - void AddExportDecl(const ir::AstNode *exportDecl, ExportDecl *decl); + void AddExportDecl(ir::AstNode *exportDecl, ExportDecl *decl); - void AddExportDecl(const ir::AstNode *exportDecl, ExportDeclList &&decls); + void AddExportDecl(ir::AstNode *exportDecl, ExportDeclList &&decls); - bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) override; + Variable *AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override; bool ExportAnalysis(); private: - bool AddImport(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl); + Variable *AddImport(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl); ArenaAllocator *allocator_; - ModuleEntry imports_; - ModuleEntry exports_; + ModuleEntry imports_; + ModuleEntry exports_; LocalExportNameMap localExports_; }; template -bool VariableScope::AddVar(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl) +Variable *VariableScope::AddVar(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl) { if (!currentVariable) { - bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::HOIST_VAR)}); - return true; + return bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::HOIST_VAR)}).first->second; } switch (currentVariable->Declaration()->Type()) { case DeclType::VAR: { currentVariable->Reset(newDecl, VariableFlags::HOIST_VAR); - break; + [[fallthrough]]; } case DeclType::PARAM: case DeclType::FUNC: { - break; + return currentVariable; } default: { - return false; + return nullptr; } } - - return true; } template -bool VariableScope::AddFunction(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, - [[maybe_unused]] ScriptExtension extension) +Variable *VariableScope::AddFunction(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) { VariableFlags flags = (extension == ScriptExtension::JS) ? VariableFlags::HOIST_VAR : VariableFlags::HOIST; if (!currentVariable) { - bindings_.insert({newDecl->Name(), allocator->New(newDecl, flags)}); - return true; + return bindings_.insert({newDecl->Name(), allocator->New(newDecl, flags)}).first->second; } if (extension != ScriptExtension::JS || IsModuleScope()) { - return false; + return nullptr; } switch (currentVariable->Declaration()->Type()) { case DeclType::VAR: case DeclType::FUNC: { currentVariable->Reset(newDecl, VariableFlags::HOIST_VAR); - break; + return currentVariable; } default: { - return false; + return nullptr; } } - - return true; } template -bool VariableScope::AddTSBinding(ArenaAllocator *allocator, [[maybe_unused]] Variable *currentVariable, Decl *newDecl, - VariableFlags flags) +Variable *VariableScope::AddTSBinding(ArenaAllocator *allocator, [[maybe_unused]] Variable *currentVariable, + Decl *newDecl, VariableFlags flags) { ASSERT(!currentVariable); - bindings_.insert({newDecl->Name(), allocator->New(newDecl, flags)}); - return true; + return bindings_.insert({newDecl->Name(), allocator->New(newDecl, flags)}).first->second; } template -bool VariableScope::AddLexical(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl) +Variable *VariableScope::AddLexical(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl) { if (currentVariable) { - return false; + return nullptr; } - bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::NONE)}); - return true; + return bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::NONE)}).first->second; } template @@ -739,8 +852,7 @@ VariableType *Scope::AddDecl(ArenaAllocator *allocator, util::StringView name, V } template -VariableType *Scope::CreateVar(ArenaAllocator *allocator, util::StringView name, VariableFlags flags, - const ir::AstNode *node) +VariableType *Scope::CreateVar(ArenaAllocator *allocator, util::StringView name, VariableFlags flags, ir::AstNode *node) { auto *decl = allocator->New(name); auto *variable = allocator->New(decl, flags); @@ -749,15 +861,15 @@ VariableType *Scope::CreateVar(ArenaAllocator *allocator, util::StringView name, } template -void Scope::PropagateBinding(ArenaAllocator *allocator, util::StringView name, Args &&...args) +Variable *Scope::PropagateBinding(ArenaAllocator *allocator, util::StringView name, Args &&...args) { auto res = bindings_.find(name); if (res == bindings_.end()) { - bindings_.insert({name, allocator->New(std::forward(args)...)}); - return; + return bindings_.insert({name, allocator->New(std::forward(args)...)}).first->second; } res->second->Reset(std::forward(args)...); + return res->second; } } // namespace panda::es2panda::binder diff --git a/binder/variable.cpp b/binder/variable.cpp index fec0ece21b7323aef38534c62802c42e6bf1e4fe..bb7dba2736eceb23d18409572a0bc998775b3b8d 100644 --- a/binder/variable.cpp +++ b/binder/variable.cpp @@ -27,6 +27,8 @@ LocalVariable::LocalVariable(Decl *decl, VariableFlags flags) : Variable(decl, f } } +LocalVariable::LocalVariable(VariableFlags flags) : Variable(flags) {} + const util::StringView &Variable::Name() const { return decl_->Name(); diff --git a/binder/variable.h b/binder/variable.h index ffa856132b7554a0cea5390034419e74124eeea8..8c0f2d733d2aad1be6ea4886bdbbfc8d1f1351b4 100644 --- a/binder/variable.h +++ b/binder/variable.h @@ -26,6 +26,7 @@ namespace panda::es2panda::checker { class Type; +enum class PropertyType; } // namespace panda::es2panda::checker namespace panda::es2panda::binder { @@ -65,7 +66,12 @@ public: VARIABLE_TYPES(DECLARE_CHECKS_CASTS) #undef DECLARE_CHECKS_CASTS - Decl *Declaration() const + const Decl *Declaration() const + { + return decl_; + } + + Decl *Declaration() { return decl_; } @@ -116,9 +122,10 @@ public: protected: explicit Variable(Decl *decl, VariableFlags flags) : decl_(decl), flags_(flags) {} + explicit Variable(VariableFlags flags) : flags_(flags) {} // NOLINTBEGIN(misc-non-private-member-variables-in-classes) - Decl *decl_; + Decl *decl_ {}; VariableFlags flags_ {}; checker::Type *tsType_ {}; // NOLINTEND(misc-non-private-member-variables-in-classes) @@ -127,6 +134,7 @@ protected: class LocalVariable : public Variable { public: explicit LocalVariable(Decl *decl, VariableFlags flags); + explicit LocalVariable(VariableFlags flags); VariableType Type() const override { @@ -143,7 +151,7 @@ public: { ASSERT(!LexicalBound()); AddFlag(VariableFlags::LEXICAL_BOUND); - vreg_ = slot; + vreg_.SetIndex(slot); } compiler::VReg Vreg() const @@ -151,17 +159,22 @@ public: return vreg_; } + compiler::VReg &Vreg() + { + return vreg_; + } + uint32_t LexIdx() const { ASSERT(LexicalBound()); - return vreg_; + return vreg_.GetIndex(); } void SetLexical([[maybe_unused]] Scope *scope) override; LocalVariable *Copy(ArenaAllocator *allocator, Decl *decl) const; private: - uint32_t vreg_ {}; + compiler::VReg vreg_ {}; }; class GlobalVariable : public Variable { diff --git a/binder/variableFlags.h b/binder/variableFlags.h index 7be9b096f119e4b7bce0ecd3c2b1e79eedb097ca..d424f91ad4f7337ae93e5cdbb544e0bcacd320ac 100644 --- a/binder/variableFlags.h +++ b/binder/variableFlags.h @@ -16,6 +16,7 @@ #ifndef ES2PANDA_COMPILER_SCOPES_VARIABLE_FLAGS_H #define ES2PANDA_COMPILER_SCOPES_VARIABLE_FLAGS_H +#include #include "plugins/ecmascript/es2panda/util/enumbitops.h" namespace panda::es2panda::binder { @@ -35,6 +36,7 @@ namespace panda::es2panda::binder { _(ENUM_LITERAL, EnumLiteralDecl) \ _(TYPE_PARAMETER, TypeParameterDecl) \ _(PROPERTY, PropertyDecl) \ + _(CLASS, ClassDecl) \ _(METHOD, MethodDecl) \ _(ENUM, EnumDecl) @@ -52,6 +54,7 @@ enum class DeclType { _(CATCH_PARAM, CatchParamScope) \ _(FUNCTION_PARAM, FunctionParamScope) \ _(CATCH, CatchScope) \ + _(CLASS, ClassScope) \ _(LOCAL, LocalScope) \ /* Variable Scopes */ \ _(LOOP, LoopScope) \ @@ -68,11 +71,20 @@ enum class ScopeType { }; enum class ResolveBindingOptions : uint32_t { - NONE = 0, - BINDINGS = 0x1U, - INTERFACES = 0x2U, - - ALL = BINDINGS | INTERFACES, + BINDINGS = 1U << 0U, + INTERFACES = 1U << 1U, + VARIABLES = 1U << 2U, + METHODS = 1U << 3U, + DECLARATION = 1U << 4U, + STATIC_VARIABLES = 1U << 5U, + STATIC_METHODS = 1U << 6U, + STATIC_DECLARATION = 1U << 7U, + ALL_VARIABLES = VARIABLES | STATIC_VARIABLES, + ALL_METHOD = METHODS | STATIC_METHODS, + ALL_DECLARATION = DECLARATION | STATIC_DECLARATION, + + LAST = STATIC_DECLARATION, + ALL = (LAST << 1U) - 1U, }; DEFINE_BITOPS(ResolveBindingOptions) @@ -99,32 +111,46 @@ enum class VariableKind { MODULE, }; -enum class VariableFlags : uint32_t { - NONE = 0, - OPTIONAL = 0x1U, - PROPERTY = 0x2U, - METHOD = 0x4U, - TYPE_ALIAS = 0x8U, - INTERFACE = 0x10U, - ENUM_LITERAL = 0x20U, - READONLY = 0x40U, - COMPUTED = 0x80U, - COMPUTED_IDENT = 0x100U, // Remove - INFERED_IN_PATTERN = 0x200U, - REST_ARG = 0x400U, - NUMERIC_NAME = 0x800U, - TYPE = 0x1000U, - LOCAL_EXPORT = 0x2000U, - - LOOP_DECL = 0x4000U, - PER_ITERATION = 0x8000U, - LEXICAL_VAR = 0x10000U, - HOIST = 0x20000U, - VAR = 0x40000U, - INITIALIZED = 0x80000U, - LEXICAL_BOUND = 0x100000U, +enum class VariableFlags : uint64_t { + NONE = 0U, + OPTIONAL = 1U << 0U, + PROPERTY = 1U << 1U, + METHOD = 1U << 2U, + TYPE_ALIAS = 1U << 3U, + INTERFACE = 1U << 4U, + ENUM_LITERAL = 1U << 5U, + READONLY = 1U << 6U, + COMPUTED = 1U << 7U, + COMPUTED_IDENT = 1U << 8U, // Remove + INFERED_IN_PATTERN = 1U << 9U, + REST_ARG = 1U << 10U, + NUMERIC_NAME = 1U << 11U, + TYPE = 1U << 12U, + LOCAL_EXPORT = 1U << 13U, + TYPE_PARAMETER = 1U << 14U, + STATIC = 1U << 15U, + CLASS = 1U << 16U, + EXPLICIT_INIT_REQUIRED = 1U << 17U, + PUBLIC = 1U << 18U, + PROTECTED = 1U << 19U, + PRIVATE = 1U << 20U, + SYNTHETIC = 1U << 21U, + METHOD_REFERENCE = 1U << 22U, + + LEXICAL = 1U << 24U, + LOOP_DECL = 1U << 25U, + PER_ITERATION = 1U << 26U, + LEXICAL_VAR = 1U << 27U, + HOIST = 1U << 28U, + VAR = 1U << 29U, + INITIALIZED = 1U << 30U, + LEXICAL_BOUND = 1U << 31U, + + BUILTIN_TYPE = 1ULL << 32ULL, HOIST_VAR = HOIST | VAR, + CLASS_OR_INTERFACE = CLASS | INTERFACE, + CLASS_OR_INTERFACE_OR_ENUM = CLASS_OR_INTERFACE | ENUM_LITERAL, }; DEFINE_BITOPS(VariableFlags) @@ -134,16 +160,25 @@ enum class LetOrConstStatus { UNINITIALIZED, }; -enum class VariableScopeFlags : uint32_t { - NONE = 0x0U, - SET_LEXICAL_FUNCTION = 0x1U, - USE_ARGS = 0x2U, - USE_SUPER = 0x4U, - INNER_ARROW = 0x8U, - NO_REG_STORE = 0x10U, +enum class ScopeFlags : uint32_t { + NONE = 0U, + SET_LEXICAL_FUNCTION = 1U << 0U, + USE_ARGS = 1U << 2U, + USE_SUPER = 1U << 3U, + INNER_ARROW = 1U << 4U, + NO_REG_STORE = 1U << 5U, + DEFER_STMT = 1U << 6U, + DECL_SCOPE = 1U << 7U, + FIELD_SCOPE = 1U << 8U, + METHOD_SCOPE = 1U << 9U, + STATIC = 1U << 10U, + + STATIC_DECL_SCOPE = DECL_SCOPE | STATIC, + STATIC_FIELD_SCOPE = FIELD_SCOPE | STATIC, + STATIC_METHOD_SCOPE = METHOD_SCOPE | STATIC, }; -DEFINE_BITOPS(VariableScopeFlags) +DEFINE_BITOPS(ScopeFlags) } // namespace panda::es2panda::binder #endif diff --git a/checker/ASchecker.cpp b/checker/ASchecker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fbafe0c3df8e0fd5f874aac271dae17046bfdcbc --- /dev/null +++ b/checker/ASchecker.cpp @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2021 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 "ASchecker.h" +#include "plugins/ecmascript/es2panda/parser/program/program.h" + +namespace panda::es2panda::checker { + +bool ASChecker::StartChecker([[maybe_unused]] binder::Binder *binder, const CompilerOptions &options) +{ + Initialize(binder); + + if (options.dumpAst) { + std::cout << Program()->Dump() << std::endl; + } + + return false; +} + +} // namespace panda::es2panda::checker diff --git a/checker/ASchecker.h b/checker/ASchecker.h new file mode 100644 index 0000000000000000000000000000000000000000..d5dcaea2ab791f592332f4536caeaaf065975dbb --- /dev/null +++ b/checker/ASchecker.h @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2021 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_CHECKER_AS_CHECKER_H +#define ES2PANDA_CHECKER_AS_CHECKER_H + +#include "plugins/ecmascript/es2panda/checker/checker.h" + +namespace panda::es2panda::checker { + +class ASChecker : public Checker { +public: + // NOLINTNEXTLINE(readability-redundant-member-init) + explicit ASChecker() : Checker() {} + + bool StartChecker([[maybe_unused]] binder::Binder *binder, + [[maybe_unused]] const CompilerOptions &options) override; + Type *CheckTypeCached([[maybe_unused]] ir::Expression *expr) override + { + return nullptr; + } + + void ResolveStructuredTypeMembers([[maybe_unused]] Type *type) override {} + + Type *GetTypeOfVariable([[maybe_unused]] binder::Variable *var) override + { + return nullptr; + } +}; + +} // namespace panda::es2panda::checker + +#endif /* CHECKER_H */ diff --git a/checker/ETSchecker.cpp b/checker/ETSchecker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5bfd93ce16680c8e1e9d2f0eb2c1000407f0b649 --- /dev/null +++ b/checker/ETSchecker.cpp @@ -0,0 +1,248 @@ +/** + * Copyright (c) 2021 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 "ETSchecker.h" + +#include "plugins/ecmascript/es2panda/ir/expression.h" +#include "plugins/ecmascript/es2panda/ir/base/classDefinition.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/statements/blockStatement.h" +#include "plugins/ecmascript/es2panda/binder/ETSBinder.h" +#include "plugins/ecmascript/es2panda/parser/program/program.h" +#include "plugins/ecmascript/es2panda/checker/ets/aliveAnalyzer.h" + +namespace panda::es2panda::checker { +void ETSChecker::InitializeBuiltins(binder::ETSBinder *binder) +{ + if (HasStatus(CheckerStatus::BUILTINS_INITIALIZED)) { + return; + } + + const auto varMap = binder->TopScope()->Bindings(); + + const auto object = varMap.find("Object"); + ASSERT(object != varMap.end()); + GetGlobalTypesHolder()->InitializeBuiltin( + object->first, BuildClassProperties(object->second->Declaration()->Node()->AsClassDefinition())); + + for (const auto &[name, var] : varMap) { + if (name == object->first) { + continue; + } + + if (var->HasFlag(binder::VariableFlags::BUILTIN_TYPE)) { + Type *type {nullptr}; + if (var->Declaration()->Node()->IsClassDefinition()) { + type = BuildClassProperties(var->Declaration()->Node()->AsClassDefinition()); + } else { + ASSERT(var->Declaration()->Node()->IsTSInterfaceDeclaration()); + type = BuildInterfaceProperties(var->Declaration()->Node()->AsTSInterfaceDeclaration()); + } + GetGlobalTypesHolder()->InitializeBuiltin(name, type); + } + } + + AddStatus(CheckerStatus::BUILTINS_INITIALIZED); +} + +bool ETSChecker::StartChecker([[maybe_unused]] binder::Binder *binder, const CompilerOptions &options) +{ + Initialize(binder); + + if (options.dumpAst) { + std::cout << Program()->Dump() << std::endl; + } + + if (options.parseOnly) { + return false; + } + + binder->SetGenStdLib(options.genStdLib); + binder->IdentifierAnalysis(); + + auto *etsBinder = static_cast(binder); + InitializeBuiltins(etsBinder); + + CheckProgram(Program(), true); + + for (auto *func : binder->Functions()) { + etsBinder->BuildFunctionName(func->Node()->AsScriptFunction()); + } + + for (auto *lambda : binder->LambdaObjects()) { + etsBinder->BuildLambdaObjectName(lambda); + } + + for (auto *functional : binder->FunctionalInterfaces()) { + etsBinder->BuildFunctionalInterfaceName(functional); + } + + return true; +} + +void ETSChecker::CheckProgram(parser::Program *program, bool runAnalysis) +{ + auto *savedProgram = Program(); + SetProgram(program); + + for (auto &[_, extPrograms] : program->ExternalSources()) { + (void)_; + for (auto *extProg : extPrograms) { + CheckProgram(extProg); + } + } + + ASSERT(Program()->Ast()->IsProgram()); + Program()->Ast()->Check(this); + + if (runAnalysis) { + AliveAnalyzer(Program()->Ast(), this); + } + + auto *binder = static_cast(Binder()); + auto *recordTable = binder->GetExternalRecordTable().find(program)->second; + + InitializeRecordElements(binder, recordTable); + + SetProgram(savedProgram); +} + +void ETSChecker::InitializeRecordElements(binder::ETSBinder *binder, binder::RecordTable *recordTable) +{ + for (auto *func : recordTable->Signatures()) { + binder->BuildFunctionName(func->Node()->AsScriptFunction()); + } +} + +Type *ETSChecker::CheckTypeCached(ir::Expression *expr) +{ + if (expr->TsType() == nullptr) { + expr->SetTsType(expr->Check(this)); + } + + return expr->TsType(); +} + +ETSObjectType *ETSChecker::AsETSObjectType(Type *(GlobalTypesHolder::*typeFunctor)()) const +{ + auto *ret = (GetGlobalTypesHolder()->*typeFunctor)(); + return ret != nullptr ? ret->AsETSObjectType() : nullptr; +} + +Type *ETSChecker::GlobalByteType() const +{ + return GetGlobalTypesHolder()->GlobalByteType(); +} + +Type *ETSChecker::GlobalShortType() const +{ + return GetGlobalTypesHolder()->GlobalShortType(); +} + +Type *ETSChecker::GlobalIntType() const +{ + return GetGlobalTypesHolder()->GlobalIntType(); +} + +Type *ETSChecker::GlobalLongType() const +{ + return GetGlobalTypesHolder()->GlobalLongType(); +} + +Type *ETSChecker::GlobalFloatType() const +{ + return GetGlobalTypesHolder()->GlobalFloatType(); +} + +Type *ETSChecker::GlobalDoubleType() const +{ + return GetGlobalTypesHolder()->GlobalDoubleType(); +} + +Type *ETSChecker::GlobalCharType() const +{ + return GetGlobalTypesHolder()->GlobalCharType(); +} + +Type *ETSChecker::GlobalETSBooleanType() const +{ + return GetGlobalTypesHolder()->GlobalETSBooleanType(); +} + +Type *ETSChecker::GlobalVoidType() const +{ + return GetGlobalTypesHolder()->GlobalETSVoidType(); +} + +Type *ETSChecker::GlobalETSNullType() const +{ + return GetGlobalTypesHolder()->GlobalETSNullType(); +} + +Type *ETSChecker::GlobalETSStringLiteralType() const +{ + return GetGlobalTypesHolder()->GlobalETSStringLiteralType(); +} + +Type *ETSChecker::GlobalWildcardType() const +{ + return GetGlobalTypesHolder()->GlobalWildcardType(); +} + +ETSObjectType *ETSChecker::GlobalETSObjectType() const +{ + return AsETSObjectType(&GlobalTypesHolder::GlobalETSObjectType); +} + +ETSObjectType *ETSChecker::GlobalBuiltinETSStringType() const +{ + return AsETSObjectType(&GlobalTypesHolder::GlobalETSStringBuiltinType); +} + +ETSObjectType *ETSChecker::GlobalBuiltinTypeType() const +{ + return AsETSObjectType(&GlobalTypesHolder::GlobalTypeBuiltinType); +} + +ETSObjectType *ETSChecker::GlobalBuiltinExceptionType() const +{ + return AsETSObjectType(&GlobalTypesHolder::GlobalExceptionBuiltinType); +} + +ETSObjectType *ETSChecker::GlobalBuiltinPanicType() const +{ + return AsETSObjectType(&GlobalTypesHolder::GlobalPanicBuiltinType); +} + +ETSObjectType *ETSChecker::GlobalBuiltinEnumType() const +{ + return globalBuiltinEnumType_; +} + +const checker::WrapperDesc &ETSChecker::PrimitiveWrapper() const +{ + return primitiveWrappers_.Wrappers(); +} + +GlobalArraySignatureMap &ETSChecker::GlobalArrayTypes() +{ + return globalArraySignatures_; +} + +const GlobalArraySignatureMap &ETSChecker::GlobalArrayTypes() const +{ + return globalArraySignatures_; +} +} // namespace panda::es2panda::checker diff --git a/checker/ETSchecker.h b/checker/ETSchecker.h new file mode 100644 index 0000000000000000000000000000000000000000..e121a4e03366c54175d63e363c8b5fcad75b2c85 --- /dev/null +++ b/checker/ETSchecker.h @@ -0,0 +1,322 @@ +/** + * Copyright (c) 2021 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_CHECKER_ETS_CHECKER_H +#define ES2PANDA_CHECKER_ETS_CHECKER_H + +#include "plugins/ecmascript/es2panda/checker/checkerContext.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/etsObjectType.h" +#include "plugins/ecmascript/es2panda/checker/checker.h" +#include "plugins/ecmascript/es2panda/binder/enumMemberResult.h" +#include "plugins/ecmascript/es2panda/util/enumbitops.h" +#include "plugins/ecmascript/es2panda/util/ustring.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/types.h" +#include "plugins/ecmascript/es2panda/checker/ets/typeConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/primitiveWrappers.h" +#include "plugins/ecmascript/es2panda/checker/types/globalTypesHolder.h" + +#include "macros.h" + +#include +#include +#include +#include +#include + +namespace panda::es2panda::binder { +class Binder; +class Decl; +class EnumVariable; +class FunctionDecl; +class LocalVariable; +class Scope; +class Variable; +class ETSBinder; +class RecordTable; +} // namespace panda::es2panda::binder + +namespace panda::es2panda::checker { +enum class OperationType { + BITWISE_AND, + BITWISE_OR, + BITWISE_XOR, + LEFT_SHIFT, + RIGHT_SHIFT, + ADDITON, + SUBSTRACTION, + MULTIPLICATION, + DIVISION, + MOD, + LESS_THAN, + LESS_THAN_EQUAL, + GREATER_THAN, + GREATER_THAN_EQUAL, +}; + +enum class OverrideErrorCode { + NO_ERROR, + OVERRIDING_STATIC, + OVERRIDEN_STATIC, + OVERRIDEN_NOT_OPEN, + INCOMPATIBLE_RETURN, + OVERRIDEN_WEAKER, +}; + +using ComputedAbstracts = + ArenaUnorderedMap, std::unordered_set>>; +using ArrayMap = ArenaUnorderedMap; +using GlobalArraySignatureMap = ArenaUnorderedMap; + +class ETSChecker : public Checker { +public: + explicit ETSChecker() + // NOLINTNEXTLINE(readability-redundant-member-init) + : Checker(), + arrayTypes_(Allocator()->Adapter()), + globalArraySignatures_(Allocator()->Adapter()), + primitiveWrappers_(Allocator()), + cachedComputedAbstracts_(Allocator()->Adapter()) + { + } + + static inline TypeFlag ETSType(const Type *type) + { + return static_cast(type->TypeFlags() & TypeFlag::ETS_TYPE); + } + + static inline TypeFlag TypeKind(const Type *type) + { + return static_cast(type->TypeFlags() & checker::TypeFlag::ETS_TYPE); + } + + Type *GlobalByteType() const; + Type *GlobalShortType() const; + Type *GlobalIntType() const; + Type *GlobalLongType() const; + Type *GlobalFloatType() const; + Type *GlobalDoubleType() const; + Type *GlobalCharType() const; + Type *GlobalETSBooleanType() const; + Type *GlobalVoidType() const; + Type *GlobalETSNullType() const; + Type *GlobalETSStringLiteralType() const; + Type *GlobalWildcardType() const; + + ETSObjectType *GlobalETSObjectType() const; + ETSObjectType *GlobalBuiltinETSStringType() const; + ETSObjectType *GlobalBuiltinTypeType() const; + ETSObjectType *GlobalBuiltinExceptionType() const; + ETSObjectType *GlobalBuiltinPanicType() const; + ETSObjectType *GlobalBuiltinEnumType() const; + + const checker::WrapperDesc &PrimitiveWrapper() const; + + GlobalArraySignatureMap &GlobalArrayTypes(); + const GlobalArraySignatureMap &GlobalArrayTypes() const; + + void InitializeBuiltins(binder::ETSBinder *binder); + bool StartChecker([[maybe_unused]] binder::Binder *binder, const CompilerOptions &options) override; + Type *CheckTypeCached(ir::Expression *expr) override; + void ResolveStructuredTypeMembers([[maybe_unused]] Type *type) override {} + Type *GetTypeOfVariable([[maybe_unused]] binder::Variable *var) override; + + // Object + ETSObjectType *BuildClassProperties(ir::ClassDefinition *classDef); + ETSObjectType *BuildAnonymousClassProperties(ir::ClassDefinition *classDef, ETSObjectType *superType); + ETSObjectType *BuildInterfaceProperties(ir::TSInterfaceDeclaration *interfaceDecl); + void BuildEnumProperties(ETSObjectType *enumType); + ETSObjectType *GetSuperType(ETSObjectType *type); + ArenaVector GetInterfaces(ETSObjectType *type); + ArenaVector GetInterfacesOfClass(ETSObjectType *type); + ArenaVector GetInterfacesOfInterface(ETSObjectType *type); + void ValidateImplementedInterface(ETSObjectType *type, Type *interface, std::unordered_set *extendsSet, + const lexer::SourcePosition &pos); + void ResolveMembersOfEnumDecl(ETSObjectType *type); + void ResolveDeclaredMembersOfObject(ETSObjectType *type); + Type *ValidateArrayIndex(ir::Expression *expr); + Type *CheckArrayElementAccess(ir::MemberExpression *expr); + ETSObjectType *CheckThisOrSuperAccess(ir::Expression *node, ETSObjectType *classType, std::string_view msg); + void CreateTypeForClassTypeParameters(ETSObjectType *type); + void CreateTypeForInterfaceTypeParameters(ETSObjectType *type); + void SetTypeParameterType(ir::TSTypeParameter *typeParam, Type *assemblerType); + void ValidateOverriding(ETSObjectType *classType, const lexer::SourcePosition &pos); + void AddImplementedSignature(std::vector *implementedSignatures, binder::LocalVariable *function, + ETSFunctionType *it); + void CheckInnerClassMembers(const ETSObjectType *classType); + void CheckClassDefinition(ir::ClassDefinition *classDef); + void FindAssignment(const ir::AstNode *node, const binder::LocalVariable *classVar, bool &initialized); + void FindAssignments(const ir::AstNode *node, const binder::LocalVariable *classVar, bool &initialized); + void CheckConstFields(const ETSObjectType *classType); + void CheckConstFieldInitialized(const ETSObjectType *classType, binder::LocalVariable *classVar); + void CheckConstFieldInitialized(const Signature *signature, binder::LocalVariable *classVar); + void ComputeAbstractsFromInterface(ETSObjectType *interfaceType); + ArenaVector &GetAbstractsForClass(ETSObjectType *classType); + std::vector CollectAbstractSignaturesFromObject(const ETSObjectType *objType); + void CreateFunctionTypesFromAbstracts(const std::vector &abstracts, + ArenaVector *target); + void CheckCyclicContructorCall(Signature *signature); + binder::LocalVariable *ResolveMemberReference(const ir::MemberExpression *memberExpr, ETSObjectType *target); + void CheckImplicitSuper(ETSObjectType *classType, Signature *ctorSig); + void CheckValidInheritance(ETSObjectType *classType, ir::ClassDefinition *classDef); + + // Type creation + ByteType *CreateByteType(int8_t value); + ETSBooleanType *CreateETSBooleanType(bool value); + DoubleType *CreateDoubleType(double value); + FloatType *CreateFloatType(float value); + IntType *CreateIntType(int32_t value); + LongType *CreateLongType(int64_t value); + ShortType *CreateShortType(int16_t value); + CharType *CreateCharType(char16_t value); + ETSStringType *CreateETSStringLiteralType(util::StringView value); + ETSArrayType *CreateETSArrayType(Type *elementType); + ETSFunctionType *CreateETSFunctionType(Signature *signature); + ETSFunctionType *CreateETSFunctionType(util::StringView name); + ETSFunctionType *CreateETSFunctionType(ArenaVector &signatures); + ETSTypeReference *CreateTypeReference(Type **ref, Type **assemblerRef, binder::LocalVariable *refVar); + ETSTypeParameter *CreateTypeParameter(Type *assemblerType); + ETSObjectType *CreateETSObjectType(util::StringView name, ir::AstNode *declNode, ETSObjectFlags flags); + ETSObjectType *CreateETSEnumType(ir::TSEnumDeclaration *enumDecl); + std::tuple CreateBuiltinArraySignatureInfo(ETSArrayType *arrayType, size_t dim); + Signature *CreateBuiltinArraySignature(ETSArrayType *arrayType, size_t dim); + IntType *CreateIntTypeFromType(Type *type); + ETSObjectType *CreateNewETSObjectType(util::StringView name, ir::AstNode *declNode, ETSObjectFlags flags); + + Signature *CreateSignature(SignatureInfo *info, Type *returnType, ir::ScriptFunction *func); + Signature *CreateSignature(SignatureInfo *info, Type *returnType, util::StringView internalName); + SignatureInfo *CreateSignatureInfo(); + + // Arithmetic + Type *NegateNumericType(Type *type, ir::Expression *node); + Type *BitwiseNegateIntegralType(Type *type, ir::Expression *node); + std::tuple CheckBinaryOperator(ir::Expression *left, ir::Expression *right, + lexer::TokenType operationType, lexer::SourcePosition pos); + Type *HandleArithmeticOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType); + template + Type *PerformArithmeticOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType); + + Type *HandleRelationOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType); + template + Type *PerformRelationOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType); + + // Function + bool ValidateSignature(Signature *signature, const ArenaVector &arguments, + const lexer::SourcePosition &pos, TypeRelationFlag initialFlags); + Signature *ValidateSignatures(ArenaVector &signatures, const ArenaVector &arguments, + const lexer::SourcePosition &pos, std::string_view signatureKind, + TypeRelationFlag flags); + Signature *ResolveCallExpression(ArenaVector &signatures, + const ArenaVector &arguments, const lexer::SourcePosition &pos, + bool isIdentifier); + + Signature *ResolveConstructExpression(ETSObjectType *type, const ArenaVector &arguments, + const lexer::SourcePosition &pos); + checker::ETSFunctionType *BuildFunctionSignature(ir::ScriptFunction *func, bool isConstructSig = false); + checker::ETSFunctionType *BuildMethodSignature(ir::MethodDefinition *method); + Signature *CheckEveryAbstractSignatureIsOverriden(ETSFunctionType *target, ETSFunctionType *source); + Signature *GetSignatureFromMethodDefinition(const ir::MethodDefinition *methodDef); + void CheckIdenticalOverloads(ETSFunctionType *func, ETSFunctionType *overload, + const lexer::SourcePosition &overloadStart); + void CheckOverride(Signature *signature); + bool CheckOverride(Signature *signature, ETSObjectType *site); + std::tuple CheckOverride(Signature *signature, Signature *other); + bool IsMethodOverridesOther(Signature *target, Signature *source); + bool IsOverridableIn(Signature *signature); + void ValidateSignatureAccessibility(ETSObjectType *callee, Signature *signature, const lexer::SourcePosition &pos); + void ResolveLambdaObject(ir::ETSMethodReferenceExpression *methodRef, ETSObjectType *functionalInterface); + void ResolveLambdaObjectCtor(ir::ClassDefinition *lambdaObject); + void ResolveLambdaObjectInvoke(ir::ClassDefinition *lambdaObject, Signature *signatureRef); + ir::Statement *ResolveLambdaObjectInvokeFuncBody(ir::ClassDefinition *lambdaObject, Signature *signatureRef); + + // Helpers + Type *GetReferencedTypeFromBase(Type *baseType, ir::Expression *name); + Type *GetReferencedTypeBase(ir::Expression *name); + Type *GetTypeFromInterfaceReference(binder::Variable *var); + Type *GetTypeFromClassReference(binder::Variable *var); + Type *GetTypeFromEnumReference(binder::Variable *var); + Type *GetTypeFromTypeParameterReference(binder::LocalVariable *var, const lexer::SourcePosition &pos); + Type *GetDefaultTypeFromPrimitiveType(Type *type); + bool IsConstantExpression(ir::Expression *expr, Type *type); + void ValidateUnaryOperatorOperand(binder::Variable *variable); + std::tuple ApplyBinaryOperatorPromotion(Type *left, Type *right, TypeFlag test); + Type *ApplyUnaryOperatorPromotion(Type *type, bool createConst = true); + Type *HandleBooleanLogicalOperators(Type *leftType, Type *rightType, lexer::TokenType tokenType); + checker::Type *CheckVariableDeclaration(ir::Identifier *ident, ir::TypeNode *typeAnnotation, ir::Expression *init, + ir::ModifierFlags flags); + void CheckTruthinessOfType(Type *type, const lexer::SourcePosition &pos); + void ConcantConstantString(util::UString &target, Type *type); + Type *HandleStringConcatenation(Type *leftType, Type *rightType); + Type *ResolveIdentifier(ir::Identifier *ident); + ETSFunctionType *FindFunctionInVectorGivenByName(util::StringView name, ArenaVector &list); + void MergeComputedAbstracts(ArenaVector &merged, ArenaVector ¤t); + void MergeSignatures(ETSFunctionType *target, ETSFunctionType *source); + ir::AstNode *FindAncestorGivenByType(ir::AstNode *node, ir::AstNodeType type); + util::StringView GetContainingObjectNameFromSignature(Signature *signature); + bool IsFunctionContainsSignature(ETSFunctionType *funcType, Signature *signature); + void CheckFunctionContainsClashingSignature(const ETSFunctionType *funcType, Signature *signature); + bool IsTypeBuiltinType(Type *type); + const ir::AstNode *FindJumpTarget(ir::AstNodeType nodeType, const ir::AstNode *node, const ir::Identifier *target); + void ValidatePropertyAccess(binder::Variable *var, ETSObjectType *obj, const lexer::SourcePosition &pos); + binder::VariableFlags GetAccessFlagFromNode(const ir::AstNode *node); + void CheckSwitchDiscriminant(ir::Expression *discriminant); + Type *ETSBuiltinTypeAsPrimitiveType(Type *objectType); + Type *PrimitiveTypeAsETSBuiltinType(Type *objectType); + void CheckForSameSwitchCases(ArenaVector *cases); + util::StringView GetStringViewFromIdentifierValue(ir::Expression *identifier); + bool CompareIdentifiersValuesAreDifferent(ir::Expression *identifier, ir::Expression *compareValue); + void CheckIdentifierSwitchCase(ir::Expression *currentCase, ir::Expression *compareCase, + const lexer::SourcePosition &pos); + util::StringView GetStringViewFromLiteral(ir::Expression *caseTest); + binder::Variable *FindVariableInClass(util::StringView name); + std::tuple GetClassScopeFromClass(ETSObjectType *classType); + bool IsSameDeclarationType(binder::LocalVariable *target, binder::LocalVariable *compare); + + // Exception + ETSObjectType *CheckExceptionType(checker::Type *type, lexer::SourcePosition pos); + ETSObjectType *CheckRuntimeExceptionType(checker::Type *type, lexer::SourcePosition pos); + + static Type *TryToInstantiate(Type *type, ArenaAllocator *allocator, TypeRelation *relation, + GlobalTypesHolder *globalTypes); + +private: + void CreateTypeForTypeParameters(ETSObjectType *type, ir::TSTypeParameterDeclaration *typeParams); + ETSObjectType *CheckException(checker::Type *type, lexer::SourcePosition pos, ETSObjectType *expected, + std::string_view msg); + ETSObjectType *CreateETSObjectTypeCheckBuiltins(util::StringView name, ir::AstNode *declNode, ETSObjectFlags flags); + void CheckProgram(parser::Program *program, bool runAnalysis = false); + void InitializeRecordElements(binder::ETSBinder *binder, binder::RecordTable *recordTable); + + template + UType HandleModulo(UType leftValue, UType rightValue); + + template + UType HandleBitWiseArithmetic(UType leftValue, UType rightValue, lexer::TokenType operationType); + + template + typename TargetType::UType GetOperand(Type *type); + + ETSObjectType *AsETSObjectType(Type *(GlobalTypesHolder::*typeFunctor)()) const; + + ArrayMap arrayTypes_; + GlobalArraySignatureMap globalArraySignatures_; + PrimitiveWrappers primitiveWrappers_; + ComputedAbstracts cachedComputedAbstracts_; + // TODO(szd): remove after enum rework + ETSObjectType *globalBuiltinEnumType_ {}; +}; + +} // namespace panda::es2panda::checker + +#endif /* CHECKER_H */ diff --git a/checker/JSchecker.cpp b/checker/JSchecker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2ba5ffed21eacd28c2077c311e1d4324fb99f1b6 --- /dev/null +++ b/checker/JSchecker.cpp @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2021 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 "JSchecker.h" + +#include "plugins/ecmascript/es2panda/binder/binder.h" +#include "plugins/ecmascript/es2panda/parser/program/program.h" + +namespace panda::es2panda::checker { + +bool JSChecker::StartChecker([[maybe_unused]] binder::Binder *binder, const CompilerOptions &options) +{ + Initialize(binder); + binder->IdentifierAnalysis(); + + if (options.dumpAst) { + std::cout << Program()->Dump() << std::endl; + } + + return !options.parseOnly; +} + +} // namespace panda::es2panda::checker diff --git a/typescript/core/checkerContext.h b/checker/JSchecker.h similarity index 48% rename from typescript/core/checkerContext.h rename to checker/JSchecker.h index 3aef77d6058ce515ba08c4ac34f4678e754853ff..6b1eefe770b585551a3287816a8a5e04d9b3a6e0 100644 --- a/typescript/core/checkerContext.h +++ b/checker/JSchecker.h @@ -13,46 +13,33 @@ * limitations under the License. */ -#ifndef ES2PANDA_PARSER_CORE_CHECKER_CONTEXT_H -#define ES2PANDA_PARSER_CORE_CHECKER_CONTEXT_H +#ifndef ES2PANDA_CHECKER_JS_CHECKER_H +#define ES2PANDA_CHECKER_JS_CHECKER_H -#include -#include "plugins/ecmascript/es2panda/util/enumbitops.h" - -#include +#include "plugins/ecmascript/es2panda/checker/checker.h" namespace panda::es2panda::checker { -enum class CheckerStatus : uint32_t { - NO_OPTS = 0x0U, - FORCE_TUPLE = 0x1U, - IN_CONST_CONTEXT = 0x2U, - KEEP_LITERAL_TYPE = 0x4U, - IN_PARAMETER = 0x8U, -}; -DEFINE_BITOPS(CheckerStatus) - -class CheckerContext { +class JSChecker : public Checker { public: - explicit CheckerContext(CheckerStatus newStatus) : status_(newStatus) {} + // NOLINTNEXTLINE(readability-redundant-member-init) + explicit JSChecker() : Checker() {} - const CheckerStatus &Status() const - { - return status_; - } + bool StartChecker([[maybe_unused]] binder::Binder *binder, const CompilerOptions &options) override; - CheckerStatus &Status() + Type *CheckTypeCached([[maybe_unused]] ir::Expression *expr) override { - return status_; + return nullptr; } - DEFAULT_COPY_SEMANTIC(CheckerContext); - DEFAULT_MOVE_SEMANTIC(CheckerContext); - ~CheckerContext() = default; + void ResolveStructuredTypeMembers([[maybe_unused]] Type *type) override {} -private: - CheckerStatus status_; + Type *GetTypeOfVariable([[maybe_unused]] binder::Variable *var) override + { + return nullptr; + } }; + } // namespace panda::es2panda::checker -#endif +#endif /* CHECKER_H */ diff --git a/checker/TSchecker.cpp b/checker/TSchecker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6865ccfb031eb91d405969906245536fcd908795 --- /dev/null +++ b/checker/TSchecker.cpp @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2021 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 "TSchecker.h" + +#include "plugins/ecmascript/es2panda/ir/statements/blockStatement.h" +#include "plugins/ecmascript/es2panda/parser/program/program.h" + +namespace panda::es2panda::checker { + +bool TSChecker::StartChecker([[maybe_unused]] binder::Binder *binder, const CompilerOptions &options) +{ + Initialize(binder); + binder->IdentifierAnalysis(); + + if (options.dumpAst) { + std::cout << Program()->Dump() << std::endl; + } + + if (options.parseOnly) { + return false; + } + + ASSERT(Program()->Ast()->IsProgram()); + Program()->Ast()->Check(this); + + return false; +} + +Type *TSChecker::CheckTypeCached(ir::Expression *expr) +{ + if (expr->TsType() == nullptr) { + expr->SetTsType(expr->Check(this)); + } + + return expr->TsType(); +} + +} // namespace panda::es2panda::checker diff --git a/typescript/checker.h b/checker/TSchecker.h similarity index 45% rename from typescript/checker.h rename to checker/TSchecker.h index dd3365372957b9a8d2e49e90ebfc509dc6e5c0df..c315ab46e201cff3dff4668a67f2b5bfd14a813b 100644 --- a/typescript/checker.h +++ b/checker/TSchecker.h @@ -13,18 +13,18 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_CHECKER_H -#define ES2PANDA_COMPILER_TYPESCRIPT_CHECKER_H +#ifndef ES2PANDA_CHECKER_TS_CHECKER_H +#define ES2PANDA_CHECKER_TS_CHECKER_H +#include "plugins/ecmascript/es2panda/checker/checker.h" #include "plugins/ecmascript/es2panda/binder/enumMemberResult.h" -#include "plugins/ecmascript/es2panda/typescript/core/checkerContext.h" -#include "plugins/ecmascript/es2panda/typescript/types/globalTypesHolder.h" -#include "plugins/ecmascript/es2panda/typescript/types/typeRelation.h" -#include "plugins/ecmascript/es2panda/typescript/types/types.h" +#include "plugins/ecmascript/es2panda/checker/types/globalTypesHolder.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/types.h" #include "plugins/ecmascript/es2panda/util/enumbitops.h" #include "plugins/ecmascript/es2panda/util/ustring.h" #include "macros.h" +#include #include #include #include @@ -104,170 +104,125 @@ enum class AstNodeType; } // namespace panda::es2panda::ir namespace panda::es2panda::checker { -using StringLiteralPool = std::unordered_map; -using NumberLiteralPool = std::unordered_map; -using FunctionParamsResolveResult = std::variant &, bool>; -using InterfacePropertyMap = std::unordered_map>; -using TypeOrNode = std::variant; -using IndexInfoTypePair = std::pair; -using PropertyMap = std::unordered_map; -using ArgRange = std::pair; - -class Checker { -public: - explicit Checker(ArenaAllocator *allocator, binder::Binder *binder); - ~Checker() = default; - NO_COPY_SEMANTIC(Checker); - NO_MOVE_SEMANTIC(Checker); - ArenaAllocator *Allocator() const - { - return allocator_; - } - - binder::Binder *Binder() - { - return binder_; - } - - binder::Scope *Scope() const - { - return scope_; - } +class TSChecker : public Checker { +public: + // NOLINTNEXTLINE(readability-redundant-member-init) + explicit TSChecker() : Checker() {} Type *GlobalNumberType() { - return globalTypes_->GlobalNumberType(); + return GetGlobalTypesHolder()->GlobalNumberType(); } Type *GlobalAnyType() { - return globalTypes_->GlobalAnyType(); + return GetGlobalTypesHolder()->GlobalAnyType(); } Type *GlobalStringType() { - return globalTypes_->GlobalStringType(); + return GetGlobalTypesHolder()->GlobalStringType(); } Type *GlobalBooleanType() { - return globalTypes_->GlobalBooleanType(); + return GetGlobalTypesHolder()->GlobalBooleanType(); } Type *GlobalVoidType() { - return globalTypes_->GlobalVoidType(); + return GetGlobalTypesHolder()->GlobalVoidType(); } Type *GlobalNullType() { - return globalTypes_->GlobalNullType(); + return GetGlobalTypesHolder()->GlobalNullType(); } Type *GlobalUndefinedType() { - return globalTypes_->GlobalUndefinedType(); + return GetGlobalTypesHolder()->GlobalUndefinedType(); } Type *GlobalUnknownType() { - return globalTypes_->GlobalUnknownType(); + return GetGlobalTypesHolder()->GlobalUnknownType(); } Type *GlobalNeverType() { - return globalTypes_->GlobalNeverType(); + return GetGlobalTypesHolder()->GlobalNeverType(); } Type *GlobalNonPrimitiveType() { - return globalTypes_->GlobalNonPrimitiveType(); + return GetGlobalTypesHolder()->GlobalNonPrimitiveType(); } Type *GlobalBigintType() { - return globalTypes_->GlobalBigintType(); + return GetGlobalTypesHolder()->GlobalBigintType(); } Type *GlobalFalseType() { - return globalTypes_->GlobalFalseType(); + return GetGlobalTypesHolder()->GlobalFalseType(); } Type *GlobalTrueType() { - return globalTypes_->GlobalTrueType(); + return GetGlobalTypesHolder()->GlobalTrueType(); } Type *GlobalNumberOrBigintType() { - return globalTypes_->GlobalNumberOrBigintType(); + return GetGlobalTypesHolder()->GlobalNumberOrBigintType(); } Type *GlobalStringOrNumberType() { - return globalTypes_->GlobalStringOrNumberType(); + return GetGlobalTypesHolder()->GlobalStringOrNumberType(); } Type *GlobalZeroType() { - return globalTypes_->GlobalZeroType(); + return GetGlobalTypesHolder()->GlobalZeroType(); } Type *GlobalEmptyStringType() { - return globalTypes_->GlobalEmptyStringType(); + return GetGlobalTypesHolder()->GlobalEmptyStringType(); } Type *GlobalZeroBigintType() { - return globalTypes_->GlobalZeroBigintType(); + return GetGlobalTypesHolder()->GlobalZeroBigintType(); } Type *GlobalPrimitiveType() { - return globalTypes_->GlobalPrimitiveType(); + return GetGlobalTypesHolder()->GlobalPrimitiveType(); } Type *GlobalEmptyTupleType() { - return globalTypes_->GlobalEmptyTupleType(); + return GetGlobalTypesHolder()->GlobalEmptyTupleType(); } Type *GlobalEmptyObjectType() { - return globalTypes_->GlobalEmptyObjectType(); + return GetGlobalTypesHolder()->GlobalEmptyObjectType(); } Type *GlobalResolvingReturnType() { - return globalTypes_->GlobalResolvingReturnType(); + return GetGlobalTypesHolder()->GlobalResolvingReturnType(); } Type *GlobalErrorType() { - return globalTypes_->GlobalErrorType(); - } - - CheckerContext Context() - { - return context_; - } - - bool HasStatus(CheckerStatus status) - { - return (context_.Status() & status) != 0; - } - - void RemoveStatus(CheckerStatus status) - { - context_.Status() &= ~status; - } - - void AddStatus(CheckerStatus status) - { - context_.Status() |= status; + return GetGlobalTypesHolder()->GlobalErrorType(); } NumberLiteralPool &NumberLiteralMap() @@ -285,82 +240,44 @@ public: return bigintLiteralMap_; } - TypeRelation *Relation() - { - return relation_; - } - - RelationHolder &IdenticalResults() - { - return identicalResults_; - } - - RelationHolder &AssignableResults() - { - return assignableResults_; - } - - RelationHolder &ComparableResults() - { - return comparableResults_; - } - - std::unordered_set &TypeStack() - { - return typeStack_; - } - - std::unordered_map &NodeCache() - { - return nodeCache_; - } - - void StartChecker(); - - Type *CheckTypeCached(const ir::Expression *expr); - - [[noreturn]] void ThrowTypeError(std::string_view message, const lexer::SourcePosition &pos); - [[noreturn]] void ThrowTypeError(std::initializer_list list, - const lexer::SourcePosition &pos); + bool StartChecker([[maybe_unused]] binder::Binder *binder, const CompilerOptions &options) override; + Type *CheckTypeCached(ir::Expression *expr) override; // Util - static bool InAssignment(const ir::AstNode *node); + static bool InAssignment(ir::AstNode *node); static bool IsAssignmentOperator(lexer::TokenType op); static bool IsLiteralType(const Type *type); - static const ir::AstNode *FindAncestorGivenByType(const ir::AstNode *node, ir::AstNodeType type); - static const ir::AstNode *FindAncestorUntilGivenType(const ir::AstNode *node, ir::AstNodeType stop); + static ir::AstNode *FindAncestorUntilGivenType(ir::AstNode *node, ir::AstNodeType stop); static bool MaybeTypeOfKind(const Type *type, TypeFlag flags); static bool MaybeTypeOfKind(const Type *type, ObjectType::ObjectTypeKind kind); - static bool IsConstantMemberAccess(const ir::Expression *expr); - static bool IsStringLike(const ir::Expression *expr); - static const ir::TSQualifiedName *ResolveLeftMostQualifiedName(const ir::TSQualifiedName *qualifiedName); - static const ir::MemberExpression *ResolveLeftMostMemberExpression(const ir::MemberExpression *expr); + static bool IsConstantMemberAccess(ir::Expression *expr); + static bool IsStringLike(ir::Expression *expr); + static ir::MemberExpression *ResolveLeftMostMemberExpression(ir::MemberExpression *expr); // Helpers void CheckTruthinessOfType(Type *type, lexer::SourcePosition lineInfo); Type *CheckNonNullType(Type *type, lexer::SourcePosition lineInfo); Type *GetBaseTypeOfLiteralType(Type *type); - void CheckReferenceExpression(const ir::Expression *expr, const char *invalidReferenceMsg, + void CheckReferenceExpression(ir::Expression *expr, const char *invalidReferenceMsg, const char *invalidOptionalChainMsg); - void CheckTestingKnownTruthyCallableOrAwaitableType(const ir::Expression *condExpr, Type *type, - const ir::AstNode *body); + void CheckTestingKnownTruthyCallableOrAwaitableType(ir::Expression *condExpr, Type *type, ir::AstNode *body); Type *ExtractDefinitelyFalsyTypes(Type *type); Type *RemoveDefinitelyFalsyTypes(Type *type); TypeFlag GetFalsyFlags(Type *type); - bool IsVariableUsedInConditionBody(const ir::AstNode *parent, binder::Variable *searchVar); - bool FindVariableInBinaryExpressionChain(const ir::AstNode *parent, binder::Variable *searchVar); - bool IsVariableUsedInBinaryExpressionChain(const ir::AstNode *parent, binder::Variable *searchVar); + bool IsVariableUsedInConditionBody(ir::AstNode *parent, binder::Variable *searchVar); + bool FindVariableInBinaryExpressionChain(ir::AstNode *parent, binder::Variable *searchVar); + bool IsVariableUsedInBinaryExpressionChain(ir::AstNode *parent, binder::Variable *searchVar); [[noreturn]] void ThrowBinaryLikeError(lexer::TokenType op, Type *leftType, Type *rightType, lexer::SourcePosition lineInfo); [[noreturn]] void ThrowAssignmentError(Type *source, Type *target, lexer::SourcePosition lineInfo, bool isAsSrcLeftType = false); - void ElaborateElementwise(Type *targetType, const ir::Expression *sourceNode, const lexer::SourcePosition &pos); - void InferSimpleVariableDeclaratorType(const ir::VariableDeclarator *declarator); - Type *GetTypeOfVariable(binder::Variable *var); + void ElaborateElementwise(Type *targetType, ir::Expression *sourceNode, const lexer::SourcePosition &pos); + void InferSimpleVariableDeclaratorType(ir::VariableDeclarator *declarator); + Type *GetTypeOfVariable(binder::Variable *var) override; Type *GetUnaryResultType(Type *operandType); - Type *GetTypeFromClassOrInterfaceReference(const ir::TSTypeReference *node, binder::Variable *var); - Type *GetTypeFromTypeAliasReference(const ir::TSTypeReference *node, binder::Variable *var); - Type *GetTypeReferenceType(const ir::TSTypeReference *node, binder::Variable *var); + Type *GetTypeFromClassOrInterfaceReference(ir::TSTypeReference *node, binder::Variable *var); + Type *GetTypeFromTypeAliasReference(ir::TSTypeReference *node, binder::Variable *var); + Type *GetTypeReferenceType(ir::TSTypeReference *node, binder::Variable *var); // Type creation Type *CreateNumberLiteralType(double value); @@ -379,12 +296,12 @@ public: Type *CreateObjectTypeWithConstructSignature(Signature *constructSignature); // Object - void ResolvePropertiesOfObjectType(ObjectType *type, const ir::Expression *member, - ArenaVector &signatureDeclarations, - ArenaVector &indexDeclarations, bool isInterface); + void ResolvePropertiesOfObjectType(ObjectType *type, ir::AstNode *member, + ArenaVector &signatureDeclarations, + ArenaVector &indexDeclarations, bool isInterface); void ResolveSignaturesOfObjectType(ObjectType *type, - ArenaVector &signatureDeclarations); - void ResolveIndexInfosOfObjectType(ObjectType *type, ArenaVector &indexDeclarations); + ArenaVector &signatureDeclarations); + void ResolveIndexInfosOfObjectType(ObjectType *type, ArenaVector &indexDeclarations); void ResolveDeclaredMembers(InterfaceType *type); bool ValidateInterfaceMemberRedeclaration(ObjectType *type, binder::Variable *prop, const lexer::SourcePosition &locInfo); @@ -393,142 +310,64 @@ public: binder::Variable *GetPropertyOfUnionType(UnionType *type, const util::StringView &name, bool getPartial, binder::VariableFlags propagateFlags); void CheckIndexConstraints(Type *type); - void ResolveStructuredTypeMembers(Type *type); void ResolveUnionTypeMembers(UnionType *type); void ResolveObjectTypeMembers(ObjectType *type); void ResolveInterfaceOrClassTypeMembers(InterfaceType *type); - Type *CheckComputedPropertyName(const ir::Expression *key); + Type *CheckComputedPropertyName(ir::Expression *key); Type *GetPropertyTypeForIndexType(Type *type, Type *indexType); IndexInfo *GetApplicableIndexInfo(Type *type, Type *indexType); ArenaVector GetBaseTypes(InterfaceType *type); + void ResolveStructuredTypeMembers(Type *type) override; // Function - Type *HandleFunctionReturn(const ir::ScriptFunction *func); + Type *HandleFunctionReturn(ir::ScriptFunction *func); void CheckFunctionParameterDeclarations(const ArenaVector ¶ms, SignatureInfo *signatureInfo); std::tuple CheckFunctionParameter( - const ir::Expression *param, SignatureInfo *signatureInfo); + ir::Expression *param, SignatureInfo *signatureInfo); std::tuple CheckFunctionIdentifierParameter( - const ir::Identifier *param); + ir::Identifier *param); std::tuple CheckFunctionAssignmentPatternParameter( - const ir::AssignmentExpression *param); + ir::AssignmentExpression *param); std::tuple CheckFunctionRestParameter( - const ir::SpreadElement *param, SignatureInfo *signatureInfo); + ir::SpreadElement *param, SignatureInfo *signatureInfo); std::tuple CheckFunctionArrayPatternParameter( - const ir::ArrayExpression *param); + ir::ArrayExpression *param); std::tuple CheckFunctionObjectPatternParameter( - const ir::ObjectExpression *param); + ir::ObjectExpression *param); void InferFunctionDeclarationType(const binder::FunctionDecl *decl, binder::Variable *funcVar); - void CollectTypesFromReturnStatements(const ir::AstNode *parent, ArenaVector *returnTypes); - void CheckAllCodePathsInNonVoidFunctionReturnOrThrow(const ir::ScriptFunction *func, lexer::SourcePosition lineInfo, + void CollectTypesFromReturnStatements(ir::AstNode *parent, ArenaVector *returnTypes); + void CheckAllCodePathsInNonVoidFunctionReturnOrThrow(ir::ScriptFunction *func, lexer::SourcePosition lineInfo, const char *errMsg); - void CreatePatternParameterName(const ir::AstNode *node, std::stringstream &ss); - void ThrowReturnTypeCircularityError(const ir::ScriptFunction *func); + void CreatePatternParameterName(ir::AstNode *node, std::stringstream &ss); + void ThrowReturnTypeCircularityError(ir::ScriptFunction *func); ArgRange GetArgRange(const ArenaVector &signatures, ArenaVector *potentialSignatures, uint32_t callArgsSize, bool *haveSignatureWithRest); bool CallMatchesSignature(const ArenaVector &args, Signature *signature, bool throwError); Type *resolveCallOrNewExpression(const ArenaVector &signatures, ArenaVector arguments, const lexer::SourcePosition &errPos); - Type *CreateParameterTypeForArrayAssignmentPattern(const ir::ArrayExpression *arrayPattern, Type *inferedType); - Type *CreateParameterTypeForObjectAssignmentPattern(const ir::ObjectExpression *objectPattern, Type *inferedType); - - // Type relation - bool IsTypeIdenticalTo(Type *source, Type *target); - bool IsTypeIdenticalTo(Type *source, Type *target, const std::string &errMsg, const lexer::SourcePosition &errPos); - bool IsTypeIdenticalTo(Type *source, Type *target, std::initializer_list list, - const lexer::SourcePosition &errPos); - bool IsTypeAssignableTo(Type *source, Type *target); - bool IsTypeAssignableTo(Type *source, Type *target, const std::string &errMsg, const lexer::SourcePosition &errPos); - bool IsTypeAssignableTo(Type *source, Type *target, std::initializer_list list, - const lexer::SourcePosition &errPos); - bool IsTypeComparableTo(Type *source, Type *target); - bool IsTypeComparableTo(Type *source, Type *target, const std::string &errMsg, const lexer::SourcePosition &errPos); - bool IsTypeComparableTo(Type *source, Type *target, std::initializer_list list, - const lexer::SourcePosition &errPos); - bool AreTypesComparable(Type *source, Type *target); - bool IsTypeEqualityComparableTo(Type *source, Type *target); - bool IsAllTypesAssignableTo(Type *source, Type *target); + Type *CreateParameterTypeForArrayAssignmentPattern(ir::ArrayExpression *arrayPattern, Type *inferedType); + Type *CreateParameterTypeForObjectAssignmentPattern(ir::ObjectExpression *objectPattern, Type *inferedType); // Binary like expression - Type *CheckBinaryOperator(Type *leftType, Type *rightType, const ir::Expression *leftExpr, - const ir::Expression *rightExpr, const ir::AstNode *expr, lexer::TokenType op); - Type *CheckPlusOperator(Type *leftType, Type *rightType, const ir::Expression *leftExpr, - const ir::Expression *rightExpr, const ir::AstNode *expr, lexer::TokenType op); - Type *CheckCompareOperator(Type *leftType, Type *rightType, const ir::Expression *leftExpr, - const ir::Expression *rightExpr, const ir::AstNode *expr, lexer::TokenType op); - Type *CheckAndOperator(Type *leftType, Type *rightType, const ir::Expression *leftExpr); - Type *CheckOrOperator(Type *leftType, Type *rightType, const ir::Expression *leftExpr); - Type *CheckInstanceofExpression(Type *leftType, Type *rightType, const ir::Expression *rightExpr, - const ir::AstNode *expr); - Type *CheckInExpression(Type *leftType, Type *rightType, const ir::Expression *leftExpr, - const ir::Expression *rightExpr, const ir::AstNode *expr); - void CheckAssignmentOperator(lexer::TokenType op, const ir::Expression *leftExpr, Type *leftType, Type *valueType); - - friend class ScopeContext; - friend class SavedCheckerContext; + Type *CheckBinaryOperator(Type *leftType, Type *rightType, ir::Expression *leftExpr, ir::Expression *rightExpr, + ir::AstNode *expr, lexer::TokenType op); + Type *CheckPlusOperator(Type *leftType, Type *rightType, ir::Expression *leftExpr, ir::Expression *rightExpr, + ir::AstNode *expr, lexer::TokenType op); + Type *CheckCompareOperator(Type *leftType, Type *rightType, ir::Expression *leftExpr, ir::Expression *rightExpr, + ir::AstNode *expr, lexer::TokenType op); + Type *CheckAndOperator(Type *leftType, Type *rightType, ir::Expression *leftExpr); + Type *CheckOrOperator(Type *leftType, Type *rightType, ir::Expression *leftExpr); + Type *CheckInstanceofExpression(Type *leftType, Type *rightType, ir::Expression *rightExpr, ir::AstNode *expr); + Type *CheckInExpression(Type *leftType, Type *rightType, ir::Expression *leftExpr, ir::Expression *rightExpr, + ir::AstNode *expr); + void CheckAssignmentOperator(lexer::TokenType op, ir::Expression *leftExpr, Type *leftType, Type *valueType); private: - ArenaAllocator *allocator_; - binder::Binder *binder_; - const ir::BlockStatement *rootNode_; - binder::Scope *scope_; - CheckerContext context_; - GlobalTypesHolder *globalTypes_; - NumberLiteralPool numberLiteralMap_; StringLiteralPool stringLiteralMap_; StringLiteralPool bigintLiteralMap_; - - TypeRelation *relation_; - - RelationHolder identicalResults_; - RelationHolder assignableResults_; - RelationHolder comparableResults_; - - std::unordered_set typeStack_; - std::unordered_map nodeCache_; - std::vector scopeStack_; }; -class ScopeContext { -public: - explicit ScopeContext(Checker *checker, binder::Scope *newScope) : checker_(checker), prevScope_(checker_->scope_) - { - checker_->scope_ = newScope; - } - - ~ScopeContext() - { - checker_->scope_ = prevScope_; - } - - NO_COPY_SEMANTIC(ScopeContext); - NO_MOVE_SEMANTIC(ScopeContext); - -private: - Checker *checker_; - binder::Scope *prevScope_; -}; - -class SavedCheckerContext { -public: - explicit SavedCheckerContext(Checker *checker, CheckerStatus newStatus) - : checker_(checker), prev_(checker->context_) - { - checker_->context_ = CheckerContext(newStatus); - } - - NO_COPY_SEMANTIC(SavedCheckerContext); - DEFAULT_MOVE_SEMANTIC(SavedCheckerContext); - - ~SavedCheckerContext() - { - checker_->context_ = prev_; - } - -private: - Checker *checker_; - CheckerContext prev_; -}; } // namespace panda::es2panda::checker #endif /* CHECKER_H */ diff --git a/typescript/core/typeRelation.cpp b/checker/checker.cpp similarity index 55% rename from typescript/core/typeRelation.cpp rename to checker/checker.cpp index 4bd2675be7187005957ada65edeb40bb3cf1385a..cb3453ef997f63fdda18dbc085481e5eeca23c43 100644 --- a/typescript/core/typeRelation.cpp +++ b/checker/checker.cpp @@ -13,11 +13,77 @@ * limitations under the License. */ -#include "plugins/ecmascript/es2panda/typescript/checker.h" - -#include +#include "checker.h" + +#include "plugins/ecmascript/es2panda/checker/types/type.h" +#include "plugins/ecmascript/es2panda/ir/expression.h" +#include "plugins/ecmascript/es2panda/ir/statements/blockStatement.h" +#include "plugins/ecmascript/es2panda/parser/program/program.h" +#include "plugins/ecmascript/es2panda/util/helpers.h" +#include "plugins/ecmascript/es2panda/binder/binder.h" +#include "plugins/ecmascript/es2panda/binder/scope.h" +#include "plugins/ecmascript/es2panda/binder/variable.h" +#include "plugins/ecmascript/es2panda/es2panda.h" +#include "plugins/ecmascript/es2panda/checker/types/globalTypesHolder.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/unionType.h" +#include "plugins/ecmascript/es2panda/checker/types/signature.h" + +#include +#include +#include namespace panda::es2panda::checker { +Checker::Checker() + : allocator_(SpaceType::SPACE_TYPE_COMPILER, nullptr, true), + context_(CheckerStatus::NO_OPTS), + globalTypes_(allocator_.New(&allocator_)), + relation_(allocator_.New(this)) +{ +} + +void Checker::Initialize(binder::Binder *binder) +{ + binder_ = binder; + scope_ = binder_->TopScope(); + program_ = binder_->Program(); +} + +void Checker::ThrowTypeError(std::initializer_list list, const lexer::SourcePosition &pos) +{ + std::stringstream ss; + + for (const auto &it : list) { + if (std::holds_alternative(it)) { + ss << std::get(it); + } else if (std::holds_alternative(it)) { + ss << std::get(it); + } else if (std::holds_alternative(it)) { + ss << TokenToString(std::get(it)); + } else if (std::holds_alternative(it)) { + std::get(it)->ToString(ss); + } else if (std::holds_alternative(it)) { + std::get(it).GetType()->ToStringAsSrc(ss); + } else if (std::holds_alternative(it)) { + ss << std::to_string(std::get(it)); + } else if (std::holds_alternative(it)) { + std::get(it)->ToString(ss, nullptr, true); + } else { + UNREACHABLE(); + } + } + + std::string err = ss.str(); + ThrowTypeError(err, pos); +} + +void Checker::ThrowTypeError(std::string_view message, const lexer::SourcePosition &pos) +{ + lexer::LineIndex index(program_->SourceCode()); + lexer::SourceLocation loc = index.GetLocation(pos); + + throw Error {ErrorType::TYPE, program_->SourceFile().Utf8(), message, loc.line, loc.col}; +} + bool Checker::IsAllTypesAssignableTo(Type *source, Type *target) { if (source->TypeFlags() == TypeFlag::UNION) { @@ -114,4 +180,20 @@ bool Checker::IsTypeEqualityComparableTo(Type *source, Type *target) { return target->HasTypeFlag(TypeFlag::NULLABLE) || IsTypeComparableTo(source, target); } + +parser::Program *Checker::Program() const +{ + return program_; +} + +void Checker::SetProgram(parser::Program *program) +{ + program_ = program; +} + +binder::Binder *Checker::Binder() const +{ + return binder_; +} + } // namespace panda::es2panda::checker diff --git a/checker/checker.h b/checker/checker.h new file mode 100644 index 0000000000000000000000000000000000000000..10f58f80d98795c72546455711aae080162ede32 --- /dev/null +++ b/checker/checker.h @@ -0,0 +1,268 @@ +/** + * Copyright (c) 2021 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_CHECKER_CHECKER_H +#define ES2PANDA_CHECKER_CHECKER_H + +#include "plugins/ecmascript/es2panda/binder/enumMemberResult.h" +#include "plugins/ecmascript/es2panda/checker/checkerContext.h" +#include "plugins/ecmascript/es2panda/checker/types/typeRelation.h" +#include "plugins/ecmascript/es2panda/util/enumbitops.h" +#include "plugins/ecmascript/es2panda/util/ustring.h" +#include "plugins/ecmascript/es2panda/es2panda.h" +#include "macros.h" + +#include +#include +#include +#include + +namespace panda::es2panda::parser { +class Program; +} // namespace panda::es2panda::parser + +namespace panda::es2panda::ir { +class AstNode; +class Expression; +class BlockStatement; +enum class AstNodeType; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::binder { +class Binder; +class Decl; +class EnumVariable; +class FunctionDecl; +class LocalVariable; +class Scope; +class Variable; +} // namespace panda::es2panda::binder + +namespace panda::es2panda::checker { +class ETSChecker; +class InterfaceType; +class GlobalTypesHolder; + +using StringLiteralPool = std::unordered_map; +using NumberLiteralPool = std::unordered_map; +using FunctionParamsResolveResult = std::variant &, bool>; +using InterfacePropertyMap = std::unordered_map>; +using TypeOrNode = std::variant; +using IndexInfoTypePair = std::pair; +using PropertyMap = std::unordered_map; +using ArgRange = std::pair; + +class Checker { +public: + explicit Checker(); + ~Checker() = default; + NO_COPY_SEMANTIC(Checker); + NO_MOVE_SEMANTIC(Checker); + + ArenaAllocator *Allocator() + { + return &allocator_; + } + + binder::Scope *Scope() const + { + return scope_; + } + + CheckerContext &Context() + { + return context_; + } + + bool HasStatus(CheckerStatus status) + { + return (context_.Status() & status) != 0; + } + + void RemoveStatus(CheckerStatus status) + { + context_.Status() &= ~status; + } + + void AddStatus(CheckerStatus status) + { + context_.Status() |= status; + } + + TypeRelation *Relation() const + { + return relation_; + } + + GlobalTypesHolder *GetGlobalTypesHolder() const + { + return globalTypes_; + } + + RelationHolder &IdenticalResults() + { + return identicalResults_; + } + + RelationHolder &AssignableResults() + { + return assignableResults_; + } + + RelationHolder &ComparableResults() + { + return comparableResults_; + } + + std::unordered_set &TypeStack() + { + return typeStack_; + } + + ETSChecker *AsETSChecker() + { + return reinterpret_cast(this); + } + + virtual bool StartChecker([[maybe_unused]] binder::Binder *binder, const CompilerOptions &options) = 0; + virtual Type *CheckTypeCached(ir::Expression *expr) = 0; + virtual Type *GetTypeOfVariable(binder::Variable *var) = 0; + virtual void ResolveStructuredTypeMembers(Type *type) = 0; + + [[noreturn]] void ThrowTypeError(std::string_view message, const lexer::SourcePosition &pos); + [[noreturn]] void ThrowTypeError(std::initializer_list list, + const lexer::SourcePosition &pos); + + bool IsTypeIdenticalTo(Type *source, Type *target); + bool IsTypeIdenticalTo(Type *source, Type *target, const std::string &errMsg, const lexer::SourcePosition &errPos); + bool IsTypeIdenticalTo(Type *source, Type *target, std::initializer_list list, + const lexer::SourcePosition &errPos); + bool IsTypeAssignableTo(Type *source, Type *target); + bool IsTypeAssignableTo(Type *source, Type *target, const std::string &errMsg, const lexer::SourcePosition &errPos); + bool IsTypeAssignableTo(Type *source, Type *target, std::initializer_list list, + const lexer::SourcePosition &errPos); + bool IsTypeComparableTo(Type *source, Type *target); + bool IsTypeComparableTo(Type *source, Type *target, const std::string &errMsg, const lexer::SourcePosition &errPos); + bool IsTypeComparableTo(Type *source, Type *target, std::initializer_list list, + const lexer::SourcePosition &errPos); + bool AreTypesComparable(Type *source, Type *target); + bool IsTypeEqualityComparableTo(Type *source, Type *target); + bool IsAllTypesAssignableTo(Type *source, Type *target); + + friend class ScopeContext; + friend class TypeStackElement; + friend class SavedCheckerContext; + +protected: + void Initialize(binder::Binder *binder); + parser::Program *Program() const; + void SetProgram(parser::Program *program); + binder::Binder *Binder() const; + +private: + ArenaAllocator allocator_; + CheckerContext context_; + GlobalTypesHolder *globalTypes_; + TypeRelation *relation_; + binder::Binder *binder_ {}; + parser::Program *program_ {}; + binder::Scope *scope_ {}; + + RelationHolder identicalResults_; + RelationHolder assignableResults_; + RelationHolder comparableResults_; + + std::unordered_set typeStack_; +}; + +class TypeStackElement { +public: + explicit TypeStackElement(Checker *checker, void *element, std::initializer_list list, + const lexer::SourcePosition &pos) + : checker_(checker), element_(element) + { + if (!checker->typeStack_.insert(element).second) { + checker_->ThrowTypeError(list, pos); + } + } + + explicit TypeStackElement(Checker *checker, void *element, std::string_view err, const lexer::SourcePosition &pos) + : checker_(checker), element_(element) + { + if (!checker->typeStack_.insert(element).second) { + checker_->ThrowTypeError(err, pos); + } + } + + ~TypeStackElement() + { + checker_->typeStack_.erase(element_); + } + + NO_COPY_SEMANTIC(TypeStackElement); + NO_MOVE_SEMANTIC(TypeStackElement); + +private: + Checker *checker_; + void *element_; +}; + +class ScopeContext { +public: + explicit ScopeContext(Checker *checker, binder::Scope *newScope) : checker_(checker), prevScope_(checker_->scope_) + { + checker_->scope_ = newScope; + } + + ~ScopeContext() + { + checker_->scope_ = prevScope_; + } + + NO_COPY_SEMANTIC(ScopeContext); + NO_MOVE_SEMANTIC(ScopeContext); + +private: + Checker *checker_; + binder::Scope *prevScope_; +}; + +class SavedCheckerContext { +public: + explicit SavedCheckerContext(Checker *checker, CheckerStatus newStatus) + : SavedCheckerContext(checker, newStatus, nullptr) + { + } + explicit SavedCheckerContext(Checker *checker, CheckerStatus newStatus, ETSObjectType *containingClass) + : checker_(checker), prev_(checker->context_) + { + checker_->context_ = CheckerContext(newStatus, containingClass); + } + + NO_COPY_SEMANTIC(SavedCheckerContext); + DEFAULT_MOVE_SEMANTIC(SavedCheckerContext); + + ~SavedCheckerContext() + { + checker_->context_ = prev_; + } + +private: + Checker *checker_; + CheckerContext prev_; +}; +} // namespace panda::es2panda::checker + +#endif /* CHECKER_H */ diff --git a/typescript/core/checkerContext.cpp b/checker/checkerContext.cpp similarity index 100% rename from typescript/core/checkerContext.cpp rename to checker/checkerContext.cpp diff --git a/checker/checkerContext.h b/checker/checkerContext.h new file mode 100644 index 0000000000000000000000000000000000000000..877b00dcbfe3c946a9d2222acba84374dd84d459 --- /dev/null +++ b/checker/checkerContext.h @@ -0,0 +1,97 @@ +/** + * Copyright (c) 2021 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_CHECKER_CHECKER_CONTEXT_H +#define ES2PANDA_CHECKER_CHECKER_CONTEXT_H + +#include +#include "plugins/ecmascript/es2panda/util/enumbitops.h" + +#include + +namespace panda::es2panda::checker { + +class ETSObjectType; +class Signature; + +enum class CheckerStatus : uint32_t { + NO_OPTS = 0U, + FORCE_TUPLE = 1U << 0U, + IN_CONST_CONTEXT = 1U << 1U, + KEEP_LITERAL_TYPE = 1U << 2U, + IN_PARAMETER = 1U << 3U, + IN_CLASS = 1U << 4U, + IN_INTERFACE = 1U << 5U, + IN_ABSTRACT = 1U << 6U, + IN_STATIC_CONTEXT = 1U << 7U, + IN_CONSTRUCTOR = 1U << 8U, + IN_STATIC_BLOCK = 1U << 9U, + INNER_CLASS = 1U << 10U, + IN_ENUM = 1U << 11U, + BUILTINS_INITIALIZED = 1U << 12U, +}; + +DEFINE_BITOPS(CheckerStatus) + +class CheckerContext { +public: + explicit CheckerContext(CheckerStatus newStatus) : CheckerContext(newStatus, nullptr) {} + explicit CheckerContext(CheckerStatus newStatus, ETSObjectType *containingClass) + : status_(newStatus), containingClass_(containingClass) + { + } + + const CheckerStatus &Status() const + { + return status_; + } + + ETSObjectType *ContainingClass() const + { + return containingClass_; + } + + Signature *ContainingSignature() const + { + return containingSignature_; + } + + CheckerStatus &Status() + { + return status_; + } + + void SetContainingSignature(Signature *containingSignature) + { + containingSignature_ = containingSignature; + } + + void SetContainingClass(ETSObjectType *containingClass) + { + containingClass_ = containingClass; + } + + DEFAULT_COPY_SEMANTIC(CheckerContext); + DEFAULT_MOVE_SEMANTIC(CheckerContext); + ~CheckerContext() = default; + +private: + CheckerStatus status_; + ETSObjectType *containingClass_ {}; + Signature *containingSignature_ {nullptr}; +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/ets/aliveAnalyzer.cpp b/checker/ets/aliveAnalyzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2132fb8f0bba35b812145c72d383d83d14691ea0 --- /dev/null +++ b/checker/ets/aliveAnalyzer.cpp @@ -0,0 +1,422 @@ +/** + * Copyright (c) 2021 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 "aliveAnalyzer.h" +#include + +#include "plugins/ecmascript/es2panda/ir/base/classDefinition.h" +#include "plugins/ecmascript/es2panda/ir/base/classProperty.h" +#include "plugins/ecmascript/es2panda/ir/base/methodDefinition.h" +#include "plugins/ecmascript/es2panda/ir/base/scriptFunction.h" +#include "plugins/ecmascript/es2panda/ir/statements/classDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/statements/variableDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/statements/doWhileStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/expressionStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/whileStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/forUpdateStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/labelledStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/forOfStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/blockStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/ifStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/switchStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/variableDeclarator.h" +#include "plugins/ecmascript/es2panda/ir/statements/throwStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/switchCaseStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/breakStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/continueStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/returnStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/tryStatement.h" +#include "plugins/ecmascript/es2panda/ir/expressions/callExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsNewClassInstanceExpression.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceDeclaration.h" +#include "plugins/ecmascript/es2panda/binder/variable.h" +#include "plugins/ecmascript/es2panda/binder/scope.h" +#include "plugins/ecmascript/es2panda/binder/declaration.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" + +namespace panda::es2panda::checker { + +void AliveAnalyzer::AnalyzeNodes(const ir::AstNode *node) +{ + node->Iterate([this](auto *childNode) { AnalyzeNode(childNode); }); +} + +void AliveAnalyzer::AnalyzeNode(const ir::AstNode *node) +{ + if (node == nullptr) { + return; + } + + switch (node->Type()) { + case ir::AstNodeType::EXPRESSION_STATEMENT: { + AnalyzeNode(node->AsExpressionStatement()->GetExpression()); + break; + } + case ir::AstNodeType::CLASS_DECLARATION: { + AnalyzeClassDecl(node->AsClassDeclaration()); + break; + } + case ir::AstNodeType::CLASS_DEFINITION: { + AnalyzeClassDef(node->AsClassDefinition()); + break; + } + case ir::AstNodeType::METHOD_DEFINITION: { + AnalyzeMethodDef(node->AsMethodDefinition()); + break; + } + case ir::AstNodeType::VARIABLE_DECLARATION: { + AnalyzeVarDef(node->AsVariableDeclaration()); + break; + } + case ir::AstNodeType::BLOCK_STATEMENT: { + AnalyzeStats(node->AsBlockStatement()->Statements()); + break; + } + case ir::AstNodeType::DO_WHILE_STATEMENT: { + AnalyzeDoLoop(node->AsDoWhileStatement()); + break; + } + case ir::AstNodeType::WHILE_STATEMENT: { + AnalyzeWhileLoop(node->AsWhileStatement()); + break; + } + case ir::AstNodeType::FOR_UPDATE_STATEMENT: { + AnalyzeForLoop(node->AsForUpdateStatement()); + break; + } + case ir::AstNodeType::FOR_OF_STATEMENT: { + AnalyzeForOfLoop(node->AsForOfStatement()); + break; + } + case ir::AstNodeType::IF_STATEMENT: { + AnalyzeIf(node->AsIfStatement()); + break; + } + case ir::AstNodeType::LABELLED_STATEMENT: { + AnalyzeLabelled(node->AsLabelledStatement()); + break; + } + case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: { + AnalyzeNewClass(node->AsETSNewClassInstanceExpression()); + break; + } + case ir::AstNodeType::CALL_EXPRESSION: { + AnalyzeCall(node->AsCallExpression()); + break; + } + case ir::AstNodeType::THROW_STATEMENT: { + AnalyzeThrow(node->AsThrowStatement()); + break; + } + case ir::AstNodeType::SWITCH_STATEMENT: { + AnalyzeSwitch(node->AsSwitchStatement()); + break; + } + case ir::AstNodeType::TRY_STATEMENT: { + AnalyzeTry(node->AsTryStatement()); + break; + } + case ir::AstNodeType::BREAK_STATEMENT: { + AnalyzeBreak(node->AsBreakStatement()); + break; + } + case ir::AstNodeType::CONTINUE_STATEMENT: { + AnalyzeContinue(node->AsContinueStatement()); + break; + } + case ir::AstNodeType::RETURN_STATEMENT: { + AnalyzeReturn(node->AsReturnStatement()); + break; + } + default: { + break; + } + } +} + +void AliveAnalyzer::AnalyzeDef(const ir::AstNode *node) +{ + AnalyzeStat(node); + if (node != nullptr && node->IsClassStaticBlock() && status_ == LivenessStatus::DEAD) { + checker_->ThrowTypeError("Initializer must be able to complete normally.", node->Start()); + } +} + +void AliveAnalyzer::AnalyzeStat(const ir::AstNode *node) +{ + if (node == nullptr) { + return; + } + + if (status_ == LivenessStatus::DEAD) { + checker_->ThrowTypeError("Unreachable statement.", node->Start()); + } + + if (node->IsClassStaticBlock()) { + AnalyzeNodes(node); + return; + } + + AnalyzeNode(node); +} + +void AliveAnalyzer::AnalyzeStats(const ArenaVector &stats) +{ + for (const auto *it : stats) { + AnalyzeStat(it); + } +} + +static bool IsStaticMember(const ir::AstNode *node) +{ + switch (node->Type()) { + case ir::AstNodeType::CLASS_PROPERTY: { + return node->IsStatic(); + } + case ir::AstNodeType::CLASS_DECLARATION: { + return node->AsClassDeclaration()->Definition()->IsStatic(); + } + case ir::AstNodeType::TS_INTERFACE_DECLARATION: { + return node->IsStatic(); + } + default: { + return false; + } + } +} + +void AliveAnalyzer::AnalyzeClassDecl(const ir::ClassDeclaration *classDecl) +{ + for (const auto *it : classDecl->Definition()->Body()) { + AnalyzeNode(it); + } +} + +void AliveAnalyzer::AnalyzeClassDef(const ir::ClassDefinition *classDef) +{ + if (classDef->Variable() == nullptr) { + return; + } + + LivenessStatus prevStatus = status_; + SetOldPendingExits(PendingExits()); + + for (const auto *it : classDef->Body()) { + if (!it->IsMethodDefinition() && IsStaticMember(it)) { + AnalyzeDef(it); + ClearPendingExits(); + } + } + + for (const auto *it : classDef->Body()) { + if (!it->IsMethodDefinition() && !IsStaticMember(it)) { + AnalyzeDef(it); + ClearPendingExits(); + } + } + + for (const auto *it : classDef->Body()) { + if (it->IsClassStaticBlock()) { + AnalyzeDef(it); + break; + } + } + + for (const auto *it : classDef->Body()) { + if (it->IsMethodDefinition()) { + AnalyzeNode(it); + } + } + + SetPendingExits(OldPendingExits()); + status_ = prevStatus; +} + +void AliveAnalyzer::AnalyzeMethodDef(const ir::MethodDefinition *methodDef) +{ + auto *func = methodDef->Function(); + + if (func->Body() == nullptr) { + return; + } + + status_ = LivenessStatus::ALIVE; + AnalyzeStat(func->Body()); + ASSERT(methodDef->TsType() && methodDef->TsType()->IsETSFunctionType()); + + if (status_ == LivenessStatus::ALIVE && + !methodDef->TsType()->AsETSFunctionType()->FindSignature(func)->ReturnType()->IsETSVoidType()) { + checker_->ThrowTypeError("Function with a non void return type must return a value.", func->Id()->Start()); + } + + ClearPendingExits(); +} + +void AliveAnalyzer::AnalyzeVarDef(const ir::VariableDeclaration *varDef) +{ + for (auto *it : varDef->Declarators()) { + if (it->Init() == nullptr) { + continue; + } + + AnalyzeNode(it->Init()); + } +} + +void AliveAnalyzer::AnalyzeDoLoop(const ir::DoWhileStatement *doWhile) +{ + SetOldPendingExits(PendingExits()); + AnalyzeStat(doWhile->Body()); + status_ = Or(status_, ResolveContinues(doWhile)); + AnalyzeNode(doWhile->Test()); + ASSERT(doWhile->Test()->TsType() && doWhile->Test()->TsType()->IsETSBooleanType()); + auto *condType = doWhile->Test()->TsType()->AsETSBooleanType(); + status_ = + And(status_, static_cast(!(condType->HasTypeFlag(TypeFlag::CONSTANT) && condType->GetValue()))); + status_ = Or(status_, ResolveBreaks(doWhile)); +} + +void AliveAnalyzer::AnalyzeWhileLoop(const ir::WhileStatement *whileStmt) +{ + SetOldPendingExits(PendingExits()); + AnalyzeNode(whileStmt->Test()); + ASSERT(whileStmt->Test()->TsType() && whileStmt->Test()->TsType()->IsETSBooleanType()); + auto *condType = whileStmt->Test()->TsType()->AsETSBooleanType(); + status_ = And(status_, + static_cast(!(condType->HasTypeFlag(TypeFlag::CONSTANT) && !condType->GetValue()))); + AnalyzeStat(whileStmt->Body()); + status_ = Or(status_, ResolveContinues(whileStmt)); + status_ = Or(ResolveBreaks(whileStmt), From(!(condType->HasTypeFlag(TypeFlag::CONSTANT) && condType->GetValue()))); +} + +void AliveAnalyzer::AnalyzeForLoop(const ir::ForUpdateStatement *forStmt) +{ + AnalyzeNode(forStmt->Init()); + SetOldPendingExits(PendingExits()); + const ETSBooleanType *condType {}; + if (forStmt->Test() != nullptr) { + AnalyzeNode(forStmt->Test()); + ASSERT(forStmt->Test()->TsType() && forStmt->Test()->TsType()->IsETSBooleanType()); + condType = forStmt->Test()->TsType()->AsETSBooleanType(); + status_ = From(!(condType->HasTypeFlag(TypeFlag::CONSTANT) && !condType->GetValue())); + } else { + status_ = LivenessStatus::ALIVE; + } + + AnalyzeStat(forStmt->Body()); + status_ = Or(status_, ResolveContinues(forStmt)); + AnalyzeNode(forStmt->Update()); + status_ = Or(ResolveBreaks(forStmt), + From(condType != nullptr && !(condType->HasTypeFlag(TypeFlag::CONSTANT) && condType->GetValue()))); +} + +void AliveAnalyzer::AnalyzeForOfLoop(const ir::ForOfStatement *forOfStmt) +{ + ASSERT(forOfStmt->Left()->IsVariableDeclaration()); + AnalyzeVarDef(forOfStmt->Left()->AsVariableDeclaration()); + AnalyzeNode(forOfStmt->Right()); + SetOldPendingExits(PendingExits()); + AnalyzeStat(forOfStmt->Body()); + status_ = Or(status_, ResolveContinues(forOfStmt)); + ResolveBreaks(forOfStmt); + status_ = LivenessStatus::ALIVE; +} + +void AliveAnalyzer::AnalyzeIf(const ir::IfStatement *ifStmt) +{ + AnalyzeNode(ifStmt->Test()); + AnalyzeStat(ifStmt->Consequent()); + if (ifStmt->Alternate() != nullptr) { + LivenessStatus prevStatus = status_; + status_ = LivenessStatus::ALIVE; + AnalyzeStat(ifStmt->Alternate()); + status_ = Or(status_, prevStatus); + } else { + status_ = LivenessStatus::ALIVE; + } +} + +void AliveAnalyzer::AnalyzeLabelled(const ir::LabelledStatement *labelledStmt) +{ + SetOldPendingExits(PendingExits()); + AnalyzeStat(labelledStmt->Body()); + status_ = Or(status_, ResolveBreaks(labelledStmt)); +} + +void AliveAnalyzer::AnalyzeNewClass(const ir::ETSNewClassInstanceExpression *newClass) +{ + for (const auto *it : newClass->GetArguments()) { + AnalyzeNode(it); + } + + if (newClass->ClassDefinition() != nullptr) { + AnalyzeNode(newClass->ClassDefinition()); + } +} + +void AliveAnalyzer::AnalyzeCall(const ir::CallExpression *callExpr) +{ + AnalyzeNode(callExpr->Callee()); + for (const auto *it : callExpr->Arguments()) { + AnalyzeNode(it); + } +} + +void AliveAnalyzer::AnalyzeThrow(const ir::ThrowStatement *throwStmt) +{ + AnalyzeNode(throwStmt->Argument()); + MarkDead(); +} + +void AliveAnalyzer::AnalyzeSwitch([[maybe_unused]] const ir::SwitchStatement *switchStmt) +{ + // TODO(aszilagyi): fix analysis to be correct + /* + PendingExitsVector oldPendingExits = std::move(pendingExits_); + AnalyzeNode(switchStmt->Discriminant()); + + std::vector constants; + for (const auto *it : switchStmt->Cases()) { + status_ = LivenessStatus::ALIVE; + AnalyzeNode(it->Test()); + AnalyzeStats(it->Consequent()); + } + + status_ = Or(status_, ResolveBreaks(switchStmt, oldPendingExits)); + */ +} + +void AliveAnalyzer::AnalyzeBreak(const ir::BreakStatement *breakStmt) +{ + RecordExit(PendingExit(breakStmt)); +} + +void AliveAnalyzer::AnalyzeContinue(const ir::ContinueStatement *contStmt) +{ + RecordExit(PendingExit(contStmt)); +} + +void AliveAnalyzer::AnalyzeReturn(const ir::ReturnStatement *retStmt) +{ + AnalyzeNode(retStmt->Argument()); + RecordExit(PendingExit(retStmt)); +} + +void AliveAnalyzer::AnalyzeTry([[maybe_unused]] const ir::TryStatement *tryStmt) +{ + // TODO(user): +} +} // namespace panda::es2panda::checker diff --git a/checker/ets/aliveAnalyzer.h b/checker/ets/aliveAnalyzer.h new file mode 100644 index 0000000000000000000000000000000000000000..7a5bce9d6d4aeee006f8743c50b7ac419e649700 --- /dev/null +++ b/checker/ets/aliveAnalyzer.h @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_ETS_ALIVE_ANALYZER_H +#define ES2PANDA_COMPILER_CHECKER_ETS_ALIVE_ANALYZER_H + +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" +#include "plugins/ecmascript/es2panda/checker/ets/baseAnalyzer.h" + +#include "utils/arena_containers.h" + +namespace panda::es2panda::ir { +class AstNode; +class Statement; +class ClassDefinition; +class MethodDefinition; +class DoWhileStatement; +class VariableDeclaration; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::checker { +class AliveAnalyzer : public BaseAnalyzer { +public: + // NOLINTNEXTLINE(readability-redundant-member-init) + AliveAnalyzer(const ir::AstNode *node, ETSChecker *checker) : BaseAnalyzer(), checker_(checker) + { + AnalyzeNodes(node); + } + + void MarkDead() override + { + status_ = LivenessStatus::DEAD; + } + + LivenessStatus Or(LivenessStatus left, LivenessStatus right) + { + return static_cast(left | right); + } + + LivenessStatus And(LivenessStatus left, LivenessStatus right) + { + return static_cast(left & right); + } + +private: + void AnalyzeNodes(const ir::AstNode *node); + void AnalyzeNode(const ir::AstNode *node); + void AnalyzeDef(const ir::AstNode *node); + void AnalyzeStat(const ir::AstNode *node); + void AnalyzeStats(const ArenaVector &stats); + void AnalyzeClassDecl(const ir::ClassDeclaration *classDecl); + void AnalyzeClassDef(const ir::ClassDefinition *classDef); + void AnalyzeMethodDef(const ir::MethodDefinition *methodDef); + void AnalyzeVarDef(const ir::VariableDeclaration *varDef); + void AnalyzeDoLoop(const ir::DoWhileStatement *doWhile); + void AnalyzeWhileLoop(const ir::WhileStatement *whileStmt); + void AnalyzeForLoop(const ir::ForUpdateStatement *forStmt); + void AnalyzeForOfLoop(const ir::ForOfStatement *forOfStmt); + void AnalyzeIf(const ir::IfStatement *ifStmt); + void AnalyzeLabelled(const ir::LabelledStatement *labelledStmt); + void AnalyzeNewClass(const ir::ETSNewClassInstanceExpression *newClass); + void AnalyzeCall(const ir::CallExpression *callExpr); + void AnalyzeThrow(const ir::ThrowStatement *throwStmt); + void AnalyzeSwitch(const ir::SwitchStatement *switchStmt); + void AnalyzeTry(const ir::TryStatement *tryStmt); + void AnalyzeBreak(const ir::BreakStatement *breakStmt); + void AnalyzeContinue(const ir::ContinueStatement *contStmt); + void AnalyzeReturn(const ir::ReturnStatement *retStmt); + + ETSChecker *checker_; + LivenessStatus status_ {LivenessStatus::ALIVE}; +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/ets/arithmetic.cpp b/checker/ets/arithmetic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b51a74683f79641a099a9f794f585e7853a5a724 --- /dev/null +++ b/checker/ets/arithmetic.cpp @@ -0,0 +1,349 @@ +/** + * Copyright (c) 2021 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 "arithmetic.h" + +#include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" +#include "plugins/ecmascript/es2panda/binder/variable.h" +#include "plugins/ecmascript/es2panda/binder/scope.h" +#include "plugins/ecmascript/es2panda/binder/declaration.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" + +namespace panda::es2panda::checker { + +Type *ETSChecker::NegateNumericType(Type *type, ir::Expression *node) +{ + ASSERT(type->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_NUMERIC)); + + TypeFlag typeKind = ETSType(type); + Type *result = nullptr; + + switch (typeKind) { + case TypeFlag::BYTE: { + result = CreateByteType(-(type->AsByteType()->GetValue())); + break; + } + case TypeFlag::CHAR: { + result = CreateCharType(-(type->AsCharType()->GetValue())); + break; + } + case TypeFlag::SHORT: { + result = CreateShortType(-(type->AsShortType()->GetValue())); + break; + } + case TypeFlag::INT: { + result = CreateIntType(-(type->AsIntType()->GetValue())); + break; + } + case TypeFlag::LONG: { + result = CreateLongType(-(type->AsLongType()->GetValue())); + break; + } + case TypeFlag::FLOAT: { + result = CreateFloatType(-(type->AsFloatType()->GetValue())); + break; + } + case TypeFlag::DOUBLE: { + result = CreateDoubleType(-(type->AsDoubleType()->GetValue())); + break; + } + default: { + UNREACHABLE(); + } + } + + node->SetTsType(result); + return result; +} + +Type *ETSChecker::BitwiseNegateIntegralType(Type *type, ir::Expression *node) +{ + ASSERT(type->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_INTEGRAL)); + + TypeFlag typeKind = ETSType(type); + + Type *result = nullptr; + + switch (typeKind) { + case TypeFlag::BYTE: { + result = CreateByteType(static_cast(~static_cast(type->AsByteType()->GetValue()))); + break; + } + case TypeFlag::CHAR: { + result = CreateCharType(~(type->AsCharType()->GetValue())); + break; + } + case TypeFlag::SHORT: { + result = CreateShortType(static_cast(~static_cast(type->AsShortType()->GetValue()))); + break; + } + case TypeFlag::INT: { + result = CreateIntType(static_cast(~static_cast(type->AsIntType()->GetValue()))); + break; + } + case TypeFlag::LONG: { + result = CreateLongType(static_cast(~static_cast(type->AsLongType()->GetValue()))); + break; + } + default: { + UNREACHABLE(); + } + } + + node->SetTsType(result); + return result; +} + +Type *ETSChecker::HandleRelationOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType) +{ + ASSERT(left->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_NUMERIC) && + right->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_NUMERIC)); + + if (left->IsDoubleType() || right->IsDoubleType()) { + return PerformRelationOperationOnTypes(left, right, operationType); + } + + if (left->IsFloatType() || right->IsFloatType()) { + return PerformRelationOperationOnTypes(left, right, operationType); + } + + if (left->IsLongType() || right->IsLongType()) { + return PerformRelationOperationOnTypes(left, right, operationType); + } + + return PerformRelationOperationOnTypes(left, right, operationType); +} + +std::tuple ETSChecker::CheckBinaryOperator(ir::Expression *left, ir::Expression *right, + lexer::TokenType operationType, lexer::SourcePosition pos) +{ + checker::Type *leftType = left->Check(this); + checker::Type *rightType = right->Check(this); + checker::Type *tsType {}; + + switch (operationType) { + case lexer::TokenType::PUNCTUATOR_MULTIPLY: + case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL: + case lexer::TokenType::PUNCTUATOR_DIVIDE: + case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL: + case lexer::TokenType::PUNCTUATOR_MOD: + case lexer::TokenType::PUNCTUATOR_MOD_EQUAL: { + auto [promotedType, bothConst] = ApplyBinaryOperatorPromotion(leftType, rightType, TypeFlag::ETS_NUMERIC); + + if (promotedType == nullptr && !bothConst) { + ThrowTypeError("Bad operand type, the types of the operands must be numeric type.", pos); + } + + if (bothConst) { + tsType = HandleArithmeticOperationOnTypes(leftType, rightType, operationType); + break; + } + + tsType = promotedType; + break; + } + + case lexer::TokenType::PUNCTUATOR_MINUS: + case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL: { + if (leftType->IsETSStringType() || rightType->IsETSStringType()) { + ThrowTypeError("Bad operand type, the types of the operands must be numeric type.", pos); + } + + [[fallthrough]]; + } + case lexer::TokenType::PUNCTUATOR_PLUS: + case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: { + if (leftType->IsETSStringType() || rightType->IsETSStringType()) { + tsType = HandleStringConcatenation(leftType, rightType); + break; + } + + auto [promotedType, bothConst] = ApplyBinaryOperatorPromotion(leftType, rightType, TypeFlag::ETS_NUMERIC); + + if (promotedType == nullptr && !bothConst) { + ThrowTypeError("Bad operand type, the types of the operands must be numeric type or String.", pos); + } + + if (bothConst) { + tsType = HandleArithmeticOperationOnTypes(leftType, rightType, operationType); + break; + } + + tsType = promotedType; + break; + } + case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT: + case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL: + case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT: + case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL: + case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT: + case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL: { + leftType = ApplyUnaryOperatorPromotion(leftType, false); + rightType = ApplyUnaryOperatorPromotion(rightType, false); + + if (leftType == nullptr || !leftType->HasTypeFlag(checker::TypeFlag::ETS_INTEGRAL) || + rightType == nullptr || !rightType->HasTypeFlag(checker::TypeFlag::ETS_INTEGRAL)) { + ThrowTypeError("Bad operand type, the types of the operands must be integral type.", pos); + } + + if (leftType->HasTypeFlag(TypeFlag::CONSTANT) && rightType->HasTypeFlag(TypeFlag::CONSTANT)) { + tsType = HandleArithmeticOperationOnTypes(leftType, rightType, operationType); + break; + } + + switch (ETSType(leftType)) { + case TypeFlag::INT: { + tsType = GlobalIntType(); + break; + } + case TypeFlag::LONG: { + tsType = GlobalLongType(); + break; + } + default: { + UNREACHABLE(); + } + } + + break; + } + case lexer::TokenType::PUNCTUATOR_BITWISE_OR: + case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: + case lexer::TokenType::PUNCTUATOR_BITWISE_AND: + case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL: + case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL: + case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: { + Type *unboxedL = ETSBuiltinTypeAsPrimitiveType(leftType); + Type *unboxedR = ETSBuiltinTypeAsPrimitiveType(rightType); + if (unboxedL != nullptr && unboxedL->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) && unboxedR != nullptr && + unboxedR->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN)) { + tsType = HandleBooleanLogicalOperators(unboxedL, unboxedR, operationType); + break; + } + + auto [promotedType, bothConst] = ApplyBinaryOperatorPromotion(leftType, rightType, TypeFlag::ETS_INTEGRAL); + + if (promotedType == nullptr && !bothConst) { + ThrowTypeError("Bad operand type, the types of the operands must be integral type.", pos); + } + + if (bothConst) { + tsType = HandleArithmeticOperationOnTypes(leftType, rightType, operationType); + break; + } + + tsType = promotedType; + break; + } + case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: + case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: { + Type *unboxedL = ETSBuiltinTypeAsPrimitiveType(leftType); + Type *unboxedR = ETSBuiltinTypeAsPrimitiveType(rightType); + if (unboxedL == nullptr || !unboxedL->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) || unboxedR == nullptr || + !unboxedR->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN)) { + ThrowTypeError("Bad operand type, the types of the operands must be boolean type.", pos); + } + + tsType = HandleBooleanLogicalOperators(unboxedL, unboxedR, operationType); + break; + } + + case lexer::TokenType::PUNCTUATOR_EQUAL: + case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: { + if ((leftType->IsETSObjectType() && rightType->IsETSObjectType()) || + (leftType->IsETSStringType() && rightType->IsETSStringType())) { + tsType = GlobalETSBooleanType(); + auto *opType = GlobalETSObjectType(); + return {tsType, opType}; + } + + Type *unboxedL = ETSBuiltinTypeAsPrimitiveType(leftType); + Type *unboxedR = ETSBuiltinTypeAsPrimitiveType(rightType); + if (unboxedL != nullptr && unboxedL->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) && unboxedR != nullptr && + unboxedR->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN)) { + if (unboxedL->HasTypeFlag(checker::TypeFlag::CONSTANT) && + unboxedR->HasTypeFlag(checker::TypeFlag::CONSTANT)) { + bool res = unboxedL->AsETSBooleanType()->GetValue() == unboxedR->AsETSBooleanType()->GetValue(); + + tsType = CreateETSBooleanType(operationType == lexer::TokenType::PUNCTUATOR_EQUAL ? res : !res); + break; + } + + tsType = GlobalETSBooleanType(); + break; + } + + [[fallthrough]]; + } + case lexer::TokenType::PUNCTUATOR_LESS_THAN: + case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: + case lexer::TokenType::PUNCTUATOR_GREATER_THAN: + case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: { + auto [promotedType, bothConst] = ApplyBinaryOperatorPromotion(leftType, rightType, TypeFlag::ETS_NUMERIC); + + if (promotedType == nullptr && !bothConst) { + ThrowTypeError("Bad operand type, the types of the operands must be numeric type.", pos); + } + + if (bothConst) { + tsType = HandleRelationOperationOnTypes(leftType, rightType, operationType); + break; + } + + tsType = GlobalETSBooleanType(); + auto *opType = promotedType; + return {tsType, opType}; + } + case lexer::TokenType::KEYW_INSTANCEOF: { + if (!leftType->HasTypeFlag(checker::TypeFlag::ETS_OBJECT) || + !rightType->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) { + ThrowTypeError("Bad operand type, the types of the operands must be same type.", pos); + } + + tsType = GlobalETSBooleanType(); + auto *opType = GlobalETSObjectType(); + return {tsType, opType}; + } + default: { + // TODO(user): + UNREACHABLE(); + break; + } + } + + return {tsType, tsType}; +} + +Type *ETSChecker::HandleArithmeticOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType) +{ + ASSERT(left->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_NUMERIC) && + right->HasTypeFlag(TypeFlag::CONSTANT | TypeFlag::ETS_NUMERIC)); + + if (left->IsDoubleType() || right->IsDoubleType()) { + return PerformArithmeticOperationOnTypes(left, right, operationType); + } + + if (left->IsFloatType() || right->IsFloatType()) { + return PerformArithmeticOperationOnTypes(left, right, operationType); + } + + if (left->IsLongType() || right->IsLongType()) { + return PerformArithmeticOperationOnTypes(left, right, operationType); + } + + return PerformArithmeticOperationOnTypes(left, right, operationType); +} + +} // namespace panda::es2panda::checker diff --git a/checker/ets/arithmetic.h b/checker/ets/arithmetic.h new file mode 100644 index 0000000000000000000000000000000000000000..919b78c5cd26d5b348da98911d75a26c03cd1577 --- /dev/null +++ b/checker/ets/arithmetic.h @@ -0,0 +1,224 @@ +/** + * Copyright (c) 2021 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_CHECKER_ETS_ARITHMETIC_H +#define ES2PANDA_COMPILER_CHECKER_ETS_ARITHMETIC_H + +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/etsBooleanType.h" + +namespace panda::es2panda::checker { + +template +typename TargetType::UType ETSChecker::GetOperand(Type *type) +{ + switch (ETSType(type)) { + case TypeFlag::BYTE: { + return type->AsByteType()->GetValue(); + } + case TypeFlag::CHAR: { + return type->AsCharType()->GetValue(); + } + case TypeFlag::SHORT: { + return type->AsShortType()->GetValue(); + } + case TypeFlag::INT: { + return type->AsIntType()->GetValue(); + } + case TypeFlag::LONG: { + return type->AsLongType()->GetValue(); + } + case TypeFlag::FLOAT: { + return type->AsFloatType()->GetValue(); + } + case TypeFlag::DOUBLE: { + return type->AsDoubleType()->GetValue(); + } + default: { + UNREACHABLE(); + } + } +} + +template +Type *ETSChecker::PerformRelationOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType) +{ + using UType = typename TargetType::UType; + + UType leftValue = GetOperand(left); + UType rightValue = GetOperand(right); + + bool result {}; + switch (operationType) { + case lexer::TokenType::PUNCTUATOR_LESS_THAN: { + result = leftValue < rightValue; + break; + } + case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: { + result = leftValue <= rightValue; + break; + } + case lexer::TokenType::PUNCTUATOR_GREATER_THAN: { + result = leftValue > rightValue; + break; + } + case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: { + result = leftValue >= rightValue; + break; + } + case lexer::TokenType::PUNCTUATOR_EQUAL: { + result = leftValue == rightValue; + break; + } + case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: { + result = leftValue != rightValue; + break; + } + default: { + UNREACHABLE(); + } + } + + return CreateETSBooleanType(result); +} + +template +Type *ETSChecker::PerformArithmeticOperationOnTypes(Type *left, Type *right, lexer::TokenType operationType) +{ + using UType = typename TargetType::UType; + + UType leftValue = GetOperand(left); + UType rightValue = GetOperand(right); + auto result = leftValue; + + switch (operationType) { + case lexer::TokenType::PUNCTUATOR_PLUS: + case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: { + result = leftValue + rightValue; + break; + } + case lexer::TokenType::PUNCTUATOR_MINUS: + case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL: { + result = leftValue - rightValue; + break; + } + case lexer::TokenType::PUNCTUATOR_DIVIDE: + case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL: { + result = leftValue / rightValue; + break; + } + case lexer::TokenType::PUNCTUATOR_MULTIPLY: + case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL: { + result = leftValue * rightValue; + break; + } + case lexer::TokenType::PUNCTUATOR_MOD: + case lexer::TokenType::PUNCTUATOR_MOD_EQUAL: { + result = HandleModulo(leftValue, rightValue); + break; + } + default: { + result = HandleBitWiseArithmetic(leftValue, rightValue, operationType); + } + } + + return Allocator()->New(result); +} + +template <> +inline IntType::UType panda::es2panda::checker::ETSChecker::HandleModulo(IntType::UType leftValue, + IntType::UType rightValue) +{ + return leftValue % rightValue; +} + +template <> +inline LongType::UType panda::es2panda::checker::ETSChecker::HandleModulo(LongType::UType leftValue, + LongType::UType rightValue) +{ + return leftValue % rightValue; +} + +template <> +inline FloatType::UType panda::es2panda::checker::ETSChecker::HandleModulo( + FloatType::UType leftValue, FloatType::UType rightValue) +{ + return std::fmod(leftValue, rightValue); +} + +template <> +inline DoubleType::UType panda::es2panda::checker::ETSChecker::HandleModulo( + DoubleType::UType leftValue, DoubleType::UType rightValue) +{ + return std::fmod(leftValue, rightValue); +} + +template +UType ETSChecker::HandleBitWiseArithmetic(UType leftValue, UType rightValue, lexer::TokenType operationType) +{ + auto unsignedLeftValue = static_cast::type>(leftValue); + auto unsignedRightValue = static_cast::type>(rightValue); + + switch (operationType) { + case lexer::TokenType::PUNCTUATOR_BITWISE_AND: + case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL: { + return unsignedLeftValue & unsignedRightValue; + } + case lexer::TokenType::PUNCTUATOR_BITWISE_OR: + case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: { + return unsignedLeftValue | unsignedRightValue; + } + case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: + case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL: { + return unsignedLeftValue ^ unsignedRightValue; + } + case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT: + case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL: { + return unsignedLeftValue << unsignedRightValue; + } + case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT: + case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL: { + // NOLINTNEXTLINE(hicpp-signed-bitwise) + return leftValue >> (rightValue & (std::numeric_limits::digits - 1)); + } + case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT: + case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL: { + static_assert(sizeof(UType) == 4 || sizeof(UType) == 8); + return unsignedLeftValue >> (unsignedRightValue & (std::numeric_limits::digits - 1U)); + } + default: { + UNREACHABLE(); + } + } +} + +template <> +inline FloatType::UType ETSChecker::HandleBitWiseArithmetic( + [[maybe_unused]] FloatType::UType leftValue, [[maybe_unused]] FloatType::UType rightValue, + [[maybe_unused]] lexer::TokenType operationType) +{ + return 0.0; +} + +template <> +inline DoubleType::UType ETSChecker::HandleBitWiseArithmetic( + [[maybe_unused]] DoubleType::UType leftValue, [[maybe_unused]] DoubleType::UType rightValue, + [[maybe_unused]] lexer::TokenType operationType) +{ + return 0.0; +} +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/ets/baseAnalyzer.cpp b/checker/ets/baseAnalyzer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5c7d472be4382f8b52f55ad0c6c8ff8fc45468be --- /dev/null +++ b/checker/ets/baseAnalyzer.cpp @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2021 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 "baseAnalyzer.h" +#include "plugins/ecmascript/es2panda/ir/astNode.h" +#include "plugins/ecmascript/es2panda/ir/statements/breakStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/continueStatement.h" + +namespace panda::es2panda::checker { +void BaseAnalyzer::ClearPendingExits() +{ + pendingExits_.clear(); +} + +PendingExitsVector &BaseAnalyzer::PendingExits() +{ + return pendingExits_; +} + +void BaseAnalyzer::SetPendingExits(const PendingExitsVector &pendingExits) +{ + pendingExits_ = pendingExits; +} + +PendingExitsVector &BaseAnalyzer::OldPendingExits() +{ + return oldPendingExits_; +} + +void BaseAnalyzer::SetOldPendingExits(const PendingExitsVector &oldPendingExits) +{ + oldPendingExits_ = oldPendingExits; +} + +const ir::AstNode *BaseAnalyzer::GetJumpTarget(const ir::AstNode *node) const +{ + if (node->IsBreakStatement()) { + return node->AsBreakStatement()->Target(); + } + + ASSERT(node->IsContinueStatement()); + return node->AsContinueStatement()->Target(); +} + +LivenessStatus BaseAnalyzer::ResolveJump(const ir::AstNode *node, ir::AstNodeType jumpKind) +{ + bool resolved = false; + PendingExitsVector exits = pendingExits_; + pendingExits_ = oldPendingExits_; + + for (auto &it : exits) { + if (it.Node()->Type() == jumpKind && node == GetJumpTarget(it.Node())) { + it.ResolveJump(); + resolved = true; + } else { + pendingExits_.push_back(it); + } + } + + return From(resolved); +} + +LivenessStatus BaseAnalyzer::ResolveContinues(const ir::AstNode *node) +{ + oldPendingExits_.clear(); + return ResolveJump(node, ir::AstNodeType::CONTINUE_STATEMENT); +} + +LivenessStatus BaseAnalyzer::ResolveBreaks(const ir::AstNode *node) +{ + return ResolveJump(node, ir::AstNodeType::BREAK_STATEMENT); +} + +} // namespace panda::es2panda::checker diff --git a/checker/ets/baseAnalyzer.h b/checker/ets/baseAnalyzer.h new file mode 100644 index 0000000000000000000000000000000000000000..de0168f824940c5124b5dda24f0fd42b946d47b5 --- /dev/null +++ b/checker/ets/baseAnalyzer.h @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_ETS_BASE_ANALYZER_H +#define ES2PANDA_COMPILER_CHECKER_ETS_BASE_ANALYZER_H + +#include "utils/arena_containers.h" +#include "plugins/ecmascript/es2panda/util/enumbitops.h" + +namespace panda::es2panda::ir { +class AstNode; +enum class AstNodeType; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::checker { +class ETSChecker; + +enum class LivenessStatus { DEAD, ALIVE }; + +DEFINE_BITOPS(LivenessStatus) + +class PendingExit { +public: + using JumpResolver = std::function; + + explicit PendingExit( + const ir::AstNode *node, JumpResolver jumpResolver = [] {}) + : node_(node), jumpResolver_(std::move(jumpResolver)) + { + } + ~PendingExit() = default; + + DEFAULT_COPY_SEMANTIC(PendingExit); + DEFAULT_NOEXCEPT_MOVE_SEMANTIC(PendingExit); + + void ResolveJump() const + { + jumpResolver_(); + } + + const ir::AstNode *Node() const + { + return node_; + } + +private: + const ir::AstNode *node_; + JumpResolver jumpResolver_; +}; + +using PendingExitsVector = std::vector; + +class BaseAnalyzer { +public: + explicit BaseAnalyzer() = default; + + virtual void MarkDead() = 0; + + void RecordExit(const PendingExit &pe) + { + pendingExits_.push_back(pe); + MarkDead(); + } + + LivenessStatus From(bool value) + { + return value ? LivenessStatus::ALIVE : LivenessStatus::DEAD; + } + + LivenessStatus ResolveJump(const ir::AstNode *node, ir::AstNodeType jumpKind); + LivenessStatus ResolveContinues(const ir::AstNode *node); + LivenessStatus ResolveBreaks(const ir::AstNode *node); + const ir::AstNode *GetJumpTarget(const ir::AstNode *node) const; + +protected: + void ClearPendingExits(); + PendingExitsVector &PendingExits(); + void SetPendingExits(const PendingExitsVector &pendingExits); + PendingExitsVector &OldPendingExits(); + void SetOldPendingExits(const PendingExitsVector &oldPendingExits); + +private: + PendingExitsVector pendingExits_; + PendingExitsVector oldPendingExits_; +}; +} // namespace panda::es2panda::checker +#endif diff --git a/checker/ets/boxingConverter.cpp b/checker/ets/boxingConverter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..051421e0a7c928109f61006bb73ae9fd6c638eae --- /dev/null +++ b/checker/ets/boxingConverter.cpp @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2021-2022 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 "boxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/types.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" +#include "plugins/ecmascript/es2panda/util/helpers.h" +#include "plugins/ecmascript/es2panda/checker/ets/primitiveWrappers.h" + +namespace panda::es2panda::checker { + +void BoxingConverter::ETSTypeFromSource(Type *source) +{ + auto typeKind = checker::ETSChecker::TypeKind(source); + + auto wrapMap = Checker()->PrimitiveWrapper(); + + switch (typeKind) { + case checker::TypeFlag::ETS_BOOLEAN: { + auto res = wrapMap.find(compiler::Signatures::BUILTIN_BOOLEAN_CLASS); + SetResult(res->second.first); + break; + } + case checker::TypeFlag::BYTE: { + auto res = wrapMap.find(compiler::Signatures::BUILTIN_BYTE_CLASS); + SetResult(res->second.first); + break; + } + case checker::TypeFlag::SHORT: { + auto res = wrapMap.find(compiler::Signatures::BUILTIN_SHORT_CLASS); + SetResult(res->second.first); + break; + } + case checker::TypeFlag::CHAR: { + auto res = wrapMap.find(compiler::Signatures::BUILTIN_CHAR_CLASS); + SetResult(res->second.first); + break; + } + case checker::TypeFlag::INT: { + auto res = wrapMap.find(compiler::Signatures::BUILTIN_INT_CLASS); + SetResult(res->second.first); + break; + } + case checker::TypeFlag::LONG: { + auto res = wrapMap.find(compiler::Signatures::BUILTIN_LONG_CLASS); + SetResult(res->second.first); + break; + } + case checker::TypeFlag::FLOAT: { + auto res = wrapMap.find(compiler::Signatures::BUILTIN_FLOAT_CLASS); + SetResult(res->second.first); + break; + } + case checker::TypeFlag::DOUBLE: { + auto res = wrapMap.find(compiler::Signatures::BUILTIN_DOUBLE_CLASS); + SetResult(res->second.first); + break; + } + default: + UNREACHABLE(); + } +} + +} // namespace panda::es2panda::checker diff --git a/checker/ets/boxingConverter.h b/checker/ets/boxingConverter.h new file mode 100644 index 0000000000000000000000000000000000000000..4ab31c527b447586f6e3c881ce32bb43a85acf7f --- /dev/null +++ b/checker/ets/boxingConverter.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_ETS_BOXING_CONVERTER_H +#define ES2PANDA_COMPILER_CHECKER_ETS_BOXING_CONVERTER_H + +#include "plugins/ecmascript/es2panda/checker/ets/typeConverter.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/etsObjectType.h" + +namespace panda::es2panda::checker { +class BoxingConverter : public TypeConverter { +public: + BoxingConverter(ETSChecker *checker, TypeRelation *relation, Type *source, Type *target) + : TypeConverter(checker, relation, target, source) + { + ASSERT(relation->GetNode()); + + if (!target->IsETSObjectType() || relation->IsTrue()) { + return; + } + + if (!source->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) { + Relation()->Result(false); + return; + } + + ETSTypeFromSource(source); + + Relation()->Result(relation->IsAssignableTo(Result(), target)); + } + + void ETSTypeFromSource(Type *source); +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/ets/function.cpp b/checker/ets/function.cpp new file mode 100644 index 0000000000000000000000000000000000000000..14aee0012add6e5c11edb02cb75dcf7dc0e96bb1 --- /dev/null +++ b/checker/ets/function.cpp @@ -0,0 +1,634 @@ +/** + * Copyright (c) 2021 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 "plugins/ecmascript/es2panda/ir/typeNode.h" +#include "plugins/ecmascript/es2panda/ir/base/scriptFunction.h" +#include "plugins/ecmascript/es2panda/ir/base/spreadElement.h" +#include "plugins/ecmascript/es2panda/ir/base/methodDefinition.h" +#include "plugins/ecmascript/es2panda/ir/base/classDefinition.h" +#include "plugins/ecmascript/es2panda/ir/base/classProperty.h" +#include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" +#include "plugins/ecmascript/es2panda/ir/expressions/memberExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/callExpression.h" +#include "plugins/ecmascript/es2panda/ir/statements/blockStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/returnStatement.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsMethodReferenceExpression.h" +#include "plugins/ecmascript/es2panda/binder/variable.h" +#include "plugins/ecmascript/es2panda/binder/scope.h" +#include "plugins/ecmascript/es2panda/binder/declaration.h" +#include "plugins/ecmascript/es2panda/binder/binder.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" +#include "plugins/ecmascript/es2panda/checker/ets/typeRelationContext.h" +#include "plugins/ecmascript/es2panda/util/helpers.h" + +namespace panda::es2panda::checker { + +bool ETSChecker::ValidateSignature(Signature *signature, const ArenaVector &arguments, + const lexer::SourcePosition &pos, TypeRelationFlag initialFlags) +{ + if ((arguments.size() < signature->MinArgCount()) || + (arguments.size() > signature->MinArgCount() && signature->RestVar() == nullptr)) { + if ((initialFlags & TypeRelationFlag::NO_THROW) == 0) { + ThrowTypeError({"Expected ", signature->MinArgCount(), " arguments, got ", arguments.size(), " ."}, pos); + } + + return false; + } + + uint32_t index = 0; + bool validateRest = false; + + for (; index < arguments.size(); index++) { + if (index >= signature->MinArgCount()) { + ASSERT(signature->RestVar()); + validateRest = true; + break; + } + + Type *argType = arguments[index]->Check(this); + auto invocationtCtx = checker::InvocationContext( + Relation(), arguments[index], argType, signature->Params()[index]->TsType(), arguments[index]->Start(), + {"Call argument at index ", index, " is not compatible with the signature's type at that index"}, + initialFlags); + + if (!invocationtCtx.IsInvocable()) { + return false; + } + } + + if (((initialFlags & TypeRelationFlag::SELF_REFERENCE) != 0) && + !signature->HasSignatureFlag(SignatureFlags::STATIC | SignatureFlags::CONSTRUCTOR) && + (HasStatus(CheckerStatus::IN_STATIC_CONTEXT))) { + return false; + } + + if (!validateRest) { + return true; + } + + do { + Type *argType = arguments[index]->Check(this); + auto invocationtCtx = checker::InvocationContext( + Relation(), arguments[index], argType, signature->RestVar()->TsType(), arguments[index]->Start(), + {"Call argument at index ", index, " is not compatible with the signature's rest parameter type"}, + initialFlags); + index++; + + if (!invocationtCtx.IsInvocable()) { + return false; + } + } while (index < arguments.size()); + + return true; +} + +Signature *ETSChecker::ValidateSignatures(ArenaVector &signatures, + const ArenaVector &arguments, + const lexer::SourcePosition &pos, std::string_view signatureKind, + TypeRelationFlag flags) +{ + for (auto *sig : signatures) { + if (ValidateSignature(sig, arguments, pos, + flags | TypeRelationFlag::NO_THROW | TypeRelationFlag::NO_UNBOXING | + TypeRelationFlag::NO_BOXING)) { + return sig; + } + } + + for (auto *sig : signatures) { + if (ValidateSignature(sig, arguments, pos, flags | TypeRelationFlag::NO_THROW)) { + return sig; + } + } + + if ((flags & TypeRelationFlag::WIDENING) != 0) { + ThrowTypeError({"No matching ", signatureKind, " signature"}, pos); + } + + return nullptr; +} + +Signature *ETSChecker::ResolveCallExpression(ArenaVector &signatures, + const ArenaVector &arguments, + const lexer::SourcePosition &pos, bool isIdentifier) +{ + auto flag = isIdentifier ? TypeRelationFlag::SELF_REFERENCE : TypeRelationFlag::NONE; + + auto *sig = ValidateSignatures(signatures, arguments, pos, "call", flag); + + if (sig != nullptr) { + return sig; + } + + return ValidateSignatures(signatures, arguments, pos, "call", flag | TypeRelationFlag::WIDENING); +} + +Signature *ETSChecker::ResolveConstructExpression(ETSObjectType *type, const ArenaVector &arguments, + const lexer::SourcePosition &pos) +{ + auto *sig = ValidateSignatures(type->ConstructSignatures(), arguments, pos, "construct", TypeRelationFlag::NONE); + + if (sig != nullptr) { + return sig; + } + + return ValidateSignatures(type->ConstructSignatures(), arguments, pos, "construct", TypeRelationFlag::WIDENING); +} + +checker::ETSFunctionType *ETSChecker::BuildMethodSignature(ir::MethodDefinition *method) +{ + if (method->TsType() != nullptr) { + return method->TsType()->AsETSFunctionType(); + } + + bool isConstructSig = method->IsConstructor(); + + auto *funcType = BuildFunctionSignature(method->Function(), isConstructSig); + + auto overloadList = method->Overloads(); + + for (size_t baseFuncCounter = 0; baseFuncCounter < overloadList.size(); baseFuncCounter++) { + auto *currentFunc = overloadList.at(baseFuncCounter); + auto *overloadType = BuildFunctionSignature(currentFunc->Function(), isConstructSig); + CheckIdenticalOverloads(funcType, overloadType, currentFunc->Start()); + for (size_t compareFuncCounter = baseFuncCounter + 1; compareFuncCounter < overloadList.size(); + compareFuncCounter++) { + auto *compareFunc = overloadList.at(compareFuncCounter); + auto *compareOverloadType = BuildFunctionSignature(compareFunc->Function(), isConstructSig); + CheckIdenticalOverloads(overloadType, compareOverloadType, compareFunc->Start()); + } + currentFunc->SetTsType(overloadType); + funcType->AddCallSignature(currentFunc->Function()->Signature()); + } + + method->Id()->Variable()->SetTsType(funcType); + return funcType; +} + +void ETSChecker::CheckIdenticalOverloads(ETSFunctionType *func, ETSFunctionType *overload, + const lexer::SourcePosition &overloadStart) +{ + SavedTypeRelationFlagsContext savedFlagsCtx(Relation(), TypeRelationFlag::NO_RETURN_TYPE_CHECK); + Relation()->IsIdenticalTo(func, overload); + if (Relation()->IsTrue()) { + ThrowTypeError("Function already declared.", overloadStart); + } +} + +checker::ETSFunctionType *ETSChecker::BuildFunctionSignature(ir::ScriptFunction *func, bool isConstructSig) +{ + auto *nameVar = func->Id()->Variable(); + + auto *signatureInfo = CreateSignatureInfo(); + signatureInfo->restVar = nullptr; + signatureInfo->minArgCount = 0; + + if ((func->IsConstructor() || !func->IsStatic()) && + !Context().ContainingClass()->HasObjectFlag(ETSObjectFlags::INTERFACE)) { + auto *thisVar = func->Scope()->ParamScope()->Params().front(); + thisVar->SetTsType(Context().ContainingClass()); + } + + for (auto *it : func->Params()) { + if (it->IsRestElement()) { + auto *restParam = it->AsRestElement(); + ASSERT(restParam->Argument()->IsIdentifier()); + + auto *restIdent = restParam->Argument()->AsIdentifier(); + + ASSERT(restIdent->Variable()); + signatureInfo->restVar = restIdent->Variable()->AsLocalVariable(); + + ASSERT(restParam->TypeAnnotation()); + signatureInfo->restVar->SetTsType(restParam->TypeAnnotation()->GetType(this)); + break; + } + + ASSERT(it->IsIdentifier()); + auto *paramIdent = it->AsIdentifier(); + + ASSERT(paramIdent->Variable()); + binder::Variable *paramVar = paramIdent->Variable(); + + ASSERT(paramIdent->TypeAnnotation()); + paramVar->SetTsType(paramIdent->TypeAnnotation()->GetType(this)); + signatureInfo->params.push_back(paramVar->AsLocalVariable()); + signatureInfo->minArgCount++; + } + + auto *returnTypeAnnotation = func->ReturnTypeAnnotation(); + checker::Type *returnType {}; + + ASSERT(returnTypeAnnotation || func->IsConstructor() || func->IsStaticBlock()); + + if (returnTypeAnnotation != nullptr) { + returnType = returnTypeAnnotation->GetType(this); + returnTypeAnnotation->SetTsType(returnType); + } else { + returnType = GlobalVoidType(); + } + + auto *signature = CreateSignature(signatureInfo, returnType, func); + signature->SetOwner(Context().ContainingClass()); + + if (isConstructSig) { + signature->AddSignatureFlag(SignatureFlags::CONSTRUCT); + } else { + signature->AddSignatureFlag(SignatureFlags::CALL); + } + + auto *funcType = CreateETSFunctionType(signature); + func->SetSignature(signature); + funcType->SetVariable(nameVar); + + if (func->IsAbstract()) { + signature->AddSignatureFlag(SignatureFlags::ABSTRACT); + signature->AddSignatureFlag(SignatureFlags::VIRTUAL); + } + + if (func->IsStatic()) { + signature->AddSignatureFlag(SignatureFlags::STATIC); + } + + if (func->IsConstructor()) { + signature->AddSignatureFlag(SignatureFlags::CONSTRUCTOR); + } + + if (func->IsOpen()) { + if (!(func->Signature()->Owner()->GetDeclNode()->IsOpen() || + func->Signature()->Owner()->HasObjectFlag(checker::ETSObjectFlags::ABSTRACT))) { + ThrowTypeError("Only open or abstract classes can have open method", func->Start()); + } + signature->AddSignatureFlag(SignatureFlags::OPEN); + signature->AddSignatureFlag(SignatureFlags::PUBLIC); + } + + if (func->IsPublic()) { + signature->AddSignatureFlag(SignatureFlags::PUBLIC); + } else if (func->IsProtected()) { + signature->AddSignatureFlag(SignatureFlags::PROTECTED); + } else if (func->IsPrivate()) { + signature->AddSignatureFlag(SignatureFlags::PRIVATE); + } + + nameVar->SetTsType(funcType); + + return funcType; +} + +Signature *ETSChecker::CheckEveryAbstractSignatureIsOverriden(ETSFunctionType *target, ETSFunctionType *source) +{ + for (auto targetSig = target->CallSignatures().begin(); targetSig != target->CallSignatures().end();) { + if (!(*targetSig)->HasSignatureFlag(SignatureFlags::ABSTRACT)) { + continue; + } + + bool isOverridden = false; + for (auto sourceSig : source->CallSignatures()) { + Relation()->IsIdenticalTo(*targetSig, sourceSig); + if (Relation()->IsTrue() && (*targetSig)->Function()->Id()->Name() == sourceSig->Function()->Id()->Name()) { + target->CallSignatures().erase(targetSig); + isOverridden = true; + break; + } + sourceSig++; + } + + if (!isOverridden) { + return *targetSig; + } + } + + return nullptr; +} + +bool ETSChecker::IsOverridableIn(Signature *signature) +{ + if (signature->HasSignatureFlag(SignatureFlags::PRIVATE)) { + return false; + } + + if (signature->HasSignatureFlag(SignatureFlags::PUBLIC)) { + return FindAncestorGivenByType(signature->Function(), ir::AstNodeType::TS_INTERFACE_DECLARATION) == nullptr || + signature->HasSignatureFlag(SignatureFlags::STATIC); + } + + return signature->HasSignatureFlag(SignatureFlags::PROTECTED); +} + +bool ETSChecker::IsMethodOverridesOther(Signature *target, Signature *source) +{ + if (source->Function()->IsConstructor()) { + return false; + } + + if (target == source) { + return true; + } + + if (IsOverridableIn(target)) { + SavedTypeRelationFlagsContext savedFlagsCtx(Relation(), TypeRelationFlag::NO_RETURN_TYPE_CHECK); + Relation()->IsIdenticalTo(target, source); + if (Relation()->IsTrue()) { + if (!source->Function()->IsOverride()) { + ThrowTypeError("Method overriding requires 'override' modifier", source->Function()->Start()); + } + return true; + } + } + + return false; +} + +std::tuple ETSChecker::CheckOverride(Signature *signature, Signature *other) +{ + if (signature->HasSignatureFlag(SignatureFlags::STATIC) != other->HasSignatureFlag(SignatureFlags::STATIC)) { + return {false, OverrideErrorCode::OVERRIDING_STATIC}; + } + + if (!(other->HasSignatureFlag(SignatureFlags::OPEN) || other->Function()->IsOverride())) { + return {false, OverrideErrorCode::OVERRIDEN_NOT_OPEN}; + } + + if (!IsTypeAssignableTo(signature->ReturnType(), other->ReturnType())) { + return {false, OverrideErrorCode::INCOMPATIBLE_RETURN}; + } + + if (signature->ProtectionFlag() > other->ProtectionFlag()) { + return {false, OverrideErrorCode::OVERRIDEN_WEAKER}; + } + + return {true, OverrideErrorCode::NO_ERROR}; +} + +bool ETSChecker::CheckOverride(Signature *signature, ETSObjectType *site) +{ + auto *target = site->GetProperty(signature->Function()->Id()->Name(), PropertySearchFlags::SEARCH_METHOD); + bool isOverridingAnySignature = false; + + if (target == nullptr) { + return isOverridingAnySignature; + } + + for (auto *it : target->TsType()->AsETSFunctionType()->CallSignatures()) { + if (it->HasSignatureFlag(SignatureFlags::ABSTRACT) || site->HasObjectFlag(ETSObjectFlags::INTERFACE)) { + isOverridingAnySignature = true; + continue; + } + + if (!IsMethodOverridesOther(it, signature)) { + continue; + } + + auto [success, errorCode] = CheckOverride(signature, it); + + if (!success) { + const char *reason {}; + switch (errorCode) { + case OverrideErrorCode::OVERRIDING_STATIC: { + reason = "overriding method is static."; + break; + } + case OverrideErrorCode::OVERRIDEN_STATIC: { + reason = "overriden method is static."; + break; + } + case OverrideErrorCode::OVERRIDEN_NOT_OPEN: { + reason = "overriden method is not open."; + break; + } + case OverrideErrorCode::INCOMPATIBLE_RETURN: { + reason = "overriding return type is not compatible with the other return type."; + break; + } + case OverrideErrorCode::OVERRIDEN_WEAKER: { + reason = "overriden method has weaker access privilage."; + break; + } + default: { + UNREACHABLE(); + } + } + + ThrowTypeError({signature->Function()->Id()->Name(), signature, " in ", signature->Owner(), + " cannot override ", it->Function()->Id()->Name(), it, " in ", it->Owner(), " because ", + reason}, + signature->Function()->Start()); + } + + isOverridingAnySignature = true; + it->AddSignatureFlag(SignatureFlags::VIRTUAL); + signature->AddSignatureFlag(SignatureFlags::OPEN); + } + + return isOverridingAnySignature; +} + +void ETSChecker::CheckOverride(Signature *signature) +{ + auto *owner = signature->Owner(); + bool isOverriding = false; + + if (!owner->HasObjectFlag(ETSObjectFlags::CLASS | ETSObjectFlags::ENUM)) { + return; + } + + ETSObjectType *iter = owner->SuperType(); + + for (auto *it : owner->Interfaces()) { + isOverriding |= CheckOverride(signature, it); + } + + while (iter != nullptr) { + isOverriding |= CheckOverride(signature, iter); + + for (auto *it : iter->Interfaces()) { + isOverriding |= CheckOverride(signature, it); + } + + iter = iter->SuperType(); + } + + if (!isOverriding && signature->Function()->IsOverride()) { + ThrowTypeError({"Method ", signature->Function()->Id()->Name(), signature, " in ", signature->Owner(), + " not overriding any method"}, + signature->Function()->Start()); + } +} + +Signature *ETSChecker::GetSignatureFromMethodDefinition(const ir::MethodDefinition *methodDef) +{ + ASSERT(methodDef->TsType() && methodDef->TsType()->IsETSFunctionType()); + + for (auto *it : methodDef->TsType()->AsETSFunctionType()->CallSignatures()) { + if (it->Function() == methodDef->Function()) { + return it; + } + } + + return nullptr; +} + +void ETSChecker::ValidateSignatureAccessibility(ETSObjectType *callee, Signature *signature, + const lexer::SourcePosition &pos) +{ + if (!signature->HasSignatureFlag(SignatureFlags::CONSTRUCTOR) && HasStatus(CheckerStatus::IN_STATIC_CONTEXT) && + !signature->HasSignatureFlag(SignatureFlags::STATIC)) { + ThrowTypeError( + {"Can not reference ", signature->Function()->Id()->Name(), " instance method in a static context."}, pos); + } + + if (signature->HasSignatureFlag(SignatureFlags::PRIVATE)) { + if (Context().ContainingClass() == callee && callee->IsSignatureInherited(signature)) { + return; + } + + auto *currentOutermost = Context().ContainingClass()->OutermostClass(); + auto *objOutermost = callee->OutermostClass(); + + if (currentOutermost != nullptr && objOutermost != nullptr && currentOutermost == objOutermost && + callee->IsSignatureInherited(signature)) { + return; + } + + ThrowTypeError({"Signature ", signature->Function()->Id()->Name(), signature, " is not visible here."}, pos); + } +} + +void ETSChecker::ResolveLambdaObject(ir::ETSMethodReferenceExpression *methodRef, ETSObjectType *functionalInterface) +{ + auto *signatureRef = methodRef->ComputedSignature(); + auto *targetType = methodRef->TargetType(); + auto *lambdaObject = methodRef->LambdaObject(); + + auto *field = lambdaObject->Body()[0]->AsClassProperty(); + auto *fieldVar = field->Key()->AsIdentifier()->Variable(); + field->SetTsType(targetType); + fieldVar->SetTsType(targetType); + + auto *ctorFunc = lambdaObject->Body()[1]->AsMethodDefinition()->Function(); + ctorFunc->Params()[0]->AsIdentifier()->Variable()->SetTsType(targetType); + + auto *lambdaObjectType = Allocator()->New(Allocator(), lambdaObject->Ident()->Name(), + lambdaObject->Ident()->Name(), lambdaObject, + checker::ETSObjectFlags::CLASS); + lambdaObjectType->AddInterface(functionalInterface); + lambdaObject->SetTsType(lambdaObjectType); + + lambdaObjectType->AddProperty(fieldVar->AsLocalVariable()); + + ResolveLambdaObjectCtor(lambdaObject); + ResolveLambdaObjectInvoke(lambdaObject, signatureRef); +} + +void ETSChecker::ResolveLambdaObjectCtor(ir::ClassDefinition *lambdaObject) +{ + auto *ctorFunc = lambdaObject->Body()[1]->AsMethodDefinition()->Function(); + ETSObjectType *lambdaObjectType = lambdaObject->TsType()->AsETSObjectType(); + + auto *thisVar = ctorFunc->Scope()->ParamScope()->Params().front(); + thisVar->SetTsType(lambdaObjectType); + + auto *ctorSignatureInfo = CreateSignatureInfo(); + ctorSignatureInfo->restVar = nullptr; + ctorSignatureInfo->minArgCount = 1; + ctorSignatureInfo->params.push_back(ctorFunc->Params()[0]->AsIdentifier()->Variable()->AsLocalVariable()); + auto *ctorSignature = CreateSignature(ctorSignatureInfo, GlobalVoidType(), ctorFunc); + ctorSignature->SetOwner(lambdaObjectType); + ctorSignature->AddSignatureFlag(checker::SignatureFlags::CONSTRUCTOR | checker::SignatureFlags::CONSTRUCT); + lambdaObjectType->AddConstructSignature(ctorSignature); + + auto *ctorType = CreateETSFunctionType(ctorSignature); + ctorFunc->SetSignature(ctorSignature); + ctorFunc->Id()->Variable()->SetTsType(ctorType); + + auto *savedClass = Context().ContainingClass(); + Context().SetContainingClass(lambdaObjectType); + ctorFunc->Body()->Check(this); + Context().SetContainingClass(savedClass); +} + +void ETSChecker::ResolveLambdaObjectInvoke(ir::ClassDefinition *lambdaObject, Signature *signatureRef) +{ + auto *invokeFunc = lambdaObject->Body()[2]->AsMethodDefinition()->Function(); + ETSObjectType *lambdaObjectType = lambdaObject->TsType()->AsETSObjectType(); + + auto *thisVar = invokeFunc->Scope()->ParamScope()->Params().front(); + thisVar->SetTsType(lambdaObjectType); + + auto *invokeSignatureInfo = CreateSignatureInfo(); + invokeSignatureInfo->restVar = nullptr; + + for (auto *it : signatureRef->Params()) { + auto paramCtx = + binder::LexicalScope::Enter(Binder(), invokeFunc->Scope()->ParamScope(), false); + + auto *paramIdent = Allocator()->New(it->Name(), Allocator()); + auto [_, var] = Binder()->AddParamDecl(paramIdent); + (void)_; + var->SetTsType(it->TsType()); + paramIdent->SetVariable(var); + invokeFunc->Params().push_back(paramIdent); + invokeSignatureInfo->minArgCount++; + invokeSignatureInfo->params.push_back(var->AsLocalVariable()); + } + + auto *invokeSignature = CreateSignature(invokeSignatureInfo, signatureRef->ReturnType(), invokeFunc); + invokeSignature->SetOwner(lambdaObjectType); + invokeSignature->AddSignatureFlag(checker::SignatureFlags::CALL); + + auto *invokeType = CreateETSFunctionType(invokeSignature); + invokeFunc->SetSignature(invokeSignature); + invokeFunc->Id()->Variable()->SetTsType(invokeType); + lambdaObjectType->AddProperty( + invokeFunc->Id()->Variable()->AsLocalVariable()); + + invokeFunc->Body()->AsBlockStatement()->Statements().push_back( + ResolveLambdaObjectInvokeFuncBody(lambdaObject, signatureRef)); +} + +ir::Statement *ETSChecker::ResolveLambdaObjectInvokeFuncBody(ir::ClassDefinition *lambdaObject, Signature *signatureRef) +{ + auto *fieldProp = lambdaObject->Body()[0]->AsClassProperty()->Key()->AsIdentifier()->Variable(); + auto *fieldPropType = fieldProp->TsType()->AsETSObjectType(); + auto *fieldIdent = Allocator()->New("field0", Allocator()); + fieldIdent->SetVariable(fieldProp); + fieldIdent->SetTsType(fieldPropType); + + auto *funcIdent = Allocator()->New(signatureRef->Function()->Id()->Name(), Allocator()); + auto *callee = Allocator()->New(fieldIdent, funcIdent, + ir::MemberExpressionKind::ELEMENT_ACCESS, false, false); + auto *found = + fieldPropType->GetProperty(signatureRef->Function()->Id()->Name(), PropertySearchFlags::SEARCH_METHOD); + callee->SetPropVar(found); + callee->SetObjectType(fieldPropType); + callee->SetTsType(found->TsType()); + + auto *invokeFunc = lambdaObject->Body()[2]->AsMethodDefinition()->Function(); + ArenaVector callParams(Allocator()->Adapter()); + for (size_t idx = 0; idx != signatureRef->Params().size(); idx++) { + auto *param = Allocator()->New(signatureRef->Params()[idx]->Name(), Allocator()); + param->SetVariable(invokeFunc->Params()[idx]->AsIdentifier()->Variable()); + param->SetTsType(invokeFunc->Params()[idx]->AsIdentifier()->Variable()->TsType()); + callParams.push_back(param); + } + + auto *resolvedCall = Allocator()->New(callee, std::move(callParams), nullptr, false); + resolvedCall->SetTsType(signatureRef->ReturnType()); + resolvedCall->SetSignature(signatureRef); + + return Allocator()->New(resolvedCall); +} +} // namespace panda::es2panda::checker diff --git a/checker/ets/helpers.cpp b/checker/ets/helpers.cpp new file mode 100644 index 0000000000000000000000000000000000000000..504780f9c80fcc3f0c61fda0c4598021e0356ac1 --- /dev/null +++ b/checker/ets/helpers.cpp @@ -0,0 +1,998 @@ +/** + * Copyright (c) 2021 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 "plugins/ecmascript/es2panda/binder/variableFlags.h" +#include "plugins/ecmascript/es2panda/checker/checker.h" +#include "plugins/ecmascript/es2panda/checker/checkerContext.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/etsObjectType.h" +#include "plugins/ecmascript/es2panda/ir/astNode.h" +#include "plugins/ecmascript/es2panda/lexer/token/tokenType.h" +#include "plugins/ecmascript/es2panda/ir/typeNode.h" +#include "plugins/ecmascript/es2panda/ir/base/scriptFunction.h" +#include "plugins/ecmascript/es2panda/ir/base/classProperty.h" +#include "plugins/ecmascript/es2panda/ir/statements/classDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/statements/variableDeclarator.h" +#include "plugins/ecmascript/es2panda/ir/statements/switchCaseStatement.h" +#include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" +#include "plugins/ecmascript/es2panda/ir/expressions/callExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/memberExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/literals/charLiteral.h" +#include "plugins/ecmascript/es2panda/ir/expressions/binaryExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/assignmentExpression.h" +#include "plugins/ecmascript/es2panda/ir/statements/labelledStatement.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsMethodReferenceExpression.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsNewClassInstanceExpression.h" +#include "plugins/ecmascript/es2panda/binder/variable.h" +#include "plugins/ecmascript/es2panda/binder/scope.h" +#include "plugins/ecmascript/es2panda/binder/declaration.h" +#include "plugins/ecmascript/es2panda/parser/program/program.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" +#include "plugins/ecmascript/es2panda/checker/ets/typeRelationContext.h" +#include "plugins/ecmascript/es2panda/checker/ets/boxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/unboxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/types.h" +#include "plugins/ecmascript/es2panda/util/helpers.h" + +namespace panda::es2panda::checker { +void ETSChecker::CheckTruthinessOfType(Type *type, const lexer::SourcePosition &pos) +{ + auto *unboxedType = ETSBuiltinTypeAsPrimitiveType(type); + + if (unboxedType == nullptr || !unboxedType->IsETSBooleanType()) { + ThrowTypeError("Condition must be of type boolean", pos); + } +} + +Type *ETSChecker::GetDefaultTypeFromPrimitiveType(Type *type) +{ + if (!type->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) { + return type; + } + + if (type->HasTypeFlag(TypeFlag::LONG)) { + return GlobalLongType(); + } + + if (type->HasTypeFlag(TypeFlag::ETS_INTEGRAL)) { + return GlobalIntType(); + } + + if (type->HasTypeFlag(TypeFlag::FLOAT | TypeFlag::DOUBLE)) { + return GlobalDoubleType(); + } + + if (type->IsETSBooleanType()) { + return GlobalETSBooleanType(); + } + return type; +} + +bool ETSChecker::IsConstantExpression(ir::Expression *expr, Type *type) +{ + return (type->HasTypeFlag(TypeFlag::CONSTANT) && (expr->IsIdentifier() || expr->IsMemberExpression())); +} + +Type *ETSChecker::GetTypeOfVariable(binder::Variable *var) +{ + if (var->TsType() != nullptr) { + return var->TsType(); + } + + binder::Decl *decl = var->Declaration(); + + switch (decl->Type()) { + case binder::DeclType::CLASS: { + auto *classDef = decl->Node()->AsClassDefinition(); + BuildClassProperties(classDef); + return classDef->TsType(); + } + case binder::DeclType::ENUM_LITERAL: + case binder::DeclType::CONST: + case binder::DeclType::LET: + case binder::DeclType::VAR: { + auto *declNode = decl->Node(); + + if (decl->Node()->IsIdentifier()) { + declNode = declNode->Parent(); + } + + return declNode->Check(this); + } + case binder::DeclType::FUNC: { + return decl->Node()->Check(this); + } + default: { + UNREACHABLE(); + } + } + + return var->TsType(); +} + +void ETSChecker::ValidatePropertyAccess(binder::Variable *var, ETSObjectType *obj, const lexer::SourcePosition &pos) +{ + if (var->HasFlag(binder::VariableFlags::METHOD)) { + return; + } + + if (var->HasFlag(binder::VariableFlags::PROPERTY) && HasStatus(CheckerStatus::IN_STATIC_CONTEXT) && + !var->HasFlag(binder::VariableFlags::STATIC)) { + ThrowTypeError({"Can not reference ", var->Name(), " instance field in a static context."}, pos); + } + + if (var->HasFlag(binder::VariableFlags::PRIVATE)) { + if (Context().ContainingClass() == obj && obj->IsPropertyInherited(var)) { + return; + } + + auto *currentOutermost = Context().ContainingClass()->OutermostClass(); + auto *objOutermost = obj->OutermostClass(); + + if (currentOutermost != nullptr && objOutermost != nullptr && currentOutermost == objOutermost && + obj->IsPropertyInherited(var)) { + return; + } + + ThrowTypeError({"Property ", var->Name(), " is not visible here."}, pos); + } +} + +std::tuple ETSChecker::GetClassScopeFromClass(ETSObjectType *classType) +{ + auto *currentClsScope = classType->GetDeclNode()->IsClassDefinition() + ? classType->GetDeclNode()->AsClassDefinition()->Scope() + : classType->GetDeclNode()->AsTSInterfaceDeclaration()->Scope(); + return {currentClsScope, classType->HasObjectFlag(ETSObjectFlags::INNER)}; +} + +binder::Variable *ETSChecker::FindVariableInClass(util::StringView name) +{ + binder::Variable *resolved = nullptr; + ETSObjectType *classIter = Context().ContainingClass(); + auto *scopeIter = Scope(); + + auto [currentClsScope, inInnerClass] = GetClassScopeFromClass(classIter); + + while (scopeIter != nullptr) { + resolved = scopeIter->FindLocal(name, binder::ResolveBindingOptions::ALL); + + if (resolved != nullptr) { + break; + } + + if (scopeIter == currentClsScope) { + if (classIter->SuperType() != nullptr) { + resolved = classIter->SuperType()->GetProperty(name, PropertySearchFlags::SEARCH_ALL | + PropertySearchFlags::SEARCH_IN_BASE | + PropertySearchFlags::SEARCH_IN_INTERFACES); + } + + if (!inInnerClass || resolved != nullptr) { + return resolved; + } + + classIter = classIter->EnclosingType(); + std::tie(currentClsScope, inInnerClass) = GetClassScopeFromClass(classIter); + } + + scopeIter = scopeIter->Parent(); + } + + return resolved; +} + +Type *ETSChecker::ResolveIdentifier(ir::Identifier *ident) +{ + binder::Variable *resolved = FindVariableInClass(ident->Name()); + + if (resolved == nullptr) { + // If the reference is not found already in the current class, then it is not bound to the class, so we have to + // find the reference in the global class first, then in the global scope + auto *scopeIter = Scope(); + while (!scopeIter->Parent()->IsGlobalScope()) { + scopeIter = scopeIter->Parent(); + } + + resolved = scopeIter->FindLocal(ident->Name(), binder::ResolveBindingOptions::ALL); + + if (resolved == nullptr) { + resolved = scopeIter->Parent()->FindLocal(ident->Name(), binder::ResolveBindingOptions::ALL); + } + } + + if (resolved == nullptr) { + ThrowTypeError({"Unresolved reference ", ident->Name()}, ident->Start()); + } + + auto *resolvedType = GetTypeOfVariable(resolved); + + switch (ident->Parent()->Type()) { + case ir::AstNodeType::CALL_EXPRESSION: { + if (ident->Parent()->AsCallExpression()->Callee() == ident && !resolvedType->IsETSFunctionType()) { + resolved = nullptr; + } + + break; + } + case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: { + if (ident->Parent()->AsETSNewClassInstanceExpression()->GetTypeRef() == ident && + !resolved->HasFlag(binder::VariableFlags::CLASS_OR_INTERFACE)) { + resolved = nullptr; + } + + break; + } + case ir::AstNodeType::ETS_METHOD_REFERENCE_EXPRESSION: { + if (ident->Parent()->AsETSMethodReferenceExpression()->Base() == nullptr && + !resolvedType->IsETSFunctionType()) { + resolved = nullptr; + } + + break; + } + case ir::AstNodeType::MEMBER_EXPRESSION: { + if (ident->Parent()->AsMemberExpression()->IsComputed()) { + if (!resolved->Declaration()->PossibleTDZ()) { + resolved = nullptr; + } + + break; + } + + if (!resolvedType->IsETSObjectType() && !resolvedType->IsETSArrayType()) { + resolved = nullptr; + } + + break; + } + case ir::AstNodeType::BINARY_EXPRESSION: { + auto *binaryExpr = ident->Parent()->AsBinaryExpression(); + if (binaryExpr->OperatorType() == lexer::TokenType::KEYW_INSTANCEOF && binaryExpr->Right() == ident) { + if (!resolvedType->IsETSObjectType()) { + resolved = nullptr; + } + + break; + } + + [[fallthrough]]; + } + case ir::AstNodeType::UPDATE_EXPRESSION: + case ir::AstNodeType::UNARY_EXPRESSION: { + if (!resolved->Declaration()->PossibleTDZ()) { + resolved = nullptr; + } + + break; + } + case ir::AstNodeType::ASSIGNMENT_EXPRESSION: { + auto *assignmentExpr = ident->Parent()->AsAssignmentExpression(); + + if (assignmentExpr->Left() == ident && !resolved->Declaration()->PossibleTDZ()) { + resolved = nullptr; + break; + } + + if (assignmentExpr->Right() == ident) { + auto *targetType = assignmentExpr->Left()->TsType(); + ASSERT(targetType != nullptr); + + if (targetType->IsETSObjectType() && + targetType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL)) { + if (!resolvedType->IsETSFunctionType()) { + resolved = nullptr; + } + + break; + } + + if (!resolved->Declaration()->PossibleTDZ()) { + resolved = nullptr; + break; + } + } + + break; + } + default: { + if (!resolved->Declaration()->PossibleTDZ() && !resolvedType->IsETSFunctionType()) { + resolved = nullptr; + } + + break; + } + } + + if (resolved == nullptr) { + ThrowTypeError({"Unresolved reference ", ident->Name()}, ident->Start()); + } + + if (resolved->HasFlag(binder::VariableFlags::METHOD)) { + ASSERT(resolved->TsType()->IsETSFunctionType() && + !resolved->TsType()->AsETSFunctionType()->CallSignatures().empty()); + auto *funcType = resolved->TsType()->AsETSFunctionType(); + + // In the case of function references, it is not enough to find the first method field and use it's function + // type, beause at the position of the call we should be able to work with every possible signature, even with + // ones that came from base classes. + resolved = funcType->CallSignatures()[0]->Owner()->CreateSyntheticVarFromEverySignature( + ident->Name(), PropertySearchFlags::SEARCH_METHOD | PropertySearchFlags::SEARCH_IN_BASE); + } + + ValidatePropertyAccess(resolved, Context().ContainingClass(), ident->Start()); + ident->SetVariable(resolved); + return resolved->TsType(); +} + +void ETSChecker::ValidateUnaryOperatorOperand(binder::Variable *variable) +{ + if (variable->Declaration()->IsConstDecl()) { + if (HasStatus(CheckerStatus::IN_CONSTRUCTOR | CheckerStatus::IN_STATIC_BLOCK) && + !variable->HasFlag(binder::VariableFlags::EXPLICIT_INIT_REQUIRED)) { + ThrowTypeError({"Cannot reassign constant field ", variable->Name()}, + variable->Declaration()->Node()->Start()); + } + if (!HasStatus(CheckerStatus::IN_CONSTRUCTOR | CheckerStatus::IN_STATIC_BLOCK)) { + ThrowTypeError({"Cannot assign to a constant variable ", variable->Name()}, + variable->Declaration()->Node()->Start()); + } + } +} + +std::tuple ETSChecker::ApplyBinaryOperatorPromotion(Type *left, Type *right, TypeFlag test) +{ + Type *unboxedL = ETSBuiltinTypeAsPrimitiveType(left); + Type *unboxedR = ETSBuiltinTypeAsPrimitiveType(right); + + if (unboxedL == nullptr || unboxedR == nullptr) { + return {nullptr, false}; + } + + if (!unboxedL->HasTypeFlag(test) || !unboxedR->HasTypeFlag(test)) { + return {nullptr, false}; + } + + if (unboxedL->HasTypeFlag(TypeFlag::CONSTANT) && unboxedR->HasTypeFlag(TypeFlag::CONSTANT)) { + return {nullptr, true}; + } + + if (unboxedL->HasTypeFlag(TypeFlag::ETS_NUMERIC) && unboxedR->HasTypeFlag(TypeFlag::ETS_NUMERIC)) { + if (unboxedL->IsDoubleType() || unboxedR->IsDoubleType()) { + return {GlobalDoubleType(), false}; + } + + if (unboxedL->IsFloatType() || unboxedR->IsFloatType()) { + return {GlobalFloatType(), false}; + } + + if (unboxedL->IsLongType() || unboxedR->IsLongType()) { + return {GlobalLongType(), false}; + } + + return {GlobalIntType(), false}; + } + + if (IsTypeIdenticalTo(unboxedL, unboxedR)) { + return {unboxedL, false}; + } + + return {GlobalETSObjectType(), false}; +} + +Type *ETSChecker::ApplyUnaryOperatorPromotion(Type *type, bool createConst) +{ + Type *unboxedType = ETSBuiltinTypeAsPrimitiveType(type); + + if (unboxedType == nullptr) { + return nullptr; + } + + switch (ETSType(unboxedType)) { + case TypeFlag::BYTE: + case TypeFlag::SHORT: + case TypeFlag::CHAR: { + if (!createConst) { + return GlobalIntType(); + } + + return CreateIntTypeFromType(unboxedType); + } + default: { + return unboxedType; + } + } +} + +Type *ETSChecker::HandleBooleanLogicalOperators(Type *leftType, Type *rightType, lexer::TokenType tokenType) +{ + using UType = typename ETSBooleanType::UType; + ASSERT(leftType->IsETSBooleanType() && rightType->IsETSBooleanType()); + + if (!leftType->HasTypeFlag(checker::TypeFlag::CONSTANT) || !rightType->HasTypeFlag(checker::TypeFlag::CONSTANT)) { + return GlobalETSBooleanType(); + } + + UType leftValue = leftType->AsETSBooleanType()->GetValue(); + UType rightValue = rightType->AsETSBooleanType()->GetValue(); + + switch (tokenType) { + case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: { + return CreateETSBooleanType(leftValue ^ rightValue); + } + case lexer::TokenType::PUNCTUATOR_BITWISE_AND: { + return CreateETSBooleanType((static_cast(leftValue) & static_cast(rightValue)) != 0); + } + case lexer::TokenType::PUNCTUATOR_BITWISE_OR: { + return CreateETSBooleanType((static_cast(leftValue) | static_cast(rightValue)) != 0); + } + case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: { + return CreateETSBooleanType(leftValue || rightValue); + } + case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: { + return CreateETSBooleanType(leftValue && rightValue); + } + default: { + break; + } + } + + UNREACHABLE(); + return nullptr; +} + +checker::Type *ETSChecker::CheckVariableDeclaration(ir::Identifier *ident, ir::TypeNode *typeAnnotation, + ir::Expression *init, ir::ModifierFlags flags) +{ + const util::StringView &varName = ident->Name(); + ASSERT(ident->Variable()); + binder::Variable *bindingVar = ident->Variable(); + checker::Type *annotationType = nullptr; + + bool isConst = (flags & ir::ModifierFlags::CONST) != 0; + + if (typeAnnotation != nullptr) { + annotationType = typeAnnotation->GetType(this); + bindingVar->SetTsType(annotationType); + } + + if (init == nullptr) { + return annotationType; + } + + checker::Type *initType = init->Check(this); + + if (annotationType != nullptr) { + AssignmentContext(Relation(), init, initType, annotationType, init->Start(), + {"Initializers type is not assignable to the target type"}); + if (isConst && initType->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) { + bindingVar->SetTsType(init->TsType()); + } + } else { + bindingVar->SetTsType(isConst ? initType : GetDefaultTypeFromPrimitiveType(initType)); + + if (bindingVar->TsType()->IsETSNullType()) { + ThrowTypeError({"Cannot infer type for variable '", varName, "'."}, init->Start()); + } + + if (initType->IsETSObjectType() && initType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::ENUM) && + !init->IsMemberExpression()) { + ThrowTypeError( + {"Cannot assign type '", initType->AsETSObjectType()->Name(), "' for variable ", varName, "."}, + init->Start()); + } + } + + return bindingVar->TsType(); +} + +Type *ETSChecker::GetTypeFromInterfaceReference(binder::Variable *var) +{ + if (var->TsType() != nullptr) { + return var->TsType(); + } + + auto *interfaceType = BuildInterfaceProperties(var->Declaration()->Node()->AsTSInterfaceDeclaration()); + var->SetTsType(interfaceType); + return interfaceType; +} + +Type *ETSChecker::GetTypeFromClassReference(binder::Variable *var) +{ + if (var->TsType() != nullptr) { + return var->TsType(); + } + + auto *classType = BuildClassProperties(var->Declaration()->Node()->AsClassDefinition()); + var->SetTsType(classType); + return classType; +} + +Type *ETSChecker::GetTypeFromEnumReference([[maybe_unused]] binder::Variable *var) +{ + if (var->TsType() != nullptr) { + return var->TsType(); + } + + return CreateETSEnumType(var->Declaration()->Node()->AsTSEnumDeclaration()); +} + +Type *ETSChecker::GetTypeFromTypeParameterReference(binder::LocalVariable *var, const lexer::SourcePosition &pos) +{ + if (HasStatus(CheckerStatus::IN_STATIC_CONTEXT)) { + ThrowTypeError({"Cannot make a static reference to the non-static type ", var->Name()}, pos); + } + + auto *typeParam = var->TsType()->AsETSTypeParameter(); + return CreateTypeReference(typeParam->GetTypeRef(), typeParam->GetAssemblerTypeRef(), var); +} + +Type *ETSChecker::GetReferencedTypeFromBase([[maybe_unused]] Type *baseType, [[maybe_unused]] ir::Expression *name) +{ + // TODO(user): + return nullptr; +} + +Type *ETSChecker::GetReferencedTypeBase(ir::Expression *name) +{ + if (name->IsTSQualifiedName()) { + // TODO(user): + return nullptr; + } + + ASSERT(name->IsIdentifier() && name->AsIdentifier()->Variable()); + auto *refVar = name->AsIdentifier()->Variable()->AsLocalVariable(); + + switch (refVar->Declaration()->Node()->Type()) { + case ir::AstNodeType::TS_INTERFACE_DECLARATION: { + return GetTypeFromInterfaceReference(refVar); + } + case ir::AstNodeType::CLASS_DECLARATION: + case ir::AstNodeType::CLASS_DEFINITION: { + return GetTypeFromClassReference(refVar); + } + case ir::AstNodeType::TS_ENUM_DECLARATION: { + return GetTypeFromEnumReference(refVar); + } + case ir::AstNodeType::TS_TYPE_PARAMETER: { + return GetTypeFromTypeParameterReference(refVar, name->Start()); + } + default: { + UNREACHABLE(); + } + } +} + +void ETSChecker::ConcantConstantString(util::UString &target, Type *type) +{ + switch (ETSType(type)) { + case TypeFlag::ETS_OBJECT: { + ASSERT(type->IsETSStringType()); + target.Append(type->AsETSStringType()->GetValue()); + break; + } + case TypeFlag::ETS_BOOLEAN: { + ETSBooleanType::UType value = type->AsETSBooleanType()->GetValue(); + target.Append(value ? "true" : "false"); + break; + } + case TypeFlag::BYTE: { + ByteType::UType value = type->AsByteType()->GetValue(); + target.Append(std::to_string(value)); + break; + } + case TypeFlag::CHAR: { + CharType::UType value = type->AsCharType()->GetValue(); + target.Append(std::to_string(value)); + break; + } + case TypeFlag::SHORT: { + ShortType::UType value = type->AsShortType()->GetValue(); + target.Append(std::to_string(value)); + break; + } + case TypeFlag::INT: { + IntType::UType value = type->AsIntType()->GetValue(); + target.Append(std::to_string(value)); + break; + } + case TypeFlag::LONG: { + LongType::UType value = type->AsLongType()->GetValue(); + target.Append(std::to_string(value)); + break; + } + case TypeFlag::FLOAT: { + FloatType::UType value = type->AsFloatType()->GetValue(); + target.Append(std::to_string(value)); + break; + } + case TypeFlag::DOUBLE: { + DoubleType::UType value = type->AsDoubleType()->GetValue(); + target.Append(std::to_string(value)); + break; + } + default: { + UNREACHABLE(); + } + } +} + +Type *ETSChecker::HandleStringConcatenation(Type *leftType, Type *rightType) +{ + ASSERT(leftType->IsETSStringType() || rightType->IsETSStringType()); + + if (!leftType->HasTypeFlag(checker::TypeFlag::CONSTANT) || !rightType->HasTypeFlag(checker::TypeFlag::CONSTANT)) { + return GlobalETSStringLiteralType(); + } + + util::UString concatenated(Allocator()); + ConcantConstantString(concatenated, leftType); + ConcantConstantString(concatenated, rightType); + + return CreateETSStringLiteralType(concatenated.View()); +} + +ETSFunctionType *ETSChecker::FindFunctionInVectorGivenByName(util::StringView name, + ArenaVector &list) +{ + for (auto *it : list) { + if (it->Name() == name) { + return it; + } + } + + return nullptr; +} + +bool ETSChecker::IsFunctionContainsSignature(ETSFunctionType *funcType, Signature *signature) +{ + for (auto *it : funcType->CallSignatures()) { + Relation()->IsIdenticalTo(it, signature); + if (Relation()->IsTrue()) { + return true; + } + } + + return false; +} + +void ETSChecker::CheckFunctionContainsClashingSignature(const ETSFunctionType *funcType, Signature *signature) +{ + for (auto *it : funcType->CallSignatures()) { + SavedTypeRelationFlagsContext strfCtx(Relation(), TypeRelationFlag::NO_RETURN_TYPE_CHECK); + Relation()->IsIdenticalTo(it, signature); + if (Relation()->IsTrue() && it->Function()->Id()->Name() == signature->Function()->Id()->Name()) { + std::stringstream ss; + it->ToString(ss, nullptr, true); + auto sigStr1 = ss.str(); + ss.str(std::string {}); // Clear buffer + signature->ToString(ss, nullptr, true); + auto sigStr2 = ss.str(); + ThrowTypeError({"Function '", it->Function()->Id()->Name(), sigStr1.c_str(), + "' is redeclared with different signature '", signature->Function()->Id()->Name(), + sigStr2.c_str(), "'"}, + signature->Function()->ReturnTypeAnnotation()->Start()); + } + } +} + +void ETSChecker::MergeSignatures(ETSFunctionType *target, ETSFunctionType *source) +{ + for (auto *s : source->CallSignatures()) { + if (IsFunctionContainsSignature(target, s)) { + continue; + } + + CheckFunctionContainsClashingSignature(target, s); + target->AddCallSignature(s); + } +} + +void ETSChecker::MergeComputedAbstracts(ArenaVector &merged, ArenaVector ¤t) +{ + for (auto *curr : current) { + auto name = curr->Name(); + auto *found = FindFunctionInVectorGivenByName(name, merged); + if (found != nullptr) { + MergeSignatures(found, curr); + continue; + } + + merged.push_back(curr); + } +} + +ir::AstNode *ETSChecker::FindAncestorGivenByType(ir::AstNode *node, ir::AstNodeType type) +{ + auto *iter = node->Parent(); + + while (iter != nullptr) { + if (iter->Type() == type) { + return iter; + } + + iter = iter->Parent(); + } + + return nullptr; +} + +util::StringView ETSChecker::GetContainingObjectNameFromSignature(Signature *signature) +{ + ASSERT(signature->Function()); + auto *iter = signature->Function()->Parent(); + + while (iter != nullptr) { + if (iter->IsClassDefinition()) { + return iter->AsClassDefinition()->Ident()->Name(); + } + + if (iter->IsTSInterfaceDeclaration()) { + return iter->AsTSInterfaceDeclaration()->Id()->Name(); + } + + iter = iter->Parent(); + } + + UNREACHABLE(); + return {""}; +} + +bool ETSChecker::IsTypeBuiltinType(Type *type) +{ + if (!type->IsETSObjectType()) { + return false; + } + + switch (type->AsETSObjectType()->BuiltInKind()) { + case ETSObjectFlags::BUILTIN_BOOLEAN: + case ETSObjectFlags::BUILTIN_BYTE: + case ETSObjectFlags::BUILTIN_SHORT: + case ETSObjectFlags::BUILTIN_CHAR: + case ETSObjectFlags::BUILTIN_INT: + case ETSObjectFlags::BUILTIN_LONG: + case ETSObjectFlags::BUILTIN_FLOAT: + case ETSObjectFlags::BUILTIN_DOUBLE: { + return true; + } + default: + return false; + } +} + +const ir::AstNode *ETSChecker::FindJumpTarget(ir::AstNodeType nodeType, const ir::AstNode *node, + const ir::Identifier *target) +{ + const auto *iter = node->Parent(); + + while (iter != nullptr) { + switch (iter->Type()) { + case ir::AstNodeType::LABELLED_STATEMENT: { + const auto *labelled = iter->AsLabelledStatement(); + if (labelled->Ident() == target) { + if (nodeType == ir::AstNodeType::CONTINUE_STATEMENT) { + return labelled->GetReferencedStatement(); + } + + return labelled; + } + + break; + } + case ir::AstNodeType::DO_WHILE_STATEMENT: + case ir::AstNodeType::WHILE_STATEMENT: + case ir::AstNodeType::FOR_UPDATE_STATEMENT: + case ir::AstNodeType::FOR_OF_STATEMENT: + case ir::AstNodeType::SWITCH_CASE_STATEMENT: + case ir::AstNodeType::SWITCH_STATEMENT: { + if (target == nullptr) { + return iter; + } + break; + } + default: { + break; + } + } + + iter = iter->Parent(); + } + + UNREACHABLE(); + return nullptr; +} + +binder::VariableFlags ETSChecker::GetAccessFlagFromNode(const ir::AstNode *node) +{ + if (node->IsPrivate()) { + return binder::VariableFlags::PRIVATE; + } + + if (node->IsProtected()) { + return binder::VariableFlags::PROTECTED; + } + + return binder::VariableFlags::PUBLIC; +} + +void ETSChecker::CheckSwitchDiscriminant(ir::Expression *discriminant) +{ + ASSERT(discriminant->TsType()); + + auto discriminantType = discriminant->TsType(); + if (discriminantType->HasTypeFlag(TypeFlag::VALID_SWITCH_TYPE)) { + return; + } + + if (discriminantType->IsETSObjectType() && + discriminantType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::VALID_SWITCH_TYPE)) { + return; + } + + ThrowTypeError({"Incompatible types. Found: ", discriminantType, + ", required: char , byte , short , int, long , Char , Byte , Short , Int, Long , String " + "or an enum type"}, + discriminant->Start()); +} + +Type *ETSChecker::ETSBuiltinTypeAsPrimitiveType(Type *objectType) +{ + if (objectType->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) { + return objectType; + } + + if (!objectType->IsETSObjectType() || + !objectType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::UNBOXABLE_TYPE)) { + return nullptr; + } + + auto savedResult = Relation()->IsTrue(); + Relation()->Result(false); + + UnboxingConverter converter = UnboxingConverter(AsETSChecker(), Relation(), objectType, objectType); + Relation()->Result(savedResult); + return converter.Result(); +} + +Type *ETSChecker::PrimitiveTypeAsETSBuiltinType(Type *objectType) +{ + if (objectType->IsETSObjectType() && objectType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE)) { + return objectType; + } + + if (!objectType->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) { + return nullptr; + } + + auto savedResult = Relation()->IsTrue(); + Relation()->Result(false); + + BoxingConverter converter = BoxingConverter(AsETSChecker(), Relation(), objectType, objectType); + Relation()->Result(savedResult); + return converter.Result(); +} + +void ETSChecker::CheckForSameSwitchCases(ArenaVector *cases) +{ + for (size_t caseNum = 0; caseNum < cases->size(); caseNum++) { + for (size_t compareCase = caseNum + 1; compareCase < cases->size(); compareCase++) { + auto caseTest = cases->at(caseNum)->Test(); + auto compareCaseTest = cases->at(compareCase)->Test(); + if (caseTest == nullptr || compareCaseTest == nullptr) { + continue; + } + + caseTest = caseTest->IsMemberExpression() ? caseTest->AsMemberExpression()->Property() : caseTest; + compareCaseTest = compareCaseTest->IsMemberExpression() ? compareCaseTest->AsMemberExpression()->Property() + : compareCaseTest; + + if (caseTest->IsIdentifier()) { + CheckIdentifierSwitchCase(caseTest, compareCaseTest, cases->at(caseNum)->Start()); + continue; + } + + if (compareCaseTest->IsIdentifier()) { + CheckIdentifierSwitchCase(compareCaseTest, caseTest, cases->at(compareCase)->Start()); + continue; + } + + if (GetStringViewFromLiteral(caseTest).Compare(GetStringViewFromLiteral(compareCaseTest)) != 0) { + continue; + } + + ThrowTypeError("Case duplicate", cases->at(compareCase)->Start()); + } + } +} + +util::StringView ETSChecker::GetStringViewFromIdentifierValue(ir::Expression *identifier) +{ + ASSERT(identifier->AsIdentifier()->Variable()->TsType()); + auto identifierType = identifier->AsIdentifier()->Variable()->TsType(); + auto identifierTypeKind = ETSChecker::TypeKind(identifierType); + switch (identifierTypeKind) { + case TypeFlag::BYTE: { + return util::StringView(std::to_string(identifierType->AsByteType()->GetValue())); + } + case TypeFlag::SHORT: { + return util::StringView(std::to_string(identifierType->AsShortType()->GetValue())); + } + case TypeFlag::CHAR: { + return util::StringView(std::to_string(identifierType->AsCharType()->GetValue())); + } + case TypeFlag::INT: { + return util::StringView(std::to_string(identifierType->AsIntType()->GetValue())); + } + case TypeFlag::LONG: { + return util::StringView(std::to_string(identifierType->AsLongType()->GetValue())); + } + default: { + UNREACHABLE(); + } + } +} + +bool ETSChecker::CompareIdentifiersValuesAreDifferent(ir::Expression *identifier, ir::Expression *compareValue) +{ + auto caseValue = GetStringViewFromIdentifierValue(identifier); + if (compareValue->IsIdentifier() && compareValue->AsIdentifier()->Variable()->Declaration()->IsConstDecl()) { + auto compareCaseValue = GetStringViewFromIdentifierValue(compareValue); + return caseValue.Compare(compareCaseValue) != 0; + } + + return caseValue.Compare(GetStringViewFromLiteral(compareValue)) != 0; +} + +void ETSChecker::CheckIdentifierSwitchCase(ir::Expression *currentCase, ir::Expression *compareCase, + const lexer::SourcePosition &pos) +{ + if (!currentCase->AsIdentifier()->Variable()->Declaration()->IsConstDecl()) { + ThrowTypeError("Constant expression required", pos); + } + if (!CompareIdentifiersValuesAreDifferent(currentCase, compareCase)) { + ThrowTypeError("Variable has same value with another switch case", pos); + } +} + +util::StringView ETSChecker::GetStringViewFromLiteral(ir::Expression *caseTest) +{ + switch (caseTest->Type()) { + case ir::AstNodeType::CHAR_LITERAL: { + return util::StringView(std::to_string(caseTest->AsCharLiteral()->Char())); + } + case ir::AstNodeType::STRING_LITERAL: + case ir::AstNodeType::NUMBER_LITERAL: { + return util::Helpers::LiteralToPropName(caseTest); + } + default: + UNREACHABLE(); + } +} + +bool ETSChecker::IsSameDeclarationType(binder::LocalVariable *target, binder::LocalVariable *compare) +{ + if (target->Declaration()->Type() != compare->Declaration()->Type()) { + return false; + } + + if ((target->HasFlag(binder::VariableFlags::METHOD_REFERENCE) && + !compare->HasFlag(binder::VariableFlags::METHOD_REFERENCE)) || + (!target->HasFlag(binder::VariableFlags::METHOD_REFERENCE) && + compare->HasFlag(binder::VariableFlags::METHOD_REFERENCE))) { + return false; + } + + return true; +} +} // namespace panda::es2panda::checker diff --git a/checker/ets/narrowingConverter.cpp b/checker/ets/narrowingConverter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cb70ab143ed4efd4cf76405ee2732521525a7ac8 --- /dev/null +++ b/checker/ets/narrowingConverter.cpp @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2021-2022 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 "narrowingConverter.h" + +namespace panda::es2panda::checker { +} // namespace panda::es2panda::checker diff --git a/checker/ets/narrowingConverter.h b/checker/ets/narrowingConverter.h new file mode 100644 index 0000000000000000000000000000000000000000..c1d4e0afdc83b04fa44b9ba23c9b2b20cf1c4f56 --- /dev/null +++ b/checker/ets/narrowingConverter.h @@ -0,0 +1,123 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_ETS_NARROWING_CONVERTER_H +#define ES2PANDA_COMPILER_CHECKER_ETS_NARROWING_CONVERTER_H + +#include "plugins/ecmascript/es2panda/checker/ets/typeConverter.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" +#include "plugins/ecmascript/es2panda/util/helpers.h" + +namespace panda::es2panda::checker { +class NarrowingConverter : public TypeConverter { +public: + explicit NarrowingConverter(ETSChecker *checker, TypeRelation *relation, Type *target, Type *source) + : TypeConverter(checker, relation, target, source) + { + if (!relation->ApplyNarrowing()) { + return; + } + + ASSERT(relation->GetNode()); + + switch (ETSChecker::ETSChecker::ETSType(target)) { + case TypeFlag::BYTE: { + ApplyNarrowing(TypeFlag::NARROWABLE_TO_BYTE); + break; + } + case TypeFlag::CHAR: { + ApplyNarrowing(TypeFlag::NARROWABLE_TO_CHAR); + break; + } + case TypeFlag::SHORT: { + ApplyNarrowing(TypeFlag::NARROWABLE_TO_SHORT); + break; + } + case TypeFlag::INT: { + ApplyNarrowing(TypeFlag::NARROWABLE_TO_INT); + break; + } + case TypeFlag::LONG: { + ApplyNarrowing(TypeFlag::NARROWABLE_TO_LONG); + break; + } + case TypeFlag::FLOAT: { + ApplyNarrowing(TypeFlag::NARROWABLE_TO_FLOAT); + break; + } + + default: { + break; + } + } + } + +private: + template + void ApplyNarrowing(TypeFlag flag) + { + if (!Source()->HasTypeFlag(flag)) { + return; + } + + switch (ETSChecker::ETSChecker::ETSType(Source())) { + case TypeFlag::CHAR: { + ApplyNarrowing(); + break; + } + case TypeFlag::SHORT: { + ApplyNarrowing(); + break; + } + case TypeFlag::INT: { + ApplyNarrowing(); + break; + } + case TypeFlag::LONG: { + ApplyNarrowing(); + break; + } + case TypeFlag::FLOAT: { + ApplyNarrowing(); + break; + } + case TypeFlag::DOUBLE: { + ApplyNarrowing(); + break; + } + default: { + break; + } + } + } + + template + void ApplyNarrowing() + { + using SType = typename SourceType::UType; + using TType = typename TargetType::UType; + SType value = reinterpret_cast(Source())->GetValue(); + + if (util::Helpers::IsTargetFitInSourceRange(value)) { + Relation()->GetNode()->SetTsType(Checker()->Allocator()->New(static_cast(value))); + Relation()->Result(true); + } else { + Relation()->Result(RelationResult::ERROR); + } + } +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/ets/narrowingWideningConverter.cpp b/checker/ets/narrowingWideningConverter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f79174d7f9089fee948a549982ce448c75f9ce39 --- /dev/null +++ b/checker/ets/narrowingWideningConverter.cpp @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2021-2022 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 "narrowingWideningConverter.h" + +namespace panda::es2panda::checker { +} // namespace panda::es2panda::checker diff --git a/checker/ets/narrowingWideningConverter.h b/checker/ets/narrowingWideningConverter.h new file mode 100644 index 0000000000000000000000000000000000000000..a20960981f64dbcecd1af0c44dc0c282849e7210 --- /dev/null +++ b/checker/ets/narrowingWideningConverter.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_ETS_NARROWING_WIDENING_CONVERTER_H +#define ES2PANDA_COMPILER_CHECKER_ETS_NARROWING_WIDENING_CONVERTER_H + +#include "plugins/ecmascript/es2panda/checker/ets/narrowingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/wideningConverter.h" + +namespace panda::es2panda::checker { +class NarrowingWideningConverter : public NarrowingConverter { +public: + explicit NarrowingWideningConverter(ETSChecker *checker, TypeRelation *relation, Type *target, Type *source) + : NarrowingConverter(checker, relation, target, source) + { + if (Relation()->IsTrue() || Relation()->IsError()) { + return; + } + + WideningConverter(checker, relation, target, source); + } +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/ets/object.cpp b/checker/ets/object.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6af955bb26a51e389f698a3acac36881cc6b205f --- /dev/null +++ b/checker/ets/object.cpp @@ -0,0 +1,1039 @@ +/** + * Copyright (c) 2021 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 +#include "plugins/ecmascript/es2panda/binder/variableFlags.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/etsObjectType.h" +#include "plugins/ecmascript/es2panda/ir/astNode.h" +#include "plugins/ecmascript/es2panda/ir/typeNode.h" +#include "plugins/ecmascript/es2panda/ir/base/classDefinition.h" +#include "plugins/ecmascript/es2panda/ir/base/classElement.h" +#include "plugins/ecmascript/es2panda/ir/base/classProperty.h" +#include "plugins/ecmascript/es2panda/ir/base/methodDefinition.h" +#include "plugins/ecmascript/es2panda/ir/base/classStaticBlock.h" +#include "plugins/ecmascript/es2panda/ir/base/scriptFunction.h" +#include "plugins/ecmascript/es2panda/ir/statements/blockStatement.h" +#include "plugins/ecmascript/es2panda/ir/statements/expressionStatement.h" +#include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" +#include "plugins/ecmascript/es2panda/ir/expressions/functionExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/memberExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/callExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/superExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/assignmentExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/thisExpression.h" +#include "plugins/ecmascript/es2panda/ir/statements/classDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsClassImplements.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsEnumDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsEnumMember.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceHeritage.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceBody.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsTypeParameter.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsTypeParameterDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsTypeReference.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsTypeReferencePart.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsNewClassInstanceExpression.h" +#include "plugins/ecmascript/es2panda/binder/variable.h" +#include "plugins/ecmascript/es2panda/binder/scope.h" +#include "plugins/ecmascript/es2panda/binder/declaration.h" +#include "plugins/ecmascript/es2panda/binder/ETSBinder.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/types.h" + +namespace panda::es2panda::checker { +ETSObjectType *ETSChecker::GetSuperType(ETSObjectType *type) +{ + if (type->HasObjectFlag(ETSObjectFlags::RESOLVED_SUPER)) { + return type->SuperType(); + } + + ASSERT(type->Variable() && type->GetDeclNode()->IsClassDefinition()); + auto *classDef = type->GetDeclNode()->AsClassDefinition(); + + if (classDef->Super() == nullptr) { + type->AddObjectFlag(ETSObjectFlags::RESOLVED_SUPER); + if (type != GlobalETSObjectType()) { + type->SetSuperType(GlobalETSObjectType()); + } + return GlobalETSObjectType(); + } + + TypeStackElement tse(this, type, {"Cyclic inheritance involving ", type->Name(), "."}, classDef->Ident()->Start()); + + Type *superType = classDef->Super()->AsTypeNode()->GetType(this); + + if (!superType->IsETSObjectType() || !superType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::CLASS)) { + ThrowTypeError("Super type is not extensible.", classDef->Super()->Start()); + } + + ETSObjectType *superObj = superType->AsETSObjectType(); + + if (!(superObj->GetDeclNode()->IsOpen() || superObj->GetDeclNode()->IsAbstract())) { + ThrowTypeError("Cannot inherit without 'open'.", classDef->Super()->Start()); + } + + type->SetSuperType(superObj); + GetSuperType(superObj); + + type->AddObjectFlag(ETSObjectFlags::RESOLVED_SUPER); + return type->SuperType(); +} + +void ETSChecker::ValidateImplementedInterface(ETSObjectType *type, Type *interface, + std::unordered_set *extendsSet, const lexer::SourcePosition &pos) +{ + if (!interface->IsETSObjectType() || !interface->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::INTERFACE)) { + ThrowTypeError("Interface expected here.", pos); + } + + if (!extendsSet->insert(interface).second) { + ThrowTypeError("Repeated interface.", pos); + } + + type->AddInterface(interface->AsETSObjectType()); + GetInterfacesOfInterface(interface->AsETSObjectType()); +} + +ArenaVector ETSChecker::GetInterfacesOfClass(ETSObjectType *type) +{ + const auto *declNode = type->GetDeclNode()->AsClassDefinition(); + + std::unordered_set extendsSet; + for (auto *it : declNode->Implements()) { + ValidateImplementedInterface(type, it->Expr()->AsTypeNode()->GetType(this), &extendsSet, it->Start()); + } + + return type->Interfaces(); +} + +ArenaVector ETSChecker::GetInterfacesOfInterface(ETSObjectType *type) +{ + const auto *declNode = type->GetDeclNode()->AsTSInterfaceDeclaration(); + + TypeStackElement tse(this, type, {"Cyclic inheritance involving ", type->Name(), "."}, declNode->Id()->Start()); + + std::unordered_set extendsSet; + for (auto *it : declNode->Extends()) { + ValidateImplementedInterface(type, it->Expr()->AsTypeNode()->GetType(this), &extendsSet, it->Start()); + } + + return type->Interfaces(); +} + +ArenaVector ETSChecker::GetInterfaces(ETSObjectType *type) +{ + if (type->HasObjectFlag(ETSObjectFlags::RESOLVED_INTERFACES)) { + return type->Interfaces(); + } + + ASSERT(type->GetDeclNode()->IsClassDefinition() || type->GetDeclNode()->IsTSInterfaceDeclaration()); + + if (type->GetDeclNode()->IsClassDefinition()) { + GetInterfacesOfClass(type); + } else { + GetInterfacesOfInterface(type); + } + + type->AddObjectFlag(ETSObjectFlags::RESOLVED_INTERFACES); + return type->Interfaces(); +} + +void ETSChecker::SetTypeParameterType(ir::TSTypeParameter *typeParam, Type *assemblerType) +{ + auto *var = typeParam->Name()->Variable(); + auto *typeParamType = CreateTypeParameter(assemblerType); + typeParamType->SetVariable(var); + var->SetTsType(typeParamType); +} + +void ETSChecker::CreateTypeForTypeParameters(ETSObjectType *type, ir::TSTypeParameterDeclaration *typeParams) +{ + if (type->HasObjectFlag(ETSObjectFlags::RESOLVED_TYPE_PARAMS)) { + return; + } + + checker::ScopeContext scopeCtx(this, typeParams->Scope()); + + for (auto *param : typeParams->Params()) { + Type *paramType = GlobalETSObjectType(); + + if (param->Constraint() != nullptr) { + if (param->Constraint()->IsETSTypeReference() && + param->Constraint()->AsETSTypeReference()->Part()->Name()->IsIdentifier() && + param->Name()->Name() == + param->Constraint()->AsETSTypeReference()->Part()->Name()->AsIdentifier()->Name()) { + ThrowTypeError({"Type variable '", param->Name()->Name(), "' cannot depend on itself"}, + param->Constraint()->Start()); + } + + auto *constraintType = param->Constraint()->GetType(this); + if (!constraintType->IsETSObjectType() || !constraintType->IsETSTypeParameter()) { + ThrowTypeError("Extends constraint must be an object", param->Constraint()->Start()); + } + + paramType = constraintType; + } + + SetTypeParameterType(param, paramType); + } + + type->AddObjectFlag(ETSObjectFlags::RESOLVED_TYPE_PARAMS); +} + +void ETSChecker::CreateTypeForInterfaceTypeParameters(ETSObjectType *type) +{ + ir::TSTypeParameterDeclaration *typeParams = type->GetDeclNode()->AsTSInterfaceDeclaration()->TypeParams(); + CreateTypeForTypeParameters(type, typeParams); +} + +void ETSChecker::CreateTypeForClassTypeParameters(ETSObjectType *type) +{ + ir::TSTypeParameterDeclaration *typeParams = type->GetDeclNode()->AsClassDefinition()->TypeParams(); + CreateTypeForTypeParameters(type, typeParams); +} + +ETSObjectType *ETSChecker::BuildInterfaceProperties(ir::TSInterfaceDeclaration *interfaceDecl) +{ + auto *var = interfaceDecl->Id()->Variable(); + ASSERT(var); + + checker::ETSObjectType *interfaceType {}; + if (var->TsType() == nullptr) { + interfaceType = CreateETSObjectType(var->Name(), interfaceDecl, + checker::ETSObjectFlags::INTERFACE | checker::ETSObjectFlags::ABSTRACT); + interfaceType->SetVariable(var); + var->SetTsType(interfaceType); + } else { + interfaceType = var->TsType()->AsETSObjectType(); + } + + if (interfaceDecl->TypeParams() != nullptr) { + CreateTypeForInterfaceTypeParameters(interfaceType); + } + + GetInterfacesOfInterface(interfaceType); + + checker::ScopeContext scopeCtx(this, interfaceDecl->Scope()); + auto savedContext = checker::SavedCheckerContext(this, checker::CheckerStatus::IN_INTERFACE, interfaceType); + + ResolveDeclaredMembersOfObject(interfaceType); + + return interfaceType; +} + +ETSObjectType *ETSChecker::BuildClassProperties(ir::ClassDefinition *classDef) +{ + if (classDef->IsOpen() && classDef->IsAbstract()) { + ThrowTypeError("Cannot use both 'open' and 'abstract' modifiers.", classDef->Start()); + } + + auto *var = classDef->Ident()->Variable(); + ASSERT(var); + + const util::StringView &className = classDef->Ident()->Name(); + auto *classScope = classDef->Scope(); + + checker::ETSObjectType *classType {}; + if (var->TsType() == nullptr) { + classType = CreateETSObjectType(className, classDef, checker::ETSObjectFlags::CLASS); + classType->SetVariable(var); + var->SetTsType(classType); + if (classDef->IsAbstract()) { + classType->AddObjectFlag(checker::ETSObjectFlags::ABSTRACT); + } + } else { + classType = var->TsType()->AsETSObjectType(); + } + + classDef->SetTsType(classType); + + if (classDef->TypeParams() != nullptr) { + CreateTypeForClassTypeParameters(classType); + } + + if (!classType->HasObjectFlag(ETSObjectFlags::RESOLVED_SUPER)) { + GetSuperType(classType); + GetInterfacesOfClass(classType); + } + + if (classType->HasObjectFlag(ETSObjectFlags::RESOLVED_MEMBERS)) { + return classType; + } + + checker::ScopeContext scopeCtx(this, classScope); + auto savedContext = checker::SavedCheckerContext(this, checker::CheckerStatus::IN_CLASS, classType); + + ResolveDeclaredMembersOfObject(classType); + + return classType; +} + +ETSObjectType *ETSChecker::BuildAnonymousClassProperties(ir::ClassDefinition *classDef, ETSObjectType *superType) +{ + auto classType = CreateETSObjectType(classDef->Ident()->Name(), classDef, checker::ETSObjectFlags::CLASS); + classDef->SetTsType(classType); + classType->SetSuperType(superType); + classType->AddObjectFlag(checker::ETSObjectFlags::RESOLVED_SUPER); + + checker::ScopeContext scopeCtx(this, classDef->Scope()); + auto savedContext = checker::SavedCheckerContext(this, checker::CheckerStatus::IN_CLASS, classType); + + ResolveDeclaredMembersOfObject(classType); + + return classType; +} + +void ETSChecker::BuildEnumProperties(ETSObjectType *enumType) +{ + checker::ScopeContext scopeCtx(this, Scope()); + auto savedContext = checker::SavedCheckerContext(this, checker::CheckerStatus::IN_ENUM, enumType); + + if (enumType->HasObjectFlag(ETSObjectFlags::RESOLVED_MEMBERS)) { + return; + } + + auto *declNode = enumType->GetDeclNode(); + auto *enumDecl = declNode->AsTSEnumDeclaration(); + + auto *valuesVar = binder::Scope::CreateVar(Allocator(), compiler::Signatures::ENUM_VALUES, + binder::VariableFlags::STATIC, declNode); + valuesVar->AddFlag(binder::VariableFlags::METHOD); + + valuesVar->SetTsType(CreateETSArrayType(enumType)); + enumType->AddProperty(valuesVar); + + for (auto *it : enumDecl->Members()) { + if (it->IsTSEnumMember()) { + auto *element = it->AsTSEnumMember(); + auto *var = element->Key()->AsIdentifier()->Variable(); + + enumType->AddProperty(var->AsLocalVariable()); + var->SetTsType(enumType); + } else if (it->IsMethodDefinition()) { + auto *method = it->AsMethodDefinition(); + BuildMethodSignature(method); + } else { + auto *func = it->AsScriptFunction(); + BuildFunctionSignature(func, true); + } + } + + enumType->AddObjectFlag(ETSObjectFlags::RESOLVED_MEMBERS); +} + +void ETSChecker::ResolveDeclaredMembersOfObject(ETSObjectType *type) +{ + if (type->HasObjectFlag(ETSObjectFlags::RESOLVED_MEMBERS)) { + return; + } + + auto *declNode = type->GetDeclNode(); + binder::ClassScope *scope = declNode->IsTSInterfaceDeclaration() + ? declNode->AsTSInterfaceDeclaration()->Scope()->AsClassScope() + : declNode->AsClassDefinition()->Scope()->AsClassScope(); + + for (auto &[_, it] : scope->InstanceFieldScope()->Bindings()) { + (void)_; + ASSERT(it->Declaration()->Node()->IsClassProperty()); + auto *classProp = it->Declaration()->Node()->AsClassProperty(); + it->AddFlag(GetAccessFlagFromNode(classProp)); + type->AddProperty(it->AsLocalVariable()); + + if (classProp->TypeAnnotation() != nullptr && classProp->TypeAnnotation()->IsETSFunctionType()) { + type->AddProperty(it->AsLocalVariable()); + it->AddFlag(binder::VariableFlags::METHOD_REFERENCE); + } + } + + for (auto &[_, it] : scope->StaticFieldScope()->Bindings()) { + (void)_; + ASSERT(it->Declaration()->Node()->IsClassProperty()); + auto *classProp = it->Declaration()->Node()->AsClassProperty(); + it->AddFlag(GetAccessFlagFromNode(classProp)); + type->AddProperty(it->AsLocalVariable()); + + if (classProp->TypeAnnotation() != nullptr && classProp->TypeAnnotation()->IsETSFunctionType()) { + type->AddProperty(it->AsLocalVariable()); + it->AddFlag(binder::VariableFlags::METHOD_REFERENCE); + } + } + + for (auto &[_, it] : scope->InstanceMethodScope()->Bindings()) { + (void)_; + auto *node = it->Declaration()->Node()->AsMethodDefinition(); + it->AddFlag(GetAccessFlagFromNode(node)); + auto *funcType = BuildMethodSignature(node); + it->SetTsType(funcType); + funcType->SetVariable(it); + node->SetTsType(funcType); + type->AddProperty(it->AsLocalVariable()); + } + + for (auto &[_, it] : scope->StaticMethodScope()->Bindings()) { + (void)_; + if (!it->Declaration()->Node()->IsMethodDefinition()) { + continue; + } + auto *node = it->Declaration()->Node()->AsMethodDefinition(); + it->AddFlag(GetAccessFlagFromNode(node)); + auto *funcType = BuildMethodSignature(node); + it->SetTsType(funcType); + funcType->SetVariable(it); + node->SetTsType(funcType); + + if (node->IsConstructor()) { + type->AddConstructSignature(funcType->CallSignatures()); + continue; + } + + type->AddProperty(it->AsLocalVariable()); + } + + for (auto &[_, it] : scope->InstanceDeclScope()->Bindings()) { + (void)_; + it->AddFlag(GetAccessFlagFromNode(it->Declaration()->Node())); + type->AddProperty(it->AsLocalVariable()); + } + + for (auto &[_, it] : scope->StaticDeclScope()->Bindings()) { + (void)_; + it->AddFlag(GetAccessFlagFromNode(it->Declaration()->Node())); + type->AddProperty(it->AsLocalVariable()); + } + + type->AddObjectFlag(ETSObjectFlags::RESOLVED_MEMBERS); +} + +std::vector ETSChecker::CollectAbstractSignaturesFromObject(const ETSObjectType *objType) +{ + std::vector abstracts; + for (const auto &prop : objType->Methods()) { + GetTypeOfVariable(prop); + + if (!prop->TsType()->IsETSFunctionType()) { + continue; + } + + for (auto *sig : prop->TsType()->AsETSFunctionType()->CallSignatures()) { + if (sig->HasSignatureFlag(SignatureFlags::ABSTRACT) && !sig->HasSignatureFlag(SignatureFlags::PRIVATE)) { + abstracts.push_back(sig); + } + } + } + + return abstracts; +} + +void ETSChecker::CreateFunctionTypesFromAbstracts(const std::vector &abstracts, + ArenaVector *target) +{ + for (auto *it : abstracts) { + auto name = it->Function()->Id()->Name(); + auto *found = FindFunctionInVectorGivenByName(name, *target); + if (found != nullptr) { + found->AddCallSignature(it); + continue; + } + + auto *created = CreateETSFunctionType(it); + created->AddTypeFlag(TypeFlag::SYNTHETIC); + target->push_back(created); + } +} + +void ETSChecker::ComputeAbstractsFromInterface(ETSObjectType *interfaceType) +{ + auto cached = cachedComputedAbstracts_.find(interfaceType); + if (cached != cachedComputedAbstracts_.end()) { + return; + } + + for (auto *it : interfaceType->Interfaces()) { + ComputeAbstractsFromInterface(it); + } + + ArenaVector merged(Allocator()->Adapter()); + CreateFunctionTypesFromAbstracts(CollectAbstractSignaturesFromObject(interfaceType), &merged); + std::unordered_set abstractInheritanceTarget; + + for (auto *interface : interfaceType->Interfaces()) { + auto found = cachedComputedAbstracts_.find(interface); + ASSERT(found != cachedComputedAbstracts_.end()); + + if (!abstractInheritanceTarget.insert(found->first).second) { + continue; + } + + MergeComputedAbstracts(merged, found->second.first); + + for (auto *base : found->second.second) { + abstractInheritanceTarget.insert(base); + } + } + + cachedComputedAbstracts_.insert({interfaceType, {merged, abstractInheritanceTarget}}); +} + +ArenaVector &ETSChecker::GetAbstractsForClass(ETSObjectType *classType) +{ + ArenaVector merged(Allocator()->Adapter()); + CreateFunctionTypesFromAbstracts(CollectAbstractSignaturesFromObject(classType), &merged); + + std::unordered_set abstractInheritanceTarget; + if (classType->SuperType() != nullptr) { + auto base = cachedComputedAbstracts_.find(classType->SuperType()); + ASSERT(base != cachedComputedAbstracts_.end()); + MergeComputedAbstracts(merged, base->second.first); + + abstractInheritanceTarget.insert(base->first); + for (auto *it : base->second.second) { + abstractInheritanceTarget.insert(it); + } + } + + for (auto *it : classType->Interfaces()) { + ComputeAbstractsFromInterface(it); + auto found = cachedComputedAbstracts_.find(it); + ASSERT(found != cachedComputedAbstracts_.end()); + + if (!abstractInheritanceTarget.insert(found->first).second) { + continue; + } + + MergeComputedAbstracts(merged, found->second.first); + + for (auto *interface : found->second.second) { + abstractInheritanceTarget.insert(interface); + } + } + + return cachedComputedAbstracts_.insert({classType, {merged, abstractInheritanceTarget}}).first->second.first; +} + +void ETSChecker::ValidateOverriding(ETSObjectType *classType, const lexer::SourcePosition &pos) +{ + if (classType->HasObjectFlag(ETSObjectFlags::CHECKED_COMPATIBLE_ABSTRACTS)) { + return; + } + + bool throwError = true; + if (classType->HasObjectFlag(ETSObjectFlags::ABSTRACT)) { + throwError = false; + } + + if (classType->SuperType() != nullptr) { + ValidateOverriding(classType->SuperType(), classType->SuperType()->GetDeclNode()->Start()); + } + + auto &abstractsToBeImplemented = GetAbstractsForClass(classType); + std::vector implementedSignatures; + + auto *superIter = classType; + do { + for (auto &it : abstractsToBeImplemented) { + for (const auto &prop : superIter->Methods()) { + GetTypeOfVariable(prop); + AddImplementedSignature(&implementedSignatures, prop, it); + } + } + superIter = superIter->SuperType(); + } while (superIter != nullptr); + + SavedTypeRelationFlagsContext savedFlagsCtx(Relation(), TypeRelationFlag::NO_RETURN_TYPE_CHECK); + bool functionOverridden; + bool foundSignature; + for (auto it = abstractsToBeImplemented.begin(); it != abstractsToBeImplemented.end();) { + functionOverridden = false; + for (auto abstractSignature = (*it)->CallSignatures().begin(); + abstractSignature != (*it)->CallSignatures().end();) { + foundSignature = false; + for (auto implemented : implementedSignatures) { + if (Relation()->IsIdenticalTo(*abstractSignature, implemented) && + (*abstractSignature)->Function()->Id()->Name() == implemented->Function()->Id()->Name() && + IsTypeAssignableTo(implemented->ReturnType(), (*abstractSignature)->ReturnType())) { + if (!implemented->Function()->IsOverride() && (implemented->Owner() == classType)) { + ThrowTypeError("Method overriding is only allowed with 'override' modifier", + implemented->Function()->Start()); + } + + if ((*it)->CallSignatures().size() > 1) { + abstractSignature = (*it)->CallSignatures().erase(abstractSignature); + foundSignature = true; + } else { + it = abstractsToBeImplemented.erase(it); + functionOverridden = true; + } + + implemented->AddSignatureFlag(SignatureFlags::OPEN); + break; + } + } + + if (functionOverridden) { + break; + } + + if (!foundSignature) { + abstractSignature++; + } + } + + if (!functionOverridden) { + it++; + } + } + + if (!abstractsToBeImplemented.empty() && throwError) { + auto unimplementedSignature = abstractsToBeImplemented.front()->CallSignatures().front(); + ThrowTypeError({classType->Name(), " is not abstract and does not override abstract method ", + unimplementedSignature->Function()->Id()->Name(), unimplementedSignature, " in ", + GetContainingObjectNameFromSignature(unimplementedSignature)}, + pos); + } + + classType->AddObjectFlag(ETSObjectFlags::CHECKED_COMPATIBLE_ABSTRACTS); +} + +void ETSChecker::AddImplementedSignature(std::vector *implementedSignatures, + binder::LocalVariable *function, ETSFunctionType *it) +{ + if (!function->TsType()->IsETSFunctionType()) { + return; + } + + for (auto signature : function->TsType()->AsETSFunctionType()->CallSignatures()) { + if (signature->Function()->IsAbstract() || signature->Function()->IsStatic()) { + continue; + } + + if (signature->Function()->Id()->Name() == it->Name()) { + implementedSignatures->emplace_back(signature); + } + } +} + +void ETSChecker::CheckClassDefinition(ir::ClassDefinition *classDef) +{ + auto *classType = classDef->TsType()->AsETSObjectType(); + auto *enclosingClass = Context().ContainingClass(); + auto newStatus = checker::CheckerStatus::IN_CLASS; + classType->SetEnclosingType(enclosingClass); + + if (!classDef->IsStatic() && enclosingClass != nullptr && !enclosingClass->HasObjectFlag(ETSObjectFlags::GLOBAL)) { + newStatus |= CheckerStatus::INNER_CLASS; + classType->AddObjectFlag(checker::ETSObjectFlags::INNER); + } + + if (classDef->IsGlobal()) { + classType->AddObjectFlag(checker::ETSObjectFlags::GLOBAL); + } + + checker::ScopeContext scopeCtx(this, classDef->Scope()); + auto savedContext = SavedCheckerContext(this, newStatus, classType); + + if (classDef->IsAbstract()) { + AddStatus(checker::CheckerStatus::IN_ABSTRACT); + classType->AddObjectFlag(checker::ETSObjectFlags::ABSTRACT); + } + + if (classDef->IsStatic() && !Context().ContainingClass()->HasObjectFlag(ETSObjectFlags::GLOBAL)) { + AddStatus(checker::CheckerStatus::IN_STATIC_CONTEXT); + } + + for (auto *it : classDef->Body()) { + if (it->IsClassProperty()) { + it->Check(this); + } + } + + for (auto *it : classDef->Body()) { + if (!it->IsClassProperty()) { + it->Check(this); + } + } + + if (classDef->IsGlobal()) { + return; + } + + for (auto *it : classType->ConstructSignatures()) { + CheckCyclicContructorCall(it); + CheckImplicitSuper(classType, it); + } + + ValidateOverriding(classType, classDef->Start()); + CheckValidInheritance(classType, classDef); + CheckConstFields(classType); +} + +void ETSChecker::CheckImplicitSuper(ETSObjectType *classType, Signature *ctorSig) +{ + if (classType == GlobalETSObjectType()) { + return; + } + + auto &stmts = ctorSig->Function()->Body()->AsBlockStatement()->Statements(); + const auto superExpr = std::find_if(stmts.begin(), stmts.end(), [](const ir::Statement *stmt) { + return stmt->IsExpressionStatement() && stmt->AsExpressionStatement()->GetExpression()->IsCallExpression() && + stmt->AsExpressionStatement()->GetExpression()->AsCallExpression()->Callee()->IsSuperExpression(); + }); + + // There is no super expression + if (superExpr == stmts.end()) { + const auto superTypeCtorSigs = classType->SuperType()->ConstructSignatures(); + const auto superTypeCtorSig = std::find_if(superTypeCtorSigs.begin(), superTypeCtorSigs.end(), + [](const Signature *sig) { return sig->Params().empty(); }); + + // Super type has no parameterless ctor + if (superTypeCtorSig == superTypeCtorSigs.end()) { + ThrowTypeError("Must call super constructor", ctorSig->Function()->Start()); + } + + auto *newSuperExpr = Allocator()->New(); + newSuperExpr->SetTsType(classType->SuperType()); + auto *newCallExpr = Allocator()->New( + newSuperExpr->AsExpression(), ArenaVector {Allocator()->Adapter()}, nullptr, false); + newCallExpr->SetSignature(*superTypeCtorSig); + newCallExpr->SetTsType(GlobalVoidType()); + auto *newExprStmt = Allocator()->New(newCallExpr->AsExpression()); + stmts.insert(stmts.begin(), newExprStmt); + } +} + +void ETSChecker::CheckConstFields(const ETSObjectType *classType) +{ + for (const auto &prop : classType->Fields()) { + if (!prop->Declaration()->IsConstDecl() || !prop->HasFlag(binder::VariableFlags::EXPLICIT_INIT_REQUIRED)) { + continue; + } + CheckConstFieldInitialized(classType, prop); + } +} + +void ETSChecker::CheckConstFieldInitialized(const ETSObjectType *classType, binder::LocalVariable *classVar) +{ + const bool classVarStatic = classVar->Declaration()->Node()->AsClassProperty()->IsStatic(); + for (const auto &prop : classType->Methods()) { + const auto &callSigs = prop->TsType()->AsETSFunctionType()->CallSignatures(); + for (const auto *signature : callSigs) { + if ((signature->Function()->IsConstructor() && !classVarStatic) || + (signature->Function()->IsStaticBlock() && classVarStatic)) { + CheckConstFieldInitialized(signature, classVar); + } + } + } +} + +void ETSChecker::FindAssignment(const ir::AstNode *node, const binder::LocalVariable *classVar, bool &initialized) +{ + if (node->IsAssignmentExpression() && node->AsAssignmentExpression()->Target() == classVar) { + if (initialized) { + ThrowTypeError({"Variable '", classVar->Declaration()->Name(), "' might already have been initialized"}, + node->Start()); + } + + initialized = true; + return; + } + + FindAssignments(node, classVar, initialized); +} + +void ETSChecker::FindAssignments(const ir::AstNode *node, const binder::LocalVariable *classVar, bool &initialized) +{ + node->Iterate( + [this, classVar, &initialized](ir::AstNode *childNode) { FindAssignment(childNode, classVar, initialized); }); +} + +void ETSChecker::CheckConstFieldInitialized(const Signature *signature, binder::LocalVariable *classVar) +{ + bool initialized = false; + const auto &stmts = signature->Function()->Body()->AsBlockStatement()->Statements(); + const auto it = stmts.begin(); + + if (it != stmts.end()) { + if (const auto *first = *it; + first->IsExpressionStatement() && first->AsExpressionStatement()->GetExpression()->IsCallExpression() && + first->AsExpressionStatement()->GetExpression()->AsCallExpression()->Callee()->IsThisExpression()) { + initialized = true; + } + } + + // TODO(szd) control flow + FindAssignments(signature->Function()->Body(), classVar, initialized); + if (!initialized) { + ThrowTypeError({"Variable '", classVar->Declaration()->Name(), "' might not have been initialized"}, + signature->Function()->End()); + } + + classVar->RemoveFlag(binder::VariableFlags::EXPLICIT_INIT_REQUIRED); +} + +void ETSChecker::CheckInnerClassMembers(const ETSObjectType *classType) +{ + for (const auto &[_, it] : classType->StaticMethods()) { + (void)_; + ThrowTypeError("Inner class cannot have static methods", it->Declaration()->Node()->Start()); + } + + for (const auto &[_, it] : classType->StaticFields()) { + (void)_; + if (!it->Declaration()->IsConstDecl()) { + ThrowTypeError("Inner class cannot have non-const static properties", it->Declaration()->Node()->Start()); + } + } +} + +Type *ETSChecker::ValidateArrayIndex(ir::Expression *expr) +{ + Type *indexType = ApplyUnaryOperatorPromotion(expr->Check(this)); + + if (indexType == nullptr || !indexType->HasTypeFlag(TypeFlag::ETS_ARRAY_INDEX)) { + ThrowTypeError("Only intergal types can be used as index.", expr->Start()); + } + + return indexType; +} + +Type *ETSChecker::CheckArrayElementAccess(ir::MemberExpression *expr) +{ + Type *arrayType = expr->Object()->Check(this); + + if (!arrayType->IsETSArrayType()) { + ThrowTypeError("Indexed access expression can only be used in array type.", expr->Object()->Start()); + } + + ValidateArrayIndex(expr->Property()); + expr->SetUnboxedPropertyType(ETSBuiltinTypeAsPrimitiveType(expr->Property()->TsType())); + + // TODO(user): apply capture conversion on this type + return arrayType->AsETSArrayType()->ElementType(); +} + +ETSObjectType *ETSChecker::CheckThisOrSuperAccess(ir::Expression *node, ETSObjectType *classType, std::string_view msg) +{ + if (node->Parent()->IsCallExpression() && (node->Parent()->AsCallExpression()->Callee() == node)) { + if (Context().ContainingSignature() == nullptr) { + ThrowTypeError({"Call to '", msg, "' must be first statement in constructor"}, node->Start()); + } + + auto *sig = Context().ContainingSignature(); + ASSERT(sig->Function()->Body() && sig->Function()->Body()->IsBlockStatement()); + + if (!sig->HasSignatureFlag(checker::SignatureFlags::CONSTRUCT)) { + ThrowTypeError({"Call to '", msg, "' must be first statement in constructor"}, node->Start()); + } + + if (sig->Function()->Body()->AsBlockStatement()->Statements().front() != node->Parent()->Parent()) { + ThrowTypeError({"Call to '", msg, "' must be first statement in constructor"}, node->Start()); + } + } + + if (HasStatus(checker::CheckerStatus::IN_STATIC_CONTEXT)) { + ThrowTypeError({"'", msg, "' cannot be referenced from a static context"}, node->Start()); + } + + if (classType->GetDeclNode()->AsClassDefinition()->IsGlobal()) { + ThrowTypeError({"Cannot reference '", msg, "' in this context."}, node->Start()); + } + + return classType; +} + +void ETSChecker::CheckCyclicContructorCall(Signature *signature) +{ + ASSERT(signature->Function()); + + if (signature->Function()->Body() == nullptr) { + return; + } + + auto *funcBody = signature->Function()->Body()->AsBlockStatement(); + + TypeStackElement tse(this, signature, "Recursive constructor invocation", signature->Function()->Start()); + + if (!funcBody->Statements().empty() && funcBody->Statements()[0]->IsExpressionStatement() && + funcBody->Statements()[0]->AsExpressionStatement()->GetExpression()->IsCallExpression() && + funcBody->Statements()[0] + ->AsExpressionStatement() + ->GetExpression() + ->AsCallExpression() + ->Callee() + ->IsThisExpression()) { + auto *constructorCall = funcBody->Statements()[0]->AsExpressionStatement()->GetExpression()->AsCallExpression(); + ASSERT(constructorCall->Signature()); + CheckCyclicContructorCall(constructorCall->Signature()); + } +} + +ETSObjectType *ETSChecker::CheckExceptionType(checker::Type *type, lexer::SourcePosition pos) +{ + auto *exceptionType = + CheckException(type, pos, GlobalBuiltinExceptionType(), compiler::Signatures::BUILTIN_EXCEPTION_CLASS); + + if (Relation()->IsAssignableTo(exceptionType, GlobalBuiltinExceptionType())) { + ThrowTypeError({"Recover clause must be used for any instance of '", exceptionType->Name(), "'"}, pos); + } + + return exceptionType; +} + +ETSObjectType *ETSChecker::CheckRuntimeExceptionType(checker::Type *type, lexer::SourcePosition pos) +{ + return CheckException(type, pos, GlobalBuiltinPanicType(), compiler::Signatures::BUILTIN_PANIC_CLASS); +} + +ETSObjectType *ETSChecker::CheckException(checker::Type *type, lexer::SourcePosition pos, ETSObjectType *expected, + std::string_view msg) +{ + if (!type->IsETSObjectType() || !Relation()->IsAssignableTo(type, expected)) { + ThrowTypeError({"Argument must be an instance of '", msg, "'"}, pos); + } + + return type->AsETSObjectType(); +} + +Type *ETSChecker::TryToInstantiate(Type *type, ArenaAllocator *allocator, TypeRelation *relation, + GlobalTypesHolder *globalTypes) +{ + // TODO(user): Handle generic functions + if (type->IsETSTypeReference() || type->IsETSFunctionType() || + (type->IsETSObjectType() && type->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::UNCOMPLETE_INSTANTIATION))) { + return type->Instantiate(allocator, relation, globalTypes); + } + + return type; +} + +binder::LocalVariable *ETSChecker::ResolveMemberReference(const ir::MemberExpression *memberExpr, ETSObjectType *target) +{ + auto searchFlag = PropertySearchFlags::SEARCH_FIELD | PropertySearchFlags::SEARCH_METHOD; + + switch (memberExpr->Parent()->Type()) { + case ir::AstNodeType::CALL_EXPRESSION: { + if (memberExpr->Parent()->AsCallExpression()->Callee() == memberExpr) { + searchFlag = PropertySearchFlags::SEARCH_METHOD; + } + + break; + } + case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: { + if (memberExpr->Parent()->AsETSNewClassInstanceExpression()->GetTypeRef() == memberExpr) { + searchFlag = PropertySearchFlags::SEARCH_DECL; + } + + break; + } + + case ir::AstNodeType::MEMBER_EXPRESSION: { + searchFlag = PropertySearchFlags::SEARCH_FIELD | PropertySearchFlags::SEARCH_DECL; + break; + } + case ir::AstNodeType::UPDATE_EXPRESSION: + case ir::AstNodeType::UNARY_EXPRESSION: + case ir::AstNodeType::BINARY_EXPRESSION: { + searchFlag = PropertySearchFlags::SEARCH_FIELD; + break; + } + case ir::AstNodeType::ASSIGNMENT_EXPRESSION: { + auto *assignmentExpr = memberExpr->Parent()->AsAssignmentExpression(); + + if (assignmentExpr->Left() == memberExpr) { + searchFlag = PropertySearchFlags::SEARCH_FIELD; + break; + } + + if (assignmentExpr->Right() == memberExpr) { + auto *targetType = assignmentExpr->Left()->TsType(); + ASSERT(targetType != nullptr); + + if (targetType->IsETSObjectType() && + targetType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL)) { + searchFlag = PropertySearchFlags::SEARCH_METHOD; + break; + } + + searchFlag = PropertySearchFlags::SEARCH_FIELD; + break; + } + } + default: { + break; + } + } + + const binder::Variable *targetRef {}; + if (memberExpr->Object()->IsIdentifier()) { + targetRef = memberExpr->Object()->AsIdentifier()->Variable(); + } else if (memberExpr->Object()->IsMemberExpression()) { + targetRef = memberExpr->Object()->AsMemberExpression()->PropVar(); + } + + if (targetRef != nullptr && targetRef->HasFlag(binder::VariableFlags::CLASS_OR_INTERFACE)) { + searchFlag &= ~(PropertySearchFlags::SEARCH_INSTANCE_FIELD | PropertySearchFlags::SEARCH_INSTANCE_METHOD | + PropertySearchFlags::SEARCH_INSTANCE_DECL); + } + + searchFlag |= PropertySearchFlags::SEARCH_IN_BASE | checker::PropertySearchFlags::SEARCH_IN_INTERFACES; + auto *prop = target->GetProperty(memberExpr->Property()->AsIdentifier()->Name(), searchFlag); + + if (prop == nullptr) { + const char *propKind = ((searchFlag & checker::PropertySearchFlags::SEARCH_METHOD) != 0) ? "Method " : "Field "; + ThrowTypeError({propKind, memberExpr->Property()->AsIdentifier()->Name(), " does not exist on this type."}, + memberExpr->Property()->Start()); + } + + return prop; +} + +void ETSChecker::CheckValidInheritance(ETSObjectType *classType, ir::ClassDefinition *classDef) +{ + if (classType->SuperType() == nullptr) { + return; + } + + const auto &allProps = classType->GetAllProperties(); + + for (auto *it : allProps) { + auto *found = classType->SuperType()->GetProperty( + it->Name(), PropertySearchFlags::SEARCH_ALL | PropertySearchFlags::SEARCH_IN_BASE | + PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION); + + if (found == nullptr) { + continue; + } + + if (!IsSameDeclarationType(it, found)) { + const char *targetType {}; + + if (it->HasFlag(binder::VariableFlags::PROPERTY)) { + targetType = "field"; + } else if (it->HasFlag(binder::VariableFlags::METHOD)) { + targetType = "method"; + } else if (it->HasFlag(binder::VariableFlags::CLASS)) { + targetType = "class"; + } else if (it->HasFlag(binder::VariableFlags::INTERFACE)) { + targetType = "interface"; + } else { + targetType = "enum"; + } + + ThrowTypeError({"Cannot inherit from class ", classType->SuperType()->Name(), ", because ", targetType, " ", + it->Name(), " is inherited with a different declaration type"}, + classDef->Super()->Start()); + } + } +} +} // namespace panda::es2panda::checker diff --git a/checker/ets/primitiveWrappers.cpp b/checker/ets/primitiveWrappers.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fba7f28ccf9125aabd3b3f5e612a050e98bb5970 --- /dev/null +++ b/checker/ets/primitiveWrappers.cpp @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2021 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 "primitiveWrappers.h" + +namespace panda::es2panda::checker { +} // namespace panda::es2panda::checker diff --git a/checker/ets/primitiveWrappers.h b/checker/ets/primitiveWrappers.h new file mode 100644 index 0000000000000000000000000000000000000000..d4da71c31a755a875023449aaa059a6e012b01e1 --- /dev/null +++ b/checker/ets/primitiveWrappers.h @@ -0,0 +1,58 @@ + +/** + * Copyright (c) 2021 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_CHECKER_ETS_PRIMITIVEWRAPPERS_H +#define ES2PANDA_COMPILER_CHECKER_ETS_PRIMITIVEWRAPPERS_H + +#include "plugins/ecmascript/es2panda/checker/types/ets/etsObjectType.h" + +namespace panda::es2panda::checker { +class ETSObjectType; + +using WrapperDesc = ArenaUnorderedMap>; + +class PrimitiveWrappers { +public: + explicit PrimitiveWrappers(ArenaAllocator *allocator) : wrappers_(allocator->Adapter()) + { + wrappers_.insert({"Boolean", {nullptr, ETSObjectFlags::BUILTIN_BOOLEAN}}); + wrappers_.insert({"Byte", {nullptr, ETSObjectFlags::BUILTIN_BYTE}}); + wrappers_.insert({"Char", {nullptr, ETSObjectFlags::BUILTIN_CHAR}}); + wrappers_.insert({"Short", {nullptr, ETSObjectFlags::BUILTIN_SHORT}}); + wrappers_.insert({"Int", {nullptr, ETSObjectFlags::BUILTIN_INT}}); + wrappers_.insert({"Long", {nullptr, ETSObjectFlags::BUILTIN_LONG}}); + wrappers_.insert({"Float", {nullptr, ETSObjectFlags::BUILTIN_FLOAT}}); + wrappers_.insert({"Double", {nullptr, ETSObjectFlags::BUILTIN_DOUBLE}}); + } + NO_COPY_SEMANTIC(PrimitiveWrappers); + NO_MOVE_SEMANTIC(PrimitiveWrappers); + ~PrimitiveWrappers() = default; + + WrapperDesc &Wrappers() + { + return wrappers_; + } + + const WrapperDesc &Wrappers() const + { + return wrappers_; + } + +private: + WrapperDesc wrappers_; +}; +} // namespace panda::es2panda::checker +#endif diff --git a/checker/ets/typeConverter.cpp b/checker/ets/typeConverter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dc32dee4b15f784ac2b501e326b7574d312a6415 --- /dev/null +++ b/checker/ets/typeConverter.cpp @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2021 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 "typeConverter.h" + +namespace panda::es2panda::checker { +TypeConverter::TypeConverter(ETSChecker *checker, TypeRelation *relation, Type *target, Type *source) + : checker_(checker), relation_(relation), target_(target), source_(source) +{ +} + +Type *TypeConverter::Result() const +{ + return result_; +} + +void TypeConverter::SetResult(Type *result) +{ + result_ = result; +} + +Type *TypeConverter::Source() const +{ + return source_; +} + +Type *TypeConverter::Target() const +{ + return target_; +} + +TypeRelation *TypeConverter::Relation() const +{ + return relation_; +} + +ETSChecker *TypeConverter::Checker() const +{ + return checker_; +} +} // namespace panda::es2panda::checker diff --git a/checker/ets/typeConverter.h b/checker/ets/typeConverter.h new file mode 100644 index 0000000000000000000000000000000000000000..6f90280a9023f986d7b3ef783d873040ae312a6c --- /dev/null +++ b/checker/ets/typeConverter.h @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_ETS_TYPE_CONVERTER_H +#define ES2PANDA_COMPILER_CHECKER_ETS_TYPE_CONVERTER_H + +#include "plugins/ecmascript/es2panda/checker/types/type.h" + +namespace panda::es2panda::checker { +class ETSChecker; + +class TypeConverter { +public: + TypeConverter(ETSChecker *checker, TypeRelation *relation, Type *target, Type *source); + Type *Result() const; + void SetResult(Type *result); + Type *Source() const; + Type *Target() const; + TypeRelation *Relation() const; + ETSChecker *Checker() const; + +private: + ETSChecker *checker_; + TypeRelation *relation_; + Type *target_; + Type *source_; + Type *result_ {}; +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/ets/typeCreation.cpp b/checker/ets/typeCreation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..62ca2e79d2207f434d57024925b508ab730b40fb --- /dev/null +++ b/checker/ets/typeCreation.cpp @@ -0,0 +1,306 @@ +/** + * Copyright (c) 2021 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 "plugins/ecmascript/es2panda/checker/ETSchecker.h" +#include "plugins/ecmascript/es2panda/binder/binder.h" +#include "plugins/ecmascript/es2panda/binder/ETSBinder.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsScript.h" +#include "plugins/ecmascript/es2panda/ir/base/classDefinition.h" +#include "plugins/ecmascript/es2panda/ir/base/scriptFunction.h" +#include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsEnumDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceDeclaration.h" +#include "plugins/ecmascript/es2panda/parser/program/program.h" +#include "plugins/ecmascript/es2panda/util/helpers.h" + +namespace panda::es2panda::checker { +ByteType *ETSChecker::CreateByteType(int8_t value) +{ + return Allocator()->New(value); +} + +ETSBooleanType *ETSChecker::CreateETSBooleanType(bool value) +{ + return Allocator()->New(value); +} + +DoubleType *ETSChecker::CreateDoubleType(double value) +{ + return Allocator()->New(value); +} + +FloatType *ETSChecker::CreateFloatType(float value) +{ + return Allocator()->New(value); +} + +IntType *ETSChecker::CreateIntType(int32_t value) +{ + return Allocator()->New(value); +} + +IntType *ETSChecker::CreateIntTypeFromType(Type *type) +{ + if (!type->HasTypeFlag(TypeFlag::CONSTANT)) { + return GlobalIntType()->AsIntType(); + } + + if (type->IsIntType()) { + return type->AsIntType(); + } + + switch (ETSType(type)) { + case TypeFlag::CHAR: { + return CreateIntType(static_cast(type->AsCharType()->GetValue())); + } + case TypeFlag::BYTE: { + return CreateIntType(static_cast(type->AsByteType()->GetValue())); + } + case TypeFlag::SHORT: { + return CreateIntType(static_cast(type->AsShortType()->GetValue())); + } + default: { + return nullptr; + } + } +} + +LongType *ETSChecker::CreateLongType(int64_t value) +{ + return Allocator()->New(value); +} + +ShortType *ETSChecker::CreateShortType(int16_t value) +{ + return Allocator()->New(value); +} + +CharType *ETSChecker::CreateCharType(char16_t value) +{ + return Allocator()->New(value); +} + +ETSStringType *ETSChecker::CreateETSStringLiteralType(util::StringView value) +{ + return Allocator()->New(Allocator(), GlobalBuiltinETSStringType(), value); +} + +ETSArrayType *ETSChecker::CreateETSArrayType(Type *elementType) +{ + auto res = arrayTypes_.find(elementType); + + if (res != arrayTypes_.end()) { + return res->second; + } + + auto *arrayType = Allocator()->New(elementType); + arrayTypes_.insert({elementType, arrayType}); + + return arrayType; +} + +ETSFunctionType *ETSChecker::CreateETSFunctionType(ArenaVector &signatures) +{ + auto *funcType = Allocator()->New(signatures[0]->Function()->Id()->Name(), Allocator()); + + for (auto *it : signatures) { + funcType->AddCallSignature(it); + } + + return funcType; +} + +ETSFunctionType *ETSChecker::CreateETSFunctionType(Signature *signature) +{ + return Allocator()->New(signature->Function()->Id()->Name(), signature, Allocator()); +} + +Signature *ETSChecker::CreateSignature(SignatureInfo *info, Type *returnType, ir::ScriptFunction *func) +{ + return Allocator()->New(info, returnType, func); +} + +Signature *ETSChecker::CreateSignature(SignatureInfo *info, Type *returnType, util::StringView internalName) +{ + return Allocator()->New(info, returnType, internalName); +} + +SignatureInfo *ETSChecker::CreateSignatureInfo() +{ + return Allocator()->New(Allocator()); +} + +ETSTypeParameter *ETSChecker::CreateTypeParameter(Type *assemblerType) +{ + return Allocator()->New(assemblerType); +} + +ETSTypeReference *ETSChecker::CreateTypeReference(Type **ref, Type **assemblerRef, binder::LocalVariable *refVar) +{ + return Allocator()->New(ref, assemblerRef, refVar); +} + +ETSFunctionType *ETSChecker::CreateETSFunctionType(util::StringView name) +{ + return Allocator()->New(name, Allocator()); +} + +ETSObjectType *ETSChecker::CreateETSObjectTypeCheckBuiltins(util::StringView name, ir::AstNode *declNode, + ETSObjectFlags flags) +{ + if (name == compiler::Signatures::BUILTIN_STRING_CLASS) { + if (GlobalBuiltinETSStringType() != nullptr) { + return GlobalBuiltinETSStringType(); + } + GetGlobalTypesHolder()->GlobalTypes()[static_cast(GlobalTypeId::ETS_STRING_BUILTIN)] = + CreateNewETSObjectType(name, declNode, flags | ETSObjectFlags::BUILTIN_STRING | ETSObjectFlags::STRING); + + GetGlobalTypesHolder()->GlobalTypes()[static_cast(GlobalTypeId::ETS_STRING)] = + Allocator()->New(Allocator(), GlobalBuiltinETSStringType()); + return GlobalBuiltinETSStringType(); + } + + auto *objType = CreateNewETSObjectType(name, declNode, flags); + + if (name == compiler::Signatures::BUILTIN_OBJECT_CLASS) { + if (GlobalETSObjectType() != nullptr) { + return GlobalETSObjectType(); + } + GetGlobalTypesHolder()->GlobalTypes()[static_cast(GlobalTypeId::ETS_OBJECT_BUILTIN)] = objType; + } else if (name == compiler::Signatures::BUILTIN_EXCEPTION_CLASS) { + if (GlobalBuiltinExceptionType() != nullptr) { + return GlobalBuiltinExceptionType(); + } + GetGlobalTypesHolder()->GlobalTypes()[static_cast(GlobalTypeId::ETS_EXCEPTION_BUILTIN)] = objType; + } else if (name == compiler::Signatures::BUILTIN_PANIC_CLASS) { + if (GlobalBuiltinPanicType() != nullptr) { + return GlobalBuiltinPanicType(); + } + GetGlobalTypesHolder()->GlobalTypes()[static_cast(GlobalTypeId::ETS_PANIC_BUILTIN)] = objType; + } else if (name == compiler::Signatures::BUILTIN_TYPE_CLASS) { + if (GlobalBuiltinTypeType() != nullptr) { + return GlobalBuiltinTypeType(); + } + GetGlobalTypesHolder()->GlobalTypes()[static_cast(GlobalTypeId::ETS_TYPE_BUILTIN)] = objType; + } else if (name == compiler::Signatures::BUILTIN_ENUM_CLASS) { + // TODO(user): + if (globalBuiltinEnumType_ != nullptr) { + return globalBuiltinEnumType_; + } + globalBuiltinEnumType_ = objType; + } + + return objType; +} + +ETSObjectType *ETSChecker::CreateETSObjectType(util::StringView name, ir::AstNode *declNode, ETSObjectFlags flags) +{ + auto res = primitiveWrappers_.Wrappers().find(name); + if (res == primitiveWrappers_.Wrappers().end()) { + return CreateETSObjectTypeCheckBuiltins(name, declNode, flags); + } + + if (res->second.first != nullptr) { + return res->second.first; + } + + auto *objType = CreateNewETSObjectType(name, declNode, flags | res->second.second); + primitiveWrappers_.Wrappers().at(name).first = objType; + return objType; +} + +ETSObjectType *ETSChecker::CreateETSEnumType(ir::TSEnumDeclaration *enumDecl) +{ + binder::Variable *enumVar = enumDecl->Key()->Variable(); + ASSERT(enumVar); + + auto *enumType = + CreateETSObjectType(enumDecl->Key()->Name(), enumDecl, (ETSObjectFlags::ENUM | ETSObjectFlags::RESOLVED_SUPER)); + enumType->SetSuperType(globalBuiltinEnumType_); + enumType->SetVariable(enumVar); + enumVar->SetTsType(enumType); + + BuildEnumProperties(enumType); + + return enumType; +} + +ETSObjectType *ETSChecker::CreateNewETSObjectType(util::StringView name, ir::AstNode *declNode, ETSObjectFlags flags) +{ + util::StringView assemblerName = name; + util::StringView prefix {}; + + auto *containingObjType = util::Helpers::GetContainingObjectType(declNode->Parent()); + + if (containingObjType != nullptr) { + prefix = containingObjType->AssemblerName(); + } else { + auto *program = static_cast(declNode->GetTopStatement())->Program(); + prefix = program->GetPackageName(); + } + + if (!prefix.Empty()) { + util::UString fullPath(prefix, Allocator()); + fullPath.Append('.'); + fullPath.Append(name); + assemblerName = fullPath.View(); + } + + return Allocator()->New(Allocator(), name, assemblerName, declNode, flags); +} + +std::tuple ETSChecker::CreateBuiltinArraySignatureInfo(ETSArrayType *arrayType, + size_t dim) +{ + std::stringstream ss; + arrayType->ToAssemblerTypeWithRank(ss); + ss << compiler::Signatures::METHOD_SEPARATOR << compiler::Signatures::CTOR << compiler::Signatures::MANGLE_BEGIN; + arrayType->ToAssemblerTypeWithRank(ss); + + auto *info = CreateSignatureInfo(); + info->minArgCount = dim; + + for (size_t i = 0; i < dim; i++) { + util::UString param(std::to_string(i), Allocator()); + auto *paramVar = binder::Scope::CreateVar(Allocator(), param.View(), binder::VariableFlags::NONE, nullptr); + paramVar->SetTsType(GlobalIntType()); + + info->params.push_back(paramVar); + + ss << compiler::Signatures::MANGLE_SEPARATOR << compiler::Signatures::PRIMITIVE_INT; + } + + ss << compiler::Signatures::MANGLE_SEPARATOR << compiler::Signatures::PRIMITIVE_VOID + << compiler::Signatures::MANGLE_SEPARATOR; + auto internalName = util::UString(ss.str(), Allocator()).View(); + + return {internalName, info}; +} + +Signature *ETSChecker::CreateBuiltinArraySignature(ETSArrayType *arrayType, size_t dim) +{ + auto res = globalArraySignatures_.find(arrayType); + + if (res != globalArraySignatures_.end()) { + return res->second; + } + + auto [internalName, info] = CreateBuiltinArraySignatureInfo(arrayType, dim); + auto *signature = CreateSignature(info, GlobalVoidType(), internalName); + globalArraySignatures_.insert({arrayType, signature}); + + return signature; +} +} // namespace panda::es2panda::checker diff --git a/checker/ets/typeRelationContext.cpp b/checker/ets/typeRelationContext.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fa9adc62f1b837578316b99cb27b70af81d58bd9 --- /dev/null +++ b/checker/ets/typeRelationContext.cpp @@ -0,0 +1,130 @@ +/** + * Copyright (c) 2021 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 "typeRelationContext.h" +#include "plugins/ecmascript/es2panda/binder/variable.h" +#include "plugins/ecmascript/es2panda/binder/scope.h" +#include "plugins/ecmascript/es2panda/binder/declaration.h" +#include "plugins/ecmascript/es2panda/ir/expressions/arrayExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsArrayType.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsTypeParameter.h" + +namespace panda::es2panda::checker { +void AssignmentContext::ValidateArrayTypeInitializerByElement(TypeRelation *relation, ir::ArrayExpression *node, + ETSArrayType *target) +{ + for (uint32_t index = 0; index < node->Elements().size(); index++) { + ir::Expression *currentArrayElem = node->Elements()[index]; + AssignmentContext(relation, currentArrayElem, currentArrayElem->Check(relation->GetChecker()->AsETSChecker()), + target->ElementType(), currentArrayElem->Start(), + {"Array element at index ", index, " is not compatible with the target array element type."}); + } +} + +bool InstantiationContext::ValidateTypeArguments(ETSObjectType *type, ir::TSTypeParameterDeclaration *typeParamDecl, + ir::TSTypeParameterInstantiation *typeArgs, + const lexer::SourcePosition &pos) +{ + if (typeParamDecl != nullptr && typeArgs == nullptr) { + checker_->ThrowTypeError({"Type '", type, "' is generic but type argument were not provided."}, pos); + } + + if (typeParamDecl == nullptr && typeArgs != nullptr) { + checker_->ThrowTypeError({"Type '", type, "' is not generic."}, pos); + } + + if (typeArgs == nullptr) { + result_ = type; + return true; + } + + ASSERT(typeParamDecl != nullptr && typeArgs != nullptr); + if (typeParamDecl->Params().size() != typeArgs->Params().size()) { + checker_->ThrowTypeError({"Type '", type, "' has ", typeParamDecl->Params().size(), + " number of type parameters, but ", typeArgs->Params().size(), + " type arguments were provided."}, + pos); + } + + return false; +} + +util::StringView InstantiationContext::GetHashFromTypeArguments(ArenaVector &typeArgTypes) +{ + std::stringstream ss; + + for (auto *it : typeArgTypes) { + // TODO(user): this is not the best solution, since A and A will produce the same string in their ToString + // method, but they are two different types + it->ToString(ss); + ss << compiler::Signatures::MANGLE_SEPARATOR; + } + + return util::UString(ss.str(), checker_->Allocator()).View(); +} + +void InstantiationContext::InstantiateType(ETSObjectType *type, ir::TSTypeParameterDeclaration *typeParamDecl, + ir::TSTypeParameterInstantiation *typeArgs) +{ + ArenaVector typeArgTypes(checker_->Allocator()->Adapter()); + typeArgTypes.reserve(typeArgs->Params().size()); + + auto flags = ETSObjectFlags::NO_OPTS; + + for (auto *it : typeArgs->Params()) { + auto *paramType = it->GetType(checker_); + typeArgTypes.push_back(it->GetType(checker_)); + + if (paramType->IsETSTypeReference()) { + flags |= ETSObjectFlags::UNCOMPLETE_INSTANTIATION; + } + } + + InstantiateType(type, typeParamDecl, typeArgTypes); + result_->AddObjectFlag(flags); +} + +void InstantiationContext::InstantiateType(ETSObjectType *type, ir::TSTypeParameterDeclaration *typeParamDecl, + ArenaVector &typeArgTypes) +{ + util::StringView hash = GetHashFromTypeArguments(typeArgTypes); + auto *found = type->GetInstantiatedType(hash); + + if (found != nullptr) { + result_ = found; + return; + } + + checker::ScopeContext scopeCtx(checker_, typeParamDecl->Scope()); + + for (size_t idx = 0; idx < typeParamDecl->Params().size(); idx++) { + auto *paramVar = typeParamDecl->Params()[idx]->Name()->Variable()->AsLocalVariable(); + auto *typeParam = paramVar->TsType()->AsETSTypeParameter(); + + typeParam->SetType(typeArgTypes[idx]); + typeArgVars_.push_back(paramVar); + } + + result_ = type->Instantiate(checker_->Allocator(), checker_->Relation(), checker_->GetGlobalTypesHolder()) + ->AsETSObjectType(); + result_->SetTypeArguments(std::move(typeArgTypes)); + type->GetInstantiationMap().insert({hash, result_}); + + for (auto *it : typeArgVars_) { + it->TsType()->AsETSTypeParameter()->SetType(nullptr); + } +} +} // namespace panda::es2panda::checker diff --git a/checker/ets/typeRelationContext.h b/checker/ets/typeRelationContext.h new file mode 100644 index 0000000000000000000000000000000000000000..61f2579c23fc342b8563f9c75471672c19833b68 --- /dev/null +++ b/checker/ets/typeRelationContext.h @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_ETS_TYPE_RELATION_CONTEXT_H +#define ES2PANDA_COMPILER_CHECKER_ETS_TYPE_RELATION_CONTEXT_H + +#include "plugins/ecmascript/es2panda/ir/expression.h" +#include "plugins/ecmascript/es2panda/ir/base/classDefinition.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsTypeParameterInstantiation.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsTypeParameterDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceDeclaration.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" + +namespace panda::es2panda::checker { +class ETSChecker; + +class AssignmentContext { +public: + AssignmentContext(TypeRelation *relation, ir::Expression *node, Type *source, Type *target, + const lexer::SourcePosition &pos, std::initializer_list list, + TypeRelationFlag flags = TypeRelationFlag::NONE) + { + flags_ |= ((flags & TypeRelationFlag::NO_BOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::BOXING; + flags_ |= ((flags & TypeRelationFlag::NO_UNBOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::UNBOXING; + flags_ |= ((flags & TypeRelationFlag::NO_WIDENING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::WIDENING; + + if (node->IsArrayExpression() && target->IsETSArrayType()) { + ValidateArrayTypeInitializerByElement(relation, node->AsArrayExpression(), target->AsETSArrayType()); + return; + } + + flags_ |= flags; + relation->SetNode(node); + + if (source->HasTypeFlag(TypeFlag::CONSTANT)) { + flags_ |= TypeRelationFlag::NARROWING; + } + + relation->SetFlags(flags_); + + if (!relation->IsAssignableTo(source, target)) { + if (((flags_ & TypeRelationFlag::UNBOXING) != 0) && source->IsETSObjectType() && !relation->IsTrue()) { + auto unboxedSourceType = relation->GetChecker()->AsETSChecker()->ETSBuiltinTypeAsPrimitiveType(source); + auto unboxedTargetType = relation->GetChecker()->AsETSChecker()->ETSBuiltinTypeAsPrimitiveType(target); + if (unboxedSourceType != nullptr && unboxedTargetType != nullptr) { + relation->IsAssignableTo(unboxedSourceType, unboxedTargetType); + } + } + } + + if (!relation->IsTrue() && (flags_ & TypeRelationFlag::NO_THROW) == 0) { + relation->RaiseError(list, pos); + } + + relation->SetNode(nullptr); + relation->SetFlags(TypeRelationFlag::NONE); + assignable_ = true; + } + + bool IsAssigable() const + { + return assignable_; + } + + void ValidateArrayTypeInitializerByElement(TypeRelation *relation, ir::ArrayExpression *node, ETSArrayType *target); + +private: + TypeRelationFlag flags_ = TypeRelationFlag::IN_ASSIGNMENT_CONTEXT; + bool assignable_ {false}; +}; + +class InvocationContext { +public: + InvocationContext(TypeRelation *relation, ir::Expression *node, Type *source, Type *target, + const lexer::SourcePosition &pos, std::initializer_list list, + TypeRelationFlag initialFlags = TypeRelationFlag::NONE) + { + flags_ |= + ((initialFlags & TypeRelationFlag::NO_BOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::BOXING; + flags_ |= + ((initialFlags & TypeRelationFlag::NO_UNBOXING) != 0) ? TypeRelationFlag::NONE : TypeRelationFlag::UNBOXING; + relation->SetNode(node); + + relation->SetFlags(flags_ | initialFlags); + + bool assignable = relation->IsAssignableTo(source, target); + if (!assignable && ((flags_ & TypeRelationFlag::UNBOXING) != 0)) { + if (source->IsETSObjectType() && !relation->IsTrue()) { + auto unboxedSourceType = relation->GetChecker()->AsETSChecker()->ETSBuiltinTypeAsPrimitiveType(source); + relation->SetFlags(flags_ | TypeRelationFlag::WIDENING); + if (unboxedSourceType != nullptr) { + assignable = relation->IsAssignableTo(unboxedSourceType, target); + } + } + } + + relation->SetNode(nullptr); + relation->SetFlags(TypeRelationFlag::NONE); + + if (!assignable) { + if ((initialFlags & TypeRelationFlag::NO_THROW) == 0) { + relation->RaiseError(list, pos); + } + return; + } + + invocable_ = true; + } + + bool IsInvocable() const + { + return invocable_; + } + +private: + TypeRelationFlag flags_ = TypeRelationFlag::NONE; + bool invocable_ {false}; +}; + +class InstantiationContext { +public: + InstantiationContext(ETSChecker *checker, ETSObjectType *type, ir::TSTypeParameterInstantiation *typeArgs, + const lexer::SourcePosition &pos) + : checker_(checker), typeArgVars_(checker->Allocator()->Adapter()) + { + ir::TSTypeParameterDeclaration *typeParamDecl = nullptr; + + if (type->HasObjectFlag(ETSObjectFlags::CLASS)) { + typeParamDecl = type->GetDeclNode()->AsClassDefinition()->TypeParams(); + } else if (type->HasObjectFlag(ETSObjectFlags::INTERFACE)) { + typeParamDecl = type->GetDeclNode()->AsTSInterfaceDeclaration()->TypeParams(); + } + if (ValidateTypeArguments(type, typeParamDecl, typeArgs, pos)) { + return; + } + + InstantiateType(type, typeParamDecl, typeArgs); + } + + InstantiationContext(ETSChecker *checker, ETSObjectType *type, ArenaVector &typeArgs) + : checker_(checker), typeArgVars_(checker->Allocator()->Adapter()) + { + ir::TSTypeParameterDeclaration *typeParamDecl = nullptr; + + if (type->HasObjectFlag(ETSObjectFlags::CLASS)) { + typeParamDecl = type->GetDeclNode()->AsClassDefinition()->TypeParams(); + } else if (type->HasObjectFlag(ETSObjectFlags::ENUM)) { + return; + } else { + ASSERT(type->HasObjectFlag(ETSObjectFlags::INTERFACE)); + typeParamDecl = type->GetDeclNode()->AsTSInterfaceDeclaration()->TypeParams(); + } + + InstantiateType(type, typeParamDecl, typeArgs); + } + + ETSObjectType *Result() + { + return result_; + } + +private: + bool ValidateTypeArguments(ETSObjectType *type, ir::TSTypeParameterDeclaration *typeParamDecl, + ir::TSTypeParameterInstantiation *typeArgs, const lexer::SourcePosition &pos); + void InstantiateType(ETSObjectType *type, ir::TSTypeParameterDeclaration *typeParamDecl, + ir::TSTypeParameterInstantiation *typeArgs); + + void InstantiateType(ETSObjectType *type, ir::TSTypeParameterDeclaration *typeParamDecl, + ArenaVector &typeArgTypes); + util::StringView GetHashFromTypeArguments(ArenaVector &typeArgTypes); + + ETSChecker *checker_; + ArenaVector typeArgVars_; + ETSObjectType *result_ {}; +}; + +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/ets/unboxingConverter.cpp b/checker/ets/unboxingConverter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..68d37f28325033e9f28b7903d4be09b16a176622 --- /dev/null +++ b/checker/ets/unboxingConverter.cpp @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2021-2022 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 "unboxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/types.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" +#include "plugins/ecmascript/es2panda/util/helpers.h" + +namespace panda::es2panda::checker { + +checker::Type *UnboxingConverter::GlobalTypeFromSource(ETSObjectFlags type) +{ + switch (type) { + case ETSObjectFlags::BUILTIN_BOOLEAN: { + return Checker()->GlobalETSBooleanType(); + } + case ETSObjectFlags::BUILTIN_BYTE: { + return Checker()->GlobalByteType(); + } + case ETSObjectFlags::BUILTIN_SHORT: { + return Checker()->GlobalShortType(); + } + case ETSObjectFlags::BUILTIN_CHAR: { + return Checker()->GlobalCharType(); + } + case ETSObjectFlags::BUILTIN_INT: { + return Checker()->GlobalIntType(); + } + case ETSObjectFlags::BUILTIN_LONG: { + return Checker()->GlobalLongType(); + } + case ETSObjectFlags::BUILTIN_FLOAT: { + return Checker()->GlobalFloatType(); + } + case ETSObjectFlags::BUILTIN_DOUBLE: { + return Checker()->GlobalDoubleType(); + } + default: + return Source(); + } +} + +} // namespace panda::es2panda::checker diff --git a/checker/ets/unboxingConverter.h b/checker/ets/unboxingConverter.h new file mode 100644 index 0000000000000000000000000000000000000000..e82d4efe64553550e2d220012a3af2a74ba7e0e3 --- /dev/null +++ b/checker/ets/unboxingConverter.h @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_ETS_UNBOXING_CONVERTER_H +#define ES2PANDA_COMPILER_CHECKER_ETS_UNBOXING_CONVERTER_H + +#include "plugins/ecmascript/es2panda/checker/ets/typeConverter.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/etsObjectType.h" + +namespace panda::es2panda::checker { + +class UnboxingConverter : public TypeConverter { +public: + UnboxingConverter(ETSChecker *checker, TypeRelation *relation, Type *source, Type *target) + : TypeConverter(checker, relation, target, source) + { + SetResult(Source()); + + if (!Source()->IsETSObjectType() || relation->IsTrue()) { + return; + } + + SetResult(GlobalTypeFromSource(Source()->AsETSObjectType()->BuiltInKind())); + + Relation()->Result(Result()->TypeFlags() == target->TypeFlags()); + } + + checker::Type *GlobalTypeFromSource(ETSObjectFlags type); +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/ets/wideningConverter.cpp b/checker/ets/wideningConverter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0c7e36c5d74c40f2a85a9cf1679ba847c909811a --- /dev/null +++ b/checker/ets/wideningConverter.cpp @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2021-2022 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 "wideningConverter.h" + +namespace panda::es2panda::checker { +} // namespace panda::es2panda::checker diff --git a/checker/ets/wideningConverter.h b/checker/ets/wideningConverter.h new file mode 100644 index 0000000000000000000000000000000000000000..2381a438b9ca85e6f613e3ba34e3f313152726c6 --- /dev/null +++ b/checker/ets/wideningConverter.h @@ -0,0 +1,201 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_ETS_WIDENING_CONVERTER_H +#define ES2PANDA_COMPILER_CHECKER_ETS_WIDENING_CONVERTER_H + +#include "plugins/ecmascript/es2panda/checker/ets/typeConverter.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" + +namespace panda::es2panda::checker { + +class WideningConverter : public TypeConverter { +public: + explicit WideningConverter(ETSChecker *checker, TypeRelation *relation, Type *target, Type *source) + : TypeConverter(checker, relation, target, source) + { + ASSERT(relation->GetNode()); + + if (!Relation()->ApplyWidening()) { + return; + } + + if (!Source()->HasTypeFlag(TypeFlag::CONSTANT)) { + ApplyGlobalWidening(); + } else { + ApplyConstWidening(); + } + } + +private: + void ApplyConstWidening() + { + switch (ETSChecker::ETSChecker::ETSType(Target())) { + case TypeFlag::SHORT: { + ApplyWidening(TypeFlag::WIDENABLE_TO_SHORT); + break; + } + case TypeFlag::INT: { + ApplyWidening(TypeFlag::WIDENABLE_TO_INT); + break; + } + case TypeFlag::LONG: { + ApplyWidening(TypeFlag::WIDENABLE_TO_LONG); + break; + } + case TypeFlag::FLOAT: { + ApplyWidening(TypeFlag::WIDENABLE_TO_FLOAT); + break; + } + case TypeFlag::DOUBLE: { + ApplyWidening(TypeFlag::WIDENABLE_TO_DOUBLE); + break; + } + default: { + break; + } + } + } + + void ApplyGlobalWidening() + { + switch (ETSChecker::ETSChecker::ETSType(Target())) { + case TypeFlag::SHORT: { + ApplyGlobalWidening(TypeFlag::WIDENABLE_TO_SHORT); + break; + } + case TypeFlag::INT: { + ApplyGlobalWidening(TypeFlag::WIDENABLE_TO_INT); + break; + } + case TypeFlag::LONG: { + ApplyGlobalWidening(TypeFlag::WIDENABLE_TO_LONG); + break; + } + case TypeFlag::FLOAT: { + ApplyGlobalWidening(TypeFlag::WIDENABLE_TO_FLOAT); + break; + } + case TypeFlag::DOUBLE: { + ApplyGlobalWidening(TypeFlag::WIDENABLE_TO_DOUBLE); + break; + } + default: { + break; + } + } + } + + void ApplyGlobalWidening(TypeFlag flag) + { + if (!Source()->HasTypeFlag(flag)) { + return; + } + + switch (ETSChecker::ETSChecker::ETSType(Source())) { + case TypeFlag::BYTE: { + Relation()->GetNode()->SetTsType(Checker()->GlobalByteType()); + break; + } + case TypeFlag::SHORT: { + Relation()->GetNode()->SetTsType(Checker()->GlobalShortType()); + break; + } + case TypeFlag::CHAR: { + Relation()->GetNode()->SetTsType(Checker()->GlobalCharType()); + break; + } + case TypeFlag::INT: { + Relation()->GetNode()->SetTsType(Checker()->GlobalIntType()); + break; + } + case TypeFlag::LONG: { + Relation()->GetNode()->SetTsType(Checker()->GlobalLongType()); + break; + } + case TypeFlag::FLOAT: { + Relation()->GetNode()->SetTsType(Checker()->GlobalFloatType()); + break; + } + case TypeFlag::DOUBLE: { + Relation()->GetNode()->SetTsType(Checker()->GlobalDoubleType()); + break; + } + default: { + return; + } + } + + Relation()->Result(true); + } + + template + void ApplyWidening(TypeFlag flag) + { + if (!Source()->HasTypeFlag(flag)) { + return; + } + + switch (ETSChecker::ETSChecker::ETSType(Source())) { + case TypeFlag::BYTE: { + ApplyWidening(); + break; + } + case TypeFlag::CHAR: { + ApplyWidening(); + break; + } + case TypeFlag::SHORT: { + ApplyWidening(); + break; + } + case TypeFlag::INT: { + ApplyWidening(); + break; + } + case TypeFlag::LONG: { + ApplyWidening(); + break; + } + case TypeFlag::FLOAT: { + ApplyWidening(); + break; + } + case TypeFlag::DOUBLE: { + ApplyWidening(); + break; + } + default: { + return; + } + } + Relation()->Result(true); + } + + template + void ApplyWidening() + { + using SType = typename SourceType::UType; + using TType = typename TargetType::UType; + SType value = reinterpret_cast(Source())->GetValue(); + + if (!Relation()->OnlyCheckWidening()) { + Relation()->GetNode()->SetTsType(Checker()->Allocator()->New(static_cast(value))); + } + } +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/typescript/core/binaryLikeExpression.cpp b/checker/ts/binaryLikeExpression.cpp similarity index 85% rename from typescript/core/binaryLikeExpression.cpp rename to checker/ts/binaryLikeExpression.cpp index b5009ff7be83e169c50f1d84f2426b7f94052da0..425a391e035da95db6338f46ed7389411f1e918a 100644 --- a/typescript/core/binaryLikeExpression.cpp +++ b/checker/ts/binaryLikeExpression.cpp @@ -16,11 +16,11 @@ #include "plugins/ecmascript/es2panda/ir/expressions/assignmentExpression.h" #include "plugins/ecmascript/es2panda/ir/expressions/memberExpression.h" -#include "plugins/ecmascript/es2panda/typescript/checker.h" +#include "plugins/ecmascript/es2panda/checker/TSchecker.h" namespace panda::es2panda::checker { -Type *Checker::CheckBinaryOperator(Type *leftType, Type *rightType, const ir::Expression *leftExpr, - const ir::Expression *rightExpr, const ir::AstNode *expr, lexer::TokenType op) +Type *TSChecker::CheckBinaryOperator(Type *leftType, Type *rightType, ir::Expression *leftExpr, + ir::Expression *rightExpr, ir::AstNode *expr, lexer::TokenType op) { CheckNonNullType(leftType, leftExpr->Start()); CheckNonNullType(rightType, rightExpr->Start()); @@ -90,8 +90,8 @@ Type *Checker::CheckBinaryOperator(Type *leftType, Type *rightType, const ir::Ex return resultType; } -Type *Checker::CheckPlusOperator(Type *leftType, Type *rightType, const ir::Expression *leftExpr, - const ir::Expression *rightExpr, const ir::AstNode *expr, lexer::TokenType op) +Type *TSChecker::CheckPlusOperator(Type *leftType, Type *rightType, ir::Expression *leftExpr, ir::Expression *rightExpr, + ir::AstNode *expr, lexer::TokenType op) { if (!leftType->HasTypeFlag(TypeFlag::STRING_LIKE) && !rightType->HasTypeFlag(TypeFlag::STRING_LIKE)) { CheckNonNullType(leftType, leftExpr->Start()); @@ -122,8 +122,8 @@ Type *Checker::CheckPlusOperator(Type *leftType, Type *rightType, const ir::Expr return resultType; } -Type *Checker::CheckCompareOperator(Type *leftType, Type *rightType, const ir::Expression *leftExpr, - const ir::Expression *rightExpr, const ir::AstNode *expr, lexer::TokenType op) +Type *TSChecker::CheckCompareOperator(Type *leftType, Type *rightType, ir::Expression *leftExpr, + ir::Expression *rightExpr, ir::AstNode *expr, lexer::TokenType op) { CheckNonNullType(leftType, leftExpr->Start()); CheckNonNullType(rightType, rightExpr->Start()); @@ -138,7 +138,7 @@ Type *Checker::CheckCompareOperator(Type *leftType, Type *rightType, const ir::E return GlobalAnyType(); } -Type *Checker::CheckAndOperator(Type *leftType, Type *rightType, const ir::Expression *leftExpr) +Type *TSChecker::CheckAndOperator(Type *leftType, Type *rightType, ir::Expression *leftExpr) { CheckTruthinessOfType(leftType, leftExpr->Start()); @@ -150,7 +150,7 @@ Type *Checker::CheckAndOperator(Type *leftType, Type *rightType, const ir::Expre return leftType; } -Type *Checker::CheckOrOperator(Type *leftType, Type *rightType, const ir::Expression *leftExpr) +Type *TSChecker::CheckOrOperator(Type *leftType, Type *rightType, ir::Expression *leftExpr) { CheckTruthinessOfType(leftType, leftExpr->Start()); @@ -169,8 +169,8 @@ static bool TypeHasCallOrConstructSignatures(Type *type) (!type->AsObjectType()->CallSignatures().empty() || !type->AsObjectType()->ConstructSignatures().empty()); } -Type *Checker::CheckInstanceofExpression(Type *leftType, Type *rightType, const ir::Expression *rightExpr, - const ir::AstNode *expr) +Type *TSChecker::CheckInstanceofExpression(Type *leftType, Type *rightType, ir::Expression *rightExpr, + ir::AstNode *expr) { if (leftType->TypeFlags() != TypeFlag::ANY && IsAllTypesAssignableTo(leftType, GlobalPrimitiveType())) { ThrowTypeError({"The left-hand side of an 'instanceof' expression must be of type 'any',", @@ -188,8 +188,8 @@ Type *Checker::CheckInstanceofExpression(Type *leftType, Type *rightType, const return GlobalBooleanType(); } -Type *Checker::CheckInExpression(Type *leftType, Type *rightType, const ir::Expression *leftExpr, - const ir::Expression *rightExpr, const ir::AstNode *expr) +Type *TSChecker::CheckInExpression(Type *leftType, Type *rightType, ir::Expression *leftExpr, ir::Expression *rightExpr, + ir::AstNode *expr) { CheckNonNullType(leftType, leftExpr->Start()); CheckNonNullType(rightType, rightExpr->Start()); @@ -209,8 +209,7 @@ Type *Checker::CheckInExpression(Type *leftType, Type *rightType, const ir::Expr return GlobalBooleanType(); } -void Checker::CheckAssignmentOperator(lexer::TokenType op, const ir::Expression *leftExpr, Type *leftType, - Type *valueType) +void TSChecker::CheckAssignmentOperator(lexer::TokenType op, ir::Expression *leftExpr, Type *leftType, Type *valueType) { if (IsAssignmentOperator(op)) { CheckReferenceExpression( diff --git a/typescript/core/destructuringContext.cpp b/checker/ts/destructuringContext.cpp similarity index 95% rename from typescript/core/destructuringContext.cpp rename to checker/ts/destructuringContext.cpp index 046ff5f385ee23a1b45348b40c9ba22b64c7a494..8dd3b23dcf768c7fe1b42904ec90525beee82785 100644 --- a/typescript/core/destructuringContext.cpp +++ b/checker/ts/destructuringContext.cpp @@ -27,12 +27,12 @@ #include "plugins/ecmascript/es2panda/ir/expression.h" namespace panda::es2panda::checker { -void DestructuringContext::Prepare(const ir::Expression *typeAnnotation, const ir::Expression *initializer, +void DestructuringContext::Prepare(ir::TypeNode *typeAnnotation, ir::Expression *initializer, const lexer::SourcePosition &loc) { if (typeAnnotation != nullptr) { typeAnnotation->Check(checker_); - Type *annotationType = typeAnnotation->AsTypeNode()->GetType(checker_); + Type *annotationType = typeAnnotation->GetType(checker_); if (initializer != nullptr) { checker_->ElaborateElementwise(annotationType, initializer, loc); @@ -52,8 +52,7 @@ void DestructuringContext::Prepare(const ir::Expression *typeAnnotation, const i } } -void DestructuringContext::HandleDestructuringAssignment(const ir::Identifier *ident, Type *inferedType, - Type *defaultType) +void DestructuringContext::HandleDestructuringAssignment(ir::Identifier *ident, Type *inferedType, Type *defaultType) { if (ident->Variable() == nullptr) { checker_->ThrowTypeError({"Cannot find name '", ident->Name(), "'."}, ident->Start()); @@ -96,7 +95,7 @@ void DestructuringContext::SetInferedTypeForVariable(binder::Variable *var, Type var->SetTsType(inferedType); } -void DestructuringContext::ValidateObjectLiteralType(ObjectType *obj_type, const ir::ObjectExpression *obj_pattern) +void DestructuringContext::ValidateObjectLiteralType(ObjectType *obj_type, ir::ObjectExpression *obj_pattern) { for (const auto *sourceProp : obj_type->Properties()) { const util::StringView &sourceName = sourceProp->Name(); @@ -124,7 +123,7 @@ void DestructuringContext::ValidateObjectLiteralType(ObjectType *obj_type, const } } -void DestructuringContext::HandleAssignmentPattern(const ir::AssignmentExpression *assignmentPattern, Type *inferedType, +void DestructuringContext::HandleAssignmentPattern(ir::AssignmentExpression *assignmentPattern, Type *inferedType, bool validateDefault) { if (!assignmentPattern->Left()->IsArrayPattern()) { @@ -368,7 +367,7 @@ Type *ArrayDestructuringContext::GetRestType([[maybe_unused]] const lexer::Sourc return checker_->CreateUnionType(std::move(tupleUnion)); } -void ArrayDestructuringContext::HandleRest(const ir::SpreadElement *rest) +void ArrayDestructuringContext::HandleRest(ir::SpreadElement *rest) { Type *inferedRestType = GetRestType(rest->Start()); @@ -397,7 +396,7 @@ void ArrayDestructuringContext::HandleRest(const ir::SpreadElement *rest) nextContext.Start(); } -Type *ArrayDestructuringContext::ConvertTupleTypeToArrayTypeIfNecessary(const ir::AstNode *node, Type *type) +Type *ArrayDestructuringContext::ConvertTupleTypeToArrayTypeIfNecessary(ir::AstNode *node, Type *type) { if (!convertTupleToArray_) { return type; @@ -419,7 +418,7 @@ Type *ArrayDestructuringContext::ConvertTupleTypeToArrayTypeIfNecessary(const ir return type; } -static void SetParameterType(const ir::AstNode *parent, Type *type) +static void SetParameterType(ir::AstNode *parent, Type *type) { parent->Iterate([type](ir::AstNode *childNode) -> void { if (childNode->IsIdentifier() && childNode->AsIdentifier()->Variable() != nullptr) { @@ -434,7 +433,7 @@ static void SetParameterType(const ir::AstNode *parent, Type *type) void ArrayDestructuringContext::SetRemainingPatameterTypes() { do { - const auto *it = id_->AsArrayPattern()->Elements()[index_]; + auto *it = id_->AsArrayPattern()->Elements()[index_]; ASSERT(it); SetParameterType(it, checker_->GlobalAnyType()); } while (++index_ != id_->AsArrayPattern()->Elements().size()); @@ -448,7 +447,7 @@ void ArrayDestructuringContext::Start() util::StringView name = util::Helpers::ToStringView(checker_->Allocator(), 0); - for (const auto *it : id_->AsArrayPattern()->Elements()) { + for (auto *it : id_->AsArrayPattern()->Elements()) { if (it->IsRestElement()) { HandleRest(it->AsRestElement()); break; @@ -521,7 +520,7 @@ void ObjectDestructuringContext::ValidateInferedType() ValidateObjectLiteralType(inferedType_->AsObjectType(), id_->AsObjectPattern()); } -void ObjectDestructuringContext::HandleRest(const ir::SpreadElement *rest) +void ObjectDestructuringContext::HandleRest(ir::SpreadElement *rest) { Type *inferedRestType = GetRestType(rest->Start()); ASSERT(rest->Argument()->IsIdentifier()); @@ -577,7 +576,7 @@ Type *ObjectDestructuringContext::GetRestType([[maybe_unused]] const lexer::Sour checker_->ThrowTypeError("Rest types may only be created from object types.", loc); } -Type *ObjectDestructuringContext::ConvertTupleTypeToArrayTypeIfNecessary(const ir::AstNode *node, Type *type) +Type *ObjectDestructuringContext::ConvertTupleTypeToArrayTypeIfNecessary(ir::AstNode *node, Type *type) { if (!convertTupleToArray_) { return type; @@ -589,7 +588,7 @@ Type *ObjectDestructuringContext::ConvertTupleTypeToArrayTypeIfNecessary(const i ASSERT(node->IsProperty()); - const ir::Property *property = node->AsProperty(); + ir::Property *property = node->AsProperty(); if (property->Value()->IsArrayPattern()) { return type; @@ -641,10 +640,10 @@ void ObjectDestructuringContext::Start() ValidateInferedType(); } - for (const auto *it : id_->AsObjectPattern()->Properties()) { + for (auto *it : id_->AsObjectPattern()->Properties()) { switch (it->Type()) { case ir::AstNodeType::PROPERTY: { - const ir::Property *property = it->AsProperty(); + ir::Property *property = it->AsProperty(); if (property->IsComputed()) { // TODO(aszilagyi) diff --git a/typescript/core/destructuringContext.h b/checker/ts/destructuringContext.h similarity index 64% rename from typescript/core/destructuringContext.h rename to checker/ts/destructuringContext.h index 5e0619773450a483e70909c195730af04b5f17b1..2f649e8595b3ca2b686fe5d6c0c7a1d01be5c17a 100644 --- a/typescript/core/destructuringContext.h +++ b/checker/ts/destructuringContext.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_TYPESCIRPT_CORE_DESTRUCTURING_CONTEXT_H -#define ES2PANDA_TYPESCIRPT_CORE_DESTRUCTURING_CONTEXT_H +#ifndef ES2PANDA_CHECKER_TS_DESTRUCTURING_CONTEXT_H +#define ES2PANDA_CHECKER_TS_DESTRUCTURING_CONTEXT_H -#include "plugins/ecmascript/es2panda/typescript/checker.h" +#include "plugins/ecmascript/es2panda/checker/TSchecker.h" #include "plugins/ecmascript/es2panda/ir/expression.h" #include @@ -31,8 +31,8 @@ class Type; class DestructuringContext { public: - DestructuringContext(Checker *checker, const ir::Expression *id, bool inAssignment, bool convertTupleToArray, - const ir::Expression *typeAnnotation, const ir::Expression *initializer) + DestructuringContext(TSChecker *checker, ir::Expression *id, bool inAssignment, bool convertTupleToArray, + ir::TypeNode *typeAnnotation, ir::Expression *initializer) : checker_(checker), id_(id), inAssignment_(inAssignment), convertTupleToArray_(convertTupleToArray) { Prepare(typeAnnotation, initializer, id->Start()); @@ -53,13 +53,11 @@ public: return inferedType_; } - void ValidateObjectLiteralType(ObjectType *obj_type, const ir::ObjectExpression *obj_pattern); - void HandleDestructuringAssignment(const ir::Identifier *ident, Type *inferedType, Type *defaultType); - void HandleAssignmentPattern(const ir::AssignmentExpression *assignmentPattern, Type *inferedType, - bool validateDefault); + void ValidateObjectLiteralType(ObjectType *obj_type, ir::ObjectExpression *obj_pattern); + void HandleDestructuringAssignment(ir::Identifier *ident, Type *inferedType, Type *defaultType); + void HandleAssignmentPattern(ir::AssignmentExpression *assignmentPattern, Type *inferedType, bool validateDefault); void SetInferedTypeForVariable(binder::Variable *var, Type *inferedType, const lexer::SourcePosition &loc); - void Prepare(const ir::Expression *typeAnnotation, const ir::Expression *initializer, - const lexer::SourcePosition &loc); + void Prepare(ir::TypeNode *typeAnnotation, ir::Expression *initializer, const lexer::SourcePosition &loc); DEFAULT_COPY_SEMANTIC(DestructuringContext); DEFAULT_MOVE_SEMANTIC(DestructuringContext); @@ -68,14 +66,14 @@ public: virtual void Start() = 0; virtual void ValidateInferedType() = 0; virtual Type *NextInferedType([[maybe_unused]] const util::StringView &searchName, bool throwError) = 0; - virtual void HandleRest(const ir::SpreadElement *rest) = 0; + virtual void HandleRest(ir::SpreadElement *rest) = 0; virtual Type *GetRestType([[maybe_unused]] const lexer::SourcePosition &loc) = 0; - virtual Type *ConvertTupleTypeToArrayTypeIfNecessary(const ir::AstNode *node, Type *type) = 0; + virtual Type *ConvertTupleTypeToArrayTypeIfNecessary(ir::AstNode *node, Type *type) = 0; protected: // NOLINTBEGIN(misc-non-private-member-variables-in-classes) - Checker *checker_; - const ir::Expression *id_; + TSChecker *checker_; + ir::Expression *id_; bool inAssignment_; bool convertTupleToArray_; Type *inferedType_ {}; @@ -87,8 +85,8 @@ protected: class ArrayDestructuringContext : public DestructuringContext { public: - ArrayDestructuringContext(Checker *checker, const ir::Expression *id, bool inAssignment, bool convertTupleToArray, - const ir::Expression *typeAnnotation, const ir::Expression *initializer) + ArrayDestructuringContext(TSChecker *checker, ir::Expression *id, bool inAssignment, bool convertTupleToArray, + ir::TypeNode *typeAnnotation, ir::Expression *initializer) : DestructuringContext(checker, id, inAssignment, convertTupleToArray, typeAnnotation, initializer) { } @@ -101,9 +99,9 @@ public: void Start() override; void ValidateInferedType() override; Type *NextInferedType([[maybe_unused]] const util::StringView &searchName, bool throwError) override; - void HandleRest(const ir::SpreadElement *rest) override; + void HandleRest(ir::SpreadElement *rest) override; Type *GetRestType([[maybe_unused]] const lexer::SourcePosition &loc) override; - Type *ConvertTupleTypeToArrayTypeIfNecessary(const ir::AstNode *node, Type *type) override; + Type *ConvertTupleTypeToArrayTypeIfNecessary(ir::AstNode *node, Type *type) override; private: uint32_t index_ {0}; @@ -111,8 +109,8 @@ private: class ObjectDestructuringContext : public DestructuringContext { public: - ObjectDestructuringContext(Checker *checker, const ir::Expression *id, bool inAssignment, bool convertTupleToArray, - const ir::Expression *typeAnnotation, const ir::Expression *initializer) + ObjectDestructuringContext(TSChecker *checker, ir::Expression *id, bool inAssignment, bool convertTupleToArray, + ir::TypeNode *typeAnnotation, ir::Expression *initializer) : DestructuringContext(checker, id, inAssignment, convertTupleToArray, typeAnnotation, initializer) { } @@ -122,9 +120,9 @@ public: void Start() override; void ValidateInferedType() override; Type *NextInferedType([[maybe_unused]] const util::StringView &searchName, bool throwError) override; - void HandleRest(const ir::SpreadElement *rest) override; + void HandleRest(ir::SpreadElement *rest) override; Type *GetRestType([[maybe_unused]] const lexer::SourcePosition &loc) override; - Type *ConvertTupleTypeToArrayTypeIfNecessary(const ir::AstNode *node, Type *type) override; + Type *ConvertTupleTypeToArrayTypeIfNecessary(ir::AstNode *node, Type *type) override; }; } // namespace panda::es2panda::checker diff --git a/typescript/core/function.cpp b/checker/ts/function.cpp similarity index 76% rename from typescript/core/function.cpp rename to checker/ts/function.cpp index 479384a3ff335187baef8a68017ad0b3b411d71e..2f4738a5c3e32abc597bc8a82b69990954cc642b 100644 --- a/typescript/core/function.cpp +++ b/checker/ts/function.cpp @@ -33,22 +33,23 @@ #include "plugins/ecmascript/es2panda/binder/scope.h" #include "plugins/ecmascript/es2panda/binder/declaration.h" -#include "plugins/ecmascript/es2panda/typescript/checker.h" -#include "plugins/ecmascript/es2panda/typescript/core/destructuringContext.h" -#include "plugins/ecmascript/es2panda/typescript/types/objectDescriptor.h" -#include "plugins/ecmascript/es2panda/typescript/types/objectType.h" +#include "plugins/ecmascript/es2panda/checker/TSchecker.h" +#include "plugins/ecmascript/es2panda/checker/ts/destructuringContext.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/objectDescriptor.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/objectType.h" #include +#include #include #include #include namespace panda::es2panda::checker { -Type *Checker::HandleFunctionReturn(const ir::ScriptFunction *func) +Type *TSChecker::HandleFunctionReturn(ir::ScriptFunction *func) { if (func->ReturnTypeAnnotation() != nullptr) { func->ReturnTypeAnnotation()->Check(this); - Type *returnType = func->ReturnTypeAnnotation()->AsTypeNode()->GetType(this); + Type *returnType = func->ReturnTypeAnnotation()->GetType(this); if (func->IsArrow() && func->Body()->IsExpression()) { ElaborateElementwise(returnType, func->Body()->AsExpression(), func->Body()->Start()); @@ -76,7 +77,7 @@ Type *Checker::HandleFunctionReturn(const ir::ScriptFunction *func) return func->Body()->Check(this); } - ArenaVector returnTypes(allocator_->Adapter()); + ArenaVector returnTypes(Allocator()->Adapter()); CollectTypesFromReturnStatements(func->Body(), &returnTypes); if (returnTypes.empty()) { @@ -96,7 +97,7 @@ Type *Checker::HandleFunctionReturn(const ir::ScriptFunction *func) return CreateUnionType(std::move(returnTypes)); } -void Checker::ThrowReturnTypeCircularityError(const ir::ScriptFunction *func) +void TSChecker::ThrowReturnTypeCircularityError(ir::ScriptFunction *func) { if (func->ReturnTypeAnnotation() != nullptr) { ThrowTypeError("Return type annotation circularly reference itself", func->ReturnTypeAnnotation()->Start()); @@ -115,8 +116,8 @@ void Checker::ThrowReturnTypeCircularityError(const ir::ScriptFunction *func) func->Start()); } -std::tuple Checker::CheckFunctionIdentifierParameter( - const ir::Identifier *param) +std::tuple TSChecker::CheckFunctionIdentifierParameter( + ir::Identifier *param) { ASSERT(param->Variable()); binder::Variable *param_var = param->Variable(); @@ -131,11 +132,11 @@ std::tuple Checker::Chec } param->TypeAnnotation()->Check(this); - param_var->SetTsType(param->TypeAnnotation()->AsTypeNode()->GetType(this)); + param_var->SetTsType(param->TypeAnnotation()->GetType(this)); return {param_var->AsLocalVariable(), nullptr, is_optional}; } -Type *Checker::CreateParameterTypeForArrayAssignmentPattern(const ir::ArrayExpression *arrayPattern, Type *inferedType) +Type *TSChecker::CreateParameterTypeForArrayAssignmentPattern(ir::ArrayExpression *arrayPattern, Type *inferedType) { if (!inferedType->IsObjectType()) { return inferedType; @@ -148,12 +149,13 @@ Type *Checker::CreateParameterTypeForArrayAssignmentPattern(const ir::ArrayExpre return inferedType; } - TupleType *newTuple = inferedTuple->Instantiate(allocator_, relation_, globalTypes_)->AsObjectType()->AsTupleType(); + TupleType *newTuple = + inferedTuple->Instantiate(Allocator(), Relation(), GetGlobalTypesHolder())->AsObjectType()->AsTupleType(); for (uint32_t index = inferedTuple->FixedLength(); index < arrayPattern->Elements().size(); index++) { - util::StringView memberIndex = util::Helpers::ToStringView(allocator_, index); + util::StringView memberIndex = util::Helpers::ToStringView(Allocator(), index); binder::LocalVariable *newMember = binder::Scope::CreateVar( - allocator_, memberIndex, binder::VariableFlags::PROPERTY | binder::VariableFlags::OPTIONAL, nullptr); + Allocator(), memberIndex, binder::VariableFlags::PROPERTY | binder::VariableFlags::OPTIONAL, nullptr); newMember->SetTsType(GlobalAnyType()); newTuple->AddProperty(newMember); } @@ -161,21 +163,20 @@ Type *Checker::CreateParameterTypeForArrayAssignmentPattern(const ir::ArrayExpre return newTuple; } -Type *Checker::CreateParameterTypeForObjectAssignmentPattern(const ir::ObjectExpression *objectPattern, - Type *inferedType) +Type *TSChecker::CreateParameterTypeForObjectAssignmentPattern(ir::ObjectExpression *objectPattern, Type *inferedType) { if (!inferedType->IsObjectType()) { return inferedType; } - ObjectType *newObject = inferedType->Instantiate(allocator_, relation_, globalTypes_)->AsObjectType(); + ObjectType *newObject = inferedType->Instantiate(Allocator(), Relation(), GetGlobalTypesHolder())->AsObjectType(); - for (const auto *it : objectPattern->Properties()) { + for (auto *it : objectPattern->Properties()) { if (it->IsRestElement()) { continue; } - const ir::Property *prop = it->AsProperty(); + ir::Property *prop = it->AsProperty(); binder::LocalVariable *foundVar = newObject->GetProperty(prop->Key()->AsIdentifier()->Name(), true); if (foundVar != nullptr) { @@ -187,10 +188,10 @@ Type *Checker::CreateParameterTypeForObjectAssignmentPattern(const ir::ObjectExp } ASSERT(prop->Value()->IsAssignmentPattern()); - const ir::AssignmentExpression *assignmentPattern = prop->Value()->AsAssignmentPattern(); + ir::AssignmentExpression *assignmentPattern = prop->Value()->AsAssignmentPattern(); binder::LocalVariable *newProp = - binder::Scope::CreateVar(allocator_, prop->Key()->AsIdentifier()->Name(), + binder::Scope::CreateVar(Allocator(), prop->Key()->AsIdentifier()->Name(), binder::VariableFlags::PROPERTY | binder::VariableFlags::OPTIONAL, nullptr); newProp->SetTsType(GetBaseTypeOfLiteralType(CheckTypeCached(assignmentPattern->Right()))); newObject->AddProperty(newProp); @@ -200,17 +201,17 @@ Type *Checker::CreateParameterTypeForObjectAssignmentPattern(const ir::ObjectExp return newObject; } -std::tuple Checker::CheckFunctionAssignmentPatternParameter( - const ir::AssignmentExpression *param) +std::tuple TSChecker::CheckFunctionAssignmentPatternParameter( + ir::AssignmentExpression *param) { if (param->Left()->IsIdentifier()) { - const ir::Identifier *paramIdent = param->Left()->AsIdentifier(); + ir::Identifier *paramIdent = param->Left()->AsIdentifier(); binder::Variable *paramVar = paramIdent->Variable(); ASSERT(paramVar); if (paramIdent->TypeAnnotation() != nullptr) { paramIdent->TypeAnnotation()->Check(this); - Type *paramType = paramIdent->TypeAnnotation()->AsTypeNode()->GetType(this); + Type *paramType = paramIdent->TypeAnnotation()->GetType(this); paramVar->SetTsType(paramType); ElaborateElementwise(paramType, param->Right(), paramIdent->Start()); return {paramVar->AsLocalVariable(), nullptr, true}; @@ -227,14 +228,14 @@ std::tuple Checker::Chec auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE | CheckerStatus::IN_PARAMETER); if (param->Left()->IsArrayPattern()) { - const ir::ArrayExpression *arrayPattern = param->Left()->AsArrayPattern(); + ir::ArrayExpression *arrayPattern = param->Left()->AsArrayPattern(); auto context = ArrayDestructuringContext(this, arrayPattern, false, true, arrayPattern->TypeAnnotation(), param->Right()); context.Start(); paramType = CreateParameterTypeForArrayAssignmentPattern(arrayPattern, context.InferedType()); CreatePatternParameterName(param->Left(), ss); } else { - const ir::ObjectExpression *objectPattern = param->Left()->AsObjectPattern(); + ir::ObjectExpression *objectPattern = param->Left()->AsObjectPattern(); auto context = ObjectDestructuringContext(this, objectPattern, false, true, objectPattern->TypeAnnotation(), param->Right()); context.Start(); @@ -242,41 +243,27 @@ std::tuple Checker::Chec CreatePatternParameterName(param->Left(), ss); } - util::UString pn(ss.str(), allocator_); + util::UString pn(ss.str(), Allocator()); binder::LocalVariable *patternVar = - binder::Scope::CreateVar(allocator_, pn.View(), binder::VariableFlags::NONE, param); + binder::Scope::CreateVar(Allocator(), pn.View(), binder::VariableFlags::NONE, param); patternVar->SetTsType(paramType); patternVar->AddFlag(binder::VariableFlags::OPTIONAL); return {patternVar->AsLocalVariable(), nullptr, true}; } -std::tuple Checker::CheckFunctionRestParameter( - const ir::SpreadElement *param, SignatureInfo *signatureInfo) +std::tuple TSChecker::CheckFunctionRestParameter( + ir::SpreadElement *param, SignatureInfo *signatureInfo) { - const ir::Expression *typeAnnotation = nullptr; - switch (param->Argument()->Type()) { - case ir::AstNodeType::IDENTIFIER: { - typeAnnotation = param->Argument()->AsIdentifier()->TypeAnnotation(); - break; - } - case ir::AstNodeType::OBJECT_PATTERN: { - typeAnnotation = param->Argument()->AsArrayPattern()->TypeAnnotation(); - break; - } - case ir::AstNodeType::ARRAY_PATTERN: { - typeAnnotation = param->Argument()->AsObjectPattern()->TypeAnnotation(); - break; - } - default: { - UNREACHABLE(); - } + ir::TypeNode *typeAnnotation = nullptr; + if (param->Argument() != nullptr) { + typeAnnotation = param->Argument()->AsAnnotatedExpression()->TypeAnnotation(); } - Type *restType = allocator_->New(GlobalAnyType()); + Type *restType = Allocator()->New(GlobalAnyType()); if (typeAnnotation != nullptr) { typeAnnotation->Check(this); - restType = typeAnnotation->AsTypeNode()->GetType(this); + restType = typeAnnotation->GetType(this); if (!restType->IsArrayType()) { ThrowTypeError("A rest parameter must be of an array type", param->Start()); } @@ -284,7 +271,7 @@ std::tuple Checker::Chec switch (param->Argument()->Type()) { case ir::AstNodeType::IDENTIFIER: { - const ir::Identifier *restIdent = param->Argument()->AsIdentifier(); + ir::Identifier *restIdent = param->Argument()->AsIdentifier(); ASSERT(restIdent->Variable()); restIdent->Variable()->SetTsType(restType->AsArrayType()->ElementType()); return {nullptr, restIdent->Variable()->AsLocalVariable(), false}; @@ -314,14 +301,14 @@ std::tuple Checker::Chec } } -std::tuple Checker::CheckFunctionArrayPatternParameter( - const ir::ArrayExpression *param) +std::tuple TSChecker::CheckFunctionArrayPatternParameter( + ir::ArrayExpression *param) { std::stringstream ss; CreatePatternParameterName(param, ss); - util::UString pn(ss.str(), allocator_); + util::UString pn(ss.str(), Allocator()); binder::LocalVariable *patternVar = - binder::Scope::CreateVar(allocator_, pn.View(), binder::VariableFlags::NONE, param); + binder::Scope::CreateVar(Allocator(), pn.View(), binder::VariableFlags::NONE, param); if (param->TypeAnnotation() != nullptr) { auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE); @@ -336,14 +323,14 @@ std::tuple Checker::Chec return {patternVar->AsLocalVariable(), nullptr, false}; } -std::tuple Checker::CheckFunctionObjectPatternParameter( - const ir::ObjectExpression *param) +std::tuple TSChecker::CheckFunctionObjectPatternParameter( + ir::ObjectExpression *param) { std::stringstream ss; CreatePatternParameterName(param, ss); - util::UString pn(ss.str(), allocator_); + util::UString pn(ss.str(), Allocator()); binder::LocalVariable *patternVar = - binder::Scope::CreateVar(allocator_, pn.View(), binder::VariableFlags::NONE, param); + binder::Scope::CreateVar(Allocator(), pn.View(), binder::VariableFlags::NONE, param); if (param->TypeAnnotation() != nullptr) { auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE); @@ -358,13 +345,12 @@ std::tuple Checker::Chec return {patternVar->AsLocalVariable(), nullptr, false}; } -std::tuple Checker::CheckFunctionParameter( - const ir::Expression *param, SignatureInfo *signatureInfo) +std::tuple TSChecker::CheckFunctionParameter( + ir::Expression *param, SignatureInfo *signatureInfo) { - auto found = nodeCache_.find(param); - if (found != nodeCache_.end()) { - ASSERT(found->second->Variable()); - binder::Variable *var = found->second->Variable(); + if (param->TsType() != nullptr) { + ASSERT(param->TsType()->Variable()); + binder::Variable *var = param->TsType()->Variable(); return {var->AsLocalVariable(), nullptr, var->HasFlag(binder::VariableFlags::OPTIONAL)}; } @@ -399,16 +385,16 @@ std::tuple Checker::Chec } if (cache) { - Type *placeholder = allocator_->New(GlobalAnyType()); + Type *placeholder = Allocator()->New(GlobalAnyType()); placeholder->SetVariable(std::get<0>(result)); - nodeCache_.insert({param, placeholder}); + param->SetTsType(placeholder); } return result; } -void Checker::CheckFunctionParameterDeclarations(const ArenaVector ¶ms, - SignatureInfo *signatureInfo) +void TSChecker::CheckFunctionParameterDeclarations(const ArenaVector ¶ms, + SignatureInfo *signatureInfo) { signatureInfo->restVar = nullptr; signatureInfo->minArgCount = 0; @@ -433,14 +419,14 @@ void Checker::CheckFunctionParameterDeclarations(const ArenaVectorIsArrayPattern() || prop_value->IsObjectPattern() || (prop_value->IsAssignmentPattern() && (prop_value->AsAssignmentPattern()->Left()->IsArrayPattern() || prop_value->AsAssignmentPattern()->Left()->IsObjectPattern())); } -void Checker::CreatePatternParameterName(const ir::AstNode *node, std::stringstream &ss) +void TSChecker::CreatePatternParameterName(ir::AstNode *node, std::stringstream &ss) { switch (node->Type()) { case ir::AstNodeType::IDENTIFIER: { @@ -480,7 +466,7 @@ void Checker::CreatePatternParameterName(const ir::AstNode *node, std::stringstr break; } case ir::AstNodeType::PROPERTY: { - const ir::Property *prop = node->AsProperty(); + ir::Property *prop = node->AsProperty(); util::StringView propName; if (prop->Key()->IsIdentifier()) { @@ -488,7 +474,8 @@ void Checker::CreatePatternParameterName(const ir::AstNode *node, std::stringstr } else { switch (prop->Key()->Type()) { case ir::AstNodeType::NUMBER_LITERAL: { - propName = util::Helpers::ToStringView(allocator_, prop->Key()->AsNumberLiteral()->Number()); + propName = util::Helpers::ToStringView(Allocator(), + prop->Key()->AsNumberLiteral()->Number().GetDouble()); break; } case ir::AstNodeType::BIGINT_LITERAL: { @@ -510,14 +497,14 @@ void Checker::CreatePatternParameterName(const ir::AstNode *node, std::stringstr if (ShouldCreatePropertyValueName(prop->Value())) { ss << ": "; - Checker::CreatePatternParameterName(prop->Value(), ss); + TSChecker::CreatePatternParameterName(prop->Value(), ss); } break; } case ir::AstNodeType::REST_ELEMENT: { ss << "..."; - Checker::CreatePatternParameterName(node->AsRestElement()->Argument(), ss); + TSChecker::CreatePatternParameterName(node->AsRestElement()->Argument(), ss); break; } default: @@ -525,7 +512,7 @@ void Checker::CreatePatternParameterName(const ir::AstNode *node, std::stringstr } } -const ir::Statement *FindSubsequentFunctionNode(const ir::BlockStatement *block, const ir::ScriptFunction *node) +ir::Statement *FindSubsequentFunctionNode(ir::BlockStatement *block, ir::ScriptFunction *node) { for (auto it = block->Statements().begin(); it != block->Statements().end(); it++) { if ((*it)->IsFunctionDeclaration() && (*it)->AsFunctionDeclaration()->Function() == node) { @@ -537,22 +524,21 @@ const ir::Statement *FindSubsequentFunctionNode(const ir::BlockStatement *block, return nullptr; } -void Checker::InferFunctionDeclarationType(const binder::FunctionDecl *decl, binder::Variable *funcVar) +void TSChecker::InferFunctionDeclarationType(const binder::FunctionDecl *decl, binder::Variable *funcVar) { - const ir::ScriptFunction *bodyDeclaration = decl->Decls().back(); + ir::ScriptFunction *bodyDeclaration = decl->Decls().back(); if (bodyDeclaration->IsOverload()) { ThrowTypeError("Function implementation is missing or not immediately following the declaration.", bodyDeclaration->Id()->Start()); } - ObjectDescriptor *descWithOverload = allocator_->New(allocator_); + ObjectDescriptor *descWithOverload = Allocator()->New(Allocator()); for (auto it = decl->Decls().begin(); it != decl->Decls().end() - 1; it++) { - const ir::ScriptFunction *func = *it; + ir::ScriptFunction *func = *it; ASSERT(func->IsOverload() && (*it)->Parent()->Parent()->IsBlockStatement()); - const ir::Statement *subsequentNode = - FindSubsequentFunctionNode((*it)->Parent()->Parent()->AsBlockStatement(), func); + ir::Statement *subsequentNode = FindSubsequentFunctionNode((*it)->Parent()->Parent()->AsBlockStatement(), func); ASSERT(subsequentNode); if (!subsequentNode->IsFunctionDeclaration()) { @@ -560,7 +546,7 @@ void Checker::InferFunctionDeclarationType(const binder::FunctionDecl *decl, bin func->Id()->Start()); } - const ir::ScriptFunction *subsequentFunc = subsequentNode->AsFunctionDeclaration()->Function(); + ir::ScriptFunction *subsequentFunc = subsequentNode->AsFunctionDeclaration()->Function(); if (subsequentFunc->Id()->Name() != func->Id()->Name()) { ThrowTypeError("Function implementation is missing or not immediately following the declaration.", @@ -573,26 +559,25 @@ void Checker::InferFunctionDeclarationType(const binder::FunctionDecl *decl, bin ScopeContext scopeCtx(this, func->Scope()); - auto *overloadSignatureInfo = allocator_->New(allocator_); + auto *overloadSignatureInfo = Allocator()->New(Allocator()); CheckFunctionParameterDeclarations(func->Params(), overloadSignatureInfo); Type *returnType = GlobalAnyType(); if (func->ReturnTypeAnnotation() != nullptr) { func->ReturnTypeAnnotation()->Check(this); - returnType = func->ReturnTypeAnnotation()->AsTypeNode()->GetType(this); + returnType = func->ReturnTypeAnnotation()->GetType(this); } - Signature *overloadSignature = allocator_->New(overloadSignatureInfo, returnType); - overloadSignature->SetNode(func); + Signature *overloadSignature = Allocator()->New(overloadSignatureInfo, returnType, func); descWithOverload->callSignatures.push_back(overloadSignature); } ScopeContext scopeCtx(this, bodyDeclaration->Scope()); - auto *signatureInfo = allocator_->New(allocator_); + auto *signatureInfo = Allocator()->New(Allocator()); CheckFunctionParameterDeclarations(bodyDeclaration->Params(), signatureInfo); - auto *bodyCallSignature = allocator_->New(signatureInfo, GlobalResolvingReturnType()); + auto *bodyCallSignature = Allocator()->New(signatureInfo, GlobalResolvingReturnType()); if (descWithOverload->callSignatures.empty()) { Type *funcType = CreateFunctionTypeWithSignature(bodyCallSignature); @@ -603,7 +588,7 @@ void Checker::InferFunctionDeclarationType(const binder::FunctionDecl *decl, bin bodyCallSignature->SetReturnType(HandleFunctionReturn(bodyDeclaration)); if (!descWithOverload->callSignatures.empty()) { - Type *funcType = allocator_->New(descWithOverload); + Type *funcType = Allocator()->New(descWithOverload); funcType->SetVariable(funcVar); funcVar->SetTsType(funcType); @@ -611,21 +596,21 @@ void Checker::InferFunctionDeclarationType(const binder::FunctionDecl *decl, bin if (bodyCallSignature->ReturnType()->IsVoidType() || IsTypeAssignableTo(bodyCallSignature->ReturnType(), iter->ReturnType()) || IsTypeAssignableTo(iter->ReturnType(), bodyCallSignature->ReturnType())) { - bodyCallSignature->AssignmentTarget(relation_, iter); + bodyCallSignature->AssignmentTarget(Relation(), iter); - if (relation_->IsTrue()) { + if (Relation()->IsTrue()) { continue; } } - ASSERT(iter->Node() && iter->Node()->IsScriptFunction()); + ASSERT(iter->Function()); ThrowTypeError("This overload signature is not compatible with its implementation signature", - iter->Node()->AsScriptFunction()->Id()->Start()); + iter->Function()->Id()->Start()); } } } -void Checker::CollectTypesFromReturnStatements(const ir::AstNode *parent, ArenaVector *returnTypes) +void TSChecker::CollectTypesFromReturnStatements(ir::AstNode *parent, ArenaVector *returnTypes) { parent->Iterate([this, returnTypes](ir::AstNode *childNode) -> void { if (childNode->IsScriptFunction()) { @@ -647,11 +632,11 @@ void Checker::CollectTypesFromReturnStatements(const ir::AstNode *parent, ArenaV }); } -static bool SearchForReturnOrThrow(const ir::AstNode *parent) +static bool SearchForReturnOrThrow(ir::AstNode *parent) { bool found = false; - parent->Iterate([&found](const ir::AstNode *childNode) -> void { + parent->Iterate([&found](ir::AstNode *childNode) -> void { if (childNode->IsThrowStatement() || childNode->IsReturnStatement()) { found = true; return; @@ -667,8 +652,8 @@ static bool SearchForReturnOrThrow(const ir::AstNode *parent) return found; } -void Checker::CheckAllCodePathsInNonVoidFunctionReturnOrThrow(const ir::ScriptFunction *func, - lexer::SourcePosition lineInfo, const char *errMsg) +void TSChecker::CheckAllCodePathsInNonVoidFunctionReturnOrThrow(ir::ScriptFunction *func, + lexer::SourcePosition lineInfo, const char *errMsg) { if (!SearchForReturnOrThrow(func->Body())) { ThrowTypeError(errMsg, lineInfo); @@ -677,8 +662,9 @@ void Checker::CheckAllCodePathsInNonVoidFunctionReturnOrThrow(const ir::ScriptFu // noImplicitReturn compiler option for TypeScript we should update this function } -ArgRange Checker::GetArgRange(const ArenaVector &signatures, ArenaVector *potentialSignatures, - uint32_t callArgsSize, bool *haveSignatureWithRest) +ArgRange TSChecker::GetArgRange(const ArenaVector &signatures, + ArenaVector *potentialSignatures, uint32_t callArgsSize, + bool *haveSignatureWithRest) { uint32_t minArg = UINT32_MAX; uint32_t maxArg = 0; @@ -704,7 +690,7 @@ ArgRange Checker::GetArgRange(const ArenaVector &signatures, ArenaV return {minArg, maxArg}; } -bool Checker::CallMatchesSignature(const ArenaVector &args, Signature *signature, bool throwError) +bool TSChecker::CallMatchesSignature(const ArenaVector &args, Signature *signature, bool throwError) { for (size_t index = 0; index < args.size(); index++) { checker::Type *sigArgType = nullptr; @@ -739,14 +725,15 @@ bool Checker::CallMatchesSignature(const ArenaVector &args, Si return true; } -Type *Checker::resolveCallOrNewExpression(const ArenaVector &signatures, - ArenaVector arguments, const lexer::SourcePosition &errPos) +Type *TSChecker::resolveCallOrNewExpression(const ArenaVector &signatures, + ArenaVector arguments, + const lexer::SourcePosition &errPos) { if (signatures.empty()) { ThrowTypeError("This expression is not callable.", errPos); } - ArenaVector potentialSignatures(allocator_->Adapter()); + ArenaVector potentialSignatures(Allocator()->Adapter()); bool haveSignatureWithRest = false; auto argRange = GetArgRange(signatures, &potentialSignatures, arguments.size(), &haveSignatureWithRest); diff --git a/typescript/core/helpers.cpp b/checker/ts/helpers.cpp similarity index 72% rename from typescript/core/helpers.cpp rename to checker/ts/helpers.cpp index 5d57490f28ac460d4cf79fe781e8b2d0ec60e2b2..5ecbb55ae54a4a9fcb0733f6d66e25da7c6c7dbc 100644 --- a/typescript/core/helpers.cpp +++ b/checker/ts/helpers.cpp @@ -21,26 +21,27 @@ #include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" #include "plugins/ecmascript/es2panda/ir/statements/variableDeclarator.h" #include "plugins/ecmascript/es2panda/ir/ts/tsQualifiedName.h" -#include "plugins/ecmascript/es2panda/ir/ts/tsPropertySignature.h" +#include "plugins/ecmascript/es2panda/ir/base/tsPropertySignature.h" #include "plugins/ecmascript/es2panda/ir/ts/tsTypeAliasDeclaration.h" #include "plugins/ecmascript/es2panda/ir/ts/tsTypeReference.h" #include "plugins/ecmascript/es2panda/ir/ts/tsTypeParameterDeclaration.h" #include "plugins/ecmascript/es2panda/ir/ts/tsTypeParameter.h" #include "plugins/ecmascript/es2panda/binder/variable.h" #include "plugins/ecmascript/es2panda/binder/scope.h" +#include "plugins/ecmascript/es2panda/util/helpers.h" -#include "plugins/ecmascript/es2panda/typescript/core/typeElaborationContext.h" -#include "plugins/ecmascript/es2panda/typescript/checker.h" +#include "plugins/ecmascript/es2panda/checker/ts/typeElaborationContext.h" +#include "plugins/ecmascript/es2panda/checker/TSchecker.h" namespace panda::es2panda::checker { -void Checker::CheckTruthinessOfType(Type *type, lexer::SourcePosition lineInfo) +void TSChecker::CheckTruthinessOfType(Type *type, lexer::SourcePosition lineInfo) { if (type->IsVoidType()) { ThrowTypeError("An expression of type void cannot be tested for truthiness", lineInfo); } } -Type *Checker::CheckNonNullType(Type *type, lexer::SourcePosition lineInfo) +Type *TSChecker::CheckNonNullType(Type *type, lexer::SourcePosition lineInfo) { if (type->IsNullType()) { ThrowTypeError("Object is possibly 'null'.", lineInfo); @@ -53,7 +54,7 @@ Type *Checker::CheckNonNullType(Type *type, lexer::SourcePosition lineInfo) return type; } -Type *Checker::GetBaseTypeOfLiteralType(Type *type) +Type *TSChecker::GetBaseTypeOfLiteralType(Type *type) { if (HasStatus(CheckerStatus::KEEP_LITERAL_TYPE)) { return type; @@ -77,7 +78,7 @@ Type *Checker::GetBaseTypeOfLiteralType(Type *type) if (type->IsUnionType()) { auto &constituentTypes = type->AsUnionType()->ConstituentTypes(); - ArenaVector newConstituentTypes(allocator_->Adapter()); + ArenaVector newConstituentTypes(Allocator()->Adapter()); newConstituentTypes.reserve(constituentTypes.size()); for (auto *it : constituentTypes) { @@ -90,12 +91,12 @@ Type *Checker::GetBaseTypeOfLiteralType(Type *type) return type; } -void Checker::CheckReferenceExpression(const ir::Expression *expr, const char *invalidReferenceMsg, - const char *invalidOptionalChainMsg) +void TSChecker::CheckReferenceExpression(ir::Expression *expr, const char *invalidReferenceMsg, + const char *invalidOptionalChainMsg) { if (expr->IsIdentifier()) { const util::StringView &name = expr->AsIdentifier()->Name(); - binder::ScopeFindResult result = scope_->Find(name); + binder::ScopeFindResult result = Scope()->Find(name); ASSERT(result.variable); if (result.variable->HasFlag(binder::VariableFlags::ENUM_LITERAL)) { @@ -110,14 +111,14 @@ void Checker::CheckReferenceExpression(const ir::Expression *expr, const char *i } } -void Checker::CheckTestingKnownTruthyCallableOrAwaitableType([[maybe_unused]] const ir::Expression *condExpr, - [[maybe_unused]] Type *type, - [[maybe_unused]] const ir::AstNode *body) +void TSChecker::CheckTestingKnownTruthyCallableOrAwaitableType([[maybe_unused]] ir::Expression *condExpr, + [[maybe_unused]] Type *type, + [[maybe_unused]] ir::AstNode *body) { // TODO(aszilagyi) rework this } -Type *Checker::ExtractDefinitelyFalsyTypes(Type *type) +Type *TSChecker::ExtractDefinitelyFalsyTypes(Type *type) { if (type->IsStringType()) { return GlobalEmptyStringType(); @@ -141,7 +142,7 @@ Type *Checker::ExtractDefinitelyFalsyTypes(Type *type) if (type->IsUnionType()) { auto &constituentTypes = type->AsUnionType()->ConstituentTypes(); - ArenaVector newConstituentTypes(allocator_->Adapter()); + ArenaVector newConstituentTypes(Allocator()->Adapter()); newConstituentTypes.reserve(constituentTypes.size()); for (auto &it : constituentTypes) { @@ -154,12 +155,12 @@ Type *Checker::ExtractDefinitelyFalsyTypes(Type *type) return GlobalNeverType(); } -Type *Checker::RemoveDefinitelyFalsyTypes(Type *type) +Type *TSChecker::RemoveDefinitelyFalsyTypes(Type *type) { if ((static_cast(GetFalsyFlags(type)) & static_cast(TypeFlag::DEFINITELY_FALSY)) != 0U) { if (type->IsUnionType()) { auto &constituentTypes = type->AsUnionType()->ConstituentTypes(); - ArenaVector newConstituentTypes(allocator_->Adapter()); + ArenaVector newConstituentTypes(Allocator()->Adapter()); for (auto &it : constituentTypes) { if ((static_cast(GetFalsyFlags(it)) & static_cast(TypeFlag::DEFINITELY_FALSY)) == @@ -185,7 +186,7 @@ Type *Checker::RemoveDefinitelyFalsyTypes(Type *type) return type; } -TypeFlag Checker::GetFalsyFlags(Type *type) +TypeFlag TSChecker::GetFalsyFlags(Type *type) { if (type->IsStringLiteralType()) { return type->AsStringLiteralType()->Value().Empty() ? TypeFlag::STRING_LITERAL : TypeFlag::NONE; @@ -217,14 +218,14 @@ TypeFlag Checker::GetFalsyFlags(Type *type) return static_cast(type->TypeFlags() & TypeFlag::POSSIBLY_FALSY); } -bool Checker::IsVariableUsedInConditionBody(const ir::AstNode *parent, binder::Variable *searchVar) +bool TSChecker::IsVariableUsedInConditionBody(ir::AstNode *parent, binder::Variable *searchVar) { bool found = false; - parent->Iterate([this, searchVar, &found](const ir::AstNode *childNode) -> void { + parent->Iterate([this, searchVar, &found](ir::AstNode *childNode) -> void { binder::Variable *resultVar = nullptr; if (childNode->IsIdentifier()) { - binder::ScopeFindResult result = scope_->Find(childNode->AsIdentifier()->Name()); + binder::ScopeFindResult result = Scope()->Find(childNode->AsIdentifier()->Name()); ASSERT(result.variable); resultVar = result.variable; } @@ -242,13 +243,13 @@ bool Checker::IsVariableUsedInConditionBody(const ir::AstNode *parent, binder::V return found; } -bool Checker::FindVariableInBinaryExpressionChain(const ir::AstNode *parent, binder::Variable *searchVar) +bool TSChecker::FindVariableInBinaryExpressionChain(ir::AstNode *parent, binder::Variable *searchVar) { bool found = false; - parent->Iterate([this, searchVar, &found](const ir::AstNode *childNode) -> void { + parent->Iterate([this, searchVar, &found](ir::AstNode *childNode) -> void { if (childNode->IsIdentifier()) { - binder::ScopeFindResult result = scope_->Find(childNode->AsIdentifier()->Name()); + binder::ScopeFindResult result = Scope()->Find(childNode->AsIdentifier()->Name()); ASSERT(result.variable); if (result.variable == searchVar) { found = true; @@ -262,7 +263,7 @@ bool Checker::FindVariableInBinaryExpressionChain(const ir::AstNode *parent, bin return found; } -bool Checker::IsVariableUsedInBinaryExpressionChain(const ir::AstNode *parent, binder::Variable *searchVar) +bool TSChecker::IsVariableUsedInBinaryExpressionChain(ir::AstNode *parent, binder::Variable *searchVar) { while (parent->IsBinaryExpression() && parent->AsBinaryExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) { @@ -276,7 +277,8 @@ bool Checker::IsVariableUsedInBinaryExpressionChain(const ir::AstNode *parent, b return false; } -void Checker::ThrowBinaryLikeError(lexer::TokenType op, Type *leftType, Type *rightType, lexer::SourcePosition lineInfo) +void TSChecker::ThrowBinaryLikeError(lexer::TokenType op, Type *leftType, Type *rightType, + lexer::SourcePosition lineInfo) { if (!HasStatus(CheckerStatus::IN_CONST_CONTEXT)) { ThrowTypeError({"operator ", op, " cannot be applied to types ", leftType, " and ", AsSrc(rightType)}, @@ -286,7 +288,7 @@ void Checker::ThrowBinaryLikeError(lexer::TokenType op, Type *leftType, Type *ri ThrowTypeError({"operator ", op, " cannot be applied to types ", leftType, " and ", rightType}, lineInfo); } -void Checker::ThrowAssignmentError(Type *source, Type *target, lexer::SourcePosition lineInfo, bool isAsSrcLeftType) +void TSChecker::ThrowAssignmentError(Type *source, Type *target, lexer::SourcePosition lineInfo, bool isAsSrcLeftType) { if (isAsSrcLeftType || !target->HasTypeFlag(TypeFlag::LITERAL)) { ThrowTypeError({"Type '", AsSrc(source), "' is not assignable to type '", target, "'."}, lineInfo); @@ -295,11 +297,11 @@ void Checker::ThrowAssignmentError(Type *source, Type *target, lexer::SourcePosi ThrowTypeError({"Type '", source, "' is not assignable to type '", target, "'."}, lineInfo); } -Type *Checker::GetUnaryResultType(Type *operandType) +Type *TSChecker::GetUnaryResultType(Type *operandType) { - if (checker::Checker::MaybeTypeOfKind(operandType, checker::TypeFlag::BIGINT_LIKE)) { + if (checker::TSChecker::MaybeTypeOfKind(operandType, checker::TypeFlag::BIGINT_LIKE)) { if (operandType->HasTypeFlag(checker::TypeFlag::UNION_OR_INTERSECTION) && - checker::Checker::MaybeTypeOfKind(operandType, checker::TypeFlag::NUMBER_LIKE)) { + checker::TSChecker::MaybeTypeOfKind(operandType, checker::TypeFlag::NUMBER_LIKE)) { return GlobalNumberOrBigintType(); } @@ -309,7 +311,7 @@ Type *Checker::GetUnaryResultType(Type *operandType) return GlobalNumberType(); } -void Checker::ElaborateElementwise(Type *targetType, const ir::Expression *sourceNode, const lexer::SourcePosition &pos) +void TSChecker::ElaborateElementwise(Type *targetType, ir::Expression *sourceNode, const lexer::SourcePosition &pos) { auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE | CheckerStatus::KEEP_LITERAL_TYPE); @@ -332,7 +334,7 @@ void Checker::ElaborateElementwise(Type *targetType, const ir::Expression *sourc ThrowAssignmentError(sourceType, targetType, pos); } -void Checker::InferSimpleVariableDeclaratorType(const ir::VariableDeclarator *declarator) +void TSChecker::InferSimpleVariableDeclaratorType(ir::VariableDeclarator *declarator) { ASSERT(declarator->Id()->IsIdentifier()); @@ -340,7 +342,7 @@ void Checker::InferSimpleVariableDeclaratorType(const ir::VariableDeclarator *de ASSERT(var); if (declarator->Id()->AsIdentifier()->TypeAnnotation() != nullptr) { - var->SetTsType(declarator->Id()->AsIdentifier()->TypeAnnotation()->AsTypeNode()->GetType(this)); + var->SetTsType(declarator->Id()->AsIdentifier()->TypeAnnotation()->GetType(this)); return; } @@ -353,20 +355,19 @@ void Checker::InferSimpleVariableDeclaratorType(const ir::VariableDeclarator *de declarator->Id()->Start()); } -Type *Checker::GetTypeOfVariable(binder::Variable *var) +Type *TSChecker::GetTypeOfVariable(binder::Variable *var) { if (var->TsType() != nullptr) { return var->TsType(); } - const binder::Decl *decl = var->Declaration(); + binder::Decl *decl = var->Declaration(); - if (!typeStack_.insert(decl->Node()).second) { - ThrowTypeError({"'", var->Name(), - "' is referenced directly or indirectly in its " - "own initializer ot type annotation."}, - decl->Node()->Start()); - } + TypeStackElement tse(this, decl->Node(), + {"'", var->Name(), + "' is referenced directly or indirectly in its " + "own initializer ot type annotation."}, + decl->Node()->Start()); switch (decl->Type()) { case binder::DeclType::CONST: @@ -380,7 +381,8 @@ Type *Checker::GetTypeOfVariable(binder::Variable *var) [[fallthrough]]; } case binder::DeclType::VAR: { - const ir::AstNode *declarator = FindAncestorGivenByType(decl->Node(), ir::AstNodeType::VARIABLE_DECLARATOR); + ir::AstNode *declarator = + util::Helpers::FindAncestorGivenByType(decl->Node(), ir::AstNodeType::VARIABLE_DECLARATOR); ASSERT(declarator); if (declarator->AsVariableDeclarator()->Id()->IsIdentifier()) { @@ -392,12 +394,12 @@ Type *Checker::GetTypeOfVariable(binder::Variable *var) break; } case binder::DeclType::PROPERTY: { - var->SetTsType(decl->Node()->AsTSPropertySignature()->TypeAnnotation()->AsTypeNode()->GetType(this)); + var->SetTsType(decl->Node()->AsTSPropertySignature()->TypeAnnotation()->GetType(this)); break; } case binder::DeclType::METHOD: { - auto *signatureInfo = allocator_->New(allocator_); - auto *callSignature = allocator_->New(signatureInfo, GlobalAnyType()); + auto *signatureInfo = Allocator()->New(Allocator()); + auto *callSignature = Allocator()->New(signatureInfo, GlobalAnyType()); var->SetTsType(CreateFunctionTypeWithSignature(callSignature)); break; } @@ -407,13 +409,13 @@ Type *Checker::GetTypeOfVariable(binder::Variable *var) break; } case binder::DeclType::PARAM: { - const ir::AstNode *declaration = FindAncestorUntilGivenType(decl->Node(), ir::AstNodeType::SCRIPT_FUNCTION); + ir::AstNode *declaration = FindAncestorUntilGivenType(decl->Node(), ir::AstNodeType::SCRIPT_FUNCTION); if (declaration->IsIdentifier()) { - const ir::Identifier *ident = declaration->AsIdentifier(); + auto *ident = declaration->AsIdentifier(); if (ident->TypeAnnotation() != nullptr) { ASSERT(ident->Variable() == var); - var->SetTsType(ident->TypeAnnotation()->AsTypeNode()->GetType(this)); + var->SetTsType(ident->TypeAnnotation()->GetType(this)); break; } @@ -421,11 +423,11 @@ Type *Checker::GetTypeOfVariable(binder::Variable *var) } if (declaration->IsAssignmentPattern() && declaration->AsAssignmentPattern()->Left()->IsIdentifier()) { - const ir::Identifier *ident = declaration->AsAssignmentPattern()->Left()->AsIdentifier(); + ir::Identifier *ident = declaration->AsAssignmentPattern()->Left()->AsIdentifier(); if (ident->TypeAnnotation() != nullptr) { ASSERT(ident->Variable() == var); - var->SetTsType(ident->TypeAnnotation()->AsTypeNode()->GetType(this)); + var->SetTsType(ident->TypeAnnotation()->GetType(this)); break; } @@ -458,18 +460,16 @@ Type *Checker::GetTypeOfVariable(binder::Variable *var) } } - typeStack_.erase(decl->Node()); return var->TsType(); } -Type *Checker::GetTypeFromClassOrInterfaceReference([[maybe_unused]] const ir::TSTypeReference *node, - binder::Variable *var) +Type *TSChecker::GetTypeFromClassOrInterfaceReference([[maybe_unused]] ir::TSTypeReference *node, binder::Variable *var) { Type *resolvedType = var->TsType(); if (resolvedType == nullptr) { - ObjectDescriptor *desc = allocator_->New(allocator_); - resolvedType = allocator_->New(allocator_, var->Name(), desc); + ObjectDescriptor *desc = Allocator()->New(Allocator()); + resolvedType = Allocator()->New(Allocator(), var->Name(), desc); resolvedType->SetVariable(var); var->SetTsType(resolvedType); } @@ -477,27 +477,25 @@ Type *Checker::GetTypeFromClassOrInterfaceReference([[maybe_unused]] const ir::T return resolvedType; } -Type *Checker::GetTypeFromTypeAliasReference(const ir::TSTypeReference *node, binder::Variable *var) +Type *TSChecker::GetTypeFromTypeAliasReference(ir::TSTypeReference *node, binder::Variable *var) { Type *resolvedType = var->TsType(); - if (resolvedType == nullptr) { - if (!typeStack_.insert(var).second) { - ThrowTypeError({"Type alias ", var->Name(), " circularly refences itself"}, node->Start()); - } + if (resolvedType != nullptr) { + return resolvedType; + } - ASSERT(var->Declaration()->Node() && var->Declaration()->Node()->IsTSTypeAliasDeclaration()); - const ir::TSTypeAliasDeclaration *declaration = var->Declaration()->Node()->AsTSTypeAliasDeclaration(); - resolvedType = declaration->TypeAnnotation()->AsTypeNode()->GetType(this); - var->SetTsType(resolvedType); + TypeStackElement tse(this, var, {"Type alias ", var->Name(), " circularly refences itself"}, node->Start()); - typeStack_.erase(var); - } + ASSERT(var->Declaration()->Node() && var->Declaration()->Node()->IsTSTypeAliasDeclaration()); + ir::TSTypeAliasDeclaration *declaration = var->Declaration()->Node()->AsTSTypeAliasDeclaration(); + resolvedType = declaration->TypeAnnotation()->GetType(this); + var->SetTsType(resolvedType); return resolvedType; } -Type *Checker::GetTypeReferenceType(const ir::TSTypeReference *node, binder::Variable *var) +Type *TSChecker::GetTypeReferenceType(ir::TSTypeReference *node, binder::Variable *var) { ASSERT(var->Declaration()); binder::Decl *decl = var->Declaration(); diff --git a/typescript/core/object.cpp b/checker/ts/object.cpp similarity index 78% rename from typescript/core/object.cpp rename to checker/ts/object.cpp index d25b48d4ff3a4249e75643ec14c09de323a76bc3..0c218f1ef2639827cabc595546ccdeaac06b5c79 100644 --- a/typescript/core/object.cpp +++ b/checker/ts/object.cpp @@ -22,10 +22,10 @@ #include "plugins/ecmascript/es2panda/ir/base/property.h" #include "plugins/ecmascript/es2panda/ir/base/scriptFunction.h" #include "plugins/ecmascript/es2panda/ir/base/spreadElement.h" -#include "plugins/ecmascript/es2panda/ir/ts/tsIndexSignature.h" -#include "plugins/ecmascript/es2panda/ir/ts/tsMethodSignature.h" -#include "plugins/ecmascript/es2panda/ir/ts/tsPropertySignature.h" -#include "plugins/ecmascript/es2panda/ir/ts/tsSignatureDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/base/tsIndexSignature.h" +#include "plugins/ecmascript/es2panda/ir/base/tsMethodSignature.h" +#include "plugins/ecmascript/es2panda/ir/base/tsPropertySignature.h" +#include "plugins/ecmascript/es2panda/ir/base/tsSignatureDeclaration.h" #include "plugins/ecmascript/es2panda/ir/ts/tsTypeLiteral.h" #include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceDeclaration.h" #include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceHeritage.h" @@ -34,11 +34,11 @@ #include "plugins/ecmascript/es2panda/binder/variable.h" #include "plugins/ecmascript/es2panda/binder/scope.h" -#include "plugins/ecmascript/es2panda/typescript/checker.h" -#include "plugins/ecmascript/es2panda/typescript/types/indexInfo.h" +#include "plugins/ecmascript/es2panda/checker/TSchecker.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/indexInfo.h" namespace panda::es2panda::checker { -void Checker::CheckIndexConstraints(Type *type) +void TSChecker::CheckIndexConstraints(Type *type) { if (!type->IsObjectType()) { return; @@ -80,7 +80,7 @@ void Checker::CheckIndexConstraints(Type *type) } } -void Checker::ResolveStructuredTypeMembers(Type *type) +void TSChecker::ResolveStructuredTypeMembers(Type *type) { if (type->IsObjectType()) { ObjectType *obj_type = type->AsObjectType(); @@ -102,17 +102,17 @@ void Checker::ResolveStructuredTypeMembers(Type *type) } } -void Checker::ResolveUnionTypeMembers(UnionType *type) +void TSChecker::ResolveUnionTypeMembers(UnionType *type) { if (type->MergedObjectType() != nullptr) { return; } - ObjectDescriptor *desc = allocator_->New(allocator_); - ArenaVector stringInfoTypes(allocator_->Adapter()); - ArenaVector numberInfoTypes(allocator_->Adapter()); - ArenaVector callSignatures(allocator_->Adapter()); - ArenaVector constructSignatures(allocator_->Adapter()); + ObjectDescriptor *desc = Allocator()->New(Allocator()); + ArenaVector stringInfoTypes(Allocator()->Adapter()); + ArenaVector numberInfoTypes(Allocator()->Adapter()); + ArenaVector callSignatures(Allocator()->Adapter()); + ArenaVector constructSignatures(Allocator()->Adapter()); for (auto *it : type->AsUnionType()->ConstituentTypes()) { if (!it->IsObjectType()) { @@ -147,19 +147,19 @@ void Checker::ResolveUnionTypeMembers(UnionType *type) desc->constructSignatures = constructSignatures; if (!stringInfoTypes.empty()) { - desc->stringIndexInfo = allocator_->New(CreateUnionType(std::move(stringInfoTypes)), "x", false); + desc->stringIndexInfo = Allocator()->New(CreateUnionType(std::move(stringInfoTypes)), "x", false); } if (!numberInfoTypes.empty()) { - desc->numberIndexInfo = allocator_->New(CreateUnionType(std::move(numberInfoTypes)), "x", false); + desc->numberIndexInfo = Allocator()->New(CreateUnionType(std::move(numberInfoTypes)), "x", false); } - ObjectType *merged_type = allocator_->New(desc); + ObjectType *merged_type = Allocator()->New(desc); merged_type->AddObjectFlag(ObjectFlags::RESOLVED_MEMBERS); type->SetMergedObjectType(merged_type); } -void Checker::ResolveInterfaceOrClassTypeMembers(InterfaceType *type) +void TSChecker::ResolveInterfaceOrClassTypeMembers(InterfaceType *type) { if (type->HasObjectFlag(ObjectFlags::RESOLVED_MEMBERS)) { return; @@ -171,16 +171,16 @@ void Checker::ResolveInterfaceOrClassTypeMembers(InterfaceType *type) type->AddObjectFlag(ObjectFlags::RESOLVED_MEMBERS); } -void Checker::ResolveObjectTypeMembers(ObjectType *type) +void TSChecker::ResolveObjectTypeMembers(ObjectType *type) { if (!type->IsObjectLiteralType() || type->HasObjectFlag(ObjectFlags::RESOLVED_MEMBERS)) { return; } ASSERT(type->Variable() && type->Variable()->Declaration()->Node()->IsTSTypeLiteral()); - const auto *typeLiteral = type->Variable()->Declaration()->Node()->AsTSTypeLiteral(); - ArenaVector signatureDeclarations(allocator_->Adapter()); - ArenaVector indexDeclarations(allocator_->Adapter()); + auto *typeLiteral = type->Variable()->Declaration()->Node()->AsTSTypeLiteral(); + ArenaVector signatureDeclarations(Allocator()->Adapter()); + ArenaVector indexDeclarations(Allocator()->Adapter()); for (auto *it : typeLiteral->Members()) { ResolvePropertiesOfObjectType(type, it, signatureDeclarations, indexDeclarations, false); @@ -192,10 +192,9 @@ void Checker::ResolveObjectTypeMembers(ObjectType *type) ResolveIndexInfosOfObjectType(type, indexDeclarations); } -void Checker::ResolvePropertiesOfObjectType(ObjectType *type, const ir::Expression *member, - ArenaVector &signatureDeclarations, - ArenaVector &indexDeclarations, - bool isInterface) +void TSChecker::ResolvePropertiesOfObjectType(ObjectType *type, ir::AstNode *member, + ArenaVector &signatureDeclarations, + ArenaVector &indexDeclarations, bool isInterface) { if (member->IsTSPropertySignature()) { binder::Variable *prop = member->AsTSPropertySignature()->Variable(); @@ -228,8 +227,8 @@ void Checker::ResolvePropertiesOfObjectType(ObjectType *type, const ir::Expressi indexDeclarations.push_back(member->AsTSIndexSignature()); } -void Checker::ResolveSignaturesOfObjectType(ObjectType *type, - ArenaVector &signatureDeclarations) +void TSChecker::ResolveSignaturesOfObjectType(ObjectType *type, + ArenaVector &signatureDeclarations) { for (auto *it : signatureDeclarations) { Type *placeholderObj = it->Check(this); @@ -243,8 +242,7 @@ void Checker::ResolveSignaturesOfObjectType(ObjectType *type, type->AddConstructSignature(placeholderObj->AsObjectType()->ConstructSignatures()[0]); } } -void Checker::ResolveIndexInfosOfObjectType(ObjectType *type, - ArenaVector &indexDeclarations) +void TSChecker::ResolveIndexInfosOfObjectType(ObjectType *type, ArenaVector &indexDeclarations) { for (auto *it : indexDeclarations) { Type *placeholderObj = it->Check(this); @@ -270,8 +268,8 @@ void Checker::ResolveIndexInfosOfObjectType(ObjectType *type, } } -binder::Variable *Checker::GetPropertyOfType(Type *type, const util::StringView &name, bool getPartial, - binder::VariableFlags propagateFlags) +binder::Variable *TSChecker::GetPropertyOfType(Type *type, const util::StringView &name, bool getPartial, + binder::VariableFlags propagateFlags) { if (type->IsObjectType()) { ResolveObjectTypeMembers(type->AsObjectType()); @@ -285,8 +283,8 @@ binder::Variable *Checker::GetPropertyOfType(Type *type, const util::StringView return nullptr; } -binder::Variable *Checker::GetPropertyOfUnionType(UnionType *type, const util::StringView &name, bool getPartial, - binder::VariableFlags propagateFlags) +binder::Variable *TSChecker::GetPropertyOfUnionType(UnionType *type, const util::StringView &name, bool getPartial, + binder::VariableFlags propagateFlags) { auto found = type->CachedSyntheticPropertis().find(name); @@ -295,7 +293,7 @@ binder::Variable *Checker::GetPropertyOfUnionType(UnionType *type, const util::S } binder::VariableFlags flags = binder::VariableFlags::PROPERTY; - ArenaVector collectedTypes(allocator_->Adapter()); + ArenaVector collectedTypes(Allocator()->Adapter()); for (auto *it : type->ConstituentTypes()) { binder::Variable *prop = GetPropertyOfType(it, name); @@ -341,17 +339,16 @@ binder::Variable *Checker::GetPropertyOfUnionType(UnionType *type, const util::S return nullptr; } - binder::Variable *syntheticProp = binder::Scope::CreateVar(allocator_, name, flags, nullptr); + binder::Variable *syntheticProp = binder::Scope::CreateVar(Allocator(), name, flags, nullptr); syntheticProp->SetTsType(CreateUnionType(std::move(collectedTypes))); type->CachedSyntheticPropertis().insert({name, syntheticProp}); return syntheticProp; } -Type *Checker::CheckComputedPropertyName(const ir::Expression *key) +Type *TSChecker::CheckComputedPropertyName(ir::Expression *key) { - auto found = nodeCache_.find(key); - if (found != nodeCache_.end()) { - return found->second; + if (key->TsType() != nullptr) { + return key->TsType(); } Type *keyType = key->Check(this); @@ -364,11 +361,11 @@ Type *Checker::CheckComputedPropertyName(const ir::Expression *key) key->Start()); } - nodeCache_.insert({key, keyType}); + key->SetTsType(keyType); return keyType; } -IndexInfo *Checker::GetApplicableIndexInfo(Type *type, Type *indexType) +IndexInfo *TSChecker::GetApplicableIndexInfo(Type *type, Type *indexType) { ResolveStructuredTypeMembers(type); bool getNumberInfo = indexType->HasTypeFlag(TypeFlag::NUMBER_LIKE); @@ -394,7 +391,7 @@ IndexInfo *Checker::GetApplicableIndexInfo(Type *type, Type *indexType) return nullptr; } -Type *Checker::GetPropertyTypeForIndexType(Type *type, Type *indexType) +Type *TSChecker::GetPropertyTypeForIndexType(Type *type, Type *indexType) { if (type->IsArrayType()) { return type->AsArrayType()->ElementType(); @@ -407,7 +404,7 @@ Type *Checker::GetPropertyTypeForIndexType(Type *type, Type *indexType) prop = GetPropertyOfType(type, indexType->AsStringLiteralType()->Value()); } else { util::StringView prop_name = - util::Helpers::ToStringView(allocator_, indexType->AsNumberLiteralType()->Value()); + util::Helpers::ToStringView(Allocator(), indexType->AsNumberLiteralType()->Value()); prop = GetPropertyOfType(type, prop_name); } @@ -439,7 +436,7 @@ Type *Checker::GetPropertyTypeForIndexType(Type *type, Type *indexType) return nullptr; } -ArenaVector Checker::GetBaseTypes(InterfaceType *type) +ArenaVector TSChecker::GetBaseTypes(InterfaceType *type) { if (type->HasObjectFlag(ObjectFlags::RESOLVED_BASE_TYPES)) { return type->Bases(); @@ -448,18 +445,16 @@ ArenaVector Checker::GetBaseTypes(InterfaceType *type) ASSERT(type->Variable() && type->Variable()->Declaration()->IsInterfaceDecl()); binder::InterfaceDecl *decl = type->Variable()->Declaration()->AsInterfaceDecl(); - if (!typeStack_.insert(type).second) { - ThrowTypeError({"Type ", type->Name(), " recursively references itself as a base type."}, - decl->Node()->AsTSInterfaceDeclaration()->Id()->Start()); - } + TypeStackElement tse(this, type, {"Type ", type->Name(), " recursively references itself as a base type."}, + decl->Node()->AsTSInterfaceDeclaration()->Id()->Start()); for (const auto *declaration : decl->Decls()) { if (declaration->Extends().empty()) { continue; } - for (const auto *extends : declaration->Extends()) { - Type *baseType = extends->Expr()->AsTypeNode()->GetType(this); + for (auto *extends : declaration->Extends()) { + Type *baseType = extends->Expr()->GetType(this); if (!baseType->HasTypeFlag(TypeFlag::OBJECT | TypeFlag::NON_PRIMITIVE | TypeFlag::ANY)) { ThrowTypeError( @@ -497,11 +492,10 @@ ArenaVector Checker::GetBaseTypes(InterfaceType *type) } type->AddObjectFlag(ObjectFlags::RESOLVED_BASE_TYPES); - typeStack_.erase(type); return type->Bases(); } -void Checker::ResolveDeclaredMembers(InterfaceType *type) +void TSChecker::ResolveDeclaredMembers(InterfaceType *type) { if (type->HasObjectFlag(ObjectFlags::RESOLVED_DECLARED_MEMBERS)) { return; @@ -510,11 +504,11 @@ void Checker::ResolveDeclaredMembers(InterfaceType *type) ASSERT(type->Variable() && type->Variable()->Declaration()->IsInterfaceDecl()); binder::InterfaceDecl *decl = type->Variable()->Declaration()->AsInterfaceDecl(); - ArenaVector signatureDeclarations(allocator_->Adapter()); - ArenaVector indexDeclarations(allocator_->Adapter()); + ArenaVector signatureDeclarations(Allocator()->Adapter()); + ArenaVector indexDeclarations(Allocator()->Adapter()); for (const auto *declaration : decl->Decls()) { - for (const auto *member : declaration->Body()->Body()) { + for (auto *member : declaration->Body()->Body()) { ResolvePropertiesOfObjectType(type, member, signatureDeclarations, indexDeclarations, true); } @@ -525,8 +519,8 @@ void Checker::ResolveDeclaredMembers(InterfaceType *type) } } -bool Checker::ValidateInterfaceMemberRedeclaration(ObjectType *type, binder::Variable *prop, - const lexer::SourcePosition &locInfo) +bool TSChecker::ValidateInterfaceMemberRedeclaration(ObjectType *type, binder::Variable *prop, + const lexer::SourcePosition &locInfo) { if (prop->HasFlag(binder::VariableFlags::COMPUTED)) { return true; diff --git a/typescript/core/typeCreation.cpp b/checker/ts/typeCreation.cpp similarity index 45% rename from typescript/core/typeCreation.cpp rename to checker/ts/typeCreation.cpp index dde86e22d41daa1e8a5b19dbca0fc5351daa8875..edd6fed5dee9b46993f47afd1583a4058248f8bf 100644 --- a/typescript/core/typeCreation.cpp +++ b/checker/ts/typeCreation.cpp @@ -13,49 +13,49 @@ * limitations under the License. */ -#include "plugins/ecmascript/es2panda/typescript/checker.h" -#include "plugins/ecmascript/es2panda/typescript/types/indexInfo.h" +#include "plugins/ecmascript/es2panda/checker/TSchecker.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/indexInfo.h" namespace panda::es2panda::checker { -Type *Checker::CreateNumberLiteralType(double value) +Type *TSChecker::CreateNumberLiteralType(double value) { auto search = numberLiteralMap_.find(value); if (search != numberLiteralMap_.end()) { return search->second; } - auto *newNumLiteralType = allocator_->New(value); + auto *newNumLiteralType = Allocator()->New(value); numberLiteralMap_.insert({value, newNumLiteralType}); return newNumLiteralType; } -Type *Checker::CreateBigintLiteralType(const util::StringView &str, bool negative) +Type *TSChecker::CreateBigintLiteralType(const util::StringView &str, bool negative) { auto search = bigintLiteralMap_.find(str); if (search != bigintLiteralMap_.end()) { return search->second; } - auto *newBigiLiteralType = allocator_->New(str, negative); + auto *newBigiLiteralType = Allocator()->New(str, negative); bigintLiteralMap_.insert({str, newBigiLiteralType}); return newBigiLiteralType; } -Type *Checker::CreateStringLiteralType(const util::StringView &str) +Type *TSChecker::CreateStringLiteralType(const util::StringView &str) { auto search = stringLiteralMap_.find(str); if (search != stringLiteralMap_.end()) { return search->second; } - auto *newStrLiteralType = allocator_->New(str); + auto *newStrLiteralType = Allocator()->New(str); stringLiteralMap_.insert({str, newStrLiteralType}); return newStrLiteralType; } -Type *Checker::CreateUnionType(std::initializer_list constituentTypes) +Type *TSChecker::CreateUnionType(std::initializer_list constituentTypes) { - ArenaVector newConstituentTypes(allocator_->Adapter()); + ArenaVector newConstituentTypes(Allocator()->Adapter()); for (auto *it : constituentTypes) { newConstituentTypes.push_back(it); @@ -64,9 +64,9 @@ Type *Checker::CreateUnionType(std::initializer_list constituentTypes) return CreateUnionType(std::move(newConstituentTypes)); } -Type *Checker::CreateUnionType(ArenaVector &constituentTypes) +Type *TSChecker::CreateUnionType(ArenaVector &constituentTypes) { - ArenaVector newConstituentTypes(allocator_->Adapter()); + ArenaVector newConstituentTypes(Allocator()->Adapter()); for (auto *it : constituentTypes) { if (it->IsUnionType()) { @@ -80,24 +80,24 @@ Type *Checker::CreateUnionType(ArenaVector &constituentTypes) newConstituentTypes.push_back(it); } - UnionType::RemoveDuplicatedTypes(relation_, newConstituentTypes); + UnionType::RemoveDuplicatedTypes(Relation(), newConstituentTypes); if (newConstituentTypes.size() == 1) { return newConstituentTypes[0]; } - auto *newUnionType = allocator_->New(newConstituentTypes); + auto *newUnionType = Allocator()->New(newConstituentTypes); - return UnionType::HandleUnionType(newUnionType, globalTypes_); + return UnionType::HandleUnionType(newUnionType, GetGlobalTypesHolder()); } -Type *Checker::CreateUnionType(ArenaVector &&constituentTypes) +Type *TSChecker::CreateUnionType(ArenaVector &&constituentTypes) { if (constituentTypes.empty()) { return nullptr; } - ArenaVector newConstituentTypes(allocator_->Adapter()); + ArenaVector newConstituentTypes(Allocator()->Adapter()); for (auto *it : constituentTypes) { if (it->IsUnionType()) { @@ -111,63 +111,63 @@ Type *Checker::CreateUnionType(ArenaVector &&constituentTypes) newConstituentTypes.push_back(it); } - UnionType::RemoveDuplicatedTypes(relation_, newConstituentTypes); + UnionType::RemoveDuplicatedTypes(Relation(), newConstituentTypes); if (newConstituentTypes.size() == 1) { return newConstituentTypes[0]; } - auto *newUnionType = allocator_->New(std::move(newConstituentTypes)); + auto *newUnionType = Allocator()->New(std::move(newConstituentTypes)); - return UnionType::HandleUnionType(newUnionType, globalTypes_); + return UnionType::HandleUnionType(newUnionType, GetGlobalTypesHolder()); } -Type *Checker::CreateObjectTypeWithCallSignature(Signature *callSignature) +Type *TSChecker::CreateObjectTypeWithCallSignature(Signature *callSignature) { - auto *objType = allocator_->New(allocator_->New(allocator_)); + auto *objType = Allocator()->New(Allocator()->New(Allocator())); objType->AddCallSignature(callSignature); return objType; } -Type *Checker::CreateObjectTypeWithConstructSignature(Signature *constructSignature) +Type *TSChecker::CreateObjectTypeWithConstructSignature(Signature *constructSignature) { - auto *objType = allocator_->New(allocator_->New(allocator_)); + auto *objType = Allocator()->New(Allocator()->New(Allocator())); objType->AddConstructSignature(constructSignature); return objType; } -Type *Checker::CreateFunctionTypeWithSignature(Signature *callSignature) +Type *TSChecker::CreateFunctionTypeWithSignature(Signature *callSignature) { - auto *funcObjType = allocator_->New(allocator_->New(allocator_)); + auto *funcObjType = Allocator()->New(Allocator()->New(Allocator())); funcObjType->AddCallSignature(callSignature); return funcObjType; } -Type *Checker::CreateConstructorTypeWithSignature(Signature *constructSignature) +Type *TSChecker::CreateConstructorTypeWithSignature(Signature *constructSignature) { - auto *constructObjType = allocator_->New(allocator_->New(allocator_)); + auto *constructObjType = Allocator()->New(Allocator()->New(Allocator())); constructObjType->AddConstructSignature(constructSignature); return constructObjType; } -Type *Checker::CreateTupleType(ObjectDescriptor *desc, ArenaVector &&elementFlags, - ElementFlags combinedFlags, uint32_t minLength, uint32_t fixedLength, bool readonly) +Type *TSChecker::CreateTupleType(ObjectDescriptor *desc, ArenaVector &&elementFlags, + ElementFlags combinedFlags, uint32_t minLength, uint32_t fixedLength, bool readonly) { - desc->stringIndexInfo = allocator_->New(GlobalAnyType(), "x", readonly); - return allocator_->New(desc, std::move(elementFlags), combinedFlags, minLength, fixedLength, readonly); + desc->stringIndexInfo = Allocator()->New(GlobalAnyType(), "x", readonly); + return Allocator()->New(desc, std::move(elementFlags), combinedFlags, minLength, fixedLength, readonly); } -Type *Checker::CreateTupleType(ObjectDescriptor *desc, ArenaVector &&elementFlags, - ElementFlags combinedFlags, uint32_t minLength, uint32_t fixedLength, bool readonly, - NamedTupleMemberPool &&namedMembers) +Type *TSChecker::CreateTupleType(ObjectDescriptor *desc, ArenaVector &&elementFlags, + ElementFlags combinedFlags, uint32_t minLength, uint32_t fixedLength, bool readonly, + NamedTupleMemberPool &&namedMembers) { - desc->stringIndexInfo = allocator_->New(GlobalAnyType(), "x", readonly); + desc->stringIndexInfo = Allocator()->New(GlobalAnyType(), "x", readonly); if (!namedMembers.empty()) { - return allocator_->New(desc, std::move(elementFlags), combinedFlags, minLength, fixedLength, - readonly, std::move(namedMembers)); + return Allocator()->New(desc, std::move(elementFlags), combinedFlags, minLength, fixedLength, + readonly, std::move(namedMembers)); } - return allocator_->New(desc, std::move(elementFlags), combinedFlags, minLength, fixedLength, readonly); + return Allocator()->New(desc, std::move(elementFlags), combinedFlags, minLength, fixedLength, readonly); } } // namespace panda::es2panda::checker diff --git a/typescript/core/typeElaborationContext.cpp b/checker/ts/typeElaborationContext.cpp similarity index 94% rename from typescript/core/typeElaborationContext.cpp rename to checker/ts/typeElaborationContext.cpp index cc448257a059cd3492325c718aa7c4f46f183963..ee616ad678dea141af337c97981f938f3ff819b9 100644 --- a/typescript/core/typeElaborationContext.cpp +++ b/checker/ts/typeElaborationContext.cpp @@ -26,7 +26,7 @@ #include "plugins/ecmascript/es2panda/ir/base/property.h" namespace panda::es2panda::checker { -Type *ElaborationContext::GetBestMatchingType(Type *indexType, const ir::Expression *sourceNode) +Type *ElaborationContext::GetBestMatchingType(Type *indexType, ir::Expression *sourceNode) { ArenaVector bestMatchingType(checker_->Allocator()->Adapter()); Type *sourceType = sourceNode != nullptr ? checker_->CheckTypeCached(sourceNode) : checker_->GlobalAnyType(); @@ -105,7 +105,7 @@ void ObjectElaborationContext::Start() continue; } - const ir::Property *prop = it->AsProperty(); + ir::Property *prop = it->AsProperty(); Type *propKeyType = nullptr; if (prop->IsComputed()) { @@ -117,8 +117,8 @@ void ObjectElaborationContext::Start() break; } case ir::AstNodeType::NUMBER_LITERAL: { - propKeyType = - checker_->Allocator()->New(prop->Key()->AsNumberLiteral()->Number()); + propKeyType = checker_->Allocator()->New( + prop->Key()->AsNumberLiteral()->Number().GetDouble()); break; } case ir::AstNodeType::STRING_LITERAL: { diff --git a/typescript/core/typeElaborationContext.h b/checker/ts/typeElaborationContext.h similarity index 76% rename from typescript/core/typeElaborationContext.h rename to checker/ts/typeElaborationContext.h index 4dc247ee4682de97ae940da767b2507fc32e612d..df8925df2e82842ca59c1c050a8f491fb2c9ba7d 100644 --- a/typescript/core/typeElaborationContext.h +++ b/checker/ts/typeElaborationContext.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_TYPESCIRPT_CORE_TYPE_ELABORATION_CONTEXT_H -#define ES2PANDA_TYPESCIRPT_CORE_TYPE_ELABORATION_CONTEXT_H +#ifndef ES2PANDA_CHECKER_TS_TYPE_ELABORATION_CONTEXT_H +#define ES2PANDA_CHECKER_TS_TYPE_ELABORATION_CONTEXT_H -#include "plugins/ecmascript/es2panda/typescript/checker.h" +#include "plugins/ecmascript/es2panda/checker/TSchecker.h" #include "plugins/ecmascript/es2panda/ir/expression.h" #include @@ -31,7 +31,7 @@ class Type; class ElaborationContext { public: - ElaborationContext(Checker *checker, Type *targetType, Type *sourceType, const ir::Expression *sourceNode, + ElaborationContext(TSChecker *checker, Type *targetType, Type *sourceType, ir::Expression *sourceNode, const lexer::SourcePosition &startPos) : checker_(checker), targetType_(targetType), @@ -45,14 +45,14 @@ public: virtual void Start() = 0; virtual void RemoveUnnecessaryTypes() = 0; - Type *GetBestMatchingType(Type *indexType, const ir::Expression *sourceNode); + Type *GetBestMatchingType(Type *indexType, ir::Expression *sourceNode); protected: // NOLINTBEGIN(misc-non-private-member-variables-in-classes) - Checker *checker_; + TSChecker *checker_; Type *targetType_; Type *sourceType_; - const ir::Expression *sourceNode_; + ir::Expression *sourceNode_; const lexer::SourcePosition startPos_; ArenaVector potentialTypes_; // NOLINTEND(misc-non-private-member-variables-in-classes) @@ -60,7 +60,7 @@ protected: class ArrayElaborationContext : public ElaborationContext { public: - ArrayElaborationContext(Checker *checker, Type *targetType, Type *sourceType, const ir::Expression *sourceNode, + ArrayElaborationContext(TSChecker *checker, Type *targetType, Type *sourceType, ir::Expression *sourceNode, const lexer::SourcePosition &startPos) : ElaborationContext(checker, targetType, sourceType, sourceNode, startPos) { @@ -75,7 +75,7 @@ private: class ObjectElaborationContext : public ElaborationContext { public: - ObjectElaborationContext(Checker *checker, Type *targetType, Type *sourceType, const ir::Expression *sourceNode, + ObjectElaborationContext(TSChecker *checker, Type *targetType, Type *sourceType, ir::Expression *sourceNode, const lexer::SourcePosition &startPos) : ElaborationContext(checker, targetType, sourceType, sourceNode, startPos) { diff --git a/typescript/core/util.cpp b/checker/ts/util.cpp similarity index 76% rename from typescript/core/util.cpp rename to checker/ts/util.cpp index 779729f20887543e94a61d72e8187a8559b6a32a..0d6f24ba634f8cfa1a9318d05dc30d13f996e037 100644 --- a/typescript/core/util.cpp +++ b/checker/ts/util.cpp @@ -19,23 +19,12 @@ #include "plugins/ecmascript/es2panda/ir/expressions/templateLiteral.h" #include "plugins/ecmascript/es2panda/ir/ts/tsQualifiedName.h" -#include "plugins/ecmascript/es2panda/typescript/checker.h" +#include "plugins/ecmascript/es2panda/checker/TSchecker.h" namespace panda::es2panda::checker { -const ir::TSQualifiedName *Checker::ResolveLeftMostQualifiedName(const ir::TSQualifiedName *qualifiedName) +ir::MemberExpression *TSChecker::ResolveLeftMostMemberExpression(ir::MemberExpression *expr) { - const ir::TSQualifiedName *iter = qualifiedName; - - while (iter->Left()->IsTSQualifiedName()) { - iter = iter->Left()->AsTSQualifiedName(); - } - - return iter; -} - -const ir::MemberExpression *Checker::ResolveLeftMostMemberExpression(const ir::MemberExpression *expr) -{ - const ir::MemberExpression *iter = expr; + ir::MemberExpression *iter = expr; while (iter->Object()->IsMemberExpression()) { iter = iter->Object()->AsMemberExpression(); @@ -44,9 +33,9 @@ const ir::MemberExpression *Checker::ResolveLeftMostMemberExpression(const ir::M return iter; } -bool Checker::InAssignment(const ir::AstNode *node) +bool TSChecker::InAssignment(ir::AstNode *node) { - const ir::AstNode *parent = node; + ir::AstNode *parent = node; while (parent->Parent() != nullptr) { if (parent->Parent()->IsAssignmentExpression()) { @@ -54,7 +43,7 @@ bool Checker::InAssignment(const ir::AstNode *node) } if (parent->Parent()->IsBinaryExpression()) { - const ir::BinaryExpression *binaryExpr = parent->Parent()->AsBinaryExpression(); + ir::BinaryExpression *binaryExpr = parent->Parent()->AsBinaryExpression(); return IsAssignmentOperator(binaryExpr->OperatorType()) && binaryExpr->Left() == parent; } @@ -67,7 +56,7 @@ bool Checker::InAssignment(const ir::AstNode *node) return false; } -bool Checker::IsAssignmentOperator(lexer::TokenType op) +bool TSChecker::IsAssignmentOperator(lexer::TokenType op) { switch (op) { case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: @@ -91,7 +80,7 @@ bool Checker::IsAssignmentOperator(lexer::TokenType op) } } -bool Checker::IsLiteralType(const Type *type) +bool TSChecker::IsLiteralType(const Type *type) { if (type->IsBooleanType()) { return true; @@ -109,23 +98,7 @@ bool Checker::IsLiteralType(const Type *type) return type->HasTypeFlag(TypeFlag::UNIT); } -const ir::AstNode *Checker::FindAncestorGivenByType(const ir::AstNode *node, ir::AstNodeType type) -{ - node = node->Parent(); - - while (node->Type() != type) { - if (node->Parent() != nullptr) { - node = node->Parent(); - continue; - } - - return nullptr; - } - - return node; -} - -const ir::AstNode *Checker::FindAncestorUntilGivenType(const ir::AstNode *node, ir::AstNodeType stop) +ir::AstNode *TSChecker::FindAncestorUntilGivenType(ir::AstNode *node, ir::AstNodeType stop) { while (node->Parent()->Type() != stop) { if (node->Parent() != nullptr) { @@ -139,7 +112,7 @@ const ir::AstNode *Checker::FindAncestorUntilGivenType(const ir::AstNode *node, return node; } -bool Checker::MaybeTypeOfKind(const Type *type, TypeFlag flags) +bool TSChecker::MaybeTypeOfKind(const Type *type, TypeFlag flags) { if (type->HasTypeFlag(flags)) { return true; @@ -159,7 +132,7 @@ bool Checker::MaybeTypeOfKind(const Type *type, TypeFlag flags) return false; } -bool Checker::MaybeTypeOfKind(const Type *type, ObjectType::ObjectTypeKind kind) +bool TSChecker::MaybeTypeOfKind(const Type *type, ObjectType::ObjectTypeKind kind) { if (type->IsObjectType() && type->AsObjectType()->Kind() == kind) { return true; @@ -179,7 +152,7 @@ bool Checker::MaybeTypeOfKind(const Type *type, ObjectType::ObjectTypeKind kind) return false; } -bool Checker::IsConstantMemberAccess(const ir::Expression *expr) +bool TSChecker::IsConstantMemberAccess(ir::Expression *expr) { switch (expr->Type()) { case ir::AstNodeType::IDENTIFIER: { @@ -196,7 +169,7 @@ bool Checker::IsConstantMemberAccess(const ir::Expression *expr) } } -bool Checker::IsStringLike(const ir::Expression *expr) +bool TSChecker::IsStringLike(ir::Expression *expr) { if (expr->IsStringLiteral()) { return true; diff --git a/checker/types/ets/byteType.cpp b/checker/types/ets/byteType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5d26660155ed07fa4a3aadcb2162b1f268859b48 --- /dev/null +++ b/checker/types/ets/byteType.cpp @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2021-2022 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 "byteType.h" +#include "plugins/ecmascript/es2panda/checker/ets/boxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/unboxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/narrowingWideningConverter.h" + +namespace panda::es2panda::checker { +void ByteType::Identical(TypeRelation *relation, Type *other) +{ + if (other->IsByteType()) { + relation->Result(true); + } +} + +void ByteType::AssignmentTarget(TypeRelation *relation, [[maybe_unused]] Type *source) +{ + if (relation->ApplyUnboxing()) { + UnboxingConverter(relation->GetChecker()->AsETSChecker(), relation, source, this); + } + NarrowingConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); +} + +bool ByteType::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) +{ + if (relation->InAssignmentContext()) { + checker::SavedTypeRelationFlagsContext savedTypeRelationFlagCtx(relation, + checker::TypeRelationFlag::ONLY_CHECK_WIDENING); + auto unboxedType = relation->GetChecker()->AsETSChecker()->ETSBuiltinTypeAsPrimitiveType(target); + if (unboxedType != nullptr) { + NarrowingWideningConverter(relation->GetChecker()->AsETSChecker(), relation, unboxedType, this); + } + } + + if (relation->ApplyBoxing()) { + BoxingConverter(relation->GetChecker()->AsETSChecker(), relation, this, target); + } + + return relation->IsTrue(); +} + +Type *ByteType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation, + [[maybe_unused]] GlobalTypesHolder *globalTypes) +{ + return this; +} +} // namespace panda::es2panda::checker diff --git a/checker/types/ets/byteType.h b/checker/types/ets/byteType.h new file mode 100644 index 0000000000000000000000000000000000000000..ad2dc2a062456e9716ebfd2a3914eea615d1250e --- /dev/null +++ b/checker/types/ets/byteType.h @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_ETS_BYTE_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_BYTE_TYPE_H + +#include "plugins/ecmascript/es2panda/checker/types/type.h" + +namespace panda::es2panda::checker { +class ByteType : public Type { +public: + using UType = int8_t; + + ByteType() : Type(TypeFlag::BYTE) {} + explicit ByteType(UType value) : Type(TypeFlag::BYTE | TypeFlag::CONSTANT), value_(value) {} + + UType GetValue() const + { + return value_; + } + + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + bool AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + + void ToString(std::stringstream &ss) const override + { + ss << "byte"; + } + + void ToAssemblerType([[maybe_unused]] std::stringstream &ss) const override + { + ss << compiler::Signatures::PRIMITIVE_BYTE; + } + +private: + UType value_ {0}; +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/types/ets/charType.cpp b/checker/types/ets/charType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dec9e5ea0a753832813b03ecc8cb4fd175a78e48 --- /dev/null +++ b/checker/types/ets/charType.cpp @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2021-2022 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 "charType.h" + +#include "plugins/ecmascript/es2panda/checker/ets/boxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/unboxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/narrowingWideningConverter.h" + +namespace panda::es2panda::checker { +void CharType::Identical(TypeRelation *relation, Type *other) +{ + if (other->IsCharType()) { + relation->Result(true); + } +} + +void CharType::AssignmentTarget(TypeRelation *relation, [[maybe_unused]] Type *source) +{ + if (relation->ApplyUnboxing()) { + UnboxingConverter(relation->GetChecker()->AsETSChecker(), relation, source, this); + } + NarrowingConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); +} + +bool CharType::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) +{ + if (relation->InAssignmentContext()) { + checker::SavedTypeRelationFlagsContext savedTypeRelationFlagCtx( + relation, checker::TypeRelationFlag::ONLY_CHECK_WIDENING | checker::TypeRelationFlag::NARROWING); + auto unboxedType = relation->GetChecker()->AsETSChecker()->ETSBuiltinTypeAsPrimitiveType(target); + if (unboxedType != nullptr) { + NarrowingWideningConverter(relation->GetChecker()->AsETSChecker(), relation, unboxedType, this); + } + } + + if (relation->ApplyBoxing()) { + BoxingConverter(relation->GetChecker()->AsETSChecker(), relation, this, target); + } + + return relation->IsTrue(); +} + +Type *CharType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation, + [[maybe_unused]] GlobalTypesHolder *globalTypes) +{ + return this; +} +} // namespace panda::es2panda::checker diff --git a/checker/types/ets/charType.h b/checker/types/ets/charType.h new file mode 100644 index 0000000000000000000000000000000000000000..0cb102ae81bcdcf5dda793cf81af07729ac36300 --- /dev/null +++ b/checker/types/ets/charType.h @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_ETS_CHAR_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_CHAR_TYPE_H + +#include "plugins/ecmascript/es2panda/checker/types/type.h" + +namespace panda::es2panda::checker { +class CharType : public Type { +public: + using UType = char16_t; + + CharType() : Type(TypeFlag::CHAR) {} + explicit CharType(UType value) : Type(TypeFlag::CHAR | TypeFlag::CONSTANT), value_(value) {} + + UType GetValue() const + { + return value_; + } + + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + bool AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + + void ToString(std::stringstream &ss) const override + { + ss << "char"; + } + + void ToAssemblerType([[maybe_unused]] std::stringstream &ss) const override + { + ss << compiler::Signatures::PRIMITIVE_CHAR; + } + +private: + UType value_ {'\0'}; +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/types/ets/doubleType.cpp b/checker/types/ets/doubleType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8f3b881bef617f74014a35dba161281ae8c96c80 --- /dev/null +++ b/checker/types/ets/doubleType.cpp @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2021-2022 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 "doubleType.h" + +#include "plugins/ecmascript/es2panda/checker/ets/boxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/unboxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/wideningConverter.h" + +namespace panda::es2panda::checker { +void DoubleType::Identical(TypeRelation *relation, Type *other) +{ + if (other->IsDoubleType()) { + relation->Result(true); + } +} + +void DoubleType::AssignmentTarget(TypeRelation *relation, [[maybe_unused]] Type *source) +{ + if (relation->ApplyUnboxing()) { + UnboxingConverter(relation->GetChecker()->AsETSChecker(), relation, source, this); + } + WideningConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); +} + +bool DoubleType::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) +{ + if (relation->ApplyBoxing()) { + BoxingConverter(relation->GetChecker()->AsETSChecker(), relation, this, target); + } + + return relation->IsTrue(); +} + +Type *DoubleType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation, + [[maybe_unused]] GlobalTypesHolder *globalTypes) +{ + return this; +} +} // namespace panda::es2panda::checker diff --git a/checker/types/ets/doubleType.h b/checker/types/ets/doubleType.h new file mode 100644 index 0000000000000000000000000000000000000000..d97a694f4a02e9d9a29c651a97279f3db423a313 --- /dev/null +++ b/checker/types/ets/doubleType.h @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_ETS_DOUBLE_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_DOUBLE_TYPE_H + +#include "plugins/ecmascript/es2panda/checker/types/type.h" + +namespace panda::es2panda::checker { +class DoubleType : public Type { +public: + using UType = double; + + DoubleType() : Type(TypeFlag::DOUBLE) {} + explicit DoubleType(UType value) : Type(TypeFlag::DOUBLE | TypeFlag::CONSTANT), value_(value) {} + + UType GetValue() const + { + return value_; + } + + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + bool AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + + void ToString(std::stringstream &ss) const override + { + ss << "double"; + } + + void ToAssemblerType([[maybe_unused]] std::stringstream &ss) const override + { + ss << compiler::Signatures::PRIMITIVE_DOUBLE; + } + +private: + UType value_ {0.0}; +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/types/ets/etsArrayType.cpp b/checker/types/ets/etsArrayType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..678610cc01d45508e79f611c9307245a12a21795 --- /dev/null +++ b/checker/types/ets/etsArrayType.cpp @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2021 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 "etsArrayType.h" + +#include "plugins/ecmascript/es2panda/binder/variable.h" +#include "plugins/ecmascript/es2panda/checker/types/typeRelation.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" + +namespace panda::es2panda::checker { +void ETSArrayType::ToString(std::stringstream &ss) const +{ + element_->ToString(ss); + ss << "[]"; +} + +void ETSArrayType::ToAssemblerType(std::stringstream &ss) const +{ + element_->ToAssemblerType(ss); +} + +void ETSArrayType::ToAssemblerTypeWithRank(std::stringstream &ss) const +{ + element_->ToAssemblerType(ss); + + for (uint32_t i = Rank(); i > 0; --i) { + ss << "[]"; + } +} + +uint32_t ETSArrayType::Rank() const +{ + uint32_t rank = 1; + auto iter = element_; + while (iter->IsETSArrayType()) { + iter = iter->AsETSArrayType()->ElementType(); + rank++; + } + + return rank; +} + +void ETSArrayType::Identical(TypeRelation *relation, Type *other) +{ + if (other->IsETSArrayType()) { + relation->IsIdenticalTo(element_, other->AsETSArrayType()->ElementType()); + } +} + +void ETSArrayType::AssignmentTarget(TypeRelation *relation, Type *source) +{ + if (source->IsETSArrayType()) { + relation->IsAssignableTo(source->AsETSArrayType()->ElementType(), element_); + } +} + +Type *ETSArrayType::Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) +{ + return relation->GetChecker()->AsETSChecker()->CreateETSArrayType( + element_->Instantiate(allocator, relation, globalTypes)); +} +} // namespace panda::es2panda::checker diff --git a/checker/types/ets/etsArrayType.h b/checker/types/ets/etsArrayType.h new file mode 100644 index 0000000000000000000000000000000000000000..d780055c37ef1b44b46042a1ef42eb60b001fe8d --- /dev/null +++ b/checker/types/ets/etsArrayType.h @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_ETS_ARRAY_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_ARRAY_TYPE_H + +#include "plugins/ecmascript/es2panda/checker/types/type.h" + +namespace panda::es2panda::checker { +class ETSArrayType : public Type { +public: + explicit ETSArrayType(Type *elementType) : Type(TypeFlag::ETS_ARRAY), element_(elementType) {} + + Type *ElementType() + { + return element_; + } + + const Type *ElementType() const + { + return element_; + } + + void ToString(std::stringstream &ss) const override; + void ToAssemblerType(std::stringstream &ss) const override; + void ToAssemblerTypeWithRank(std::stringstream &ss) const override; + + uint32_t Rank() const override; + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + +private: + Type *element_; +}; +} // namespace panda::es2panda::checker + +#endif /* TYPESCRIPT_TYPES_ARRAY_TYPE_H */ diff --git a/checker/types/ets/etsBooleanType.cpp b/checker/types/ets/etsBooleanType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e9b355d9efeefbe983e35c470de0788c56950874 --- /dev/null +++ b/checker/types/ets/etsBooleanType.cpp @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2021-2022 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 "etsBooleanType.h" + +#include "plugins/ecmascript/es2panda/checker/ets/boxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/unboxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" + +namespace panda::es2panda::checker { +void ETSBooleanType::Identical(TypeRelation *relation, Type *other) +{ + if (other->IsETSBooleanType()) { + relation->Result(true); + } +} + +void ETSBooleanType::AssignmentTarget([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *source) +{ + if (relation->ApplyUnboxing()) { + UnboxingConverter(relation->GetChecker()->AsETSChecker(), relation, source, this); + } +} + +bool ETSBooleanType::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) +{ + if (relation->ApplyBoxing()) { + BoxingConverter(relation->GetChecker()->AsETSChecker(), relation, this, target); + } + + return relation->IsTrue(); +} + +Type *ETSBooleanType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation, + [[maybe_unused]] GlobalTypesHolder *globalTypes) +{ + return this; +} +} // namespace panda::es2panda::checker diff --git a/checker/types/ets/etsBooleanType.h b/checker/types/ets/etsBooleanType.h new file mode 100644 index 0000000000000000000000000000000000000000..c9fc78c95edef09265b94501a176d19d19154342 --- /dev/null +++ b/checker/types/ets/etsBooleanType.h @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_ETS_BOOLEAN_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_BOOLEAN_TYPE_H + +#include "plugins/ecmascript/es2panda/checker/types/type.h" + +namespace panda::es2panda::checker { +class ETSBooleanType : public Type { +public: + using UType = bool; + ETSBooleanType() : Type(TypeFlag::ETS_BOOLEAN) {} + explicit ETSBooleanType(UType value) : Type(TypeFlag::ETS_BOOLEAN | TypeFlag::CONSTANT), value_(value) {} + + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + bool AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + + UType GetValue() const + { + return value_; + } + + void ToString(std::stringstream &ss) const override + { + ss << "boolean"; + } + + void ToAssemblerType([[maybe_unused]] std::stringstream &ss) const override + { + ss << compiler::Signatures::PRIMITIVE_BOOLEAN; + } + +private: + UType value_ {false}; +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/types/ets/etsFunctionType.cpp b/checker/types/ets/etsFunctionType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..265a44cfce6e8eca1e14302767ec50f2cbf36745 --- /dev/null +++ b/checker/types/ets/etsFunctionType.cpp @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2021-2022 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 "etsFunctionType.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsMethodReferenceExpression.h" +#include "plugins/ecmascript/es2panda/checker/types/typeRelation.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" + +namespace panda::es2panda::checker { + +Signature *ETSFunctionType::FirstAbstractSignature() +{ + for (auto *it : callSignatures_) { + if (it->HasSignatureFlag(SignatureFlags::ABSTRACT)) { + return it; + } + } + + return nullptr; +} + +void ETSFunctionType::ToString(std::stringstream &ss) const +{ + callSignatures_[0]->ToString(ss, nullptr); +} + +void ETSFunctionType::Identical(TypeRelation *relation, Type *other) +{ + if (!other->IsETSFunctionType()) { + return; + } + + if (callSignatures_.size() == 1 && callSignatures_[0]->HasSignatureFlag(SignatureFlags::TYPE)) { + AssignmentTarget(relation, other); + return; + } + + callSignatures_[0]->Identical(relation, other->AsETSFunctionType()->CallSignatures()[0]); +} + +void ETSFunctionType::AssignmentTarget(TypeRelation *relation, Type *source) +{ + if (!source->IsETSFunctionType() && + (!source->IsETSObjectType() || !source->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL))) { + return; + } + + ASSERT(callSignatures_.size() == 1 && callSignatures_[0]->HasSignatureFlag(SignatureFlags::TYPE)); + + Signature *target = callSignatures_[0]; + Signature *match {}; + auto *sourceFuncType = source->IsETSFunctionType() ? source->AsETSFunctionType() + : source->AsETSObjectType()->GetFunctionalInterfaceInvokeType(); + + for (auto *it : sourceFuncType->CallSignatures()) { + if (target->MinArgCount() != it->MinArgCount()) { + continue; + } + + if ((target->RestVar() != nullptr && it->RestVar() == nullptr) || + (target->RestVar() == nullptr && it->RestVar() != nullptr)) { + continue; + } + + size_t idx = 0; + for (; idx != target->MinArgCount(); idx++) { + if (!relation->IsIdenticalTo(target->Params()[idx]->TsType(), it->Params()[idx]->TsType())) { + break; + } + } + + if (idx != target->MinArgCount()) { + continue; + } + + if (target->RestVar() != nullptr && + !relation->IsIdenticalTo(target->RestVar()->TsType(), it->RestVar()->TsType())) { + continue; + } + + if (!relation->IsAssignableTo(target->ReturnType(), it->ReturnType())) { + continue; + } + + match = it; + break; + } + + if (match == nullptr) { + relation->Result(false); + return; + } + + ASSERT(relation->GetNode() != nullptr); + if (relation->GetNode()->IsETSMethodReferenceExpression()) { + auto *methodRef = relation->GetNode()->AsETSMethodReferenceExpression(); + methodRef->SetComputedSignature(match); + relation->GetChecker()->AsETSChecker()->ResolveLambdaObject( + relation->GetNode()->AsETSMethodReferenceExpression(), callSignatures_[0]->Owner()); + } + + relation->Result(true); +} + +Type *ETSFunctionType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation, + [[maybe_unused]] GlobalTypesHolder *globalTypes) +{ + auto *copiedType = relation->GetChecker()->AsETSChecker()->CreateETSFunctionType(name_); + + for (auto *it : callSignatures_) { + copiedType->AddCallSignature(it->Copy(allocator, relation, globalTypes)); + } + + return copiedType; +} +} // namespace panda::es2panda::checker diff --git a/checker/types/ets/etsFunctionType.h b/checker/types/ets/etsFunctionType.h new file mode 100644 index 0000000000000000000000000000000000000000..ec4c9f4333b2777a588493ae9da282d0bf1177d4 --- /dev/null +++ b/checker/types/ets/etsFunctionType.h @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_ETS_FUNCTION_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_FUNCTION_TYPE_H + +#include "plugins/ecmascript/es2panda/checker/types/type.h" +#include "plugins/ecmascript/es2panda/checker/types/signature.h" + +namespace panda::es2panda::checker { + +class ETSFunctionType : public Type { +public: + explicit ETSFunctionType(util::StringView name, Signature *signature, ArenaAllocator *allocator) + : Type(TypeFlag::FUNCTION), callSignatures_(allocator->Adapter()), name_(name) + { + callSignatures_.push_back(signature); + } + + explicit ETSFunctionType(util::StringView name, ArenaAllocator *allocator) + : Type(TypeFlag::FUNCTION), callSignatures_(allocator->Adapter()), name_(name) + { + } + + ArenaVector &CallSignatures() + { + return callSignatures_; + } + + const ArenaVector &CallSignatures() const + { + return callSignatures_; + } + + util::StringView Name() const + { + return name_; + } + + void AddCallSignature(Signature *signature) + { + callSignatures_.push_back(signature); + } + + void SetReferencedSignature(Signature *refSignature) + { + refSignature_ = refSignature; + } + + Signature *GetReferencedSignature() const + { + return refSignature_; + } + + Signature *FindSignature(const ir::ScriptFunction *func) const + { + for (auto *it : callSignatures_) { + if (it->Function() == func) { + return it; + } + } + + return nullptr; + } + + void ToAssemblerType([[maybe_unused]] std::stringstream &ss) const override + { + ss << "ets.lang.Object"; + } + + Signature *FirstAbstractSignature(); + void ToString(std::stringstream &ss) const override; + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + +private: + ArenaVector callSignatures_; + util::StringView name_; + Signature *refSignature_ {}; +}; +} // namespace panda::es2panda::checker + +#endif /* TYPESCRIPT_TYPES_FUNCTION_TYPE_H */ diff --git a/checker/types/ets/etsObjectType.cpp b/checker/types/ets/etsObjectType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f637bdb7ddf5b626e3bdea909cb0b447bd2b8f8e --- /dev/null +++ b/checker/types/ets/etsObjectType.cpp @@ -0,0 +1,462 @@ +/** + * Copyright (c) 2021-2022 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 "etsObjectType.h" +#include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" +#include "plugins/ecmascript/es2panda/ir/base/scriptFunction.h" +#include "plugins/ecmascript/es2panda/checker/types/typeRelation.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" +#include "plugins/ecmascript/es2panda/binder/declaration.h" + +namespace panda::es2panda::checker { + +void ETSObjectType::Iterate(const PropertyTraverser &cb) const +{ + for (const auto *prop : GetAllProperties()) { + cb(prop); + } + + if (superType_ != nullptr) { + superType_->Iterate(cb); + } + + for (const auto *interface : interfaces_) { + interface->Iterate(cb); + } +} + +binder::LocalVariable *ETSObjectType::GetProperty(const util::StringView &name, PropertySearchFlags flags) const +{ + binder::LocalVariable *res {}; + if ((flags & PropertySearchFlags::SEARCH_INSTANCE_FIELD) != 0) { + res = GetOwnProperty(name); + } + + if (res == nullptr && ((flags & PropertySearchFlags::SEARCH_STATIC_FIELD) != 0)) { + res = GetOwnProperty(name); + } + + if (res == nullptr && ((flags & PropertySearchFlags::SEARCH_INSTANCE_DECL) != 0)) { + res = GetOwnProperty(name); + } + + if (res == nullptr && ((flags & PropertySearchFlags::SEARCH_STATIC_DECL) != 0)) { + res = GetOwnProperty(name); + } + + if (res == nullptr && (flags & PropertySearchFlags::SEARCH_METHOD) != 0) { + if ((flags & PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION) != 0) { + if ((flags & PropertySearchFlags::SEARCH_INSTANCE_METHOD) != 0) { + res = GetOwnProperty(name); + } + + if (res == nullptr && ((flags & PropertySearchFlags::SEARCH_STATIC_METHOD) != 0)) { + res = GetOwnProperty(name); + } + } else { + return CreateSyntheticVarFromEverySignature(name, flags); + } + } + + if ((flags & (PropertySearchFlags::SEARCH_IN_INTERFACES | PropertySearchFlags::SEARCH_IN_BASE)) == 0) { + return res; + } + + if (res != nullptr) { + return res; + } + + if ((flags & PropertySearchFlags::SEARCH_IN_INTERFACES) != 0) { + for (auto *interface : interfaces_) { + res = interface->GetProperty(name, flags); + + if (res != nullptr) { + return res; + } + } + } + + if (superType_ != nullptr && ((flags & PropertySearchFlags::SEARCH_IN_BASE) != 0)) { + res = superType_->GetProperty(name, flags); + } + + return res; +} + +binder::LocalVariable *ETSObjectType::CreateSyntheticVarFromEverySignature(const util::StringView &name, + PropertySearchFlags flags) const +{ + binder::LocalVariable *res = + allocator_->New(binder::VariableFlags::SYNTHETIC | binder::VariableFlags::METHOD); + ETSFunctionType *funcType = allocator_->New(name, allocator_); + funcType->AddTypeFlag(TypeFlag::SYNTHETIC); + CollectSignaturesForSyntheticType(funcType, name, flags); + + if (funcType->CallSignatures().empty()) { + return nullptr; + } + + res->SetTsType(funcType); + funcType->SetVariable(res); + return res; +} + +void ETSObjectType::CollectSignaturesForSyntheticType(ETSFunctionType *funcType, const util::StringView &name, + PropertySearchFlags flags) const +{ + if ((flags & PropertySearchFlags::SEARCH_STATIC_METHOD) != 0) { + auto *found = GetOwnProperty(name); + if (found != nullptr) { + if (found->TsType()->IsETSFunctionType()) { + for (auto *it : found->TsType()->AsETSFunctionType()->CallSignatures()) { + if (((flags & PropertySearchFlags::INGORE_ABSTRACT) != 0) && + it->HasSignatureFlag(SignatureFlags::ABSTRACT)) { + continue; + } + + funcType->AddCallSignature(it); + } + } + + if (found->TsType()->IsETSObjectType()) { + auto *functionalInterface = found->TsType()->AsETSObjectType(); + ASSERT(functionalInterface->HasObjectFlag(ETSObjectFlags::FUNCTIONAL)); + funcType->AddCallSignature(functionalInterface->GetOwnProperty("invoke") + ->TsType() + ->AsETSFunctionType() + ->CallSignatures()[0]); + } + } + } + + if ((flags & PropertySearchFlags::SEARCH_INSTANCE_METHOD) != 0) { + auto *found = GetOwnProperty(name); + if (found != nullptr) { + if (found->TsType()->IsETSFunctionType()) { + for (auto *it : found->TsType()->AsETSFunctionType()->CallSignatures()) { + if (((flags & PropertySearchFlags::INGORE_ABSTRACT) != 0) && + it->HasSignatureFlag(SignatureFlags::ABSTRACT)) { + continue; + } + + funcType->AddCallSignature(it); + } + } + + if (found->TsType()->IsETSObjectType()) { + auto *functionalInterface = found->TsType()->AsETSObjectType(); + ASSERT(functionalInterface->HasObjectFlag(ETSObjectFlags::FUNCTIONAL)); + funcType->AddCallSignature(functionalInterface->GetOwnProperty("invoke") + ->TsType() + ->AsETSFunctionType() + ->CallSignatures()[0]); + } + } + } + + if (superType_ != nullptr && ((flags & PropertySearchFlags::SEARCH_IN_BASE) != 0)) { + superType_->CollectSignaturesForSyntheticType(funcType, name, flags); + } +} + +std::vector ETSObjectType::GetAllProperties() const +{ + std::vector allProperties; + for (const auto &[_, prop] : InstanceFields()) { + (void)_; + allProperties.push_back(prop); + } + + for (const auto &[_, prop] : StaticFields()) { + (void)_; + allProperties.push_back(prop); + } + + for (const auto &[_, prop] : InstanceMethods()) { + (void)_; + allProperties.push_back(prop); + } + + for (const auto &[_, prop] : StaticMethods()) { + (void)_; + allProperties.push_back(prop); + } + + for (const auto &[_, prop] : InstanceDecls()) { + (void)_; + allProperties.push_back(prop); + } + + for (const auto &[_, prop] : StaticDecls()) { + (void)_; + allProperties.push_back(prop); + } + + return allProperties; +} + +std::vector ETSObjectType::Methods() const +{ + std::vector methods; + for (const auto &[_, prop] : InstanceMethods()) { + (void)_; + methods.push_back(prop); + } + + for (const auto &[_, prop] : StaticMethods()) { + (void)_; + methods.push_back(prop); + } + + return methods; +} + +std::vector ETSObjectType::Fields() const +{ + std::vector fields; + for (const auto &[_, prop] : InstanceFields()) { + (void)_; + fields.push_back(prop); + } + + for (const auto &[_, prop] : StaticFields()) { + (void)_; + fields.push_back(prop); + } + + return fields; +} + +std::vector ETSObjectType::ForeignProperties() const +{ + std::vector foreignProps; + std::unordered_set ownProps; + ownProps.reserve(properties_.size()); + + for (const auto *prop : GetAllProperties()) { + ownProps.insert(prop->Name()); + } + + auto allProps = CollectAllProperties(); + for (const auto &[name, var] : allProps) { + if (ownProps.find(name) == ownProps.end()) { + foreignProps.push_back(var); + } + } + + return foreignProps; +} + +std::unordered_map ETSObjectType::CollectAllProperties() const +{ + std::unordered_map propMap; + propMap.reserve(properties_.size()); + Iterate([&propMap](const binder::LocalVariable *var) { propMap.insert({var->Name(), var}); }); + + return propMap; +} + +void ETSObjectType::ToString(std::stringstream &ss) const +{ + ss << name_; +} + +void ETSObjectType::Identical(TypeRelation *relation, Type *other) +{ + if (!other->IsETSObjectType() || !other->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::CLASS)) { + return; + } + + if (variable_ != other->Variable()) { + return; + } + + ETSObjectType *otherObj = other->AsETSObjectType(); + if (typeArguments_.empty() != otherObj->TypeArguments().empty()) { + return; + } + + if (!typeArguments_.empty()) { + ASSERT(typeArguments_.size() == otherObj->TypeArguments().size()); + for (size_t idx = 0; idx < typeArguments_.size(); idx++) { + typeArguments_[idx]->Identical(relation, otherObj->TypeArguments()[idx]); + if (!relation->IsTrue()) { + return; + } + } + } + + relation->Result(true); +} + +bool ETSObjectType::AssignmentSource([[maybe_unused]] TypeRelation *relation, Type *target) +{ + if (HasObjectFlag(ETSObjectFlags::ENUM)) { + relation->GetChecker()->ThrowTypeError("Can not assignable to Enum object.", this->GetDeclNode()->Start()); + } + + return target->IsETSNullType(); +} + +void ETSObjectType::AssignmentTarget(TypeRelation *relation, Type *source) +{ + if (source->IsETSNullType()) { + relation->Result(true); + return; + } + + if (HasObjectFlag(ETSObjectFlags::FUNCTIONAL)) { + auto found = properties_[static_cast(PropertyType::INSTANCE_METHOD)].find("invoke"); + ASSERT(found != properties_[static_cast(PropertyType::INSTANCE_METHOD)].end()); + relation->IsAssignableTo(source, found->second->TsType()); + return; + } + + IsSubtype(relation, source); +} + +void ETSObjectType::IsSubtype(TypeRelation *relation, Type *source) +{ + if (!source->IsETSObjectType() || !source->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::CLASS)) { + return; + } + + ETSObjectType *sourceObj = source->AsETSObjectType(); + Type *sup = sourceObj->AsSuper(relation->GetChecker(), variable_); + if (sup == nullptr) { + return; + } + + relation->Result(true); +} + +Type *ETSObjectType::AsSuper(Checker *checker, binder::Variable *sourceVar) +{ + if (sourceVar == nullptr) { + return nullptr; + } + + if (variable_ == sourceVar) { + return this; + } + + if (!checker->TypeStack().insert(sourceVar).second) { + return nullptr; + } + + Type *superType = checker->AsETSChecker()->GetSuperType(this); + + if (superType == nullptr) { + return this; + } + + if (!superType->IsETSObjectType()) { + checker->TypeStack().erase(sourceVar); + return nullptr; + } + + ETSObjectType *superObj = superType->AsETSObjectType(); + + if (superObj->HasObjectFlag(ETSObjectFlags::CLASS)) { + Type *res = superObj->AsSuper(checker, sourceVar); + if (res != nullptr) { + checker->TypeStack().erase(sourceVar); + return res; + } + } + + if (sourceVar->Declaration()->Node()->IsTSInterfaceDeclaration()) { + ArenaVector interfaces = checker->AsETSChecker()->GetInterfaces(this); + for (auto *it : interfaces) { + Type *res = it->AsSuper(checker, sourceVar); + if (res != nullptr) { + checker->TypeStack().erase(sourceVar); + return res; + } + } + } + + checker->TypeStack().erase(sourceVar); + return nullptr; +} + +binder::LocalVariable *ETSObjectType::CopyProperty(binder::LocalVariable *prop, ArenaAllocator *allocator, + TypeRelation *relation, GlobalTypesHolder *globalTypes) +{ + auto *copiedProp = prop->Copy(allocator, prop->Declaration()); + auto *copiedPropType = ETSChecker::TryToInstantiate(relation->GetChecker()->AsETSChecker()->GetTypeOfVariable(prop), + allocator, relation, globalTypes); + copiedPropType->SetVariable(copiedProp); + copiedProp->SetTsType(copiedPropType); + return copiedProp; +} + +Type *ETSObjectType::Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) +{ + auto *checker = relation->GetChecker()->AsETSChecker(); + auto *copiedType = checker->CreateNewETSObjectType(name_, declNode_, flags_); + copiedType->RemoveObjectFlag(ETSObjectFlags::CHECKED_COMPATIBLE_ABSTRACTS | + ETSObjectFlags::UNCOMPLETE_INSTANTIATION); + + copiedType->SetVariable(variable_); + copiedType->SetSuperType(superType_); + + for (auto *it : interfaces_) { + copiedType->AddInterface(it); + } + + for (auto &[_, prop] : InstanceFields()) { + (void)_; + auto *copiedProp = CopyProperty(prop, allocator, relation, globalTypes); + copiedType->AddProperty(copiedProp); + } + + for (auto &[_, prop] : StaticFields()) { + (void)_; + auto *copiedProp = CopyProperty(prop, allocator, relation, globalTypes); + copiedType->AddProperty(copiedProp); + } + + for (auto &[_, prop] : InstanceMethods()) { + (void)_; + auto *copiedProp = CopyProperty(prop, allocator, relation, globalTypes); + copiedType->AddProperty(copiedProp); + } + + for (auto &[_, prop] : StaticMethods()) { + (void)_; + auto *copiedProp = CopyProperty(prop, allocator, relation, globalTypes); + copiedType->AddProperty(copiedProp); + } + + for (auto &[_, prop] : InstanceDecls()) { + (void)_; + auto *copiedProp = CopyProperty(prop, allocator, relation, globalTypes); + copiedType->AddProperty(copiedProp); + } + + for (auto &[_, prop] : StaticDecls()) { + (void)_; + auto *copiedProp = CopyProperty(prop, allocator, relation, globalTypes); + copiedType->AddProperty(copiedProp); + } + + for (auto *it : constructSignatures_) { + copiedType->AddConstructSignature(it->Copy(allocator, relation, globalTypes)); + } + + return copiedType; +} +} // namespace panda::es2panda::checker diff --git a/checker/types/ets/etsObjectType.h b/checker/types/ets/etsObjectType.h new file mode 100644 index 0000000000000000000000000000000000000000..097b8f81e9c3715ee5ba2cca698ba6ecade05910 --- /dev/null +++ b/checker/types/ets/etsObjectType.h @@ -0,0 +1,410 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_ETS_OBJECT_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_OBJECT_TYPE_H + +#include "plugins/ecmascript/es2panda/checker/types/type.h" +#include "plugins/ecmascript/es2panda/checker/types/signature.h" + +namespace panda::es2panda::checker { + +enum class ETSObjectFlags : uint32_t { + NO_OPTS = 0U, + CLASS = 1U << 0U, + INTERFACE = 1U << 1U, + INSTANCE = 1U << 2U, + ABSTRACT = 1U << 3U, + GLOBAL = 1U << 4U, + ENUM = 1U << 5U, + FUNCTIONAL = 1U << 6U, + RESOLVED_MEMBERS = 1U << 7U, + RESOLVED_INTERFACES = 1U << 8U, + RESOLVED_SUPER = 1U << 9U, + RESOLVED_TYPE_PARAMS = 1U << 10U, + CHECKED_COMPATIBLE_ABSTRACTS = 1U << 11U, + NULL_TYPE = 1U << 12U, + STRING = 1U << 13U, + UNCOMPLETE_INSTANTIATION = 1U << 14U, + INNER = 1U << 15U, + + BUILTIN_STRING = 1U << 23U, + BUILTIN_BOOLEAN = 1U << 24U, + BUILTIN_BYTE = 1U << 25U, + BUILTIN_CHAR = 1U << 26U, + BUILTIN_SHORT = 1U << 27U, + BUILTIN_INT = 1U << 28U, + BUILTIN_LONG = 1U << 29U, + BUILTIN_FLOAT = 1U << 30U, + BUILTIN_DOUBLE = 1U << 31U, + + UNBOXABLE_TYPE = BUILTIN_BOOLEAN | BUILTIN_BYTE | BUILTIN_CHAR | BUILTIN_SHORT | BUILTIN_INT | BUILTIN_LONG | + BUILTIN_FLOAT | BUILTIN_DOUBLE, + BUILTIN_TYPE = BUILTIN_STRING | UNBOXABLE_TYPE, + VALID_SWITCH_TYPE = + BUILTIN_BYTE | BUILTIN_CHAR | BUILTIN_SHORT | BUILTIN_INT | BUILTIN_LONG | BUILTIN_STRING | ENUM, + GLOBAL_CLASS = CLASS | GLOBAL, + FUNCTIONAL_INTERFACE = INTERFACE | ABSTRACT | FUNCTIONAL, +}; + +DEFINE_BITOPS(ETSObjectFlags) + +enum class PropertySearchFlags : uint32_t { + NO_OPTS = 0, + SEARCH_INSTANCE_METHOD = 1U << 0U, + SEARCH_INSTANCE_FIELD = 1U << 1U, + SEARCH_INSTANCE_DECL = 1U << 2U, + SEARCH_STATIC_METHOD = 1U << 3U, + SEARCH_STATIC_FIELD = 1U << 4U, + SEARCH_STATIC_DECL = 1U << 5U, + + SEARCH_IN_BASE = 1U << 6U, + SEARCH_IN_INTERFACES = 1U << 7U, + INGORE_ABSTRACT = 1U << 8U, + ALLOW_FUNCTIONAL_INTERFACE = 1U << 9U, + DISALLOW_SYNTHETIC_METHOD_CREATION = 1U << 10U, + + SEARCH_METHOD = SEARCH_INSTANCE_METHOD | SEARCH_STATIC_METHOD, + SEARCH_FIELD = SEARCH_INSTANCE_FIELD | SEARCH_STATIC_FIELD, + SEARCH_DECL = SEARCH_INSTANCE_DECL | SEARCH_STATIC_DECL, + SEARCH_ALL = SEARCH_METHOD | SEARCH_FIELD | SEARCH_DECL, +}; + +DEFINE_BITOPS(PropertySearchFlags) + +enum class PropertyType { + INSTANCE_METHOD, + INSTANCE_FIELD, + INSTANCE_DECL, + STATIC_METHOD, + STATIC_FIELD, + STATIC_DECL, + COUNT, +}; + +class ETSObjectType : public Type { +public: + using PropertyMap = ArenaUnorderedMap; + using InstantiationMap = ArenaUnorderedMap; + using PropertyTraverser = std::function; + using PropertyHolder = std::array(PropertyType::COUNT)>; + + explicit ETSObjectType(ArenaAllocator *allocator) : ETSObjectType(allocator, ETSObjectFlags::NO_OPTS) {} + + explicit ETSObjectType(ArenaAllocator *allocator, ETSObjectFlags flags) + : ETSObjectType(allocator, "", "", nullptr, flags) + { + } + + explicit ETSObjectType(ArenaAllocator *allocator, util::StringView name, util::StringView assemblerName, + ir::AstNode *declNode, ETSObjectFlags flags) + : ETSObjectType(allocator, name, assemblerName, declNode, flags, + std::make_index_sequence(PropertyType::COUNT)> {}) + { + } + + void AddConstructSignature(Signature *signature) + { + constructSignatures_.push_back(signature); + } + + void AddConstructSignature(const ArenaVector &signatures) + { + constructSignatures_.insert(constructSignatures_.end(), signatures.begin(), signatures.end()); + } + + void AddInterface(ETSObjectType *interface) + { + interfaces_.push_back(interface); + } + + void SetSuperType(ETSObjectType *super) + { + superType_ = super; + } + + void SetTypeArguments(ArenaVector &&typeArgs) + { + typeArguments_ = std::move(typeArgs); + } + + void SetEnclosingType(ETSObjectType *enclosingType) + { + enclosingType_ = enclosingType; + } + + PropertyMap InstanceMethods() const + { + return properties_[static_cast(PropertyType::INSTANCE_METHOD)]; + } + + PropertyMap InstanceFields() const + { + return properties_[static_cast(PropertyType::INSTANCE_FIELD)]; + } + + PropertyMap InstanceDecls() const + { + return properties_[static_cast(PropertyType::INSTANCE_DECL)]; + } + + PropertyMap StaticMethods() const + { + return properties_[static_cast(PropertyType::STATIC_METHOD)]; + } + + PropertyMap StaticFields() const + { + return properties_[static_cast(PropertyType::STATIC_FIELD)]; + } + + PropertyMap StaticDecls() const + { + return properties_[static_cast(PropertyType::STATIC_DECL)]; + } + + const ArenaVector &TypeArguments() const + { + return typeArguments_; + } + + ArenaVector &TypeArguments() + { + return typeArguments_; + } + + const ArenaVector &ConstructSignatures() const + { + return constructSignatures_; + } + + ArenaVector &ConstructSignatures() + { + return constructSignatures_; + } + + const ArenaVector &Interfaces() const + { + return interfaces_; + } + + ArenaVector &Interfaces() + { + return interfaces_; + } + + ir::AstNode *GetDeclNode() const + { + return declNode_; + } + + const ETSObjectType *SuperType() const + { + return superType_; + } + + ETSObjectType *SuperType() + { + return superType_; + } + + const ETSObjectType *EnclosingType() const + { + return enclosingType_; + } + + ETSObjectType *EnclosingType() + { + return enclosingType_; + } + + ETSObjectType *OutermostClass() + { + auto *iter = enclosingType_; + + while (iter != nullptr && iter->EnclosingType() != nullptr) { + iter = iter->EnclosingType(); + } + + return iter; + } + + bool IsPropertyInherited(binder::Variable *var) + { + if (var->HasFlag(binder::VariableFlags::PRIVATE)) { + return GetProperty(var->Name(), PropertySearchFlags::SEARCH_FIELD | PropertySearchFlags::SEARCH_DECL) == + var; + } + + return true; + } + + bool IsSignatureInherited(Signature *signature) + { + if (signature->HasSignatureFlag(SignatureFlags::PRIVATE)) { + return signature->Owner() == this; + } + + return true; + } + + const util::StringView &Name() const + { + return name_; + } + + const util::StringView &AssemblerName() const + { + return assemblerName_; + } + + void SetName(const util::StringView &newName) + { + name_ = newName; + } + + void SetAssemblerName(const util::StringView &newName) + { + assemblerName_ = newName; + } + + ETSObjectFlags ObjectFlags() const + { + return flags_; + } + + void AddObjectFlag(ETSObjectFlags flag) + { + flags_ |= flag; + } + + void RemoveObjectFlag(ETSObjectFlags flag) + { + flags_ &= ~flag; + } + + bool HasObjectFlag(ETSObjectFlags flag) const + { + return (flags_ & flag) != 0; + } + + ETSFunctionType *GetFunctionalInterfaceInvokeType() + { + ASSERT(HasObjectFlag(ETSObjectFlags::FUNCTIONAL)); + auto *invoke = GetOwnProperty("invoke"); + ASSERT(invoke && invoke->TsType() && invoke->TsType()->IsETSFunctionType()); + return invoke->TsType()->AsETSFunctionType(); + } + + ETSObjectFlags BuiltInKind() + { + return static_cast(flags_ & ETSObjectFlags::BUILTIN_TYPE); + } + + ETSObjectType *GetInstantiatedType(util::StringView hash) + { + auto found = instantiationMap_.find(hash); + + if (found != instantiationMap_.end()) { + return found->second; + } + + return nullptr; + } + + InstantiationMap &GetInstantiationMap() + { + return instantiationMap_; + } + + template + binder::LocalVariable *GetOwnProperty(const util::StringView &name) const + { + auto found = properties_[static_cast(type)].find(name); + if (found != properties_[static_cast(type)].end()) { + return found->second; + } + return nullptr; + } + + template + void AddProperty(binder::LocalVariable *prop) + { + properties_[static_cast(type)].emplace(prop->Name(), prop); + } + + std::vector ForeignProperties() const; + binder::LocalVariable *GetProperty(const util::StringView &name, PropertySearchFlags flags) const; + std::vector GetAllProperties() const; + void CreatePropertyMap(ArenaAllocator *allocator); + binder::LocalVariable *CopyProperty(binder::LocalVariable *prop, ArenaAllocator *allocator, TypeRelation *relation, + GlobalTypesHolder *globalTypes); + std::vector Methods() const; + std::vector Fields() const; + binder::LocalVariable *CreateSyntheticVarFromEverySignature(const util::StringView &name, + PropertySearchFlags flags) const; + void CollectSignaturesForSyntheticType(ETSFunctionType *funcType, const util::StringView &name, + PropertySearchFlags flags) const; + + void Iterate(const PropertyTraverser &cb) const; + void ToString(std::stringstream &ss) const override; + void Identical(TypeRelation *relation, Type *other) override; + bool AssignmentSource(TypeRelation *relation, Type *target) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + void IsSubtype(TypeRelation *relation, Type *source) override; + Type *AsSuper(Checker *checker, binder::Variable *sourceVar) override; + + void ToAssemblerType([[maybe_unused]] std::stringstream &ss) const override + { + ss << assemblerName_; + } + +private: + template + explicit ETSObjectType(ArenaAllocator *allocator, util::StringView name, util::StringView assemblerName, + ir::AstNode *declNode, ETSObjectFlags flags, [[maybe_unused]] std::index_sequence s) + : Type(TypeFlag::ETS_OBJECT), + allocator_(allocator), + name_(name), + assemblerName_(assemblerName), + declNode_(declNode), + constructSignatures_(allocator->Adapter()), + interfaces_(allocator->Adapter()), + flags_(flags), + instantiationMap_(allocator->Adapter()), + typeArguments_(allocator->Adapter()), + properties_ {(void(Is), PropertyMap {allocator->Adapter()})...} + { + } + + std::unordered_map CollectAllProperties() const; + + ArenaAllocator *allocator_; + util::StringView name_; + util::StringView assemblerName_; + ir::AstNode *declNode_; + ArenaVector constructSignatures_; + ArenaVector interfaces_; + ETSObjectFlags flags_; + InstantiationMap instantiationMap_; + ArenaVector typeArguments_; + PropertyHolder properties_; + ETSObjectType *superType_ {}; + ETSObjectType *enclosingType_ {}; +}; +} // namespace panda::es2panda::checker + +#endif /* TYPESCRIPT_TYPES_FUNCTION_TYPE_H */ diff --git a/checker/types/ets/etsStringType.cpp b/checker/types/ets/etsStringType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..847fdb887cf539b7c2c0183ab5b74336d0b9657e --- /dev/null +++ b/checker/types/ets/etsStringType.cpp @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2021-2022 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 "etsStringType.h" + +#include "plugins/ecmascript/es2panda/binder/ETSBinder.h" + +namespace panda::es2panda::checker { +void ETSStringType::Identical(TypeRelation *relation, Type *other) +{ + if (other->IsETSStringType()) { + relation->Result(true); + } +} + +void ETSStringType::AssignmentTarget([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *source) +{ + if (source->IsETSStringType()) { + relation->Result(true); + } +} + +Type *ETSStringType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation, + [[maybe_unused]] GlobalTypesHolder *globalTypes) +{ + return this; +} +} // namespace panda::es2panda::checker diff --git a/checker/types/ets/etsStringType.h b/checker/types/ets/etsStringType.h new file mode 100644 index 0000000000000000000000000000000000000000..9c791744560d2a987a4c634513dded5b9c6f4e6a --- /dev/null +++ b/checker/types/ets/etsStringType.h @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_ETS_STRING_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_STRING_TYPE_H + +#include "plugins/ecmascript/es2panda/checker/types/ets/etsObjectType.h" + +namespace panda::es2panda::checker { +class ETSStringType : public ETSObjectType { +public: + explicit ETSStringType(ArenaAllocator *allocator, [[maybe_unused]] ETSObjectType *super) + : ETSObjectType(allocator, ETSObjectFlags::CLASS | ETSObjectFlags::STRING | ETSObjectFlags::RESOLVED_SUPER) + { + SetSuperType(super); + } + + explicit ETSStringType(ArenaAllocator *allocator, ETSObjectType *super, util::StringView value) + : ETSObjectType(allocator, ETSObjectFlags::CLASS | ETSObjectFlags::STRING | ETSObjectFlags::RESOLVED_SUPER), + value_(value) + { + SetSuperType(super); + AddTypeFlag(TypeFlag::CONSTANT); + variable_ = super->Variable(); + } + + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + + void ToString(std::stringstream &ss) const override + { + ss << "string"; + } + + void ToAssemblerType([[maybe_unused]] std::stringstream &ss) const override + { + ss << compiler::Signatures::BUILTIN_STRING; + } + + util::StringView GetValue() const + { + return value_; + } + +private: + util::StringView value_ {}; +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/types/ets/etsTypeParameter.cpp b/checker/types/ets/etsTypeParameter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4a98ab157c2bb1e85ab5c5f7ef8fda84a4fcb060 --- /dev/null +++ b/checker/types/ets/etsTypeParameter.cpp @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2021-2022 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 "etsTypeParameter.h" + +namespace panda::es2panda::checker { +void ETSTypeParameter::ToString([[maybe_unused]] std::stringstream &ss) const +{ + UNREACHABLE(); +} + +void ETSTypeParameter::Identical([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *other) +{ + UNREACHABLE(); +} + +void ETSTypeParameter::AssignmentTarget([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *source) +{ + UNREACHABLE(); +} +} // namespace panda::es2panda::checker diff --git a/checker/types/ets/etsTypeParameter.h b/checker/types/ets/etsTypeParameter.h new file mode 100644 index 0000000000000000000000000000000000000000..a085f1be09bb634ae39ea8f7a10b1476bc0e9c4a --- /dev/null +++ b/checker/types/ets/etsTypeParameter.h @@ -0,0 +1,64 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_ETS_TYPE_PARAMETER_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_TYPE_PARAMETER_TYPE_H + +#include "plugins/ecmascript/es2panda/checker/types/type.h" + +namespace panda::es2panda::checker { +class ETSTypeParameter : public Type { +public: + explicit ETSTypeParameter() : Type(TypeFlag::ETS_TYPE_PARAMETER) {} + explicit ETSTypeParameter(Type *assemblerType) : Type(TypeFlag::ETS_TYPE_PARAMETER), assemblerType_(assemblerType) + { + } + + void SetType(Type *type) + { + type_ = type; + } + + Type *GetType() + { + return type_; + } + + Type *GetAssemblerType() + { + return assemblerType_; + } + + Type **GetTypeRef() + { + return &type_; + } + + Type **GetAssemblerTypeRef() + { + return &assemblerType_; + } + + void ToString(std::stringstream &ss) const override; + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + +private: + Type *type_ {}; + Type *assemblerType_ {}; +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/types/ets/etsTypeReference.cpp b/checker/types/ets/etsTypeReference.cpp new file mode 100644 index 0000000000000000000000000000000000000000..55f8f7327724335a19ff01a4dd1a781d897521bf --- /dev/null +++ b/checker/types/ets/etsTypeReference.cpp @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2021-2022 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 "etsTypeReference.h" +#include "plugins/ecmascript/es2panda/checker/types/typeRelation.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" + +namespace panda::es2panda::checker { + +util::StringView ETSTypeReference::ReferencedName() const +{ + return varRef_->Name(); +} + +void ETSTypeReference::ToString(std::stringstream &ss) const +{ + ss << ReferencedName(); +} + +void ETSTypeReference::ToAssemblerType(std::stringstream &ss) const +{ + ASSERT(*assemblerRef_); + (*assemblerRef_)->ToAssemblerTypeWithRank(ss); +} + +void ETSTypeReference::Identical(TypeRelation *relation, Type *other) +{ + if ((*ref_) != nullptr) { + (*ref_)->Identical(relation, other); + return; + } + + if (!other->IsETSTypeReference()) { + return; + } + + relation->Result(varRef_ == other->AsETSTypeReference()->VarRef()); +} + +void ETSTypeReference::AssignmentTarget(TypeRelation *relation, Type *source) +{ + if ((*ref_) != nullptr) { + (*ref_)->AssignmentTarget(relation, source); + } +} + +bool ETSTypeReference::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) +{ + if ((*ref_) == nullptr) { + return false; + } + + if (!target->IsETSTypeReference()) { + return true; + } + + if (!relation->IsAssignableTo(this, target)) { + return true; + } + + relation->Result(true); + return false; +} + +Type *ETSTypeReference::Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) +{ + if ((*ref_) != nullptr) { + return relation->GetChecker()->AsETSChecker()->TryToInstantiate(*ref_, allocator, relation, globalTypes); + } + + return relation->GetChecker()->AsETSChecker()->CreateTypeReference(ref_, assemblerRef_, varRef_); +} +} // namespace panda::es2panda::checker diff --git a/checker/types/ets/etsTypeReference.h b/checker/types/ets/etsTypeReference.h new file mode 100644 index 0000000000000000000000000000000000000000..90c0764d47cd529f0d91baaf365ffb07baedc0d4 --- /dev/null +++ b/checker/types/ets/etsTypeReference.h @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_ETS_TYPE_REFERENCE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_TYPE_REFERENCE_H + +#include "plugins/ecmascript/es2panda/checker/types/type.h" + +namespace panda::es2panda::binder { +class LocalVariable; +} // namespace panda::es2panda::binder + +namespace panda::es2panda::checker { +class ETSTypeReference : public Type { +public: + explicit ETSTypeReference(Type **ref, Type **assemblerRef, binder::LocalVariable *varRef) + : Type(TypeFlag::ETS_TYPE_REFERENCE), ref_(ref), assemblerRef_(assemblerRef), varRef_(varRef) + { + } + + Type *Ref() + { + return *ref_; + } + + const Type *Ref() const + { + return *ref_; + } + + binder::LocalVariable *VarRef() + { + return varRef_; + } + + const binder::LocalVariable *VarRef() const + { + return varRef_; + } + + util::StringView ReferencedName() const; + + void ToString(std::stringstream &ss) const override; + void ToAssemblerType(std::stringstream &ss) const override; + void Identical(TypeRelation *relation, Type *other) override; + bool AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + +private: + Type **ref_; + Type **assemblerRef_; + binder::LocalVariable *varRef_ {}; +}; +} // namespace panda::es2panda::checker + +#endif /* ES2PANDA_COMPILER_CHECKER_TYPES_TS_TYPE_REFERENCE_H */ diff --git a/checker/types/ets/etsVoidType.cpp b/checker/types/ets/etsVoidType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3a7dd3bb11cb4ad4ba0f817dcd883880fc5711fe --- /dev/null +++ b/checker/types/ets/etsVoidType.cpp @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2021-2022 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 "etsVoidType.h" + +namespace panda::es2panda::checker { +void ETSVoidType::Identical(TypeRelation *relation, Type *other) +{ + if (other->IsETSVoidType()) { + relation->Result(true); + } +} + +void ETSVoidType::AssignmentTarget([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *source) {} + +Type *ETSVoidType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation, + [[maybe_unused]] GlobalTypesHolder *globalTypes) +{ + return this; +} +} // namespace panda::es2panda::checker diff --git a/lexer/templates/keywordsMap.h.erb b/checker/types/ets/etsVoidType.h similarity index 42% rename from lexer/templates/keywordsMap.h.erb rename to checker/types/ets/etsVoidType.h index 46d4cc8bb71168a2192318ccf6e2891e82176f48..7ebd9a25c61321f12d40fd8d4043b79c8c3bf557 100644 --- a/lexer/templates/keywordsMap.h.erb +++ b/checker/types/ets/etsVoidType.h @@ -13,37 +13,32 @@ * limitations under the License. */ -// Autogenerated file -- DO NOT EDIT! +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_ETS_VOID_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_VOID_TYPE_H -#include "plugins/ecmascript/es2panda/lexer/keywordString.h" -#include "utils/span.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" -namespace panda::es2panda::lexer { -class KeywordsMap { +namespace panda::es2panda::checker { +class ETSVoidType : public Type { public: - KeywordsMap() = delete; - - static Span Map(char32_t cp) { - switch(cp) { -% keywords.each do |group| - case LEX_CHAR_LOWERCASE_<%= group.keys[0][0].upcase %>: { - return Span(KEYWORDS_<%= group.keys[0][0].upcase %>); - } -% end - default: { - return Span(); - } - } + ETSVoidType() : Type(TypeFlag::ETS_VOID) {} + + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + + void ToString(std::stringstream &ss) const override + { + ss << "void"; } -% keywords.each do |group| + void ToAssemblerType([[maybe_unused]] std::stringstream &ss) const override + { + ss << compiler::Signatures::PRIMITIVE_VOID; + } - static constexpr std::array> KEYWORDS_<%= group.keys[0][0].upcase %> = {{ -% group.each do |key, kw| - {"<%= key%>", <%= kw[0] %>, <%= kw[1] %>}, -% end - }}; -% end +private: }; +} // namespace panda::es2panda::checker -} // namespace panda::es2panda::lexer +#endif diff --git a/checker/types/ets/floatType.cpp b/checker/types/ets/floatType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fbebd8d1835e25d6209c06376872a320fac8986b --- /dev/null +++ b/checker/types/ets/floatType.cpp @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2021-2022 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 "floatType.h" + +#include "plugins/ecmascript/es2panda/checker/ets/boxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/unboxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/narrowingWideningConverter.h" + +namespace panda::es2panda::checker { +void FloatType::Identical(TypeRelation *relation, Type *other) +{ + if (other->IsFloatType()) { + relation->Result(true); + } +} + +void FloatType::AssignmentTarget(TypeRelation *relation, [[maybe_unused]] Type *source) +{ + if (relation->ApplyUnboxing()) { + UnboxingConverter(relation->GetChecker()->AsETSChecker(), relation, source, this); + } + NarrowingWideningConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); +} + +bool FloatType::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) +{ + if (relation->InAssignmentContext()) { + checker::SavedTypeRelationFlagsContext savedTypeRelationFlagCtx( + relation, checker::TypeRelationFlag::ONLY_CHECK_WIDENING | checker::TypeRelationFlag::NARROWING); + auto unboxedType = relation->GetChecker()->AsETSChecker()->ETSBuiltinTypeAsPrimitiveType(target); + if (unboxedType != nullptr) { + NarrowingWideningConverter(relation->GetChecker()->AsETSChecker(), relation, unboxedType, this); + } + } + + if (relation->ApplyBoxing()) { + BoxingConverter(relation->GetChecker()->AsETSChecker(), relation, this, target); + } + + return relation->IsTrue(); +} + +Type *FloatType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation, + [[maybe_unused]] GlobalTypesHolder *globalTypes) +{ + return this; +} +} // namespace panda::es2panda::checker diff --git a/checker/types/ets/floatType.h b/checker/types/ets/floatType.h new file mode 100644 index 0000000000000000000000000000000000000000..642b8d7d387c499c7962288feadc3d8659f5de8e --- /dev/null +++ b/checker/types/ets/floatType.h @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_ETS_FLOAT_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_FLOAT_TYPE_H + +#include "plugins/ecmascript/es2panda/checker/types/type.h" + +namespace panda::es2panda::checker { +class FloatType : public Type { +public: + using UType = float; + + FloatType() : Type(TypeFlag::FLOAT) {} + explicit FloatType(UType value) : Type(TypeFlag::FLOAT | TypeFlag::CONSTANT), value_(value) {} + + UType GetValue() const + { + return value_; + } + + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + bool AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + + void ToString(std::stringstream &ss) const override + { + ss << "float"; + } + + void ToAssemblerType([[maybe_unused]] std::stringstream &ss) const override + { + ss << compiler::Signatures::PRIMITIVE_FLOAT; + } + +private: + UType value_ {0.0}; +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/types/ets/intType.cpp b/checker/types/ets/intType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc50dcdf8bc8e14fe05cd98396bb2fb1e79727b9 --- /dev/null +++ b/checker/types/ets/intType.cpp @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2021-2022 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 "intType.h" + +#include "plugins/ecmascript/es2panda/checker/ets/boxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/unboxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/narrowingWideningConverter.h" + +namespace panda::es2panda::checker { +void IntType::Identical(TypeRelation *relation, Type *other) +{ + if (other->IsIntType()) { + relation->Result(true); + } +} + +void IntType::AssignmentTarget(TypeRelation *relation, [[maybe_unused]] Type *source) +{ + if (relation->ApplyUnboxing()) { + UnboxingConverter(relation->GetChecker()->AsETSChecker(), relation, source, this); + } + NarrowingWideningConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); +} + +bool IntType::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) +{ + if (relation->InAssignmentContext()) { + checker::SavedTypeRelationFlagsContext savedTypeRelationFlagCtx( + relation, checker::TypeRelationFlag::ONLY_CHECK_WIDENING | checker::TypeRelationFlag::NARROWING); + auto unboxedType = relation->GetChecker()->AsETSChecker()->ETSBuiltinTypeAsPrimitiveType(target); + if (unboxedType != nullptr) { + NarrowingWideningConverter(relation->GetChecker()->AsETSChecker(), relation, unboxedType, this); + } + } + + if (relation->ApplyBoxing()) { + BoxingConverter(relation->GetChecker()->AsETSChecker(), relation, this, target); + } + + return relation->IsTrue(); +} + +Type *IntType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation, + [[maybe_unused]] GlobalTypesHolder *globalTypes) +{ + return this; +} +} // namespace panda::es2panda::checker diff --git a/checker/types/ets/intType.h b/checker/types/ets/intType.h new file mode 100644 index 0000000000000000000000000000000000000000..00916e43daadf1c00e9940de39b32a55340380d2 --- /dev/null +++ b/checker/types/ets/intType.h @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_ETS_INT_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_INT_TYPE_H + +#include "plugins/ecmascript/es2panda/checker/types/type.h" + +namespace panda::es2panda::checker { +class IntType : public Type { +public: + using UType = int32_t; + + IntType() : Type(TypeFlag::INT) {} + explicit IntType(UType value) : Type(TypeFlag::INT | TypeFlag::CONSTANT), value_(value) {} + + UType GetValue() const + { + return value_; + } + + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + bool AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + + void ToString(std::stringstream &ss) const override + { + ss << "int"; + } + + void ToAssemblerType([[maybe_unused]] std::stringstream &ss) const override + { + ss << compiler::Signatures::PRIMITIVE_INT; + } + +private: + UType value_ {0}; +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/types/ets/longType.cpp b/checker/types/ets/longType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fbe9345addd08ece717355542d9b96d5f5be60cb --- /dev/null +++ b/checker/types/ets/longType.cpp @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2021-2022 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 "longType.h" + +#include "plugins/ecmascript/es2panda/checker/ets/boxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/unboxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/narrowingWideningConverter.h" + +namespace panda::es2panda::checker { +void LongType::Identical(TypeRelation *relation, Type *other) +{ + if (other->IsLongType()) { + relation->Result(true); + } +} + +void LongType::AssignmentTarget(TypeRelation *relation, [[maybe_unused]] Type *source) +{ + if (relation->ApplyUnboxing()) { + UnboxingConverter(relation->GetChecker()->AsETSChecker(), relation, source, this); + } + NarrowingWideningConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); +} + +bool LongType::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) +{ + if (relation->InAssignmentContext()) { + checker::SavedTypeRelationFlagsContext savedTypeRelationFlagCtx( + relation, checker::TypeRelationFlag::ONLY_CHECK_WIDENING | checker::TypeRelationFlag::NARROWING); + auto unboxedType = relation->GetChecker()->AsETSChecker()->ETSBuiltinTypeAsPrimitiveType(target); + if (unboxedType != nullptr) { + NarrowingWideningConverter(relation->GetChecker()->AsETSChecker(), relation, unboxedType, this); + } + } + + if (relation->ApplyBoxing()) { + BoxingConverter(relation->GetChecker()->AsETSChecker(), relation, this, target); + } + + return relation->IsTrue(); +} + +Type *LongType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation, + [[maybe_unused]] GlobalTypesHolder *globalTypes) +{ + return this; +} +} // namespace panda::es2panda::checker diff --git a/checker/types/ets/longType.h b/checker/types/ets/longType.h new file mode 100644 index 0000000000000000000000000000000000000000..af9f0f504043113791a11d62421c1436f7cde2bc --- /dev/null +++ b/checker/types/ets/longType.h @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_ETS_LONG_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_LONG_TYPE_H + +#include "plugins/ecmascript/es2panda/checker/types/type.h" + +namespace panda::es2panda::checker { +class LongType : public Type { +public: + using UType = int64_t; + + LongType() : Type(TypeFlag::LONG) {} + explicit LongType(UType value) : Type(TypeFlag::LONG | TypeFlag::CONSTANT), value_(value) {} + + UType GetValue() const + { + return value_; + } + + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + bool AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + + void ToString(std::stringstream &ss) const override + { + ss << "long"; + } + + void ToAssemblerType([[maybe_unused]] std::stringstream &ss) const override + { + ss << compiler::Signatures::PRIMITIVE_LONG; + } + +private: + UType value_ {0}; +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/types/ets/shortType.cpp b/checker/types/ets/shortType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5c5431c582a5022b7b93f3d4b9f5bb4ef5a39309 --- /dev/null +++ b/checker/types/ets/shortType.cpp @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2021-2022 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 "shortType.h" + +#include "plugins/ecmascript/es2panda/checker/ets/boxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/unboxingConverter.h" +#include "plugins/ecmascript/es2panda/checker/ets/narrowingWideningConverter.h" + +namespace panda::es2panda::checker { +void ShortType::Identical(TypeRelation *relation, Type *other) +{ + if (other->IsShortType()) { + relation->Result(true); + } +} + +void ShortType::AssignmentTarget(TypeRelation *relation, [[maybe_unused]] Type *source) +{ + if (relation->ApplyUnboxing()) { + UnboxingConverter(relation->GetChecker()->AsETSChecker(), relation, source, this); + } + NarrowingWideningConverter(relation->GetChecker()->AsETSChecker(), relation, this, source); +} + +bool ShortType::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) +{ + if (relation->InAssignmentContext()) { + checker::SavedTypeRelationFlagsContext savedTypeRelationFlagCtx( + relation, checker::TypeRelationFlag::ONLY_CHECK_WIDENING | checker::TypeRelationFlag::NARROWING); + auto unboxedType = relation->GetChecker()->AsETSChecker()->ETSBuiltinTypeAsPrimitiveType(target); + if (unboxedType != nullptr) { + NarrowingWideningConverter(relation->GetChecker()->AsETSChecker(), relation, unboxedType, this); + } + } + + if (relation->ApplyBoxing()) { + BoxingConverter(relation->GetChecker()->AsETSChecker(), relation, this, target); + } + + return relation->IsTrue(); +} + +Type *ShortType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation, + [[maybe_unused]] GlobalTypesHolder *globalTypes) +{ + return this; +} +} // namespace panda::es2panda::checker diff --git a/checker/types/ets/shortType.h b/checker/types/ets/shortType.h new file mode 100644 index 0000000000000000000000000000000000000000..ea685bd6715245836885206f5f8d9172cefa5e55 --- /dev/null +++ b/checker/types/ets/shortType.h @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_ETS_SHORT_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_SHORT_TYPE_H + +#include "plugins/ecmascript/es2panda/checker/types/type.h" + +namespace panda::es2panda::checker { +class ShortType : public Type { +public: + using UType = int16_t; + + ShortType() : Type(TypeFlag::SHORT) {} + explicit ShortType(UType value) : Type(TypeFlag::SHORT | TypeFlag::CONSTANT), value_(value) {} + + UType GetValue() const + { + return value_; + } + + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + bool AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *target) override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + + void ToString(std::stringstream &ss) const override + { + ss << "short"; + } + + void ToAssemblerType([[maybe_unused]] std::stringstream &ss) const override + { + ss << compiler::Signatures::PRIMITIVE_SHORT; + } + +private: + UType value_ {0}; +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/checker/types/ets/types.h b/checker/types/ets/types.h new file mode 100644 index 0000000000000000000000000000000000000000..5cad297bafd80cd08a1ce6c356d5213c77fe155b --- /dev/null +++ b/checker/types/ets/types.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_ETS_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_H + +#include "byteType.h" +#include "charType.h" +#include "doubleType.h" +#include "floatType.h" +#include "intType.h" +#include "longType.h" +#include "shortType.h" +#include "etsBooleanType.h" +#include "etsFunctionType.h" +#include "etsVoidType.h" +#include "etsStringType.h" +#include "etsObjectType.h" +#include "etsArrayType.h" +#include "wildcardType.h" +#include "etsTypeReference.h" +#include "etsTypeParameter.h" +#include "plugins/ecmascript/es2panda/checker/types/signature.h" + +#endif /* TYPES_H */ diff --git a/checker/types/ets/wildcardType.cpp b/checker/types/ets/wildcardType.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ca8c974c8f3377960198601f2f93ab72895bb176 --- /dev/null +++ b/checker/types/ets/wildcardType.cpp @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2021-2022 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 "wildcardType.h" + +namespace panda::es2panda::checker { +void WildcardType::ToString(std::stringstream &ss) const +{ + ss << "wildcard"; +} + +void WildcardType::Identical(TypeRelation *relation, Type *other) +{ + if (other->IsWildcardType()) { + relation->Result(true); + } +} + +void WildcardType::AssignmentTarget([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *source) {} + +Type *WildcardType::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation, + [[maybe_unused]] GlobalTypesHolder *globalTypes) +{ + return this; +} +} // namespace panda::es2panda::checker diff --git a/checker/types/ets/wildcardType.h b/checker/types/ets/wildcardType.h new file mode 100644 index 0000000000000000000000000000000000000000..a0b3df5a4e2adf33ba9af9558a63c676c6051737 --- /dev/null +++ b/checker/types/ets/wildcardType.h @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_ETS_WILDCARD_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_ETS_WILDCARD_TYPE_H + +#include "plugins/ecmascript/es2panda/checker/types/type.h" + +namespace panda::es2panda::checker { +class WildcardType : public Type { +public: + WildcardType() : Type(TypeFlag::WILDCARD) {} + + void ToString(std::stringstream &ss) const override; + void Identical(TypeRelation *relation, Type *other) override; + void AssignmentTarget(TypeRelation *relation, Type *source) override; + Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) override; + + void ToAssemblerType([[maybe_unused]] std::stringstream &ss) const override + { + ss << "wildcard"; + } +}; +} // namespace panda::es2panda::checker + +#endif diff --git a/typescript/types/globalTypesHolder.cpp b/checker/types/globalTypesHolder.cpp similarity index 32% rename from typescript/types/globalTypesHolder.cpp rename to checker/types/globalTypesHolder.cpp index 0b0a4e0db0545ec103b24a78218e2b6e4c7eee06..ea93ab12ea80dcdfcee6c26887a0622b686fe880 100644 --- a/typescript/types/globalTypesHolder.cpp +++ b/checker/types/globalTypesHolder.cpp @@ -15,28 +15,41 @@ #include "globalTypesHolder.h" -#include "plugins/ecmascript/es2panda/typescript/types/numberType.h" -#include "plugins/ecmascript/es2panda/typescript/types/anyType.h" -#include "plugins/ecmascript/es2panda/typescript/types/stringType.h" -#include "plugins/ecmascript/es2panda/typescript/types/booleanType.h" -#include "plugins/ecmascript/es2panda/typescript/types/voidType.h" -#include "plugins/ecmascript/es2panda/typescript/types/nullType.h" -#include "plugins/ecmascript/es2panda/typescript/types/undefinedType.h" -#include "plugins/ecmascript/es2panda/typescript/types/unknownType.h" -#include "plugins/ecmascript/es2panda/typescript/types/neverType.h" -#include "plugins/ecmascript/es2panda/typescript/types/nonPrimitiveType.h" -#include "plugins/ecmascript/es2panda/typescript/types/bigintType.h" -#include "plugins/ecmascript/es2panda/typescript/types/booleanLiteralType.h" -#include "plugins/ecmascript/es2panda/typescript/types/bigintLiteralType.h" -#include "plugins/ecmascript/es2panda/typescript/types/numberLiteralType.h" -#include "plugins/ecmascript/es2panda/typescript/types/stringLiteralType.h" -#include "plugins/ecmascript/es2panda/typescript/types/tupleType.h" -#include "plugins/ecmascript/es2panda/typescript/types/objectLiteralType.h" -#include "plugins/ecmascript/es2panda/typescript/types/unionType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/numberType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/anyType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/stringType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/booleanType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/voidType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/nullType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/undefinedType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/unknownType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/neverType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/nonPrimitiveType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/bigintType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/booleanLiteralType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/bigintLiteralType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/numberLiteralType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/stringLiteralType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/tupleType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/objectLiteralType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/unionType.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/byteType.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/charType.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/doubleType.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/floatType.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/intType.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/longType.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/shortType.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/etsBooleanType.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/etsStringType.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/etsVoidType.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/etsObjectType.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/wildcardType.h" namespace panda::es2panda::checker { -GlobalTypesHolder::GlobalTypesHolder(ArenaAllocator *allocator) +GlobalTypesHolder::GlobalTypesHolder(ArenaAllocator *allocator) : builtinNameMappings_(allocator->Adapter()) { + // TS specific types globalTypes_[static_cast(GlobalTypeId::NUMBER)] = allocator->New(); globalTypes_[static_cast(GlobalTypeId::ANY)] = allocator->New(); globalTypes_[static_cast(GlobalTypeId::STRING)] = allocator->New(); @@ -65,6 +78,58 @@ GlobalTypesHolder::GlobalTypesHolder(ArenaAllocator *allocator) globalTypes_[static_cast(GlobalTypeId::EMPTY_OBJECT)] = allocator->New(); globalTypes_[static_cast(GlobalTypeId::RESOLVING_RETURN_TYPE)] = allocator->New(); globalTypes_[static_cast(GlobalTypeId::ERROR_TYPE)] = allocator->New(); + + // ETS specific types + globalTypes_[static_cast(GlobalTypeId::BYTE)] = allocator->New(); + globalTypes_[static_cast(GlobalTypeId::SHORT)] = allocator->New(); + globalTypes_[static_cast(GlobalTypeId::INT)] = allocator->New(); + globalTypes_[static_cast(GlobalTypeId::LONG)] = allocator->New(); + globalTypes_[static_cast(GlobalTypeId::FLOAT)] = allocator->New(); + globalTypes_[static_cast(GlobalTypeId::DOUBLE)] = allocator->New(); + globalTypes_[static_cast(GlobalTypeId::CHAR)] = allocator->New(); + globalTypes_[static_cast(GlobalTypeId::ETS_BOOLEAN)] = allocator->New(); + globalTypes_[static_cast(GlobalTypeId::ETS_VOID)] = allocator->New(); + auto *globalNullType = allocator->New(allocator); + globalNullType->AsETSObjectType()->AddObjectFlag(ETSObjectFlags::NULL_TYPE); + globalTypes_[static_cast(GlobalTypeId::ETS_NULL)] = globalNullType; + globalTypes_[static_cast(GlobalTypeId::ETS_WILDCARD)] = allocator->New(); + + builtinNameMappings_.emplace("Boolean", GlobalTypeId::ETS_BOOLEAN_BUILTIN); + builtinNameMappings_.emplace("Byte", GlobalTypeId::ETS_BYTE_BUILTIN); + builtinNameMappings_.emplace("Char", GlobalTypeId::ETS_CHAR_BUILTIN); + builtinNameMappings_.emplace("Comparable", GlobalTypeId::ETS_COMPARABLE_BUILTIN); + builtinNameMappings_.emplace("Console", GlobalTypeId::ETS_CONSOLE_BUILTIN); + builtinNameMappings_.emplace("Date", GlobalTypeId::ETS_DATE_BUILTIN); + builtinNameMappings_.emplace("Double", GlobalTypeId::ETS_DOUBLE_BUILTIN); + builtinNameMappings_.emplace("Exception", GlobalTypeId::ETS_EXCEPTION_BUILTIN); + builtinNameMappings_.emplace("Float", GlobalTypeId::ETS_FLOAT_BUILTIN); + builtinNameMappings_.emplace("Floating", GlobalTypeId::ETS_FLOATING_BUILTIN); + builtinNameMappings_.emplace("Int", GlobalTypeId::ETS_INTEGER_BUILTIN); + builtinNameMappings_.emplace("Integral", GlobalTypeId::ETS_INTEGRAL_BUILTIN); + builtinNameMappings_.emplace("Long", GlobalTypeId::ETS_LONG_BUILTIN); + builtinNameMappings_.emplace("Map", GlobalTypeId::ETS_MAP_BUILTIN); + builtinNameMappings_.emplace("Object", GlobalTypeId::ETS_OBJECT_BUILTIN); + builtinNameMappings_.emplace("Panic", GlobalTypeId::ETS_PANIC_BUILTIN); + builtinNameMappings_.emplace("Runtime", GlobalTypeId::ETS_RUNTIME_BUILTIN); + builtinNameMappings_.emplace("Set", GlobalTypeId::ETS_SET_BUILTIN); + builtinNameMappings_.emplace("Short", GlobalTypeId::ETS_SHORT_BUILTIN); + builtinNameMappings_.emplace("StackTraceElement", GlobalTypeId::ETS_STACK_TRACE_ELEMENT_BUILTIN); + builtinNameMappings_.emplace("StackTrace", GlobalTypeId::ETS_STACK_TRACE_BUILTIN); + builtinNameMappings_.emplace("NullPointerException", GlobalTypeId::ETS_NULL_POINTER_EXCEPTION_BUILTIN); + builtinNameMappings_.emplace("ArrayIndexOutOfBoundsException", + GlobalTypeId::ETS_ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION_BUILTIN); + builtinNameMappings_.emplace("ArithmeticException", GlobalTypeId::ETS_ARITHMETIC_EXCEPTION_BUILTIN); + builtinNameMappings_.emplace("ClassNotFoundException", GlobalTypeId::ETS_CLASS_NOT_FOUND_EXCEPTION_BUILTIN); + builtinNameMappings_.emplace("OutOfMemoryError", GlobalTypeId::ETS_OUT_OF_MEMORY_ERROR_BUILTIN); + builtinNameMappings_.emplace("NoSuchMethodError", GlobalTypeId::ETS_NO_SUCH_METHOD_ERROR_BUILTIN); + builtinNameMappings_.emplace("AssertionPanic", GlobalTypeId::ETS_ASSERTION_PANIC_BUILTIN); + builtinNameMappings_.emplace("DivideByZeroPanic", GlobalTypeId::ETS_DIVIDE_BY_ZERO_PANIC_BUILTIN); + builtinNameMappings_.emplace("NullPointerPanic", GlobalTypeId::ETS_NULL_POINTER_PANIC_BUILTIN); + builtinNameMappings_.emplace("UncatchedExceptionPanic", GlobalTypeId::ETS_UNCATCHED_EXCEPTION_PANIC_BUILTIN); + builtinNameMappings_.emplace("String", GlobalTypeId::ETS_STRING_BUILTIN); + builtinNameMappings_.emplace("StringBuilder", GlobalTypeId::ETS_STRING_BUILDER_BUILTIN); + builtinNameMappings_.emplace("Type", GlobalTypeId::ETS_TYPE_BUILTIN); + builtinNameMappings_.emplace("Types", GlobalTypeId::ETS_TYPES_BUILTIN); } Type *GlobalTypesHolder::GlobalNumberType() @@ -181,4 +246,245 @@ Type *GlobalTypesHolder::GlobalErrorType() { return globalTypes_.at(static_cast(GlobalTypeId::ERROR_TYPE)); } + +Type *GlobalTypesHolder::GlobalByteType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::BYTE)); +} + +Type *GlobalTypesHolder::GlobalShortType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::SHORT)); +} + +Type *GlobalTypesHolder::GlobalIntType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::INT)); +} + +Type *GlobalTypesHolder::GlobalLongType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::LONG)); +} + +Type *GlobalTypesHolder::GlobalFloatType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::FLOAT)); +} + +Type *GlobalTypesHolder::GlobalDoubleType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::DOUBLE)); +} + +Type *GlobalTypesHolder::GlobalCharType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::CHAR)); +} + +Type *GlobalTypesHolder::GlobalETSBooleanType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_BOOLEAN)); +} + +Type *GlobalTypesHolder::GlobalETSStringLiteralType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_STRING)); +} + +Type *GlobalTypesHolder::GlobalETSVoidType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_VOID)); +} + +Type *GlobalTypesHolder::GlobalETSObjectType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_OBJECT_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalETSNullType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_NULL)); +} + +Type *GlobalTypesHolder::GlobalWildcardType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_WILDCARD)); +} + +Type *GlobalTypesHolder::GlobalETSBooleanBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_BOOLEAN_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalByteBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_BYTE_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalCharBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_CHAR_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalComparableBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_COMPARABLE_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalConsoleBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_CONSOLE_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalDoubleBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_DOUBLE_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalExceptionBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_EXCEPTION_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalFloatBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_FLOAT_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalFloatingBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_FLOATING_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalIntegerBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_INTEGER_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalIntegralBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_INTEGRAL_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalLongBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_LONG_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalMapBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_MAP_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalPanicBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_PANIC_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalRuntimeBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_RUNTIME_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalSetBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_SET_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalShortBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_SHORT_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalStackTraceElementBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_STACK_TRACE_ELEMENT_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalStackTraceBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_STACK_TRACE_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalNullPointerExceptionBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_NULL_POINTER_EXCEPTION_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalArrayIndexOutOfBoundsExceptionBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalArithmeticExceptionBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_ARITHMETIC_EXCEPTION_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalClassNotFoundExceptionBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_CLASS_NOT_FOUND_EXCEPTION_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalClassOutOfMemoryErrorBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_OUT_OF_MEMORY_ERROR_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalNoSuchMethodErrorBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_NO_SUCH_METHOD_ERROR_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalAssertionPanicBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_ASSERTION_PANIC_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalDivideByZeroPanicBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_DIVIDE_BY_ZERO_PANIC_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalNullPointerPanicBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_NULL_POINTER_PANIC_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalUncatchedExceptionPanicBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_UNCATCHED_EXCEPTION_PANIC_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalETSStringBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_STRING_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalStringBuilderBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_STRING_BUILDER_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalTypeBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_TYPE_BUILTIN)); +} + +Type *GlobalTypesHolder::GlobalTypesBuiltinType() +{ + return globalTypes_.at(static_cast(GlobalTypeId::ETS_TYPES_BUILTIN)); +} + +void GlobalTypesHolder::InitializeBuiltin(const util::StringView name, Type *type) +{ + const auto typeId = builtinNameMappings_.find(name); + if (typeId == builtinNameMappings_.end()) { + std::cerr << "WARNING: Did not find '" << name << "' builtin in GlobalTypeHolder, it should be added." + << std::endl; + return; + } + globalTypes_.at(static_cast(typeId->second)) = type; +} } // namespace panda::es2panda::checker diff --git a/typescript/types/globalTypesHolder.h b/checker/types/globalTypesHolder.h similarity index 35% rename from typescript/types/globalTypesHolder.h rename to checker/types/globalTypesHolder.h index 6981d5da7c6a01cc0314314770aba7a136b66517..5cd1d3a8930fcd5f356c5670a3ca71719f401b33 100644 --- a/typescript/types/globalTypesHolder.h +++ b/checker/types/globalTypesHolder.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_GLOBAL_TYPES_HOLDER_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_GLOBAL_TYPES_HOLDER_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_GLOBAL_TYPES_HOLDER_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_GLOBAL_TYPES_HOLDER_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { enum class GlobalTypeId { @@ -43,7 +43,54 @@ enum class GlobalTypeId { EMPTY_OBJECT, RESOLVING_RETURN_TYPE, ERROR_TYPE, - COUNT + BYTE, + SHORT, + INT, + LONG, + FLOAT, + DOUBLE, + CHAR, + ETS_BOOLEAN, + ETS_STRING, + ETS_VOID, + ETS_OBJECT_BUILTIN, + ETS_NULL, + ETS_WILDCARD, + ETS_BOOLEAN_BUILTIN, + ETS_BYTE_BUILTIN, + ETS_CHAR_BUILTIN, + ETS_COMPARABLE_BUILTIN, + ETS_CONSOLE_BUILTIN, + ETS_DATE_BUILTIN, + ETS_DOUBLE_BUILTIN, + ETS_EXCEPTION_BUILTIN, + ETS_FLOAT_BUILTIN, + ETS_FLOATING_BUILTIN, + ETS_INTEGER_BUILTIN, + ETS_INTEGRAL_BUILTIN, + ETS_LONG_BUILTIN, + ETS_MAP_BUILTIN, + ETS_PANIC_BUILTIN, + ETS_RUNTIME_BUILTIN, + ETS_SET_BUILTIN, + ETS_SHORT_BUILTIN, + ETS_STACK_TRACE_ELEMENT_BUILTIN, + ETS_STACK_TRACE_BUILTIN, + ETS_NULL_POINTER_EXCEPTION_BUILTIN, + ETS_ARRAY_INDEX_OUT_OF_BOUNDS_EXCEPTION_BUILTIN, + ETS_ARITHMETIC_EXCEPTION_BUILTIN, + ETS_CLASS_NOT_FOUND_EXCEPTION_BUILTIN, + ETS_OUT_OF_MEMORY_ERROR_BUILTIN, + ETS_NO_SUCH_METHOD_ERROR_BUILTIN, + ETS_ASSERTION_PANIC_BUILTIN, + ETS_DIVIDE_BY_ZERO_PANIC_BUILTIN, + ETS_NULL_POINTER_PANIC_BUILTIN, + ETS_UNCATCHED_EXCEPTION_PANIC_BUILTIN, + ETS_STRING_BUILTIN, + ETS_STRING_BUILDER_BUILTIN, + ETS_TYPE_BUILTIN, + ETS_TYPES_BUILTIN, + COUNT, }; class GlobalTypesHolder { @@ -53,6 +100,7 @@ public: NO_COPY_SEMANTIC(GlobalTypesHolder); NO_MOVE_SEMANTIC(GlobalTypesHolder); + // TS specific types Type *GlobalNumberType(); Type *GlobalAnyType(); Type *GlobalStringType(); @@ -77,9 +125,72 @@ public: Type *GlobalResolvingReturnType(); Type *GlobalErrorType(); + // ETS specific types + Type *GlobalByteType(); + Type *GlobalShortType(); + Type *GlobalIntType(); + Type *GlobalLongType(); + Type *GlobalFloatType(); + Type *GlobalDoubleType(); + Type *GlobalCharType(); + Type *GlobalETSBooleanType(); + Type *GlobalETSStringLiteralType(); + Type *GlobalETSVoidType(); + Type *GlobalETSObjectType(); + Type *GlobalETSNullType(); + Type *GlobalWildcardType(); + Type *GlobalETSBooleanBuiltinType(); + Type *GlobalByteBuiltinType(); + Type *GlobalCharBuiltinType(); + Type *GlobalComparableBuiltinType(); + Type *GlobalConsoleBuiltinType(); + Type *GlobalDoubleBuiltinType(); + Type *GlobalExceptionBuiltinType(); + Type *GlobalFloatBuiltinType(); + Type *GlobalFloatingBuiltinType(); + Type *GlobalIntegerBuiltinType(); + Type *GlobalIntegralBuiltinType(); + Type *GlobalLongBuiltinType(); + Type *GlobalMapBuiltinType(); + Type *GlobalPanicBuiltinType(); + Type *GlobalRuntimeBuiltinType(); + Type *GlobalSetBuiltinType(); + Type *GlobalShortBuiltinType(); + Type *GlobalStackTraceElementBuiltinType(); + Type *GlobalStackTraceBuiltinType(); + Type *GlobalNullPointerExceptionBuiltinType(); + Type *GlobalArrayIndexOutOfBoundsExceptionBuiltinType(); + Type *GlobalArithmeticExceptionBuiltinType(); + Type *GlobalClassNotFoundExceptionBuiltinType(); + Type *GlobalClassOutOfMemoryErrorBuiltinType(); + Type *GlobalNoSuchMethodErrorBuiltinType(); + Type *GlobalAssertionPanicBuiltinType(); + Type *GlobalDivideByZeroPanicBuiltinType(); + Type *GlobalNullPointerPanicBuiltinType(); + Type *GlobalUncatchedExceptionPanicBuiltinType(); + Type *GlobalETSStringBuiltinType(); + Type *GlobalStringBuilderBuiltinType(); + Type *GlobalTypeBuiltinType(); + Type *GlobalTypesBuiltinType(); + + void InitializeBuiltin(util::StringView name, Type *type); + + using Holder = std::array(GlobalTypeId::COUNT)>; + + Holder &GlobalTypes() + { + return globalTypes_; + } + + const Holder &GlobalTypes() const + { + return globalTypes_; + } + private: - std::array(GlobalTypeId::COUNT)> globalTypes_ {}; + Holder globalTypes_ {}; + ArenaMap builtinNameMappings_; }; } // namespace panda::es2panda::checker -#endif /* ES2PANDA_COMPILER_TYPESCRIPT_TYPES_GLOBAL_TYPES_HOLDER_H */ +#endif /* ES2PANDA_COMPILER_CHECKER_TYPES_TS_GLOBAL_TYPES_HOLDER_H */ diff --git a/typescript/types/signature.cpp b/checker/types/signature.cpp similarity index 81% rename from typescript/types/signature.cpp rename to checker/types/signature.cpp index 80140201dee63f0a928e8940b0480e69d1a15fba..a9aa5785d0b7e9e890bff24fbe5b7ef43149f8f3 100644 --- a/typescript/types/signature.cpp +++ b/checker/types/signature.cpp @@ -15,18 +15,29 @@ #include "signature.h" +#include "plugins/ecmascript/es2panda/binder/scope.h" +#include "plugins/ecmascript/es2panda/ir/base/scriptFunction.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" + namespace panda::es2panda::checker { + +util::StringView Signature::InternalName() const +{ + return internalName_.Empty() ? func_->Scope()->InternalName() : internalName_; +} + Signature *Signature::Copy(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) { - checker::SignatureInfo *copiedInfo = allocator->New(signatureInfo_, allocator); + SignatureInfo *copiedInfo = allocator->New(signatureInfo_, allocator); - for (auto *it : copiedInfo->params) { - it->SetTsType(it->TsType()->Instantiate(allocator, relation, globalTypes)); + for (size_t idx = 0; idx < signatureInfo_->params.size(); idx++) { + copiedInfo->params[idx]->SetTsType( + ETSChecker::TryToInstantiate(signatureInfo_->params[idx]->TsType(), allocator, relation, globalTypes)); } - Type *copiedReturnType = returnType_->Instantiate(allocator, relation, globalTypes); + Type *copiedReturnType = ETSChecker::TryToInstantiate(returnType_, allocator, relation, globalTypes); - return allocator->New(copiedInfo, copiedReturnType); + return allocator->New(copiedInfo, copiedReturnType, func_); } void Signature::ToString(std::stringstream &ss, const binder::Variable *variable, bool printAsMethod) const @@ -80,7 +91,11 @@ void Signature::Identical(TypeRelation *relation, Signature *other) return; } - relation->IsIdenticalTo(returnType_, other->ReturnType()); + if (relation->NoReturnTypeCheck()) { + relation->Result(true); + } else { + relation->IsIdenticalTo(returnType_, other->ReturnType()); + } if (relation->IsTrue()) { for (uint64_t i = 0; i < signatureInfo_->params.size(); i++) { diff --git a/typescript/types/signature.h b/checker/types/signature.h similarity index 57% rename from typescript/types/signature.h rename to checker/types/signature.h index ea843d456cd778d4791374596e8388de36fb63c1..eec7c2f554b4ac7f0fe73875bcd313201e0e0ace 100644 --- a/typescript/types/signature.h +++ b/checker/types/signature.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_SIGNATURE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_SIGNATURE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_SIGNATURE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_SIGNATURE_H #include "type.h" @@ -49,12 +49,41 @@ public: // NOLINTEND(misc-non-private-member-variables-in-classes) }; +enum class SignatureFlags : uint32_t { + NO_OPTS = 0U, + VIRTUAL = 1U << 0U, + ABSTRACT = 1U << 1U, + CALL = 1U << 2U, + CONSTRUCT = 1U << 3U, + PUBLIC = 1U << 4U, + PROTECTED = 1U << 5U, + PRIVATE = 1U << 6U, + STATIC = 1U << 7U, + OPEN = 1U << 8U, + CONSTRUCTOR = 1U << 9U, + TYPE = 1U << 10U, + + FUNCTIONAL_INTERFACE_SIGNATURE = VIRTUAL | ABSTRACT | CALL | PUBLIC | TYPE, +}; + +DEFINE_BITOPS(SignatureFlags) + class Signature { public: Signature(SignatureInfo *signature_info, Type *returnType) : signatureInfo_(signature_info), returnType_(returnType) { } + Signature(SignatureInfo *signature_info, Type *returnType, util::StringView internalName) + : signatureInfo_(signature_info), returnType_(returnType), internalName_(internalName) + { + } + + Signature(SignatureInfo *signature_info, Type *returnType, ir::ScriptFunction *func) + : signatureInfo_(signature_info), returnType_(returnType), func_(func) + { + } + ~Signature() = default; NO_COPY_SEMANTIC(Signature); NO_MOVE_SEMANTIC(Signature); @@ -94,14 +123,24 @@ public: returnType_ = type; } - void SetNode(const ir::AstNode *node) + void SetOwner(ETSObjectType *owner) { - node_ = node; + ownerObj_ = owner; } - const ir::AstNode *Node() const + ir::ScriptFunction *Function() { - return node_; + return func_; + } + + ETSObjectType *Owner() + { + return ownerObj_; + } + + const ir::ScriptFunction *Function() const + { + return func_; } const binder::LocalVariable *RestVar() const @@ -109,6 +148,49 @@ public: return signatureInfo_->restVar; } + uint8_t ProtectionFlag() const + { + if ((flags_ & SignatureFlags::PRIVATE) != 0) { + return 2; + } + + if ((flags_ & SignatureFlags::PROTECTED) != 0) { + return 1; + } + + return 0; + } + + void AddSignatureFlag(SignatureFlags flag) + { + flags_ |= flag; + } + + void RemoveSignatureFlag(SignatureFlags flag) + { + flags_ &= ~flag; + } + + bool HasSignatureFlag(SignatureFlags flag) const + { + return (flags_ & flag) != 0; + } + + void ToAssemblerType(std::stringstream &ss) const + { + ss << compiler::Signatures::MANGLE_BEGIN; + + for (const auto *param : signatureInfo_->params) { + param->TsType()->ToAssemblerTypeWithRank(ss); + ss << compiler::Signatures::MANGLE_SEPARATOR; + } + + returnType_->ToAssemblerTypeWithRank(ss); + ss << compiler::Signatures::MANGLE_SEPARATOR; + } + + util::StringView InternalName() const; + Signature *Copy(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes); void ToString(std::stringstream &ss, const binder::Variable *variable, bool printAsMethod = false) const; @@ -118,7 +200,10 @@ public: private: checker::SignatureInfo *signatureInfo_; Type *returnType_; - const ir::AstNode *node_ {}; + ir::ScriptFunction *func_ {}; + SignatureFlags flags_ {SignatureFlags::NO_OPTS}; + util::StringView internalName_ {}; + ETSObjectType *ownerObj_ {}; }; } // namespace panda::es2panda::checker diff --git a/typescript/types/anyType.cpp b/checker/types/ts/anyType.cpp similarity index 100% rename from typescript/types/anyType.cpp rename to checker/types/ts/anyType.cpp diff --git a/typescript/types/anyType.h b/checker/types/ts/anyType.h similarity index 87% rename from typescript/types/anyType.h rename to checker/types/ts/anyType.h index 8105bebf3d2fac2c932a07697de33c69ee32ef4e..76c38e62e2c0dd7b8e71f5247a05362a4e321aaf 100644 --- a/typescript/types/anyType.h +++ b/checker/types/ts/anyType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_ANY_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_ANY_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_ANY_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_ANY_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class AnyType : public Type { @@ -32,4 +32,4 @@ public: }; } // namespace panda::es2panda::checker -#endif /* TYPESCRIPT_TYPES_ANY_TYPE_H */ +#endif diff --git a/typescript/types/arrayType.cpp b/checker/types/ts/arrayType.cpp similarity index 96% rename from typescript/types/arrayType.cpp rename to checker/types/ts/arrayType.cpp index 78d10551309e3615db6ecd83954032757cb976d7..90deba9dcec39cc2a4a06579d7a7b8397c9f52a8 100644 --- a/typescript/types/arrayType.cpp +++ b/checker/types/ts/arrayType.cpp @@ -16,7 +16,7 @@ #include "arrayType.h" #include "plugins/ecmascript/es2panda/binder/variable.h" -#include "plugins/ecmascript/es2panda/typescript/types/objectType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/objectType.h" namespace panda::es2panda::checker { void ArrayType::ToString(std::stringstream &ss) const diff --git a/typescript/types/arrayType.h b/checker/types/ts/arrayType.h similarity index 89% rename from typescript/types/arrayType.h rename to checker/types/ts/arrayType.h index 4e28e70f87da6e2fc62562c4b1e3cf939495bae3..27e61711aed3b01c1441b98639ddc3fab0fefe76 100644 --- a/typescript/types/arrayType.h +++ b/checker/types/ts/arrayType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_ARRAY_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_ARRAY_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_ARRAY_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_ARRAY_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class ArrayType : public Type { diff --git a/typescript/types/bigintLiteralType.cpp b/checker/types/ts/bigintLiteralType.cpp similarity index 100% rename from typescript/types/bigintLiteralType.cpp rename to checker/types/ts/bigintLiteralType.cpp diff --git a/typescript/types/bigintLiteralType.h b/checker/types/ts/bigintLiteralType.h similarity index 89% rename from typescript/types/bigintLiteralType.h rename to checker/types/ts/bigintLiteralType.h index aef7b39c946270b9ce40b4d669237a09c9ccd35a..f0fd4b1f5aab1fc17ccd3acac17ec73138dd9b6e 100644 --- a/typescript/types/bigintLiteralType.h +++ b/checker/types/ts/bigintLiteralType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_BIGINT_LITERAL_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_BIGINT_LITERAL_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_BIGINT_LITERAL_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_BIGINT_LITERAL_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class BigintLiteralType : public Type { diff --git a/typescript/types/bigintType.cpp b/checker/types/ts/bigintType.cpp similarity index 100% rename from typescript/types/bigintType.cpp rename to checker/types/ts/bigintType.cpp diff --git a/typescript/types/bigintType.h b/checker/types/ts/bigintType.h similarity index 87% rename from typescript/types/bigintType.h rename to checker/types/ts/bigintType.h index d3022f3cce285e5d89fe82d5c6a1e477aef09f8f..07636b22d9af4d8e4d798fd6c22bf820ec0daa87 100644 --- a/typescript/types/bigintType.h +++ b/checker/types/ts/bigintType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_BIGINT_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_BIGINT_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_BIGINT_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_BIGINT_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class BigintType : public Type { diff --git a/typescript/types/booleanLiteralType.cpp b/checker/types/ts/booleanLiteralType.cpp similarity index 100% rename from typescript/types/booleanLiteralType.cpp rename to checker/types/ts/booleanLiteralType.cpp diff --git a/typescript/types/booleanLiteralType.h b/checker/types/ts/booleanLiteralType.h similarity index 88% rename from typescript/types/booleanLiteralType.h rename to checker/types/ts/booleanLiteralType.h index 8f7b46c3bd097e58706d986998b68a3b736bc168..f10bfc2dddb1d0578377987be639a4cb49ef3822 100644 --- a/typescript/types/booleanLiteralType.h +++ b/checker/types/ts/booleanLiteralType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_BOOLEAN_LITERAL_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_BOOLEAN_LITERAL_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_BOOLEAN_LITERAL_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_BOOLEAN_LITERAL_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class BooleanLiteralType : public Type { diff --git a/typescript/types/booleanType.cpp b/checker/types/ts/booleanType.cpp similarity index 100% rename from typescript/types/booleanType.cpp rename to checker/types/ts/booleanType.cpp diff --git a/typescript/types/booleanType.h b/checker/types/ts/booleanType.h similarity index 87% rename from typescript/types/booleanType.h rename to checker/types/ts/booleanType.h index 463b5c596ebcde2a9ad0723dbc5505ae5d4e8295..e16b95c68df31635ed687e66a56e1839ac53e33b 100644 --- a/typescript/types/booleanType.h +++ b/checker/types/ts/booleanType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_BOOLEAN_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_BOOLEAN_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_BOOLEAN_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_BOOLEAN_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class BooleanType : public Type { diff --git a/typescript/types/constructorType.cpp b/checker/types/ts/constructorType.cpp similarity index 95% rename from typescript/types/constructorType.cpp rename to checker/types/ts/constructorType.cpp index 15edc26fdf47ac831356bee877f17f1770c3f553..3af66bc5a91bcdbcb86601254079cb4e9787e90e 100644 --- a/typescript/types/constructorType.cpp +++ b/checker/types/ts/constructorType.cpp @@ -15,7 +15,7 @@ #include "constructorType.h" -#include "plugins/ecmascript/es2panda/typescript/types/signature.h" +#include "plugins/ecmascript/es2panda/checker/types/signature.h" namespace panda::es2panda::checker { void ConstructorType::ToString(std::stringstream &ss) const diff --git a/typescript/types/constructorType.h b/checker/types/ts/constructorType.h similarity index 90% rename from typescript/types/constructorType.h rename to checker/types/ts/constructorType.h index 0776b2f193004da160a744769e3c83c2a3b5342d..4c930cee425ce0e369eee677a3dd9dc3867c3c02 100644 --- a/typescript/types/constructorType.h +++ b/checker/types/ts/constructorType.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_CONSTRUCTOR_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_CONSTRUCTOR_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_CONSTRUCTOR_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_CONSTRUCTOR_TYPE_H #include "objectType.h" diff --git a/typescript/types/elementFlags.h b/checker/types/ts/elementFlags.h similarity index 79% rename from typescript/types/elementFlags.h rename to checker/types/ts/elementFlags.h index dba25576772509a4917428825d2b2089505178f4..d3a73c35be46b19ed7998127e1885d9f19109247 100644 --- a/typescript/types/elementFlags.h +++ b/checker/types/ts/elementFlags.h @@ -13,18 +13,18 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_ELEMENT_FLAGS_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_ELEMENT_FLAGS_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_ELEMENT_FLAGS_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_ELEMENT_FLAGS_H #include "plugins/ecmascript/es2panda/util/enumbitops.h" namespace panda::es2panda::checker { enum class ElementFlags : uint32_t { - NO_OPTS = 0x0U, - REQUIRED = 0x1U, // T - OPTIONAL = 0x2U, // T? - REST = 0x4U, // ...T[] - VARIADIC = 0x8U, // ...T + NO_OPTS = 0U, + REQUIRED = 1U << 0U, // T + OPTIONAL = 1U << 1U, // T? + REST = 1U << 2U, // ...T[] + VARIADIC = 1U << 3U, // ...T FIXED = REQUIRED | OPTIONAL, VARIABLE = REST | VARIADIC, NON_REQUIRED = OPTIONAL | REST | VARIADIC, diff --git a/typescript/types/enumLiteralType.cpp b/checker/types/ts/enumLiteralType.cpp similarity index 96% rename from typescript/types/enumLiteralType.cpp rename to checker/types/ts/enumLiteralType.cpp index 79984a9e32661057121f220fcf9734694ac7e4dd..f6b7bc3ce6ca41c2cef8bdd709c7f723c75c7aca 100644 --- a/typescript/types/enumLiteralType.cpp +++ b/checker/types/ts/enumLiteralType.cpp @@ -16,7 +16,7 @@ #include "enumLiteralType.h" #include "plugins/ecmascript/es2panda/binder/variable.h" -#include "plugins/ecmascript/es2panda/typescript/types/enumType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/enumType.h" namespace panda::es2panda::checker { void EnumLiteralType::ToString(std::stringstream &ss) const diff --git a/typescript/types/enumLiteralType.h b/checker/types/ts/enumLiteralType.h similarity index 90% rename from typescript/types/enumLiteralType.h rename to checker/types/ts/enumLiteralType.h index 1b601a2c4d15262755aa8003f01b4255ac1f2c39..a0ddeb725d36e97e3a8705c8a1ad1927fad01a01 100644 --- a/typescript/types/enumLiteralType.h +++ b/checker/types/ts/enumLiteralType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_ENUM_LITERAL_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_ENUM_LITERAL_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_ENUM_LITERAL_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_ENUM_LITERAL_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::binder { class Scope; diff --git a/typescript/types/enumType.cpp b/checker/types/ts/enumType.cpp similarity index 100% rename from typescript/types/enumType.cpp rename to checker/types/ts/enumType.cpp diff --git a/typescript/types/enumType.h b/checker/types/ts/enumType.h similarity index 90% rename from typescript/types/enumType.h rename to checker/types/ts/enumType.h index 2c8cf92f81c9f5a72466590bdbc7af79af9b06cf..1a06fb73103f080b108979faee20237c1943a678 100644 --- a/typescript/types/enumType.h +++ b/checker/types/ts/enumType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_ENUM_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_ENUM_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_ENUM_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_ENUM_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::binder { class EnumVariable; diff --git a/typescript/types/functionType.cpp b/checker/types/ts/functionType.cpp similarity index 96% rename from typescript/types/functionType.cpp rename to checker/types/ts/functionType.cpp index 274dceefc78a31f0a282d67e5160d40e607dcd29..d3748baf39a03d71cd0cfd04610a69862d87eee4 100644 --- a/typescript/types/functionType.cpp +++ b/checker/types/ts/functionType.cpp @@ -15,7 +15,7 @@ #include "functionType.h" -#include "plugins/ecmascript/es2panda/typescript/types/signature.h" +#include "plugins/ecmascript/es2panda/checker/types/signature.h" namespace panda::es2panda::checker { void FunctionType::ToString(std::stringstream &ss) const diff --git a/typescript/types/functionType.h b/checker/types/ts/functionType.h similarity index 90% rename from typescript/types/functionType.h rename to checker/types/ts/functionType.h index 4527c6b99d990db49391de168eef8d164ddb8639..f1b465a54bf29dc70687363036581e95c8d9aac3 100644 --- a/typescript/types/functionType.h +++ b/checker/types/ts/functionType.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_FUNCTION_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_FUNCTION_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_FUNCTION_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_FUNCTION_TYPE_H #include #include "objectType.h" diff --git a/typescript/types/indexInfo.cpp b/checker/types/ts/indexInfo.cpp similarity index 100% rename from typescript/types/indexInfo.cpp rename to checker/types/ts/indexInfo.cpp diff --git a/typescript/types/indexInfo.h b/checker/types/ts/indexInfo.h similarity index 92% rename from typescript/types/indexInfo.h rename to checker/types/ts/indexInfo.h index 111079a3b2b0088cc01a184758cbc2773908bd15..79e252ebcc013646fe3405e2ea82dc73c1f7daf6 100644 --- a/typescript/types/indexInfo.h +++ b/checker/types/ts/indexInfo.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_INDEX_INFO_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_INDEX_INFO_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_INDEX_INFO_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_INDEX_INFO_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class IndexInfo { diff --git a/typescript/types/interfaceType.cpp b/checker/types/ts/interfaceType.cpp similarity index 98% rename from typescript/types/interfaceType.cpp rename to checker/types/ts/interfaceType.cpp index f9f48b683e4cb305b8d18212d58e7afd972efa1d..2d093f1f7335737249c146ba0634280a51dc4789 100644 --- a/typescript/types/interfaceType.cpp +++ b/checker/types/ts/interfaceType.cpp @@ -16,8 +16,8 @@ #include "interfaceType.h" #include "plugins/ecmascript/es2panda/binder/variable.h" -#include "plugins/ecmascript/es2panda/typescript/checker.h" -#include "plugins/ecmascript/es2panda/typescript/types/typeParameter.h" +#include "plugins/ecmascript/es2panda/checker/checker.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/typeParameter.h" #include #include diff --git a/typescript/types/interfaceType.h b/checker/types/ts/interfaceType.h similarity index 97% rename from typescript/types/interfaceType.h rename to checker/types/ts/interfaceType.h index 1788e719a66730147f4846cb82a8224618addbf7..d929906a047b34efac7b7755a61bb9be6a7b6115 100644 --- a/typescript/types/interfaceType.h +++ b/checker/types/ts/interfaceType.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_INTERFACE_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_INTERFACE_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_INTERFACE_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_INTERFACE_TYPE_H #include "objectType.h" diff --git a/typescript/types/neverType.cpp b/checker/types/ts/neverType.cpp similarity index 100% rename from typescript/types/neverType.cpp rename to checker/types/ts/neverType.cpp diff --git a/typescript/types/neverType.h b/checker/types/ts/neverType.h similarity index 87% rename from typescript/types/neverType.h rename to checker/types/ts/neverType.h index d103dc77c7a56373bf927213e226cdb5078c209b..606e255f48a2cd6e507d4020e45eb1da4cee23ef 100644 --- a/typescript/types/neverType.h +++ b/checker/types/ts/neverType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_NEVER_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_NEVER_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_NEVER_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_NEVER_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class NeverType : public Type { diff --git a/typescript/types/nonPrimitiveType.cpp b/checker/types/ts/nonPrimitiveType.cpp similarity index 100% rename from typescript/types/nonPrimitiveType.cpp rename to checker/types/ts/nonPrimitiveType.cpp diff --git a/typescript/types/nonPrimitiveType.h b/checker/types/ts/nonPrimitiveType.h similarity index 86% rename from typescript/types/nonPrimitiveType.h rename to checker/types/ts/nonPrimitiveType.h index ea646d161dbc88a12e2257c486b63b33d058c717..380aaa316788b4cc6cc6dbac0f75b40888bd9ab7 100644 --- a/typescript/types/nonPrimitiveType.h +++ b/checker/types/ts/nonPrimitiveType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_NON_PRIMITIVE_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_NON_PRIMITIVE_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_NON_PRIMITIVE_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_NON_PRIMITIVE_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class NonPrimitiveType : public Type { diff --git a/typescript/types/nullType.cpp b/checker/types/ts/nullType.cpp similarity index 100% rename from typescript/types/nullType.cpp rename to checker/types/ts/nullType.cpp diff --git a/typescript/types/nullType.h b/checker/types/ts/nullType.h similarity index 88% rename from typescript/types/nullType.h rename to checker/types/ts/nullType.h index 45f37fc480b5422cc9eb69b7c290fa46e71b4825..16b4a614cb7720d5f3ac9abd8f802aac14199956 100644 --- a/typescript/types/nullType.h +++ b/checker/types/ts/nullType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_NULL_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_NULL_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_NULL_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_NULL_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class NullType : public Type { diff --git a/typescript/types/numberLiteralType.cpp b/checker/types/ts/numberLiteralType.cpp similarity index 96% rename from typescript/types/numberLiteralType.cpp rename to checker/types/ts/numberLiteralType.cpp index f72e6f6cd3d53d590db488ef941c79824bb47463..c94f81681957f84b2c831aedb939a730cb05c99c 100644 --- a/typescript/types/numberLiteralType.cpp +++ b/checker/types/ts/numberLiteralType.cpp @@ -17,7 +17,7 @@ #include "plugins/ecmascript/es2panda/util/helpers.h" #include "plugins/ecmascript/es2panda/binder/variable.h" -#include "plugins/ecmascript/es2panda/typescript/types/enumType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/enumType.h" namespace panda::es2panda::checker { void NumberLiteralType::ToString(std::stringstream &ss) const diff --git a/typescript/types/numberLiteralType.h b/checker/types/ts/numberLiteralType.h similarity index 88% rename from typescript/types/numberLiteralType.h rename to checker/types/ts/numberLiteralType.h index e3c3a9a96e59395191c7094b2eaadef1847d6d60..b3c34d89830c857f12b1c91976f36a1d1c76af93 100644 --- a/typescript/types/numberLiteralType.h +++ b/checker/types/ts/numberLiteralType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_NUMBER_LITERAL_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_NUMBER_LITERAL_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_NUMBER_LITERAL_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_NUMBER_LITERAL_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class NumberLiteralType : public Type { diff --git a/typescript/types/numberType.cpp b/checker/types/ts/numberType.cpp similarity index 96% rename from typescript/types/numberType.cpp rename to checker/types/ts/numberType.cpp index 0a89535ac400d56a021839a7b265bb3a93b8a05f..9381711f7c62d043395bac2a8074e5e1e447cfa8 100644 --- a/typescript/types/numberType.cpp +++ b/checker/types/ts/numberType.cpp @@ -16,7 +16,7 @@ #include "numberType.h" #include "plugins/ecmascript/es2panda/binder/variable.h" -#include "plugins/ecmascript/es2panda/typescript/types/enumType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/enumType.h" namespace panda::es2panda::checker { void NumberType::ToString(std::stringstream &ss) const diff --git a/typescript/types/numberType.h b/checker/types/ts/numberType.h similarity index 87% rename from typescript/types/numberType.h rename to checker/types/ts/numberType.h index 702417e7796f235ec3e8ab90d0a9e63429c8c9ce..f8505c863caf6500fea3a8081e3fbe6ea73b393a 100644 --- a/typescript/types/numberType.h +++ b/checker/types/ts/numberType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_NUMBER_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_NUMBER_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_NUMBER_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_NUMBER_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class NumberType : public Type { diff --git a/typescript/types/objectDescriptor.cpp b/checker/types/ts/objectDescriptor.cpp similarity index 93% rename from typescript/types/objectDescriptor.cpp rename to checker/types/ts/objectDescriptor.cpp index df7949ba5116a33edd1960d0969944ee32d19b31..6d9ec80ba95116d31e2ddd3b2e9e35264dc59740 100644 --- a/typescript/types/objectDescriptor.cpp +++ b/checker/types/ts/objectDescriptor.cpp @@ -16,8 +16,8 @@ #include "objectDescriptor.h" #include "plugins/ecmascript/es2panda/binder/variable.h" -#include "plugins/ecmascript/es2panda/typescript/types/indexInfo.h" -#include "plugins/ecmascript/es2panda/typescript/types/signature.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/indexInfo.h" +#include "plugins/ecmascript/es2panda/checker/types/signature.h" namespace panda::es2panda::checker { binder::LocalVariable *ObjectDescriptor::FindProperty(const util::StringView &name) const diff --git a/typescript/types/objectDescriptor.h b/checker/types/ts/objectDescriptor.h similarity index 93% rename from typescript/types/objectDescriptor.h rename to checker/types/ts/objectDescriptor.h index 1d1ba16e5aaab6b3ac9044069e6a8b63593e10d2..f852f50148240e0bcc7f13fa62a210a5ee2af9bf 100644 --- a/typescript/types/objectDescriptor.h +++ b/checker/types/ts/objectDescriptor.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_OBJECT_DESCRIPTOR_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_OBJECT_DESCRIPTOR_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_OBJECT_DESCRIPTOR_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_OBJECT_DESCRIPTOR_H #include "macros.h" #include "plugins/ecmascript/es2panda/util/ustring.h" diff --git a/typescript/types/objectLiteralType.cpp b/checker/types/ts/objectLiteralType.cpp similarity index 94% rename from typescript/types/objectLiteralType.cpp rename to checker/types/ts/objectLiteralType.cpp index 729940cd10cd974fc56eaf6ae0c63618b70a8410..7581f32896e955870a1c156bc5406894810defbb 100644 --- a/typescript/types/objectLiteralType.cpp +++ b/checker/types/ts/objectLiteralType.cpp @@ -16,11 +16,11 @@ #include "objectLiteralType.h" #include "plugins/ecmascript/es2panda/binder/variable.h" -#include "plugins/ecmascript/es2panda/typescript/types/indexInfo.h" -#include "plugins/ecmascript/es2panda/typescript/types/signature.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/indexInfo.h" +#include "plugins/ecmascript/es2panda/checker/types/signature.h" namespace panda::es2panda::checker { -class Checker; +class TSChecker; void ObjectLiteralType::ToString(std::stringstream &ss) const { diff --git a/typescript/types/objectLiteralType.h b/checker/types/ts/objectLiteralType.h similarity index 90% rename from typescript/types/objectLiteralType.h rename to checker/types/ts/objectLiteralType.h index a20328d7cc24b36b848d7d244ee3c4d39335fde9..e9b5695c1c07ce046180a7f0065239b2ee4f9b4f 100644 --- a/typescript/types/objectLiteralType.h +++ b/checker/types/ts/objectLiteralType.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_OBJECT_LITERAL_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_OBJECT_LITERAL_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_OBJECT_LITERAL_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_OBJECT_LITERAL_TYPE_H #include "objectType.h" diff --git a/typescript/types/objectType.cpp b/checker/types/ts/objectType.cpp similarity index 97% rename from typescript/types/objectType.cpp rename to checker/types/ts/objectType.cpp index 966a57af84fc0ac37001b6c38602e9764ae361dc..fd34d587312879684e5a1e0c0917bb37da26b7b8 100644 --- a/typescript/types/objectType.cpp +++ b/checker/types/ts/objectType.cpp @@ -15,10 +15,10 @@ #include "objectType.h" -#include "plugins/ecmascript/es2panda/typescript/types/indexInfo.h" -#include "plugins/ecmascript/es2panda/typescript/types/interfaceType.h" -#include "plugins/ecmascript/es2panda/typescript/types/signature.h" -#include "plugins/ecmascript/es2panda/typescript/checker.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/indexInfo.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/interfaceType.h" +#include "plugins/ecmascript/es2panda/checker/types/signature.h" +#include "plugins/ecmascript/es2panda/checker/checker.h" namespace panda::es2panda::checker { bool ObjectType::EachSignatureRelatedToSomeSignature(TypeRelation *relation, diff --git a/typescript/types/objectType.h b/checker/types/ts/objectType.h similarity index 89% rename from typescript/types/objectType.h rename to checker/types/ts/objectType.h index 0b07f5a262b090cd42471aa39296139ef70ad5c2..76adb47f952b7621eaaa6f1d8be65f7fe76f3643 100644 --- a/typescript/types/objectType.h +++ b/checker/types/ts/objectType.h @@ -13,12 +13,12 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_OBJECT_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_OBJECT_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_OBJECT_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_OBJECT_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" -#include "plugins/ecmascript/es2panda/typescript/types/objectDescriptor.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/objectDescriptor.h" #include "plugins/ecmascript/es2panda/binder/variable.h" #include "plugins/ecmascript/es2panda/util/ustring.h" #include "plugins/ecmascript/es2panda/util/enumbitops.h" @@ -33,11 +33,11 @@ OBJECT_TYPE_MAPPING(DECLARE_OBJECT_TYPENAMES) #undef DECLARE_OBJECT_TYPENAMES enum class ObjectFlags : uint32_t { - NO_OPTS = 0x0U, - CHECK_EXCESS_PROPS = 0x1U, - RESOLVED_MEMBERS = 0x2U, - RESOLVED_BASE_TYPES = 0x4U, - RESOLVED_DECLARED_MEMBERS = 0x8U, + NO_OPTS = 0U, + CHECK_EXCESS_PROPS = 1U << 0U, + RESOLVED_MEMBERS = 1U << 1U, + RESOLVED_BASE_TYPES = 1U << 2U, + RESOLVED_DECLARED_MEMBERS = 1U << 3U, }; DEFINE_BITOPS(ObjectFlags) @@ -76,10 +76,13 @@ public: OBJECT_TYPE_MAPPING(OBJECT_TYPE_AS_CASTS) #undef OBJECT_TYPE_AS_CASTS - explicit ObjectType(ObjectType::ObjectTypeKind kind) : Type(TypeFlag::OBJECT), kind_(kind) {} + explicit ObjectType(ObjectType::ObjectTypeKind kind) + : Type(TypeFlag::OBJECT), kind_(kind), objFlag_(ObjectFlags::NO_OPTS) + { + } ObjectType(ObjectType::ObjectTypeKind kind, ObjectDescriptor *desc) - : Type(TypeFlag::OBJECT), kind_(kind), desc_(desc) + : Type(TypeFlag::OBJECT), kind_(kind), desc_(desc), objFlag_(ObjectFlags::NO_OPTS) { } diff --git a/typescript/types/stringLiteralType.cpp b/checker/types/ts/stringLiteralType.cpp similarity index 100% rename from typescript/types/stringLiteralType.cpp rename to checker/types/ts/stringLiteralType.cpp diff --git a/typescript/types/stringLiteralType.h b/checker/types/ts/stringLiteralType.h similarity index 88% rename from typescript/types/stringLiteralType.h rename to checker/types/ts/stringLiteralType.h index 0c9c078260b85db4bf03e246dca11904c3e770a8..c4df8c8e409ab4e17506aae50bfbc86e4466c7d8 100644 --- a/typescript/types/stringLiteralType.h +++ b/checker/types/ts/stringLiteralType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_STRING_LITERAL_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_STRING_LITERAL_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_STRING_LITERAL_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_STRING_LITERAL_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class StringLiteralType : public Type { diff --git a/typescript/types/stringType.cpp b/checker/types/ts/stringType.cpp similarity index 100% rename from typescript/types/stringType.cpp rename to checker/types/ts/stringType.cpp diff --git a/typescript/types/stringType.h b/checker/types/ts/stringType.h similarity index 87% rename from typescript/types/stringType.h rename to checker/types/ts/stringType.h index 86e746e9666fd10964452cf69055f0bf4552e501..c21499b93d45715d421a35571c57ff563e67c03f 100644 --- a/typescript/types/stringType.h +++ b/checker/types/ts/stringType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_STRING_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_STRING_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_STRING_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_STRING_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class StringType : public Type { diff --git a/typescript/types/tupleType.cpp b/checker/types/ts/tupleType.cpp similarity index 97% rename from typescript/types/tupleType.cpp rename to checker/types/ts/tupleType.cpp index 4358db09c4fad1f86304340dfc38b7a0e8b7f1f8..e6830f28dcf07ab9ec48d89f90baadaf982ed411 100644 --- a/typescript/types/tupleType.cpp +++ b/checker/types/ts/tupleType.cpp @@ -15,10 +15,10 @@ #include "tupleType.h" -#include "plugins/ecmascript/es2panda/typescript/checker.h" +#include "plugins/ecmascript/es2panda/checker/TSchecker.h" namespace panda::es2panda::checker { -Type *TupleType::ConvertToArrayType(Checker *checker) +Type *TupleType::ConvertToArrayType(TSChecker *checker) { ArenaVector unionTypes(checker->Allocator()->Adapter()); diff --git a/typescript/types/tupleType.h b/checker/types/ts/tupleType.h similarity index 92% rename from typescript/types/tupleType.h rename to checker/types/ts/tupleType.h index b69978fcab37c1353ef4b5376a7ecf08a560ee66..3ee5d44a1d0e6fbdd076c1b36a32709b26b10737 100644 --- a/typescript/types/tupleType.h +++ b/checker/types/ts/tupleType.h @@ -13,14 +13,14 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_TUPLE_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_TUPLE_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_TUPLE_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_TUPLE_TYPE_H #include "macros.h" #include "plugins/ecmascript/es2panda/binder/variable.h" -#include "plugins/ecmascript/es2panda/typescript/types/elementFlags.h" -#include "plugins/ecmascript/es2panda/typescript/types/objectType.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/elementFlags.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/objectType.h" namespace panda::es2panda::checker { using NamedTupleMemberPool = std::unordered_map; @@ -101,7 +101,7 @@ public: return res->second; } - Type *ConvertToArrayType(Checker *checker); + Type *ConvertToArrayType(TSChecker *checker); void ToString(std::stringstream &ss) const override; void Identical(TypeRelation *relation, Type *other) override; diff --git a/typescript/types/typeParameter.cpp b/checker/types/ts/typeParameter.cpp similarity index 100% rename from typescript/types/typeParameter.cpp rename to checker/types/ts/typeParameter.cpp diff --git a/typescript/types/typeParameter.h b/checker/types/ts/typeParameter.h similarity index 90% rename from typescript/types/typeParameter.h rename to checker/types/ts/typeParameter.h index fe30e2cdc333e6d45986246aa5fcb9aec2237111..2b6a51dd20a6f28ff5fde96091f173c4fb00e87b 100644 --- a/typescript/types/typeParameter.h +++ b/checker/types/ts/typeParameter.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_TYPE_PARAMETER_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_TYPE_PARAMETER_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_TYPE_PARAMETER_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_TYPE_PARAMETER_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class TypeParameter : public Type { diff --git a/typescript/types/typeReference.cpp b/checker/types/ts/typeReference.cpp similarity index 100% rename from typescript/types/typeReference.cpp rename to checker/types/ts/typeReference.cpp diff --git a/typescript/types/typeReference.h b/checker/types/ts/typeReference.h similarity index 84% rename from typescript/types/typeReference.h rename to checker/types/ts/typeReference.h index aa90e1c700ff47acb8dc905d1a5cb2373be3d520..9b7c47f126cde25361368183afc9c7ffc902e981 100644 --- a/typescript/types/typeReference.h +++ b/checker/types/ts/typeReference.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_TYPE_REFERENCE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_TYPE_REFERENCE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_TYPE_REFERENCE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_TYPE_REFERENCE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class TypeReference : public Type { @@ -44,4 +44,4 @@ private: }; } // namespace panda::es2panda::checker -#endif /* ES2PANDA_COMPILER_TYPESCRIPT_TYPES_TYPE_REFERENCE_H */ +#endif /* ES2PANDA_COMPILER_CHECKER_TYPES_TS_TYPE_REFERENCE_H */ diff --git a/typescript/types/types.h b/checker/types/ts/types.h similarity index 89% rename from typescript/types/types.h rename to checker/types/ts/types.h index 7b3d9810067b7606d298e2e23db021488b202cf8..404ed5af88d9253efcea8b90d187cfd1a7f6aba2 100644 --- a/typescript/types/types.h +++ b/checker/types/ts/types.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_H #include "anyType.h" #include "arrayType.h" @@ -42,8 +42,8 @@ #include "unknownType.h" #include "voidType.h" #include "indexInfo.h" -#include "signature.h" #include "typeParameter.h" #include "typeReference.h" +#include "plugins/ecmascript/es2panda/checker/types/signature.h" #endif /* TYPES_H */ diff --git a/typescript/types/undefinedType.cpp b/checker/types/ts/undefinedType.cpp similarity index 100% rename from typescript/types/undefinedType.cpp rename to checker/types/ts/undefinedType.cpp diff --git a/typescript/types/undefinedType.h b/checker/types/ts/undefinedType.h similarity index 87% rename from typescript/types/undefinedType.h rename to checker/types/ts/undefinedType.h index 64cdef883017f9a6e22ad82444af5ef1a42085be..d58e4561f52d22da001ab8d71243b6f1ec2cf8ea 100644 --- a/typescript/types/undefinedType.h +++ b/checker/types/ts/undefinedType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_UNDEFINED_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_UNDEFINED_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_UNDEFINED_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_UNDEFINED_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class UndefinedType : public Type { diff --git a/typescript/types/unionType.cpp b/checker/types/ts/unionType.cpp similarity index 98% rename from typescript/types/unionType.cpp rename to checker/types/ts/unionType.cpp index 9bb413659e76d3d538991e696ec15e032fec5cb2..b408ae424aef0491bfb49ffb22e02d4837f8174a 100644 --- a/typescript/types/unionType.cpp +++ b/checker/types/ts/unionType.cpp @@ -16,7 +16,7 @@ #include "unionType.h" #include -#include "plugins/ecmascript/es2panda/typescript/types/globalTypesHolder.h" +#include "plugins/ecmascript/es2panda/checker/types/globalTypesHolder.h" namespace panda::es2panda::checker { void UnionType::ToString(std::stringstream &ss) const diff --git a/typescript/types/unionType.h b/checker/types/ts/unionType.h similarity index 96% rename from typescript/types/unionType.h rename to checker/types/ts/unionType.h index 70e1850176907212c3a48fe6eb4d1a4c39da1f2d..90222f83e57e3d1be8af4f226f85113284dbbcb9 100644 --- a/typescript/types/unionType.h +++ b/checker/types/ts/unionType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_UNION_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_UNION_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_UNION_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_UNION_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class GlobalTypesHolder; diff --git a/typescript/types/unknownType.cpp b/checker/types/ts/unknownType.cpp similarity index 100% rename from typescript/types/unknownType.cpp rename to checker/types/ts/unknownType.cpp diff --git a/typescript/types/unknownType.h b/checker/types/ts/unknownType.h similarity index 87% rename from typescript/types/unknownType.h rename to checker/types/ts/unknownType.h index 892b81655e8bb79e2a8313eacd9d050785189f74..205e1e501e29f49e8b3e65a81e1698fa511bdd68 100644 --- a/typescript/types/unknownType.h +++ b/checker/types/ts/unknownType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_UNKNOWN_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_UNKNOWN_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_UNKNOWN_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_UNKNOWN_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class UnknownType : public Type { diff --git a/typescript/types/voidType.cpp b/checker/types/ts/voidType.cpp similarity index 100% rename from typescript/types/voidType.cpp rename to checker/types/ts/voidType.cpp diff --git a/typescript/types/voidType.h b/checker/types/ts/voidType.h similarity index 87% rename from typescript/types/voidType.h rename to checker/types/ts/voidType.h index 9f8fcfd2cd23703e0ed6fe8e19479aa919078cbc..2776c0d5bd8558660fde4ab4003d2c42fb0b5100 100644 --- a/typescript/types/voidType.h +++ b/checker/types/ts/voidType.h @@ -13,10 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_VOID_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_VOID_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TS_VOID_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TS_VOID_TYPE_H -#include "type.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" namespace panda::es2panda::checker { class VoidType : public Type { diff --git a/typescript/types/type.cpp b/checker/types/type.cpp similarity index 63% rename from typescript/types/type.cpp rename to checker/types/type.cpp index 4d4ba4b5b725da84414899075f1e115203f38c65..c61cb48b4edff1a95e2aa31a4fba295e635a9b50 100644 --- a/typescript/types/type.cpp +++ b/checker/types/type.cpp @@ -15,11 +15,21 @@ #include "type.h" -#include "plugins/ecmascript/es2panda/typescript/types/typeFlag.h" -#include "plugins/ecmascript/es2panda/typescript/types/typeFacts.h" -#include "plugins/ecmascript/es2panda/typescript/types/typeRelation.h" +#include "plugins/ecmascript/es2panda/checker/types/typeFlag.h" +#include "plugins/ecmascript/es2panda/checker/types/typeRelation.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/etsObjectType.h" namespace panda::es2panda::checker { +bool Type::IsETSNullType() const +{ + return IsETSObjectType() && AsETSObjectType()->HasObjectFlag(ETSObjectFlags::NULL_TYPE); +} + +bool Type::IsETSStringType() const +{ + return IsETSObjectType() && AsETSObjectType()->HasObjectFlag(ETSObjectFlags::STRING); +} + void Type::ToStringAsSrc(std::stringstream &ss) const { ToString(ss); @@ -35,8 +45,20 @@ bool Type::AssignmentSource([[maybe_unused]] TypeRelation *relation, [[maybe_unu return false; } +TypeFacts Type::GetTypeFacts() const +{ + return TypeFacts::NONE; +} + void Type::Compare([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *other) {} +void Type::IsSubtype([[maybe_unused]] TypeRelation *relation, [[maybe_unused]] Type *source) {} + +Type *Type::AsSuper([[maybe_unused]] Checker *checker, [[maybe_unused]] binder::Variable *sourceVar) +{ + return nullptr; +} + Type *Type::Instantiate([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] TypeRelation *relation, [[maybe_unused]] GlobalTypesHolder *globalTypes) { diff --git a/typescript/types/type.h b/checker/types/type.h similarity index 72% rename from typescript/types/type.h rename to checker/types/type.h index c0411b81cbc3f694640f133d8cd118be166072ca..58170c8d1193d45ba2758ac6d6c4f7f70dfd32cb 100644 --- a/typescript/types/type.h +++ b/checker/types/type.h @@ -13,12 +13,13 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_TYPE_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_TYPE_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TYPE_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TYPE_H -#include "plugins/ecmascript/es2panda/typescript/types/typeFacts.h" -#include "plugins/ecmascript/es2panda/typescript/types/typeMapping.h" -#include "plugins/ecmascript/es2panda/typescript/types/typeRelation.h" +#include "generated/signatures.h" +#include "plugins/ecmascript/es2panda/checker/types/typeMapping.h" +#include "plugins/ecmascript/es2panda/checker/types/typeRelation.h" +#include "plugins/ecmascript/es2panda/checker/types/typeFacts.h" #include "macros.h" #include @@ -36,6 +37,7 @@ class GlobalTypesHolder; #define DECLARE_TYPENAMES(typeFlag, typeName) class typeName; TYPE_MAPPING(DECLARE_TYPENAMES) #undef DECLARE_TYPENAMES +class ETSStringType; class Type { public: @@ -74,6 +76,21 @@ public: TYPE_MAPPING(TYPE_AS_CASTS) #undef TYPE_AS_CASTS + bool IsETSStringType() const; + bool IsETSNullType() const; + + ETSStringType *AsETSStringType() + { + ASSERT(IsETSObjectType()); + return reinterpret_cast(this); + } + + const ETSStringType *AsETSStringType() const + { + ASSERT(IsETSObjectType()); + return reinterpret_cast(this); + } + TypeFlag TypeFlags() const { return typeFlags_; @@ -114,16 +131,35 @@ public: return variable_; } + util::StringView ToAssemblerTypeView(ArenaAllocator *allocator) const + { + std::stringstream ss; + ToAssemblerType(ss); + return util::UString(ss.str(), allocator).View(); + } + virtual void ToString(std::stringstream &ss) const = 0; virtual void ToStringAsSrc(std::stringstream &ss) const; - virtual TypeFacts GetTypeFacts() const = 0; + virtual TypeFacts GetTypeFacts() const; + virtual void ToAssemblerType([[maybe_unused]] std::stringstream &ss) const {}; + virtual void ToAssemblerTypeWithRank([[maybe_unused]] std::stringstream &ss) const + { + ToAssemblerType(ss); + }; + + virtual uint32_t Rank() const + { + return 0; + } virtual void Identical(TypeRelation *relation, Type *other); virtual void AssignmentTarget(TypeRelation *relation, Type *source) = 0; virtual bool AssignmentSource(TypeRelation *relation, Type *target); virtual void Compare(TypeRelation *relation, Type *other); + virtual void IsSubtype(TypeRelation *relation, Type *source); + virtual Type *AsSuper(Checker *checker, binder::Variable *sourceVar); - virtual Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes) = 0; + virtual Type *Instantiate(ArenaAllocator *allocator, TypeRelation *relation, GlobalTypesHolder *globalTypes); protected: // NOLINTBEGIN(misc-non-private-member-variables-in-classes) diff --git a/typescript/types/typeFacts.h b/checker/types/typeFacts.h similarity index 75% rename from typescript/types/typeFacts.h rename to checker/types/typeFacts.h index 3a92604a730ab78bb42cd54b33eae883eb7b595f..e5fe539a11e92d911ce151cec1c2251cebd1215c 100644 --- a/typescript/types/typeFacts.h +++ b/checker/types/typeFacts.h @@ -13,38 +13,38 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_TYPE_FACTS_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_TYPE_FACTS_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TYPE_FACTS_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TYPE_FACTS_H #include "plugins/ecmascript/es2panda/util/enumbitops.h" namespace panda::es2panda::checker { enum class TypeFacts : uint32_t { - NONE = 0x0U, - TYPEOF_EQ_STRING = 0x1U, // typeof x === "string" - TYPEOF_EQ_NUMBER = 0x2U, // typeof x === "number" - TYPEOF_EQ_BIGINT = 0x4U, // typeof x === "bigint" - TYPEOF_EQ_BOOLEAN = 0x8U, // typeof x === "boolean" - TYPEOF_EQ_SYMBOL = 0x10U, // typeof x === "symbol" - TYPEOF_EQ_OBJECT = 0x20U, // typeof x === "object" - TYPEOF_EQ_FUNCTION = 0x40U, // typeof x === "function" - TYPEOF_EQ_HOST_OBJECT = 0x80U, // typeof x === "xxx" - TYPEOF_NE_STRING = 0x100U, // typeof x !== "string" - TYPEOF_NE_NUMBER = 0x200U, // typeof x !== "number" - TYPEOF_NE_BIGINT = 0x400U, // typeof x !== "bigint" - TYPEOF_NE_BOOLEAN = 0x800U, // typeof x !== "boolean" - TYPEOF_NE_SYMBOL = 0x1000U, // typeof x !== "symbol" - TYPEOF_NE_OBJECT = 0x2000U, // typeof x !== "object" - TYPEOF_NE_FUNCTION = 0x4000U, // typeof x !== "function" - TYPEOF_NE_HOST_OBJECT = 0x8000U, // typeof x !== "xxx" - EQ_UNDEFINED = 0x10000U, // x === undefined - EQ_NULL = 0x20000U, // x === null - EQ_UNDEFINED_OR_NULL = 0x40000U, // x === undefined / x === null - NE_UNDEFINED = 0x80000U, // x !== undefined - NE_NULL = 0x100000U, // x !== null - NE_UNDEFINED_OR_NULL = 0x200000U, // x != undefined / x != null - TRUTHY = 0x400000U, // x - FALSY = 0x800000U, // !x + NONE = 0U, + TYPEOF_EQ_STRING = 1U << 0U, // typeof x === "string" + TYPEOF_EQ_NUMBER = 1U << 1U, // typeof x === "number" + TYPEOF_EQ_BIGINT = 1U << 2U, // typeof x === "bigint" + TYPEOF_EQ_BOOLEAN = 1U << 3U, // typeof x === "boolean" + TYPEOF_EQ_SYMBOL = 1U << 4U, // typeof x === "symbol" + TYPEOF_EQ_OBJECT = 1U << 5U, // typeof x === "object" + TYPEOF_EQ_FUNCTION = 1U << 6U, // typeof x === "function" + TYPEOF_EQ_HOST_OBJECT = 1U << 7U, // typeof x === "xxx" + TYPEOF_NE_STRING = 1U << 8U, // typeof x !== "string" + TYPEOF_NE_NUMBER = 1U << 9U, // typeof x !== "number" + TYPEOF_NE_BIGINT = 1U << 10U, // typeof x !== "bigint" + TYPEOF_NE_BOOLEAN = 1U << 11U, // typeof x !== "boolean" + TYPEOF_NE_SYMBOL = 1U << 12U, // typeof x !== "symbol" + TYPEOF_NE_OBJECT = 1U << 13U, // typeof x !== "object" + TYPEOF_NE_FUNCTION = 1U << 14U, // typeof x !== "function" + TYPEOF_NE_HOST_OBJECT = 1U << 15U, // typeof x !== "xxx" + EQ_UNDEFINED = 1U << 16U, // x === undefined + EQ_NULL = 1U << 17U, // x === null + EQ_UNDEFINED_OR_NULL = 1U << 18U, // x === undefined / x === null + NE_UNDEFINED = 1U << 19U, // x !== undefined + NE_NULL = 1U << 20U, // x !== null + NE_UNDEFINED_OR_NULL = 1U << 21U, // x != undefined / x != null + TRUTHY = 1U << 22U, // x + FALSY = 1U << 23U, // !x LAST = FALSY, ALL = (LAST << 1U) - 1U, diff --git a/typescript/types/typeFlag.h b/checker/types/typeFlag.h similarity index 33% rename from typescript/types/typeFlag.h rename to checker/types/typeFlag.h index 613221f8c60e8cc1b1e3651ae272898653468d09..a7fadb8128034607038d48b8519997c8baab5e38 100644 --- a/typescript/types/typeFlag.h +++ b/checker/types/typeFlag.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_TYPE_FLAG_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_TYPE_FLAG_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TYPE_FLAG_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TYPE_FLAG_H #include "plugins/ecmascript/es2panda/util/enumbitops.h" @@ -23,39 +23,76 @@ namespace panda::es2panda::checker { enum class TypeFlag : uint64_t { NONE = 0, - NUMBER = 1ULL << 0U, // x: number - STRING = 1ULL << 1U, // x: string - BOOLEAN = 1ULL << 2U, // x: boolean - VOID = 1ULL << 3U, // x: void - NULL_TYPE = 1ULL << 4U, // x: null - UNDEFINED = 1ULL << 5U, // x: undefined - UNKNOWN = 1ULL << 6U, // x: unknown - NEVER = 1ULL << 7U, // x: never - UNION = 1ULL << 8U, // x: a | b - OBJECT = 1ULL << 9U, // x: object - BIGINT = 1ULL << 10U, // x: bigint - BOOLEAN_LITERAL = 1ULL << 11U, // x: true - NUMBER_LITERAL = 1ULL << 12U, // x: 10 - STRING_LITERAL = 1ULL << 13U, // x: "foo" - BIGINT_LITERAL = 1ULL << 14U, // x: 10n - ENUM = 1ULL << 15U, // enum x - ENUM_LITERAL = 1ULL << 16U, // member of enum - SYMBOL = 1ULL << 17U, // x: symbol - UNIQUE_SYMBOL = 1ULL << 18U, // one of JS unique symbols - TYPE_PARAMETER = 1ULL << 19U, // function - INTERSECTION = 1ULL << 20U, // x: a & b - INDEX = 1ULL << 21U, // keyof x - INDEX_ACCESS = 1ULL << 22U, // x[a] - CONDITIONAL = 1ULL << 23U, // x extends a ? b : c - SUBSTITUTION = 1ULL << 24U, // type parameter substitution - TEMPLATE_LITERAL = 1ULL << 25U, // x: `hello ${World}` - STRING_MAPPING = 1ULL << 27U, // Uppercase/Lowercase type - ANY = 1ULL << 28U, // x: any - ARRAY = 1ULL << 29U, // x: number[] - FUNCTION = 1ULL << 30U, // x: (a) => b - NON_PRIMITIVE = 1ULL << 31U, // x: object - TYPE_REFERENCE = 1ULL << 32U, - READONLY = 1ULL << 33U, + NUMBER = 1ULL << 0ULL, // x: number + STRING = 1ULL << 1ULL, // x: string + BOOLEAN = 1ULL << 2ULL, // x: boolean + VOID = 1ULL << 3ULL, // x: void + NULL_TYPE = 1ULL << 4ULL, // x: null + UNDEFINED = 1ULL << 5ULL, // x: undefined + UNKNOWN = 1ULL << 6ULL, // x: unknown + NEVER = 1ULL << 7ULL, // x: never + UNION = 1ULL << 8ULL, // x: a | b + OBJECT = 1ULL << 9ULL, // x: object + BIGINT = 1ULL << 10ULL, // x: bigint + BOOLEAN_LITERAL = 1ULL << 11ULL, // x: true + NUMBER_LITERAL = 1ULL << 12ULL, // x: 10 + STRING_LITERAL = 1ULL << 13ULL, // x: "foo" + BIGINT_LITERAL = 1ULL << 14ULL, // x: 10n + ENUM = 1ULL << 15ULL, // enum x + ENUM_LITERAL = 1ULL << 16ULL, // member of enum + SYMBOL = 1ULL << 17ULL, // x: symbol + UNIQUE_SYMBOL = 1ULL << 18ULL, // one of JS unique symbols + TYPE_PARAMETER = 1ULL << 19ULL, // function + INTERSECTION = 1ULL << 20ULL, // x: a & b + INDEX = 1ULL << 21ULL, // keyof x + INDEX_ACCESS = 1ULL << 22ULL, // x[a] + CONDITIONAL = 1ULL << 23ULL, // x extends a ? b : c + SUBSTITUTION = 1ULL << 24ULL, // type parameter substitution + TEMPLATE_LITERAL = 1ULL << 25ULL, // x: `hello ${World}` + STRING_MAPPING = 1ULL << 27ULL, // Uppercase/Lowercase type + ANY = 1ULL << 28ULL, // x: any + ARRAY = 1ULL << 29ULL, // x: number[] + FUNCTION = 1ULL << 30ULL, // x: (a) => b + NON_PRIMITIVE = 1ULL << 31ULL, // x: object + TYPE_REFERENCE = 1ULL << 32ULL, // x: A + READONLY = 1ULL << 33ULL, // type assigned to a readonly property + CONSTANT = 1ULL << 34ULL, // type for constant expressions containing the associated constant value + BYTE = 1ULL << 35ULL, // x: byte + SHORT = 1ULL << 36ULL, // x: short + INT = 1ULL << 37ULL, // x: int + LONG = 1ULL << 38ULL, // x: long + FLOAT = 1ULL << 39ULL, // x: float + DOUBLE = 1ULL << 40ULL, // x: double + CHAR = 1ULL << 41ULL, // x: char + ETS_BOOLEAN = 1ULL << 42ULL, // ETS boolean type + ETS_VOID = 1ULL << 43ULL, // ETS void type + ETS_OBJECT = 1ULL << 44ULL, // ETS class or interface type + ETS_ARRAY = 1ULL << 45ULL, // ETS array type + SYNTHETIC = 1ULL << 46ULL, // synthetic type created by the checker for specific checks + WILDCARD = 1ULL << 47ULL, // new A() + ETS_TYPE_PARAMETER = 1ULL << 48ULL, // ETS type parameter + ETS_TYPE_REFERENCE = 1ULL << 49ULL, // ETS type reference + ETS_TYPE = BYTE | SHORT | INT | LONG | FLOAT | DOUBLE | CHAR | ETS_BOOLEAN | ETS_VOID | ETS_OBJECT | ETS_ARRAY | + WILDCARD | ETS_TYPE_PARAMETER, + ETS_PRIMITIVE = BYTE | SHORT | INT | LONG | FLOAT | DOUBLE | CHAR | ETS_BOOLEAN | ETS_VOID, + ETS_ARRAY_INDEX = BYTE | SHORT | INT, + ETS_INTEGRAL = BYTE | CHAR | SHORT | INT | LONG, + ETS_FLOATING_POINT = FLOAT | DOUBLE, + ETS_NUMERIC = ETS_INTEGRAL | FLOAT | DOUBLE, + ETS_ARRAY_OR_OBJECT = ETS_ARRAY | ETS_OBJECT, + ETS_WIDE_NUMERIC = LONG | DOUBLE, + VALID_SWITCH_TYPE = BYTE | SHORT | INT | CHAR | LONG | ENUM_LITERAL, + NARROWABLE_TO_FLOAT = DOUBLE, + NARROWABLE_TO_LONG = FLOAT | NARROWABLE_TO_FLOAT, + NARROWABLE_TO_INT = LONG | NARROWABLE_TO_LONG, + NARROWABLE_TO_CHAR = SHORT | INT | NARROWABLE_TO_INT, + NARROWABLE_TO_SHORT = INT | NARROWABLE_TO_INT, + NARROWABLE_TO_BYTE = CHAR | NARROWABLE_TO_CHAR, + WIDENABLE_TO_SHORT = BYTE, + WIDENABLE_TO_INT = CHAR | SHORT | WIDENABLE_TO_SHORT, + WIDENABLE_TO_LONG = INT | WIDENABLE_TO_INT, + WIDENABLE_TO_FLOAT = LONG | WIDENABLE_TO_LONG, + WIDENABLE_TO_DOUBLE = FLOAT | WIDENABLE_TO_FLOAT, COMPUTED_TYPE_LITERAL_NAME = STRING_LITERAL | NUMBER_LITERAL | ENUM, COMPUTED_NAME = COMPUTED_TYPE_LITERAL_NAME | STRING | NUMBER | ANY | SYMBOL, ANY_OR_UNKNOWN = ANY | UNKNOWN, diff --git a/checker/types/typeMapping.h b/checker/types/typeMapping.h new file mode 100644 index 0000000000000000000000000000000000000000..ae3b69582fb8fa2ca7e9bca81299a63b34fcf632 --- /dev/null +++ b/checker/types/typeMapping.h @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2021-2022 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_CHECKER_TYPES_TYPE_MAPPING_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TYPE_MAPPING_H + +#include "typeFlag.h" + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) +#define TYPE_MAPPING(_) \ + _(TypeFlag::ARRAY, ArrayType) \ + _(TypeFlag::ANY, AnyType) \ + _(TypeFlag::BIGINT_LITERAL, BigintLiteralType) \ + _(TypeFlag::NUMBER, NumberType) \ + _(TypeFlag::STRING, StringType) \ + _(TypeFlag::BOOLEAN, BooleanType) \ + _(TypeFlag::VOID, VoidType) \ + _(TypeFlag::NULL_TYPE, NullType) \ + _(TypeFlag::UNDEFINED, UndefinedType) \ + _(TypeFlag::UNKNOWN, UnknownType) \ + _(TypeFlag::NEVER, NeverType) \ + _(TypeFlag::UNION, UnionType) \ + _(TypeFlag::OBJECT, ObjectType) \ + _(TypeFlag::BIGINT, BigintType) \ + _(TypeFlag::BOOLEAN_LITERAL, BooleanLiteralType) \ + _(TypeFlag::NUMBER_LITERAL, NumberLiteralType) \ + _(TypeFlag::STRING_LITERAL, StringLiteralType) \ + _(TypeFlag::ENUM, EnumType) \ + _(TypeFlag::ENUM_LITERAL, EnumLiteralType) \ + _(TypeFlag::TYPE_PARAMETER, TypeParameter) \ + _(TypeFlag::TYPE_REFERENCE, TypeReference) \ + _(TypeFlag::BYTE, ByteType) \ + _(TypeFlag::SHORT, ShortType) \ + _(TypeFlag::INT, IntType) \ + _(TypeFlag::LONG, LongType) \ + _(TypeFlag::FLOAT, FloatType) \ + _(TypeFlag::DOUBLE, DoubleType) \ + _(TypeFlag::CHAR, CharType) \ + _(TypeFlag::ETS_BOOLEAN, ETSBooleanType) \ + _(TypeFlag::ETS_VOID, ETSVoidType) \ + _(TypeFlag::FUNCTION, ETSFunctionType) \ + _(TypeFlag::ETS_OBJECT, ETSObjectType) \ + _(TypeFlag::ETS_ARRAY, ETSArrayType) \ + _(TypeFlag::NON_PRIMITIVE, NonPrimitiveType) \ + _(TypeFlag::WILDCARD, WildcardType) \ + _(TypeFlag::ETS_TYPE_PARAMETER, ETSTypeParameter) \ + _(TypeFlag::ETS_TYPE_REFERENCE, ETSTypeReference) + +#define OBJECT_TYPE_MAPPING(_) \ + _(ObjectType::ObjectTypeKind::FUNCTION, FunctionType) \ + _(ObjectType::ObjectTypeKind::TUPLE, TupleType) \ + _(ObjectType::ObjectTypeKind::LITERAL, ObjectLiteralType) \ + _(ObjectType::ObjectTypeKind::INTERFACE, InterfaceType) +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif /* TYPE_MAPPING_H */ diff --git a/typescript/types/typeRelation.cpp b/checker/types/typeRelation.cpp similarity index 84% rename from typescript/types/typeRelation.cpp rename to checker/types/typeRelation.cpp index da3491542ae9eabfd183a173d554d18b834c8ebe..efdac8fd2c9e5aa4074cf242cef5050a66d3e27e 100644 --- a/typescript/types/typeRelation.cpp +++ b/checker/types/typeRelation.cpp @@ -15,33 +15,11 @@ #include "typeRelation.h" -#include "plugins/ecmascript/es2panda/typescript/checker.h" -#include "plugins/ecmascript/es2panda/typescript/types/indexInfo.h" -#include "plugins/ecmascript/es2panda/typescript/types/signature.h" +#include "plugins/ecmascript/es2panda/checker/checker.h" +#include "plugins/ecmascript/es2panda/checker/types/ts/indexInfo.h" +#include "plugins/ecmascript/es2panda/checker/types/signature.h" namespace panda::es2panda::checker { -const Type *AsSrc::GetType() const -{ - return type_; -} - -TypeRelation::TypeRelation(Checker *checker) : checker_(checker) {} - -bool TypeRelation::IsTrue() const -{ - return result_ == RelationResult::TRUE; -} - -const Checker *TypeRelation::GetChecker() const -{ - return checker_; -} - -Checker *TypeRelation::GetChecker() -{ - return checker_; -} - ArenaAllocator *TypeRelation::Allocator() { return checker_->Allocator(); @@ -116,6 +94,7 @@ bool TypeRelation::IsIdenticalTo(IndexInfo *source, IndexInfo *target) return result_ == RelationResult::TRUE; } +// TODO(user): applyNarrowing -> flag bool TypeRelation::IsAssignableTo(Type *source, Type *target) { result_ = CacheLookup(source, target, checker_->AssignableResults(), RelationType::ASSIGNABLE); @@ -130,8 +109,10 @@ bool TypeRelation::IsAssignableTo(Type *source, Type *target) target->AssignmentTarget(this, source); } - checker_->AssignableResults().cached.insert( - {{source->Id(), target->Id()}, {result_, RelationType::ASSIGNABLE}}); + if (flags_ == TypeRelationFlag::NONE) { + checker_->AssignableResults().cached.insert( + {{source->Id(), target->Id()}, {result_, RelationType::ASSIGNABLE}}); + } } return result_ == RelationResult::TRUE; @@ -165,9 +146,4 @@ void TypeRelation::RaiseError(std::initializer_list lis { checker_->ThrowTypeError(list, loc); } - -void TypeRelation::Result(bool res) -{ - result_ = res ? RelationResult::TRUE : RelationResult::FALSE; -} } // namespace panda::es2panda::checker diff --git a/typescript/types/typeRelation.h b/checker/types/typeRelation.h similarity index 44% rename from typescript/types/typeRelation.h rename to checker/types/typeRelation.h index c55b30ea16c94209bab5a2f83c0fb1d14885d225..d8e8dfe1259c283bd85e27bef8c46f5c272a255f 100644 --- a/typescript/types/typeRelation.h +++ b/checker/types/typeRelation.h @@ -13,27 +13,58 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_TYPESCRIPT_TYPES_TYPE_RELATION_H -#define ES2PANDA_COMPILER_TYPESCRIPT_TYPES_TYPE_RELATION_H +#ifndef ES2PANDA_COMPILER_CHECKER_TYPES_TYPE_RELATION_H +#define ES2PANDA_COMPILER_CHECKER_TYPES_TYPE_RELATION_H #include "plugins/ecmascript/es2panda/lexer/token/sourceLocation.h" #include "plugins/ecmascript/es2panda/lexer/token/tokenType.h" -#include "macros.h" #include "plugins/ecmascript/es2panda/util/ustring.h" +#include "plugins/ecmascript/es2panda/util/enumbitops.h" + +#include "macros.h" #include #include +namespace panda::es2panda::ir { +class Expression; +} // namespace panda::es2panda::ir + namespace panda::es2panda::checker { class Signature; class IndexInfo; class Type; class Checker; -enum class RelationResult { TRUE, FALSE, UNKNOWN, MAYBE, CACHE_MISS }; +enum class TypeRelationFlag : uint32_t { + NONE = 0U, + NARROWING = 1U << 0U, + WIDENING = 1U << 1U, + BOXING = 1U << 2U, + UNBOXING = 1U << 3U, + CAPTURE = 1U << 4U, + STRING = 1U << 5U, + VALUE_SET = 1U << 6U, + UNCHECKED = 1U << 7U, + NO_THROW = 1U << 8U, + SELF_REFERENCE = 1U << 9U, + NO_RETURN_TYPE_CHECK = 1U << 10U, + DIRECT_RETURN = 1U << 11U, + NO_WIDENING = 1U << 12U, + NO_BOXING = 1U << 13U, + NO_UNBOXING = 1U << 14U, + ONLY_CHECK_WIDENING = 1U << 15U, + IN_ASSIGNMENT_CONTEXT = 1U << 16U, + + ASSIGNMENT_CONTEXT = WIDENING | BOXING | UNBOXING, +}; + +enum class RelationResult { TRUE, FALSE, UNKNOWN, MAYBE, CACHE_MISS, ERROR }; enum class RelationType { COMPARABLE, ASSIGNABLE, IDENTICAL }; +DEFINE_BITOPS(TypeRelationFlag) + class RelationKey { public: uint64_t sourceId; @@ -74,17 +105,86 @@ class AsSrc { public: explicit AsSrc(const Type *type) : type_(const_cast(type)) {} - const Type *GetType() const; + const Type *GetType() const + { + return type_; + } private: Type *type_; }; -using TypeErrorMessageElement = std::variant; +using TypeErrorMessageElement = + std::variant; class TypeRelation { public: - explicit TypeRelation(Checker *checker); + explicit TypeRelation(Checker *checker) : checker_(checker), result_(RelationResult::FALSE) {} + + bool IsTrue() const + { + return result_ == RelationResult::TRUE; + } + + bool IsError() const + { + return result_ == RelationResult::ERROR; + } + + bool ApplyNarrowing() const + { + return (flags_ & TypeRelationFlag::NARROWING) != 0; + } + + bool ApplyWidening() const + { + return (flags_ & TypeRelationFlag::WIDENING) != 0; + } + + bool ApplyBoxing() const + { + return (flags_ & TypeRelationFlag::BOXING) != 0; + } + + bool ApplyUnboxing() const + { + return (flags_ & TypeRelationFlag::UNBOXING) != 0; + } + + bool NoReturnTypeCheck() const + { + return (flags_ & TypeRelationFlag::NO_RETURN_TYPE_CHECK) != 0; + } + + bool DirectReturn() const + { + return (flags_ & TypeRelationFlag::DIRECT_RETURN) != 0; + } + + bool InAssignmentContext() const + { + return (flags_ & TypeRelationFlag::IN_ASSIGNMENT_CONTEXT) != 0; + } + + bool OnlyCheckWidening() const + { + return (flags_ & TypeRelationFlag::ONLY_CHECK_WIDENING) != 0; + } + + const Checker *GetChecker() const + { + return checker_; + } + + ir::Expression *GetNode() const + { + return node_; + } + + Checker *GetChecker() + { + return checker_; + } bool IsIdenticalTo(Type *source, Type *target); bool IsIdenticalTo(Signature *source, Signature *target); @@ -94,18 +194,58 @@ public: void RaiseError(const std::string &errMsg, const lexer::SourcePosition &loc) const; void RaiseError(std::initializer_list list, const lexer::SourcePosition &loc) const; - void Result(bool res); - const Checker *GetChecker() const; - Checker *GetChecker(); + void Result(bool res) + { + result_ = res ? RelationResult::TRUE : RelationResult::FALSE; + } + + void Result(RelationResult res) + { + result_ = res; + } + + void SetNode(ir::Expression *node) + { + node_ = node; + } + + void SetFlags(TypeRelationFlag flags) + { + flags_ = flags; + } + ArenaAllocator *Allocator(); - bool IsTrue() const; + + friend class SavedTypeRelationFlagsContext; private: RelationResult CacheLookup(const Type *source, const Type *target, const RelationHolder &holder, RelationType type) const; Checker *checker_; - RelationResult result_ {RelationResult::FALSE}; + RelationResult result_ {}; + TypeRelationFlag flags_ {}; + ir::Expression *node_ {}; +}; +class SavedTypeRelationFlagsContext { +public: + explicit SavedTypeRelationFlagsContext(TypeRelation *relation, TypeRelationFlag newFlag) + : relation_(relation), prev_(relation->flags_) + { + relation_->flags_ = newFlag; + } + + NO_COPY_SEMANTIC(SavedTypeRelationFlagsContext); + DEFAULT_MOVE_SEMANTIC(SavedTypeRelationFlagsContext); + + ~SavedTypeRelationFlagsContext() + { + relation_->flags_ = prev_; + } + +private: + TypeRelation *relation_; + TypeRelationFlag prev_; }; } // namespace panda::es2panda::checker diff --git a/compiler/base/catchTable.cpp b/compiler/base/catchTable.cpp index b6992dd7491090fa47b5472f781df1207e43f0fd..eda1103bb51a13e93b4e91e4bfe53e7d791b8b09 100644 --- a/compiler/base/catchTable.cpp +++ b/compiler/base/catchTable.cpp @@ -18,8 +18,8 @@ #include "plugins/ecmascript/es2panda/compiler/core/pandagen.h" namespace panda::es2panda::compiler { -TryLabelSet::TryLabelSet(PandaGen *pg) - : try_(pg->AllocLabel(), pg->AllocLabel()), catch_(pg->AllocLabel(), pg->AllocLabel()) +TryLabelSet::TryLabelSet(CodeGen *cg) + : try_(cg->AllocLabel(), cg->AllocLabel()), catch_(cg->AllocLabel(), cg->AllocLabel()) { } } // namespace panda::es2panda::compiler diff --git a/compiler/base/catchTable.h b/compiler/base/catchTable.h index 5dfd29a23e1ad2e26668376e0bbcdc3e5a2cf296..50236a066d1a8cde3b82bc73adcdbe1aa0ff44a5 100644 --- a/compiler/base/catchTable.h +++ b/compiler/base/catchTable.h @@ -20,11 +20,11 @@ #include "plugins/ecmascript/es2panda/compiler/core/labelPair.h" namespace panda::es2panda::compiler { -class PandaGen; +class CodeGen; class TryLabelSet { public: - explicit TryLabelSet(PandaGen *pg); + explicit TryLabelSet(CodeGen *cg); ~TryLabelSet() = default; DEFAULT_COPY_SEMANTIC(TryLabelSet); @@ -67,7 +67,7 @@ private: class CatchTable { public: - CatchTable(PandaGen *pg, uint32_t depth) : labelSet_(pg), depth_(depth) {} + CatchTable(CodeGen *cg, uint32_t depth) : labelSet_(cg), depth_(depth) {} ~CatchTable() = default; NO_COPY_SEMANTIC(CatchTable); diff --git a/compiler/base/condition.cpp b/compiler/base/condition.cpp index 647f20923989019b1f56be6d94a072ac60e16621..ce9558c83562cedd3d2ec8a8852d3245f146846d 100644 --- a/compiler/base/condition.cpp +++ b/compiler/base/condition.cpp @@ -16,6 +16,7 @@ #include "condition.h" #include "plugins/ecmascript/es2panda/compiler/core/pandagen.h" +#include "plugins/ecmascript/es2panda/compiler/core/ETSGen.h" #include "plugins/ecmascript/es2panda/ir/expressions/binaryExpression.h" #include "plugins/ecmascript/es2panda/ir/expressions/unaryExpression.h" @@ -89,4 +90,83 @@ void Condition::Compile(PandaGen *pg, const ir::Expression *expr, Label *falseLa pg->ToBoolean(expr); pg->BranchIfFalse(expr, falseLabel); } + +Condition::Result Condition::CheckConstantExpr(const ir::Expression *expr) +{ + if (expr->TsType()->HasTypeFlag(checker::TypeFlag::CONSTANT)) { + auto res = expr->TsType()->AsETSBooleanType()->GetValue(); + return res ? Result::CONST_TRUE : Result::CONST_FALSE; + } + + return Result::UNKNOWN; +} + +void Condition::Compile(ETSGen *etsg, const ir::Expression *expr, Label *falseLabel) +{ + if (expr->IsBinaryExpression()) { + const auto *binExpr = expr->AsBinaryExpression(); + + switch (binExpr->OperatorType()) { + case lexer::TokenType::PUNCTUATOR_EQUAL: + case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: + case lexer::TokenType::PUNCTUATOR_LESS_THAN: + case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: + case lexer::TokenType::PUNCTUATOR_GREATER_THAN: + case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: + case lexer::TokenType::KEYW_INSTANCEOF: { + auto ttctx = TargetTypeContext(etsg, binExpr->OperationType()); + + RegScope rs(etsg); + VReg lhs = etsg->AllocReg(); + + binExpr->Left()->Compile(etsg); + etsg->ApplyConversionAndStoreAccumulator(binExpr, lhs, binExpr->OperationType()); + binExpr->Right()->Compile(etsg); + etsg->Condition(binExpr, binExpr->OperatorType(), lhs, falseLabel); + return; + } + case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: { + binExpr->Left()->Compile(etsg); + etsg->ApplyConversion(binExpr->Left(), binExpr->OperationType()); + etsg->BranchIfFalse(binExpr, falseLabel); + + binExpr->Right()->Compile(etsg); + etsg->ApplyConversion(binExpr->Right(), binExpr->OperationType()); + etsg->BranchIfFalse(binExpr, falseLabel); + return; + } + case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: { + auto *endLabel = etsg->AllocLabel(); + + binExpr->Left()->Compile(etsg); + etsg->ApplyConversion(binExpr->Left(), binExpr->OperationType()); + etsg->BranchIfTrue(binExpr, endLabel); + + binExpr->Right()->Compile(etsg); + etsg->ApplyConversion(binExpr->Right(), binExpr->OperationType()); + etsg->BranchIfFalse(binExpr, falseLabel); + etsg->SetLabel(binExpr, endLabel); + return; + } + default: { + break; + } + } + } else if (expr->IsUnaryExpression() && + expr->AsUnaryExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK) { + expr->AsUnaryExpression()->Argument()->Compile(etsg); + etsg->ApplyConversion(expr->AsUnaryExpression()->Argument(), etsg->Checker()->GlobalETSBooleanType()); + etsg->BranchIfTrue(expr, falseLabel); + return; + } + + // TODO(user): Handle implicit bool conversion: not zero int == true, not null obj ref == true, otherwise false + ASSERT(expr->TsType()->IsETSBooleanType() || + (expr->TsType()->IsETSObjectType() && + expr->TsType()->AsETSObjectType()->HasObjectFlag( + checker::ETSObjectFlags::BUILTIN_BOOLEAN))); // already checked by checker::CheckTruthinessOfType() + expr->Compile(etsg); + etsg->ApplyConversion(expr, etsg->Checker()->GlobalETSBooleanType()); + etsg->BranchIfFalse(expr, falseLabel); +} } // namespace panda::es2panda::compiler diff --git a/compiler/base/condition.h b/compiler/base/condition.h index c7d8e655d421b1ae238940e0a0f2a1a0706548c9..c969d104755cf40a1e6367eb08f8962633bc7b31 100644 --- a/compiler/base/condition.h +++ b/compiler/base/condition.h @@ -20,13 +20,22 @@ namespace panda::es2panda::compiler { class PandaGen; +class ETSGen; class Label; class Condition { public: Condition() = delete; + enum class Result { + CONST_TRUE, + CONST_FALSE, + UNKNOWN, + }; + static void Compile(PandaGen *pg, const ir::Expression *expr, Label *falseLabel); + static void Compile(ETSGen *etsg, const ir::Expression *expr, Label *falseLabel); + static Result CheckConstantExpr(const ir::Expression *expr); }; } // namespace panda::es2panda::compiler diff --git a/compiler/base/destructuring.cpp b/compiler/base/destructuring.cpp index ea61f4d3d05a8edbd93b5062997f80e6e40590d3..694faf5c2bba7b503d34dbdc0c9916ab62a2605b 100644 --- a/compiler/base/destructuring.cpp +++ b/compiler/base/destructuring.cpp @@ -40,7 +40,7 @@ static void GenRestElement(PandaGen *pg, const ir::SpreadElement *restElement, DestructuringRestIterator iterator(destIterator); // create left reference for rest element - LReference lref = LReference::CreateLRef(pg, restElement, isDeclaration); + auto lref = JSLReference::Create(pg, restElement, isDeclaration); // create an empty array first pg->CreateEmptyArray(restElement); @@ -104,7 +104,7 @@ static void GenArray(PandaGen *pg, const ir::ArrayExpression *array) init = element->AsAssignmentPattern()->Right(); } - LReference lref = LReference::CreateLRef(pg, target, array->IsDeclaration()); + auto lref = JSLReference::Create(pg, target, array->IsDeclaration()); iterator.Step(); if (init != nullptr) { @@ -192,7 +192,7 @@ static void GenObjectWithRest(PandaGen *pg, const ir::ObjectExpression *object, for (const auto *element : properties) { if (element->IsRestElement()) { RegScope restScope(pg); - LReference lref = LReference::CreateLRef(pg, element, object->IsDeclaration()); + auto lref = JSLReference::Create(pg, element, object->IsDeclaration()); pg->CreateObjectWithExcludedKeys(element, rhs, propStart, properties.size() - 1); lref.SetValue(); break; @@ -214,7 +214,7 @@ static void GenObjectWithRest(PandaGen *pg, const ir::ObjectExpression *object, pg->StoreAccumulator(key, propReg); - LReference lref = LReference::CreateLRef(pg, target, object->IsDeclaration()); + auto lref = JSLReference::Create(pg, target, object->IsDeclaration()); pg->LoadAccumulator(element, propReg); pg->LoadObjByValue(element, rhs); @@ -252,7 +252,7 @@ static void GenObject(PandaGen *pg, const ir::ObjectExpression *object, VReg rhs Operand propOperand = pg->ToOwnPropertyKey(key, propExpr->IsComputed()); - LReference lref = LReference::CreateLRef(pg, target, object->IsDeclaration()); + auto lref = JSLReference::Create(pg, target, object->IsDeclaration()); if (std::holds_alternative(propOperand)) { pg->LoadAccumulator(element, std::get(propOperand)); diff --git a/compiler/base/literals.h b/compiler/base/literals.h index 9534b830563b54593aa33567a3c5268fe063936e..a1e30e6c3f32432274e0a4754759f128745c50d7 100644 --- a/compiler/base/literals.h +++ b/compiler/base/literals.h @@ -27,7 +27,7 @@ class TaggedTemplateExpression; } // namespace panda::es2panda::ir namespace panda::es2panda::checker { -class Checker; +class TSChecker; class Type; } // namespace panda::es2panda::checker @@ -54,7 +54,6 @@ enum class LiteralTag { class Literal { public: explicit Literal() = default; - ~Literal() = default; explicit Literal(LiteralTag tag, const util::StringView &str) : tag_(tag), value_(str.Mutf8()) {} explicit Literal(const util::StringView &str) : tag_(LiteralTag::STRING), value_(str.Mutf8()) {} @@ -64,6 +63,7 @@ public: DEFAULT_COPY_SEMANTIC(Literal); DEFAULT_MOVE_SEMANTIC(Literal); + ~Literal() = default; static Literal NullLiteral() { diff --git a/compiler/base/lreference.cpp b/compiler/base/lreference.cpp index 0f2e0b36f745781ef5328fc1be09259787731ece..1baed6a795888966300f847c1fca991b07704e70 100644 --- a/compiler/base/lreference.cpp +++ b/compiler/base/lreference.cpp @@ -19,7 +19,10 @@ #include "plugins/ecmascript/es2panda/compiler/base/destructuring.h" #include "plugins/ecmascript/es2panda/compiler/core/function.h" #include "plugins/ecmascript/es2panda/compiler/core/pandagen.h" +#include "plugins/ecmascript/es2panda/compiler/core/ETSGen.h" #include "plugins/ecmascript/es2panda/ir/base/spreadElement.h" +#include "plugins/ecmascript/es2panda/ir/base/classProperty.h" +#include "plugins/ecmascript/es2panda/ir/base/classDefinition.h" #include "plugins/ecmascript/es2panda/ir/expressions/assignmentExpression.h" #include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" #include "plugins/ecmascript/es2panda/ir/expressions/memberExpression.h" @@ -28,66 +31,104 @@ #include "plugins/ecmascript/es2panda/util/helpers.h" namespace panda::es2panda::compiler { -// LReference -LReference::LReference(const ir::AstNode *node, PandaGen *pg, bool isDeclaration, ReferenceKind refKind, - binder::ScopeFindResult res) - : node_(node), pg_(pg), refKind_(refKind), res_(res), isDeclaration_(isDeclaration) +LReference::LReferenceBase LReference::CreateBase(CodeGen *cg, const ir::AstNode *node, bool isDeclaration) { - if (refKind_ != ReferenceKind::MEMBER) { + switch (node->Type()) { + case ir::AstNodeType::IDENTIFIER: { + const util::StringView &name = node->AsIdentifier()->Name(); + binder::ScopeFindResult res = cg->Scope()->Find(name, binder::ResolveBindingOptions::ALL); + + if (res.variable == nullptr) { + res.variable = node->AsIdentifier()->Variable(); + } + + return {cg, node, ReferenceKind::VAR_OR_GLOBAL, res, isDeclaration}; + } + case ir::AstNodeType::MEMBER_EXPRESSION: { + return {cg, node, ReferenceKind::MEMBER, {}, false}; + } + case ir::AstNodeType::VARIABLE_DECLARATION: { + ASSERT(node->AsVariableDeclaration()->Declarators().size() == 1); + return CreateBase(cg, node->AsVariableDeclaration()->Declarators()[0]->Id(), true); + } + case ir::AstNodeType::VARIABLE_DECLARATOR: { + return CreateBase(cg, node->AsVariableDeclarator()->Id(), true); + } + case ir::AstNodeType::ARRAY_PATTERN: + case ir::AstNodeType::OBJECT_PATTERN: { + return {cg, node, ReferenceKind::DESTRUCTURING, {}, isDeclaration}; + } + case ir::AstNodeType::ASSIGNMENT_PATTERN: { + return CreateBase(cg, node->AsAssignmentPattern()->Left(), true); + } + case ir::AstNodeType::REST_ELEMENT: { + return CreateBase(cg, node->AsRestElement()->Argument(), true); + } + default: { + UNREACHABLE(); + } + } +} + +JSLReference::JSLReference(CodeGen *cg, const ir::AstNode *node, ReferenceKind refKind, binder::ScopeFindResult res, + bool isDeclaration) + : LReference(node, refKind, res, isDeclaration), pg_(static_cast(cg)) +{ + if (Kind() != ReferenceKind::MEMBER) { return; } - const auto *memberExpr = node_->AsMemberExpression(); + const auto *memberExpr = Node()->AsMemberExpression(); if (memberExpr->Object()->IsSuperExpression()) { - refKind_ = ReferenceKind::SUPER; + SetKind(ReferenceKind::SUPER); } else if (memberExpr->IsPrivateReference()) { - refKind_ = ReferenceKind::PRIVATE; + SetKind(ReferenceKind::PRIVATE); privateCtor_ = pg_->AllocReg(); - Function::LoadClassContexts(node_, pg_, privateCtor_, memberExpr->Property()->AsIdentifier()->Name()); + Function::LoadClassContexts(Node(), pg_, privateCtor_, memberExpr->Property()->AsIdentifier()->Name()); } obj_ = pg_->AllocReg(); memberExpr->Object()->Compile(pg_); - pg->StoreAccumulator(node_, obj_); + pg_->StoreAccumulator(Node(), obj_); prop_ = pg_->ToNamedPropertyKey(memberExpr->Property(), memberExpr->IsComputed()); if (std::holds_alternative(prop_)) { return; } - if (std::holds_alternative(prop_) && refKind_ != ReferenceKind::SUPER) { + if (std::holds_alternative(prop_) && Kind() != ReferenceKind::SUPER) { return; } memberExpr->Property()->Compile(pg_); VReg propReg = pg_->AllocReg(); - pg_->StoreAccumulator(node_, propReg); + pg_->StoreAccumulator(Node(), propReg); prop_ = propReg; } -void LReference::GetValue() +void JSLReference::GetValue() const { - switch (refKind_) { + switch (Kind()) { case ReferenceKind::VAR_OR_GLOBAL: { - pg_->LoadVar(node_->AsIdentifier(), res_); + pg_->LoadVar(Node()->AsIdentifier(), Result()); break; } case ReferenceKind::MEMBER: { if (std::holds_alternative(prop_)) { - pg_->LoadObjProperty(node_, obj_); + pg_->LoadObjProperty(Node(), obj_); break; } [[fallthrough]]; } case ReferenceKind::SUPER: { - pg_->LoadObjProperty(node_, prop_); + pg_->LoadObjProperty(Node(), prop_); break; } case ReferenceKind::PRIVATE: { - pg_->ClassPrivateFieldGet(node_, privateCtor_, obj_, std::get(prop_)); + pg_->ClassPrivateFieldGet(Node(), privateCtor_, obj_, std::get(prop_)); break; } default: { @@ -96,29 +137,29 @@ void LReference::GetValue() } } -void LReference::SetValue() +void JSLReference::SetValue() const { - switch (refKind_) { + switch (Kind()) { case ReferenceKind::VAR_OR_GLOBAL: { - pg_->StoreVar(node_, res_, isDeclaration_); + pg_->StoreVar(Node(), Result(), IsDeclaration()); break; } case ReferenceKind::SUPER: { - pg_->StoreSuperProperty(node_, obj_, prop_); + pg_->StoreSuperProperty(Node(), obj_, prop_); break; } case ReferenceKind::MEMBER: { - pg_->StoreObjProperty(node_, obj_, prop_); + pg_->StoreObjProperty(Node(), obj_, prop_); break; } case ReferenceKind::PRIVATE: { - pg_->ClassPrivateFieldSet(node_, privateCtor_, obj_, std::get(prop_)); + pg_->ClassPrivateFieldSet(Node(), privateCtor_, obj_, std::get(prop_)); break; } case ReferenceKind::DESTRUCTURING: { - Destructuring::Compile(pg_, node_->AsExpression()); + Destructuring::Compile(pg_, Node()->AsExpression()); break; } default: { @@ -127,48 +168,106 @@ void LReference::SetValue() } } -ReferenceKind LReference::Kind() const +ETSLReference::ETSLReference(CodeGen *cg, const ir::AstNode *node, ReferenceKind refKind, binder::ScopeFindResult res, + bool isDeclaration) + : LReference(node, refKind, res, isDeclaration), etsg_(static_cast(cg)) { - return refKind_; -} + if (Kind() != ReferenceKind::MEMBER) { + SetKind(ResolveReferenceKind(res.variable)); + return; + } -binder::Variable *LReference::Variable() const -{ - return res_.variable; + const auto *memberExpr = Node()->AsMemberExpression(); + staticObjRef_ = memberExpr->Object()->TsType(); + + if (!memberExpr->IsComputed() && memberExpr->PropVar()->HasFlag(binder::VariableFlags::STATIC)) { + return; + } + + TargetTypeContext ttctx(etsg_, memberExpr->Object()->TsType()); + memberExpr->Object()->Compile(etsg_); + baseReg_ = etsg_->AllocReg(); + etsg_->StoreAccumulator(node, baseReg_); + + if (memberExpr->IsComputed()) { + TargetTypeContext pttctx(etsg_, memberExpr->Property()->TsType()); + memberExpr->Property()->Compile(etsg_); + propReg_ = etsg_->AllocReg(); + etsg_->ApplyConversionAndStoreAccumulator(node, propReg_, memberExpr->GetUnboxedPropertyType()); + } } -LReference LReference::CreateLRef(PandaGen *pg, const ir::AstNode *node, bool isDeclaration) +ReferenceKind ETSLReference::ResolveReferenceKind(const binder::Variable *variable) { - switch (node->Type()) { - case ir::AstNodeType::IDENTIFIER: { - const util::StringView &name = node->AsIdentifier()->Name(); - binder::ScopeFindResult res = pg->Scope()->Find(name); + if (variable->HasFlag(binder::VariableFlags::SYNTHETIC)) { + return ReferenceKind::METHOD; + } + + auto *declNode = variable->Declaration()->Node(); - return {node, pg, isDeclaration, ReferenceKind::VAR_OR_GLOBAL, res}; + switch (declNode->Type()) { + case ir::AstNodeType::CLASS_PROPERTY: { + auto *classField = declNode->AsClassProperty(); + return classField->IsStatic() ? ReferenceKind::STATIC_FIELD : ReferenceKind::FIELD; } - case ir::AstNodeType::MEMBER_EXPRESSION: { - return {node, pg, false, ReferenceKind::MEMBER, {}}; + case ir::AstNodeType::CLASS_DEFINITION: { + auto *classDef = declNode->AsClassDefinition(); + return classDef->IsStatic() ? ReferenceKind::STATIC_CLASS : ReferenceKind::CLASS; } - case ir::AstNodeType::VARIABLE_DECLARATION: { - ASSERT(node->AsVariableDeclaration()->Declarators().size() == 1); - return LReference::CreateLRef(pg, node->AsVariableDeclaration()->Declarators()[0]->Id(), true); + case ir::AstNodeType::METHOD_DEFINITION: { + return ReferenceKind::METHOD; } - case ir::AstNodeType::VARIABLE_DECLARATOR: { - return LReference::CreateLRef(pg, node->AsVariableDeclarator()->Id(), true); + case ir::AstNodeType::TS_INTERFACE_DECLARATION: { } - case ir::AstNodeType::ARRAY_PATTERN: - case ir::AstNodeType::OBJECT_PATTERN: { - return {node, pg, isDeclaration, ReferenceKind::DESTRUCTURING, {}}; + default: { + break; } - case ir::AstNodeType::ASSIGNMENT_PATTERN: { - return LReference::CreateLRef(pg, node->AsAssignmentPattern()->Left(), true); + } + + return ReferenceKind::LOCAL; +} + +void ETSLReference::GetValue() const +{ + switch (Kind()) { + case ReferenceKind::MEMBER: { + Node()->AsMemberExpression()->Compile(etsg_); + break; } - case ir::AstNodeType::REST_ELEMENT: { - return LReference::CreateLRef(pg, node->AsRestElement()->Argument(), true); + default: { + etsg_->LoadVar(Node()->AsIdentifier(), Variable()); + break; + } + } +} + +void ETSLReference::SetValue() const +{ + switch (Kind()) { + case ReferenceKind::MEMBER: { + auto *memberExpr = Node()->AsMemberExpression(); + etsg_->ApplyConversion(Node(), memberExpr->TsType()); + + if (memberExpr->IsComputed()) { + etsg_->StoreArrayElement(Node(), baseReg_, propReg_); + break; + } + + auto &propName = memberExpr->Property()->AsIdentifier()->Name(); + if (memberExpr->PropVar()->HasFlag(binder::VariableFlags::STATIC)) { + util::StringView fullName = etsg_->FormClassPropReference(staticObjRef_->AsETSObjectType(), propName); + etsg_->StoreStaticProperty(Node(), memberExpr->TsType(), fullName); + break; + } + + etsg_->StoreProperty(Node(), memberExpr->TsType(), baseReg_, propName); + break; } default: { - UNREACHABLE(); + etsg_->StoreVar(Node()->AsIdentifier(), Result()); + break; } } } + } // namespace panda::es2panda::compiler diff --git a/compiler/base/lreference.h b/compiler/base/lreference.h index 0df234059dafff85af7b193ffbfc76265b633e8b..54e6a3e591e6297ccec88bca6a548fd7e89112e0 100644 --- a/compiler/base/lreference.h +++ b/compiler/base/lreference.h @@ -13,8 +13,8 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_BASE_LREFERENCE_H -#define ES2PANDA_COMPILER_BASE_LREFERENCE_H +#ifndef ES2PANDA_COMPILER_BASE_JSLREFERENCE_H +#define ES2PANDA_COMPILER_BASE_JSLREFERENCE_H #include "plugins/ecmascript/es2panda/binder/scope.h" #include "plugins/ecmascript/es2panda/ir/irnode.h" @@ -23,6 +23,10 @@ namespace panda::es2panda::ir { class AstNode; } // namespace panda::es2panda::ir +namespace panda::es2panda::checker { +class ETSObjectType; +} // namespace panda::es2panda::checker + namespace panda::es2panda::compiler { enum class ReferenceKind { MEMBER, @@ -30,34 +34,122 @@ enum class ReferenceKind { SUPER, VAR_OR_GLOBAL, DESTRUCTURING, + LOCAL, + STATIC_FIELD, + FIELD, + CLASS, + STATIC_CLASS, + METHOD, + STATIC_METHOD, }; +class CodeGen; +class ETSGen; class PandaGen; class LReference { public: - LReference(const ir::AstNode *node, PandaGen *pg, bool isDeclaration, ReferenceKind refKind, - binder::ScopeFindResult res); ~LReference() = default; NO_COPY_SEMANTIC(LReference); - NO_MOVE_SEMANTIC(LReference); + DEFAULT_MOVE_SEMANTIC(LReference); + + ReferenceKind Kind() const + { + return refKind_; + } + + void SetKind(ReferenceKind refKind) + { + refKind_ = refKind; + } + + binder::Variable *Variable() const + { + return res_.variable; + } + + const ir::AstNode *Node() const + { + return node_; + } + + binder::ScopeFindResult &Result() + { + return res_; + } + + const binder::ScopeFindResult &Result() const + { + return res_; + } - void GetValue(); - void SetValue(); - binder::Variable *Variable() const; - ReferenceKind Kind() const; + bool IsDeclaration() const + { + return isDeclaration_; + } - static LReference CreateLRef(PandaGen *pg, const ir::AstNode *node, bool isDeclaration); +protected: + using LReferenceBase = std::tuple; + static LReferenceBase CreateBase(CodeGen *cg, const ir::AstNode *node, bool isDeclaration); + + explicit LReference(const ir::AstNode *node, ReferenceKind refKind, binder::ScopeFindResult res, bool isDeclaration) + : node_(node), refKind_(refKind), res_(res), isDeclaration_(isDeclaration) + { + } private: const ir::AstNode *node_; - PandaGen *pg_; ReferenceKind refKind_; binder::ScopeFindResult res_; + bool isDeclaration_; +}; + +class JSLReference : public LReference { +public: + JSLReference(CodeGen *cg, const ir::AstNode *node, ReferenceKind refKind, binder::ScopeFindResult res, + bool isDeclaration); + ~JSLReference() = default; + NO_COPY_SEMANTIC(JSLReference); + NO_MOVE_SEMANTIC(JSLReference); + + void GetValue() const; + void SetValue() const; + + static JSLReference Create(CodeGen *cg, const ir::AstNode *node, bool isDeclaration) + { + return std::make_from_tuple(CreateBase(cg, node, isDeclaration)); + } + +private: + PandaGen *pg_; VReg obj_; VReg privateCtor_ {}; Operand prop_; - bool isDeclaration_; +}; + +class ETSLReference : public LReference { +public: + ETSLReference(CodeGen *cg, const ir::AstNode *node, ReferenceKind refKind, binder::ScopeFindResult res, + bool isDeclaration); + ~ETSLReference() = default; + NO_COPY_SEMANTIC(ETSLReference); + NO_MOVE_SEMANTIC(ETSLReference); + + void GetValue() const; + void SetValue() const; + + static ETSLReference Create(CodeGen *cg, const ir::AstNode *node, bool isDeclaration) + { + return std::make_from_tuple(CreateBase(cg, node, isDeclaration)); + } + + static ReferenceKind ResolveReferenceKind(const binder::Variable *variable); + +private: + ETSGen *etsg_; + const checker::Type *staticObjRef_ {}; + VReg baseReg_ {}; + VReg propReg_ {}; }; } // namespace panda::es2panda::compiler diff --git a/compiler/base/optionalChain.cpp b/compiler/base/optionalChain.cpp index c9cec7519e2da41b3b661a4036f541a464fa11df..e115517ddf131e7374d838c458cb4caeaca9cf61 100644 --- a/compiler/base/optionalChain.cpp +++ b/compiler/base/optionalChain.cpp @@ -40,7 +40,7 @@ void OptionalChain::Check(compiler::VReg obj) RegScope rs(pg_); - if (obj == INVALID_VREG) { + if (obj.IsInvalid()) { obj = pg_->AllocReg(); pg_->StoreAccumulator(node_, obj); } diff --git a/compiler/core/ETSGen.cpp b/compiler/core/ETSGen.cpp new file mode 100644 index 0000000000000000000000000000000000000000..436486331c19659ffd411649699f0a70094eba19 --- /dev/null +++ b/compiler/core/ETSGen.cpp @@ -0,0 +1,1305 @@ +/* + * Copyright (c) 2022 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 "ETSGen.h" + +#include "plugins/ecmascript/es2panda/ir/base/scriptFunction.h" +#include "plugins/ecmascript/es2panda/ir/base/classDefinition.h" +#include "plugins/ecmascript/es2panda/ir/statement.h" +#include "plugins/ecmascript/es2panda/ir/expressions/assignmentExpression.h" +#include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" +#include "plugins/ecmascript/es2panda/ir/expressions/binaryExpression.h" +#include "plugins/ecmascript/es2panda/ir/statements/deferStatement.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceDeclaration.h" +#include "plugins/ecmascript/es2panda/compiler/base/lreference.h" +#include "plugins/ecmascript/es2panda/compiler/base/catchTable.h" +#include "plugins/ecmascript/es2panda/compiler/core/dynamicContext.h" +#include "plugins/ecmascript/es2panda/compiler/core/compilerContext.h" +#include "plugins/ecmascript/es2panda/binder/ETSBinder.h" +#include "plugins/ecmascript/es2panda/binder/variable.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" +#include "plugins/ecmascript/es2panda/checker/checker.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/types.h" +#include "plugins/ecmascript/es2panda/parser/program/program.h" + +namespace panda::es2panda::compiler { + +const checker::ETSChecker *ETSGen::Checker() const +{ + return Context()->Checker()->AsETSChecker(); +} + +const checker::Type *ETSGen::ReturnType() const +{ + return RootNode()->AsScriptFunction()->Signature()->ReturnType(); +} + +void ETSGen::ApplyConversionAndStoreAccumulator(const ir::AstNode *node, VReg &vreg, const checker::Type *targetType) +{ + ApplyConversion(node, targetType); + StoreAccumulator(node, vreg); +} + +VReg ETSGen::StoreException(const ir::AstNode *node) +{ + VReg exception = AllocReg(); + Ra().Emit(node, exception); + + acc_.SetType(Checker()->GlobalBuiltinExceptionType()); + exception.SetType(acc_.GetType()); + return exception; +} + +void ETSGen::StoreAccumulator(const ir::AstNode *node, VReg &vreg) +{ + if (acc_.GetType()->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT)) { + Ra().Emit(node, vreg); + } else if (acc_.GetType()->HasTypeFlag(checker::TypeFlag::ETS_WIDE_NUMERIC)) { + Ra().Emit(node, vreg); + } else { + Ra().Emit(node, vreg); + } + + vreg.SetType(acc_.GetType()); +} + +void ETSGen::LoadAccumulator(const ir::AstNode *node, VReg vreg) +{ + if (vreg.GetType()->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT)) { + Ra().Emit(node, vreg); + } else if (vreg.GetType()->HasTypeFlag(checker::TypeFlag::ETS_WIDE_NUMERIC)) { + Ra().Emit(node, vreg); + } else { + Ra().Emit(node, vreg); + } + + acc_.SetType(vreg.GetType()); +} + +void ETSGen::MoveVreg(const ir::AstNode *node, VReg &vd, VReg vs) +{ + if (vs.GetType()->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT)) { + Ra().Emit(node, vd, vs); + } else if (vs.GetType()->HasTypeFlag(checker::TypeFlag::ETS_WIDE_NUMERIC)) { + Ra().Emit(node, vd, vs); + } else { + Ra().Emit(node, vd, vs); + } + + vd.SetType(vs.GetType()); +} + +void ETSGen::LoadVar(const ir::AstNode *node, binder::Variable *var) +{ + auto *local = var->AsLocalVariable(); + + switch (ETSLReference::ResolveReferenceKind(var)) { + case ReferenceKind::STATIC_FIELD: { + auto fullName = FormClassPropReference(var); + LoadStaticProperty(node, var->TsType(), fullName); + break; + } + case ReferenceKind::FIELD: { + LoadProperty(node, var->TsType(), GetThisReg(), var->Name()); + break; + } + case ReferenceKind::METHOD: + case ReferenceKind::STATIC_METHOD: + case ReferenceKind::CLASS: + case ReferenceKind::STATIC_CLASS: { + acc_.SetType(var->TsType()); + break; + } + case ReferenceKind::LOCAL: { + LoadAccumulator(node, local->Vreg()); + break; + } + default: { + UNREACHABLE(); + } + } + + if (targetType_ != nullptr) { + ApplyConversion(node, targetType_); + } +} + +void ETSGen::StoreVar(const ir::AstNode *node, const binder::ScopeFindResult &result) +{ + auto *local = result.variable->AsLocalVariable(); + ApplyConversion(node, local->TsType()); + + switch (ETSLReference::ResolveReferenceKind(result.variable)) { + case ReferenceKind::STATIC_FIELD: { + auto fullName = FormClassPropReference(result.variable); + StoreStaticProperty(node, result.variable->TsType(), fullName); + break; + } + case ReferenceKind::FIELD: { + StoreProperty(node, result.variable->TsType(), GetThisReg(), result.name); + break; + } + case ReferenceKind::LOCAL: { + StoreAccumulator(node, local->Vreg()); + break; + } + default: { + UNREACHABLE(); + } + } +} + +util::StringView ETSGen::FormClassPropReference(const checker::ETSObjectType *classType, const util::StringView &name) +{ + std::stringstream ss; + ss << classType->AssemblerName() << '.' << name; + auto res = ProgElement()->Strings().emplace(ss.str()); + + return util::StringView(*res.first); +} + +util::StringView ETSGen::FormClassPropReference(binder::Variable *var) +{ + auto containingObjectType = util::Helpers::GetContainingObjectType(var->Declaration()->Node()); + return FormClassPropReference(containingObjectType, var->Name()); +} + +void ETSGen::StoreStaticOwnProperty(const ir::AstNode *node, const checker::Type *propType, + const util::StringView &name) +{ + util::StringView fullName = FormClassPropReference(containingObjectType_, name); + StoreStaticProperty(node, propType, fullName); +} + +void ETSGen::StoreStaticProperty(const ir::AstNode *node, const checker::Type *propType, + const util::StringView &fullName) +{ + if (propType->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT)) { + Sa().Emit(node, fullName); + } else if (propType->HasTypeFlag(checker::TypeFlag::ETS_WIDE_NUMERIC)) { + Sa().Emit(node, fullName); + } else { + Sa().Emit(node, fullName); + } +} + +void ETSGen::LoadStaticProperty(const ir::AstNode *node, const checker::Type *propType, + const util::StringView &fullName) +{ + if (propType->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT)) { + Sa().Emit(node, fullName); + } else if (propType->HasTypeFlag(checker::TypeFlag::ETS_WIDE_NUMERIC)) { + Sa().Emit(node, fullName); + } else { + Sa().Emit(node, fullName); + } + + acc_.SetType(propType); +} + +void ETSGen::StoreProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg, + const util::StringView &name) +{ + util::StringView fullName = FormClassPropReference(objReg.GetType()->AsETSObjectType(), name); + + if (propType->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT)) { + Ra().Emit(node, objReg, fullName); + } else if (propType->HasTypeFlag(checker::TypeFlag::ETS_WIDE_NUMERIC)) { + Ra().Emit(node, objReg, fullName); + } else { + Ra().Emit(node, objReg, fullName); + } +} + +void ETSGen::LoadProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg, + const util::StringView &name) +{ + util::StringView fullName = FormClassPropReference(objReg.GetType()->AsETSObjectType(), name); + + if (propType->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT)) { + Ra().Emit(node, objReg, fullName); + } else if (propType->HasTypeFlag(checker::TypeFlag::ETS_WIDE_NUMERIC)) { + Ra().Emit(node, objReg, fullName); + } else { + Ra().Emit(node, objReg, fullName); + } + + acc_.SetType(propType); +} + +void ETSGen::LoadThis(const ir::AstNode *node) +{ + LoadAccumulator(node, GetThisReg()); +} + +VReg &ETSGen::GetThisReg() +{ + binder::ScopeFindResult res = Scope()->Find(binder::Binder::MANDATORY_PARAM_THIS); + return res.variable->AsLocalVariable()->Vreg(); +} + +void ETSGen::LoadDefaultValue([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] const checker::Type *type) +{ + if (type->IsETSObjectType() || type->IsETSArrayType()) { + LoadAccumulatorNull(node, type); + } else if (type->IsETSBooleanType()) { + LoadAccumulatorBoolean(node, type->AsETSBooleanType()->GetValue()); + } else { + auto ttctx = TargetTypeContext(this, type); + LoadAccumulatorInt(node, 0); + } +} + +void ETSGen::EmitReturnVoid(const ir::AstNode *node) +{ + Sa().Emit(node); +} + +void ETSGen::ReturnAcc(const ir::AstNode *node) +{ + if (acc_.GetType()->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT)) { + Sa().Emit(node); + } else if (acc_.GetType()->HasTypeFlag(checker::TypeFlag::ETS_WIDE_NUMERIC)) { + Sa().Emit(node); + } else { + Sa().Emit(node); + } +} + +bool ETSGen::TryLoadConstantExpression(const ir::Expression *node) +{ + const auto *type = node->TsType(); + + if (!type->HasTypeFlag(checker::TypeFlag::CONSTANT)) { + return false; + } + + auto typeKind = checker::ETSChecker::TypeKind(type); + + switch (typeKind) { + case checker::TypeFlag::CHAR: { + LoadAccumulatorChar(node, type->AsCharType()->GetValue()); + break; + } + case checker::TypeFlag::ETS_BOOLEAN: { + LoadAccumulatorBoolean(node, type->AsETSBooleanType()->GetValue()); + break; + } + case checker::TypeFlag::BYTE: { + LoadAccumulatorByte(node, type->AsByteType()->GetValue()); + break; + } + case checker::TypeFlag::SHORT: { + LoadAccumulatorShort(node, type->AsShortType()->GetValue()); + break; + } + case checker::TypeFlag::INT: { + LoadAccumulatorInt(node, type->AsIntType()->GetValue()); + break; + } + case checker::TypeFlag::LONG: { + LoadAccumulatorWideInt(node, type->AsLongType()->GetValue()); + break; + } + case checker::TypeFlag::FLOAT: { + LoadAccumulatorFloat(node, type->AsFloatType()->GetValue()); + break; + } + case checker::TypeFlag::DOUBLE: { + LoadAccumulatorDouble(node, type->AsDoubleType()->GetValue()); + break; + } + case checker::TypeFlag::ETS_OBJECT: { + LoadAccumulatorString(node, type->AsETSObjectType()->AsETSStringType()->GetValue()); + break; + } + default: { + UNREACHABLE(); + } + } + + return true; +} + +void ETSGen::ApplyConversion(const ir::AstNode *node, const checker::Type *targetType) +{ + auto ttctx = TargetTypeContext(this, targetType); + + auto typeKind = checker::ETSChecker::TypeKind(targetType); + + auto sourceType = acc_.GetType(); + + if (sourceType->IsETSObjectType() && + sourceType->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::UNBOXABLE_TYPE) && + targetType->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) { + EmitUnboxingConversion(node, targetType, typeKind); + return; + } + + if (sourceType->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE) && targetType->IsETSObjectType() && + targetType->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::UNBOXABLE_TYPE)) { + EmitBoxingConversion(node, targetType, sourceType); + return; + } + + switch (typeKind) { + case checker::TypeFlag::DOUBLE: { + CastToDouble(node); + break; + } + case checker::TypeFlag::FLOAT: { + CastToFloat(node); + break; + } + case checker::TypeFlag::LONG: { + CastToLong(node); + break; + } + default: { + break; + } + } +} + +void ETSGen::EmitUnboxingConversion(const ir::AstNode *node, const checker::Type *targetType, + checker::TypeFlag typeKind) +{ + switch (typeKind) { + case checker::TypeFlag::ETS_BOOLEAN: { + Ra().Emit(node, Signatures::BUILTIN_BOOLEAN_BOOLEAN_VALUE, VReg::RegStart(), 0); + acc_.SetType(targetType); + break; + } + case checker::TypeFlag::BYTE: { + Ra().Emit(node, Signatures::BUILTIN_BYTE_BYTE_VALUE, VReg::RegStart(), 0); + acc_.SetType(targetType); + break; + } + case checker::TypeFlag::CHAR: { + Ra().Emit(node, Signatures::BUILTIN_CHAR_CHAR_VALUE, VReg::RegStart(), 0); + acc_.SetType(targetType); + break; + } + case checker::TypeFlag::SHORT: { + Ra().Emit(node, Signatures::BUILTIN_SHORT_SHORT_VALUE, VReg::RegStart(), 0); + acc_.SetType(targetType); + break; + } + case checker::TypeFlag::INT: { + Ra().Emit(node, Signatures::BUILTIN_INT_INT_VALUE, VReg::RegStart(), 0); + acc_.SetType(targetType); + break; + } + case checker::TypeFlag::LONG: { + Ra().Emit(node, Signatures::BUILTIN_LONG_LONG_VALUE, VReg::RegStart(), 0); + acc_.SetType(targetType); + break; + } + case checker::TypeFlag::FLOAT: { + Ra().Emit(node, Signatures::BUILTIN_FLOAT_FLOAT_VALUE, VReg::RegStart(), 0); + acc_.SetType(targetType); + break; + } + case checker::TypeFlag::DOUBLE: { + Ra().Emit(node, Signatures::BUILTIN_DOUBLE_DOUBLE_VALUE, VReg::RegStart(), 0); + acc_.SetType(targetType); + break; + } + default: { + UNREACHABLE(); + } + } +} + +void ETSGen::EmitBoxingConversion(const ir::AstNode *node, const checker::Type *targetType, + const checker::Type *sourceType) +{ + auto sourceTypeKind = checker::ETSChecker::TypeKind(sourceType); + switch (sourceTypeKind) { + case checker::TypeFlag::ETS_BOOLEAN: { + Ra().Emit(node, Signatures::BUILTIN_BOOLEAN_VALUE_OF, VReg::RegStart(), 0); + acc_.SetType(targetType); + break; + } + case checker::TypeFlag::BYTE: { + Ra().Emit(node, Signatures::BUILTIN_BYTE_VALUE_OF, VReg::RegStart(), 0); + acc_.SetType(targetType); + break; + } + case checker::TypeFlag::CHAR: { + Ra().Emit(node, Signatures::BUILTIN_CHAR_VALUE_OF, VReg::RegStart(), 0); + acc_.SetType(targetType); + break; + } + case checker::TypeFlag::SHORT: { + Ra().Emit(node, Signatures::BUILTIN_SHORT_VALUE_OF, VReg::RegStart(), 0); + acc_.SetType(targetType); + break; + } + case checker::TypeFlag::INT: { + Ra().Emit(node, Signatures::BUILTIN_INT_VALUE_OF, VReg::RegStart(), 0); + acc_.SetType(targetType); + break; + } + case checker::TypeFlag::LONG: { + Ra().Emit(node, Signatures::BUILTIN_LONG_VALUE_OF, VReg::RegStart(), 0); + acc_.SetType(targetType); + break; + } + case checker::TypeFlag::FLOAT: { + Ra().Emit(node, Signatures::BUILTIN_FLOAT_VALUE_OF, VReg::RegStart(), 0); + acc_.SetType(targetType); + break; + } + case checker::TypeFlag::DOUBLE: { + Ra().Emit(node, Signatures::BUILTIN_DOUBLE_VALUE_OF, VReg::RegStart(), 0); + acc_.SetType(targetType); + break; + } + default: { + UNREACHABLE(); + } + } +} + +void ETSGen::SwapBinaryOpArgs(const ir::AstNode *node, VReg &lhs) +{ + RegScope rs(this); + auto tmp = AllocReg(); + + StoreAccumulator(node, tmp); + LoadAccumulator(node, lhs); + MoveVreg(node, lhs, tmp); +} + +void ETSGen::CastToBoolean([[maybe_unused]] const ir::AstNode *node) +{ + auto typeKind = checker::ETSChecker::TypeKind(acc_.GetType()); + switch (typeKind) { + case checker::TypeFlag::ETS_BOOLEAN: + case checker::TypeFlag::BYTE: + case checker::TypeFlag::CHAR: + case checker::TypeFlag::SHORT: + case checker::TypeFlag::INT: { + Sa().Emit(node); + return; + } + case checker::TypeFlag::LONG: { + Sa().Emit(node); + break; + } + case checker::TypeFlag::FLOAT: { + Sa().Emit(node); + Sa().Emit(node); + break; + } + case checker::TypeFlag::DOUBLE: { + Sa().Emit(node); + Sa().Emit(node); + break; + } + default: { + UNREACHABLE(); + } + } + + acc_.SetType(Checker()->GlobalETSBooleanType()); +} + +void ETSGen::CastToByte([[maybe_unused]] const ir::AstNode *node) +{ + auto typeKind = checker::ETSChecker::TypeKind(acc_.GetType()); + switch (typeKind) { + case checker::TypeFlag::ETS_BOOLEAN: + case checker::TypeFlag::BYTE: { + return; + } + case checker::TypeFlag::CHAR: + case checker::TypeFlag::SHORT: + case checker::TypeFlag::INT: { + Sa().Emit(node); + return; + } + case checker::TypeFlag::LONG: { + Sa().Emit(node); + Sa().Emit(node); + break; + } + case checker::TypeFlag::FLOAT: { + Sa().Emit(node); + Sa().Emit(node); + break; + } + case checker::TypeFlag::DOUBLE: { + Sa().Emit(node); + Sa().Emit(node); + break; + } + default: { + UNREACHABLE(); + } + } + + acc_.SetType(Checker()->GlobalByteType()); +} + +void ETSGen::CastToChar([[maybe_unused]] const ir::AstNode *node) +{ + auto typeKind = checker::ETSChecker::TypeKind(acc_.GetType()); + switch (typeKind) { + case checker::TypeFlag::ETS_BOOLEAN: + case checker::TypeFlag::BYTE: + case checker::TypeFlag::CHAR: + case checker::TypeFlag::SHORT: { + Sa().Emit(node); + return; + } + case checker::TypeFlag::INT: { + Sa().Emit(node); + return; + } + case checker::TypeFlag::LONG: { + Sa().Emit(node); + Sa().Emit(node); + break; + } + case checker::TypeFlag::FLOAT: { + Sa().Emit(node); + Sa().Emit(node); + break; + } + case checker::TypeFlag::DOUBLE: { + Sa().Emit(node); + Sa().Emit(node); + break; + } + default: { + UNREACHABLE(); + } + } + + acc_.SetType(Checker()->GlobalCharType()); +} + +void ETSGen::CastToShort([[maybe_unused]] const ir::AstNode *node) +{ + auto typeKind = checker::ETSChecker::TypeKind(acc_.GetType()); + switch (typeKind) { + case checker::TypeFlag::ETS_BOOLEAN: + case checker::TypeFlag::BYTE: + case checker::TypeFlag::CHAR: + case checker::TypeFlag::SHORT: { + return; + } + case checker::TypeFlag::INT: { + Sa().Emit(node); + return; + } + case checker::TypeFlag::LONG: { + Sa().Emit(node); + Sa().Emit(node); + break; + } + case checker::TypeFlag::FLOAT: { + Sa().Emit(node); + Sa().Emit(node); + break; + } + case checker::TypeFlag::DOUBLE: { + Sa().Emit(node); + Sa().Emit(node); + break; + } + default: { + UNREACHABLE(); + } + } + + acc_.SetType(Checker()->GlobalShortType()); +} + +void ETSGen::CastToDouble(const ir::AstNode *node) +{ + auto typeKind = checker::ETSChecker::TypeKind(acc_.GetType()); + switch (typeKind) { + case checker::TypeFlag::ETS_BOOLEAN: + case checker::TypeFlag::BYTE: + case checker::TypeFlag::CHAR: + case checker::TypeFlag::SHORT: + case checker::TypeFlag::INT: { + Sa().Emit(node); + break; + } + case checker::TypeFlag::LONG: { + Sa().Emit(node); + break; + } + case checker::TypeFlag::FLOAT: { + Sa().Emit(node); + break; + } + case checker::TypeFlag::DOUBLE: { + return; + } + default: { + UNREACHABLE(); + } + } + + acc_.SetType(Checker()->GlobalDoubleType()); +} + +void ETSGen::CastToFloat(const ir::AstNode *node) +{ + auto typeKind = checker::ETSChecker::TypeKind(acc_.GetType()); + switch (typeKind) { + case checker::TypeFlag::ETS_BOOLEAN: + case checker::TypeFlag::BYTE: + case checker::TypeFlag::CHAR: + case checker::TypeFlag::SHORT: + case checker::TypeFlag::INT: { + Sa().Emit(node); + break; + } + case checker::TypeFlag::LONG: { + Sa().Emit(node); + break; + } + case checker::TypeFlag::FLOAT: { + return; + } + case checker::TypeFlag::DOUBLE: { + Sa().Emit(node); + break; + } + default: { + UNREACHABLE(); + } + } + + acc_.SetType(Checker()->GlobalFloatType()); +} + +void ETSGen::CastToLong(const ir::AstNode *node) +{ + auto typeKind = checker::ETSChecker::TypeKind(acc_.GetType()); + switch (typeKind) { + case checker::TypeFlag::ETS_BOOLEAN: + case checker::TypeFlag::BYTE: + case checker::TypeFlag::CHAR: + case checker::TypeFlag::SHORT: + case checker::TypeFlag::INT: { + Sa().Emit(node); + break; + } + case checker::TypeFlag::LONG: { + return; + } + case checker::TypeFlag::FLOAT: { + Sa().Emit(node); + break; + } + case checker::TypeFlag::DOUBLE: { + Sa().Emit(node); + break; + } + default: { + UNREACHABLE(); + } + } + + acc_.SetType(Checker()->GlobalLongType()); +} + +void ETSGen::CastToInt(const ir::AstNode *node) +{ + auto typeKind = checker::ETSChecker::TypeKind(acc_.GetType()); + switch (typeKind) { + case checker::TypeFlag::ETS_BOOLEAN: + case checker::TypeFlag::BYTE: + case checker::TypeFlag::CHAR: + case checker::TypeFlag::SHORT: + case checker::TypeFlag::INT: { + return; + } + case checker::TypeFlag::LONG: { + Sa().Emit(node); + break; + } + case checker::TypeFlag::FLOAT: { + Sa().Emit(node); + break; + } + case checker::TypeFlag::DOUBLE: { + Sa().Emit(node); + break; + } + default: { + UNREACHABLE(); + } + } + + acc_.SetType(Checker()->GlobalIntType()); +} + +void ETSGen::ToBinaryResult(const ir::AstNode *node, Label *ifFalse) +{ + Label *end = AllocLabel(); + Sa().Emit(node, 1); + Sa().Emit(node, end); + SetLabel(node, ifFalse); + Sa().Emit(node, 0); + SetLabel(node, end); +} + +void ETSGen::Binary(const ir::AstNode *node, lexer::TokenType op, VReg lhs) +{ + ApplyConversion(node, targetType_); + + switch (op) { + case lexer::TokenType::PUNCTUATOR_EQUAL: { + Label *ifFalse = AllocLabel(); + BinaryEquality(node, lhs, ifFalse); + break; + } + case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: { + Label *ifFalse = AllocLabel(); + BinaryEquality(node, lhs, ifFalse); + break; + } + case lexer::TokenType::PUNCTUATOR_LESS_THAN: { + Label *ifFalse = AllocLabel(); + BinaryRelation(node, lhs, ifFalse); + break; + } + case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: { + Label *ifFalse = AllocLabel(); + BinaryRelation(node, lhs, ifFalse); + break; + } + case lexer::TokenType::PUNCTUATOR_GREATER_THAN: { + Label *ifFalse = AllocLabel(); + BinaryRelation(node, lhs, ifFalse); + break; + } + case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: { + Label *ifFalse = AllocLabel(); + BinaryRelation(node, lhs, ifFalse); + break; + } + case lexer::TokenType::PUNCTUATOR_PLUS: + case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: { + SwapBinaryOpArgs(node, lhs); + BinaryArithmetic(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_MINUS: + case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL: { + SwapBinaryOpArgs(node, lhs); + BinaryArithmetic(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_MULTIPLY: + case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL: { + SwapBinaryOpArgs(node, lhs); + BinaryArithmetic(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_DIVIDE: + case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL: { + SwapBinaryOpArgs(node, lhs); + BinaryArithmetic(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_MOD: + case lexer::TokenType::PUNCTUATOR_MOD_EQUAL: { + SwapBinaryOpArgs(node, lhs); + BinaryArithmetic(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT: + case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL: { + SwapBinaryOpArgs(node, lhs); + BinaryBitwiseArithmetic(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT: + case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL: { + SwapBinaryOpArgs(node, lhs); + BinaryBitwiseArithmetic(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT: + case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL: { + SwapBinaryOpArgs(node, lhs); + BinaryBitwiseArithmetic(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_BITWISE_AND: + case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL: { + BinaryBitwiseArithmetic(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_BITWISE_OR: + case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: { + BinaryBitwiseArithmetic(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: + case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL: { + BinaryBitwiseArithmetic(node, lhs); + break; + } + case lexer::TokenType::KEYW_INSTANCEOF: { + SwapBinaryOpArgs(node, lhs); + IsInstance(node, lhs.GetType()->AsETSObjectType()->AssemblerName()); + break; + } + default: { + UNREACHABLE(); + } + } +} + +void ETSGen::Condition(const ir::AstNode *node, lexer::TokenType op, VReg lhs, Label *ifFalse) +{ + ApplyConversion(node, targetType_); + + switch (op) { + case lexer::TokenType::PUNCTUATOR_EQUAL: { + BinaryEqualityCondition(node, lhs, ifFalse); + break; + } + case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: { + BinaryEqualityCondition(node, lhs, ifFalse); + break; + } + case lexer::TokenType::PUNCTUATOR_LESS_THAN: { + BinaryRelationCondition(node, lhs, ifFalse); + break; + } + case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: { + BinaryRelationCondition(node, lhs, ifFalse); + break; + } + case lexer::TokenType::PUNCTUATOR_GREATER_THAN: { + BinaryRelationCondition(node, lhs, ifFalse); + break; + } + case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: { + BinaryRelationCondition(node, lhs, ifFalse); + break; + } + case lexer::TokenType::KEYW_INSTANCEOF: { + SwapBinaryOpArgs(node, lhs); + Sa().Emit(node, lhs.GetType()->AsETSObjectType()->AssemblerName()); + BranchIfFalse(node, ifFalse); + acc_.SetType(Checker()->GlobalETSBooleanType()); + break; + } + default: { + UNREACHABLE(); + } + } +} + +void ETSGen::EmitNullPointerException(const ir::AstNode *node) +{ + VReg exception = StoreException(node); + NewObject(node, exception, Signatures::BUILTIN_NULLPOINTER_EXCEPTION); + CallThisStatic0(node, exception, Signatures::BUILTIN_NULLPOINTER_EXCEPTION_CTOR); + EmitThrow(node, exception); +} + +void ETSGen::CompileStatements(const ArenaVector &statements) +{ + for (const auto *stmt : statements) { + stmt->Compile(this); + } +} + +void ETSGen::CompileDeferStatements(const ArenaVector &statements) +{ + for (auto it = statements.crbegin(); it != statements.crend(); it++) { + auto *stmt = *it; + if (!stmt->IsDeferStatement()) { + continue; + } + stmt->AsDeferStatement()->Stmt()->Compile(this); + } +} + +void ETSGen::CompileStatementList(const ArenaVector &statements) +{ + if (!Scope()->HasFlag(binder::ScopeFlags::DEFER_STMT)) { + CompileStatements(statements); + return; + } + + ASSERT(!statements.empty()); + auto *node = statements.front(); + + TrapContext trapCtx(this); + const auto &labelSet = trapCtx.LabelSet(); + + SetLabel(node, labelSet.TryBegin()); + CompileStatements(statements); + CompileDeferStatements(statements); + SetLabel(node, labelSet.TryEnd()); + Branch(node, labelSet.CatchEnd()); + + SetLabel(node, labelSet.CatchBegin()); + VReg exception = StoreException(node); + CompileDeferStatements(statements); + EmitThrow(node, exception); + SetLabel(node, labelSet.CatchEnd()); +} + +void ETSGen::Negate(const ir::AstNode *node) +{ + auto typeKind = checker::ETSChecker::TypeKind(acc_.GetType()); + + switch (typeKind) { + case checker::TypeFlag::BYTE: + case checker::TypeFlag::SHORT: + case checker::TypeFlag::CHAR: + case checker::TypeFlag::INT: { + Sa().Emit(node); + return; + } + case checker::TypeFlag::LONG: { + Sa().Emit(node); + break; + } + case checker::TypeFlag::FLOAT: { + Sa().Emit(node); + break; + } + case checker::TypeFlag::DOUBLE: { + Sa().Emit(node); + break; + } + default: { + UNREACHABLE(); + } + } +} + +void ETSGen::LogicalNot(const ir::AstNode *node) +{ + ASSERT(acc_.GetType()->IsETSBooleanType()); + Sa().Emit(node); + Sa().Emit(node, 1); +} + +void ETSGen::Unary(const ir::AstNode *node, lexer::TokenType op) +{ + switch (op) { + case lexer::TokenType::PUNCTUATOR_PLUS: { + break; // NOP -> Unary numeric promotion is performed + } + case lexer::TokenType::PUNCTUATOR_MINUS: { + UnaryMinus(node); + break; + } + case lexer::TokenType::PUNCTUATOR_TILDE: { + UnaryTilde(node); + break; + } + case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: { + LogicalNot(node); + break; + } + default: { + UNREACHABLE(); + } + } +} + +void ETSGen::UnaryMinus(const ir::AstNode *node) +{ + switch (checker::ETSChecker::ETSType(acc_.GetType())) { + case checker::TypeFlag::LONG: { + Sa().Emit(node); + break; + } + case checker::TypeFlag::INT: + case checker::TypeFlag::SHORT: + case checker::TypeFlag::CHAR: + case checker::TypeFlag::BYTE: { + Sa().Emit(node); + break; + } + case checker::TypeFlag::DOUBLE: { + Sa().Emit(node); + break; + } + case checker::TypeFlag::FLOAT: { + Sa().Emit(node); + break; + } + default: { + UNREACHABLE(); + } + } +} + +void ETSGen::UnaryTilde(const ir::AstNode *node) +{ + switch (checker::ETSChecker::ETSType(acc_.GetType())) { + case checker::TypeFlag::LONG: { + Sa().Emit(node); + break; + } + case checker::TypeFlag::INT: + case checker::TypeFlag::SHORT: + case checker::TypeFlag::CHAR: + case checker::TypeFlag::BYTE: { + Sa().Emit(node); + break; + } + default: { + UNREACHABLE(); + } + } +} + +void ETSGen::Update(const ir::AstNode *node, lexer::TokenType op) +{ + switch (op) { + case lexer::TokenType::PUNCTUATOR_PLUS_PLUS: { + UpdateOperator(node); + break; + } + case lexer::TokenType::PUNCTUATOR_MINUS_MINUS: { + UpdateOperator(node); + break; + } + default: { + UNREACHABLE(); + } + } +} + +void ETSGen::StringBuilderAppend(const ir::AstNode *node, VReg builder) +{ + RegScope rs(this); + util::StringView signature {}; + + node->Compile(this); + + switch (checker::ETSChecker::ETSType(acc_.GetType())) { + case checker::TypeFlag::ETS_BOOLEAN: { + signature = Signatures::BUILTIN_STRING_BUILDER_APPEND_BOOLEAN; + break; + } + case checker::TypeFlag::CHAR: { + signature = Signatures::BUILTIN_STRING_BUILDER_APPEND_CHAR; + break; + } + case checker::TypeFlag::SHORT: + case checker::TypeFlag::BYTE: + case checker::TypeFlag::INT: { + signature = Signatures::BUILTIN_STRING_BUILDER_APPEND_INT; + break; + } + case checker::TypeFlag::LONG: { + signature = Signatures::BUILTIN_STRING_BUILDER_APPEND_LONG; + break; + } + case checker::TypeFlag::FLOAT: { + Sa().Emit(node); + [[fallthrough]]; + } + case checker::TypeFlag::DOUBLE: { + signature = Signatures::BUILTIN_STRING_BUILDER_APPEND_DOUBLE; + break; + } + default: { + signature = Signatures::BUILTIN_STRING_BUILDER_APPEND_BUILTIN_STRING; + break; + } + } + + if (acc_.GetType()->IsETSObjectType() && !acc_.GetType()->IsETSStringType()) { + Ra().Emit(node, Signatures::BUILTIN_OBJECT_TO_STRING, VReg::RegStart(), 0); + } + + VReg arg0 = AllocReg(); + StoreAccumulator(node, arg0); + + CallThisStatic1(node, builder, signature, arg0); +} + +void ETSGen::AppendString(const ir::Expression *expr, VReg &builder) +{ + ASSERT((expr->IsBinaryExpression() && + expr->AsBinaryExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS) || + (expr->IsAssignmentExpression() && + expr->AsAssignmentExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS_EQUAL)); + + if (expr->IsBinaryExpression()) { + StringBuilder(expr->AsBinaryExpression()->Left(), expr->AsBinaryExpression()->Right(), builder); + } else { + StringBuilder(expr->AsAssignmentExpression()->Left(), expr->AsAssignmentExpression()->Right(), builder); + } +} + +void ETSGen::StringBuilder(const ir::Expression *left, const ir::Expression *right, VReg &builder) +{ + if (left->IsBinaryExpression()) { + AppendString(left->AsBinaryExpression(), builder); + } else { + StringBuilderAppend(left, builder); + } + + StringBuilderAppend(right, builder); +} + +void ETSGen::BuildString(const ir::Expression *node) +{ + RegScope rs(this); + VReg builder = AllocReg(); + + Sa().Emit(node, Signatures::BUILTIN_STRING_BUILDER_CTOR, VReg::RegStart(), VReg::RegStart()); + StoreAccumulator(node, builder); + + AppendString(node, builder); + CallThisStatic0(node, builder, Signatures::BUILTIN_STRING_BUILDER_TO_STRING); + + acc_.SetType(Checker()->GlobalETSStringLiteralType()); +} + +void ETSGen::NewObject(const ir::AstNode *node, VReg &ctor, util::StringView name) +{ + Ra().Emit(node, ctor, name); +} + +void ETSGen::NewArray(const ir::AstNode *node, VReg &arr, VReg dim, const checker::Type *arrType) +{ + std::stringstream ss; + arrType->ToAssemblerTypeWithRank(ss); + auto res = ProgElement()->Strings().emplace(ss.str()); + + Ra().Emit(node, arr, dim, util::StringView(*res.first)); +} + +void ETSGen::LoadArrayLength(const ir::AstNode *node, VReg arrayReg) +{ + Ra().Emit(node, arrayReg); + acc_.SetType(Checker()->GlobalIntType()); +} + +void ETSGen::LoadArrayElement(const ir::AstNode *node, VReg objectReg) +{ + auto *elementType = objectReg.GetType()->AsETSArrayType()->ElementType(); + + switch (checker::ETSChecker::ETSType(elementType)) { + case checker::TypeFlag::BYTE: { + Ra().Emit(node, objectReg); + break; + } + case checker::TypeFlag::CHAR: + case checker::TypeFlag::SHORT: { + Ra().Emit(node, objectReg); + break; + } + case checker::TypeFlag::ETS_BOOLEAN: + case checker::TypeFlag::INT: { + Ra().Emit(node, objectReg); + break; + } + case checker::TypeFlag::LONG: { + Ra().Emit(node, objectReg); + break; + } + case checker::TypeFlag::FLOAT: { + Ra().Emit(node, objectReg); + break; + } + case checker::TypeFlag::DOUBLE: { + Ra().Emit(node, objectReg); + break; + } + case checker::TypeFlag::ETS_ARRAY: + case checker::TypeFlag::ETS_OBJECT: { + Ra().Emit(node, objectReg); + break; + } + + default: { + UNREACHABLE(); + } + } + + acc_.SetType(elementType); +} + +void ETSGen::StoreArrayElement(const ir::AstNode *node, VReg objectReg, VReg index) +{ + auto *elementType = objectReg.GetType()->AsETSArrayType()->ElementType(); + + switch (checker::ETSChecker::ETSType(elementType)) { + case checker::TypeFlag::BYTE: { + Ra().Emit(node, objectReg, index); + break; + } + case checker::TypeFlag::CHAR: + case checker::TypeFlag::SHORT: { + Ra().Emit(node, objectReg, index); + break; + } + case checker::TypeFlag::ETS_BOOLEAN: + case checker::TypeFlag::INT: { + Ra().Emit(node, objectReg, index); + break; + } + case checker::TypeFlag::LONG: { + Ra().Emit(node, objectReg, index); + break; + } + case checker::TypeFlag::FLOAT: { + Ra().Emit(node, objectReg, index); + break; + } + case checker::TypeFlag::DOUBLE: { + Ra().Emit(node, objectReg, index); + break; + } + case checker::TypeFlag::ETS_ARRAY: + case checker::TypeFlag::ETS_OBJECT: { + Ra().Emit(node, objectReg, index); + break; + } + + default: { + UNREACHABLE(); + } + } + + acc_.SetType(elementType); +} + +void ETSGen::ThrowException(const ir::Expression *expr) +{ + RegScope rs(this); + + expr->Compile(this); + VReg arg = AllocReg(); + StoreAccumulator(expr, arg); + EmitThrow(expr, arg); +} +} // namespace panda::es2panda::compiler diff --git a/compiler/core/ETSGen.h b/compiler/core/ETSGen.h new file mode 100644 index 0000000000000000000000000000000000000000..43f72693fbfe0ddd3515a18f466dae967c4c8a15 --- /dev/null +++ b/compiler/core/ETSGen.h @@ -0,0 +1,670 @@ +/** + * Copyright (c) 2021-2022 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_CORE_ETSGEN_H +#define ES2PANDA_COMPILER_CORE_ETSGEN_H + +#include "plugins/ecmascript/es2panda/ir/astNode.h" +#include "plugins/ecmascript/es2panda/binder/ETSBinder.h" +#include "plugins/ecmascript/es2panda/compiler/core/codeGen.h" +#include "plugins/ecmascript/es2panda/compiler/core/ETSfunction.h" +#include "plugins/ecmascript/es2panda/compiler/core/targetTypeContext.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" +#include "plugins/ecmascript/es2panda/util/helpers.h" + +namespace panda::es2panda::compiler { + +class ETSGen : public CodeGen { +public: + explicit ETSGen(ArenaAllocator *allocator, RegSpiller *spiller, CompilerContext *context, + binder::FunctionScope *scope, ProgramElement *programElement) + : CodeGen(allocator, spiller, context, scope, programElement), + containingObjectType_(util::Helpers::GetContainingObjectType(RootNode())) + { + ETSFunction::Compile(this); + } + + const checker::ETSChecker *Checker() const; + const checker::Type *ReturnType() const; + + const checker::ETSObjectType *ContainingObjectType() const + { + return containingObjectType_; + } + + VReg &Acc() + { + return acc_; + } + + const VReg &Acc() const + { + return acc_; + } + + VReg StoreException(const ir::AstNode *node); + void ApplyConversionAndStoreAccumulator(const ir::AstNode *node, VReg &vreg, const checker::Type *targetType); + void StoreAccumulator(const ir::AstNode *node, VReg &vreg); + void LoadAccumulator(const ir::AstNode *node, VReg vreg); + void MoveVreg(const ir::AstNode *node, VReg &vd, VReg vs); + + void LoadVar(const ir::AstNode *node, binder::Variable *var); + void StoreVar(const ir::AstNode *node, const binder::ScopeFindResult &result); + + void LoadStaticProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &fullName); + void StoreStaticProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &fullName); + + void StoreStaticOwnProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &name); + util::StringView FormClassPropReference(const checker::ETSObjectType *classType, const util::StringView &name); + + void StoreProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg, + const util::StringView &name); + void LoadProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg, + const util::StringView &name); + + void LoadThis(const ir::AstNode *node); + VReg &GetThisReg(); + + void LoadDefaultValue(const ir::AstNode *node, const checker::Type *type); + void EmitReturnVoid(const ir::AstNode *node); + void ReturnAcc(const ir::AstNode *node); + + void IsInstance(const ir::AstNode *node, util::StringView name) + { + Sa().Emit(node, name); + acc_.SetType(Checker()->GlobalETSBooleanType()); + } + + void Binary(const ir::AstNode *node, lexer::TokenType op, VReg lhs); + void Unary(const ir::AstNode *node, lexer::TokenType op); + void Update(const ir::AstNode *node, lexer::TokenType op); + + bool TryLoadConstantExpression(const ir::Expression *node); + void Condition(const ir::AstNode *node, lexer::TokenType op, VReg lhs, Label *ifFalse); + + void BranchIfFalse(const ir::AstNode *node, Label *ifFalse) + { + Sa().Emit(node, ifFalse); + } + + void BranchIfTrue(const ir::AstNode *node, Label *ifFalse) + { + Sa().Emit(node, ifFalse); + } + + void EmitThrow(const ir::AstNode *node, VReg err) + { + Ra().Emit(node, err); + } + + void EmitNullPointerException(const ir::AstNode *node); + + void ThrowException(const ir::Expression *expr); + + void Negate(const ir::AstNode *node); + void LogicalNot(const ir::AstNode *node); + + void LoadAccumulatorByte(const ir::AstNode *node, int8_t number) + { + LoadAccumulatorNumber(node, number, checker::TypeFlag::BYTE); + } + + void LoadAccumulatorShort(const ir::AstNode *node, int16_t number) + { + LoadAccumulatorNumber(node, number, checker::TypeFlag::SHORT); + } + + void LoadAccumulatorInt(const ir::AstNode *node, int32_t number) + { + LoadAccumulatorNumber(node, number, checker::TypeFlag::INT); + } + + void LoadAccumulatorWideInt(const ir::AstNode *node, int64_t number) + { + LoadAccumulatorNumber(node, number, checker::TypeFlag::LONG); + } + + void LoadAccumulatorFloat(const ir::AstNode *node, float number) + { + LoadAccumulatorNumber(node, number, checker::TypeFlag::FLOAT); + } + + void LoadAccumulatorDouble(const ir::AstNode *node, double number) + { + LoadAccumulatorNumber(node, number, checker::TypeFlag::DOUBLE); + } + + void LoadAccumulatorBoolean(const ir::AstNode *node, bool value) + { + auto typeKind = + targetType_ != nullptr ? checker::ETSChecker::TypeKind(targetType_) : checker::TypeFlag::ETS_BOOLEAN; + Sa().Emit(node, value ? 1 : 0); + if (typeKind == checker::TypeFlag::ETS_OBJECT && + targetType_->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN)) { + Ra().Emit(node, Signatures::BUILTIN_BOOLEAN_VALUE_OF, VReg::RegStart(), 0); + acc_.SetType(targetType_); + } else { + acc_.SetType(Checker()->GlobalETSBooleanType()); + } + } + + void LoadAccumulatorString(const ir::AstNode *node, util::StringView str) + { + Sa().Emit(node, str); + acc_.SetType(Checker()->GlobalETSStringLiteralType()); + } + + void LoadAccumulatorNull(const ir::AstNode *node, const checker::Type *type) + { + Sa().Emit(node); + acc_.SetType(type); + } + + void LoadAccumulatorChar(const ir::AstNode *node, char16_t value) + { + auto typeKind = targetType_ != nullptr ? checker::ETSChecker::TypeKind(targetType_) : checker::TypeFlag::CHAR; + Sa().Emit(node, value); + if (typeKind == checker::TypeFlag::ETS_OBJECT && + targetType_->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_CHAR)) { + Ra().Emit(node, Signatures::BUILTIN_CHAR_VALUE_OF, VReg::RegStart(), 0); + acc_.SetType(targetType_); + } else { + acc_.SetType(Checker()->GlobalCharType()); + } + } + + void ApplyConversion(const ir::AstNode *node) + { + if (targetType_ != nullptr) { + ApplyConversion(node, targetType_); + } + } + void ApplyConversion(const ir::AstNode *node, const checker::Type *targetType); + void EmitUnboxingConversion(const ir::AstNode *node, const checker::Type *targetType, checker::TypeFlag typeKind); + void EmitBoxingConversion(const ir::AstNode *node, const checker::Type *targetType, + const checker::Type *sourceType); + void SwapBinaryOpArgs(const ir::AstNode *node, VReg &lhs); + + void LoadArrayLength(const ir::AstNode *node, VReg arrayReg); + void LoadArrayElement(const ir::AstNode *node, VReg objectReg); + void StoreArrayElement(const ir::AstNode *node, VReg objectReg, VReg index); + + void CompileStatementList(const ArenaVector &statements); + + // Cast + void CastToBoolean(const ir::AstNode *node); + void CastToByte(const ir::AstNode *node); + void CastToChar(const ir::AstNode *node); + void CastToShort(const ir::AstNode *node); + void CastToDouble(const ir::AstNode *node); + void CastToFloat(const ir::AstNode *node); + void CastToLong(const ir::AstNode *node); + void CastToInt(const ir::AstNode *node); + + // Call, Construct + void NewArray(const ir::AstNode *node, VReg &arr, VReg dim, const checker::Type *arrType); + void NewObject(const ir::AstNode *node, VReg &ctor, util::StringView name); + void BuildString(const ir::Expression *node); + void InitObject(const ir::AstNode *node, checker::Signature *signature, + const ArenaVector &arguments) + { + CallImpl(node, signature, arguments); + } + + void CallStatic(const ir::AstNode *node, checker::Signature *signature, + const ArenaVector &arguments) + { + CallImpl(node, signature, arguments); + } + + void CallThisStatic(const ir::AstNode *node, VReg &ctor, checker::Signature *signature, + const ArenaVector &arguments) + { + CallThisImpl(node, ctor, signature, arguments); + } + + void CallThisVirtual(const ir::AstNode *node, VReg &ctor, checker::Signature *signature, + const ArenaVector &arguments) + { + CallThisImpl(node, ctor, signature, arguments); + } + + void CallThisStatic0(const ir::AstNode *node, VReg &ctor, util::StringView name) + { + Ra().Emit(node, name, ctor, VReg::RegStart()); + } + + void CallThisStatic1(const ir::AstNode *node, VReg &ctor, util::StringView name, VReg &arg0) + { + Ra().Emit(node, name, ctor, arg0); + } + + void CallThisStatic2(const ir::AstNode *node, VReg &ctor, util::StringView name, VReg &arg0, VReg &arg1) + { + Ra().Emit(node, name, ctor, arg0, arg1, VReg::RegStart()); + } + + void GetType(const ir::AstNode *node, bool isETSPrimitive) + { + if (isETSPrimitive) { + // TODO(SzD) LoadStaticProperty if ETS stdlib has static TYPE constants otherwise fallback to LdaType + } else { + auto classRef = acc_.GetType()->AsETSObjectType()->AssemblerName(); + Sa().Emit(node, classRef); + } + } + + ~ETSGen() = default; + NO_COPY_SEMANTIC(ETSGen); + NO_MOVE_SEMANTIC(ETSGen); + +private: + void StringBuilderAppend(const ir::AstNode *node, VReg builder); + void AppendString(const ir::Expression *binExpr, VReg &builder); + void StringBuilder(const ir::Expression *left, const ir::Expression *right, VReg &builder); + void CompileStatements(const ArenaVector &statements); + void CompileDeferStatements(const ArenaVector &statements); + util::StringView FormClassPropReference(binder::Variable *var); + void UnaryMinus(const ir::AstNode *node); + void UnaryTilde(const ir::AstNode *node); + + template + void StoreValueIntoArray(const ir::AstNode *node, VReg &arr, VReg &index) + { + Ra().Emit(node, arr, index); + } + + template + void UpdateOperator(const ir::AstNode *node) + { + switch (checker::ETSChecker::ETSType(acc_.GetType())) { + case checker::TypeFlag::LONG: { + RegScope scope(this); + VReg reg = AllocReg(); + Ra().Emit(node, reg, 1LL); + Ra().Emit(node, reg); + break; + } + case checker::TypeFlag::INT: + case checker::TypeFlag::CHAR: + case checker::TypeFlag::SHORT: + case checker::TypeFlag::BYTE: { + Sa().Emit(node, 1); + break; + } + case checker::TypeFlag::DOUBLE: { + RegScope scope(this); + VReg reg = AllocReg(); + Ra().Emit(node, reg, 1.0); + Ra().Emit(node, reg); + break; + } + case checker::TypeFlag::FLOAT: { + RegScope scope(this); + VReg reg = AllocReg(); + Ra().Emit(node, reg, 1.0F); + Ra().Emit(node, reg); + break; + } + default: { + UNREACHABLE(); + } + } + } + + template + void BinaryEqualityObj(const ir::AstNode *node, VReg lhs, Label *ifFalse) + { + Ra().Emit(node, lhs, ifFalse); + } + + template + void BinaryNumberComparison(const ir::AstNode *node, VReg lhs, Label *ifFalse) + { + Ra().Emit(node, lhs); + Sa().Emit(node, ifFalse); + } + + template + void BinaryEquality(const ir::AstNode *node, VReg lhs, Label *ifFalse) + { + BinaryEqualityCondition(node, lhs, ifFalse); + ToBinaryResult(node, ifFalse); + acc_.SetType(Checker()->GlobalETSBooleanType()); + } + + template + void BinaryEqualityCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse) + { + auto typeKind = checker::ETSChecker::TypeKind(targetType_); + + switch (typeKind) { + case checker::TypeFlag::ETS_OBJECT: { + ASSERT(lhs.GetType()->IsETSObjectType()); + if (lhs.GetType()->IsETSStringType()) { + RegScope rs(this); + VReg arg0 = AllocReg(); + StoreAccumulator(node, arg0); + CallThisStatic1(node, lhs, Signatures::BUILTIN_STRING_EQUALS, arg0); + acc_.SetType(Checker()->GlobalETSBooleanType()); + if constexpr (std::is_same_v) { + LogicalNot(node); + } + BranchIfFalse(node, ifFalse); + return; + } + + BinaryEqualityObj(node, lhs, ifFalse); + + break; + } + case checker::TypeFlag::DOUBLE: { + BinaryNumberComparison(node, lhs, ifFalse); + break; + } + case checker::TypeFlag::FLOAT: { + BinaryNumberComparison(node, lhs, ifFalse); + break; + } + case checker::TypeFlag::LONG: { + BinaryNumberComparison(node, lhs, ifFalse); + break; + } + case checker::TypeFlag::ETS_BOOLEAN: + case checker::TypeFlag::BYTE: + case checker::TypeFlag::CHAR: + case checker::TypeFlag::SHORT: + case checker::TypeFlag::INT: { + Ra().Emit(node, lhs, ifFalse); + break; + } + default: { + UNREACHABLE(); + } + } + + acc_.SetType(Checker()->GlobalETSBooleanType()); + } + + template + void BinaryRelation(const ir::AstNode *node, VReg lhs, Label *ifFalse) + { + BinaryRelationCondition(node, lhs, ifFalse); + ToBinaryResult(node, ifFalse); + acc_.SetType(Checker()->GlobalETSBooleanType()); + } + + template + void BinaryRelationCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse) + { + auto typeKind = checker::ETSChecker::TypeKind(targetType_); + + switch (typeKind) { + case checker::TypeFlag::DOUBLE: { + BinaryNumberComparison(node, lhs, ifFalse); + break; + } + case checker::TypeFlag::FLOAT: { + BinaryNumberComparison(node, lhs, ifFalse); + break; + } + case checker::TypeFlag::LONG: { + BinaryNumberComparison(node, lhs, ifFalse); + break; + } + case checker::TypeFlag::ETS_BOOLEAN: + case checker::TypeFlag::BYTE: + case checker::TypeFlag::SHORT: + case checker::TypeFlag::INT: { + Ra().Emit(node, lhs, ifFalse); + break; + } + default: { + UNREACHABLE(); + } + } + + acc_.SetType(Checker()->GlobalETSBooleanType()); + } + + template + void BinaryArithmetic(const ir::AstNode *node, VReg lhs) + { + auto typeKind = checker::ETSChecker::TypeKind(targetType_); + + switch (typeKind) { + case checker::TypeFlag::DOUBLE: { + Ra().Emit(node, lhs); + acc_.SetType(Checker()->GlobalDoubleType()); + break; + } + case checker::TypeFlag::FLOAT: { + Ra().Emit(node, lhs); + acc_.SetType(Checker()->GlobalFloatType()); + break; + } + default: { + BinaryBitwiseArithmetic(node, lhs); + } + } + } + + template + void BinaryBitwiseArithmetic(const ir::AstNode *node, VReg lhs) + { + auto typeKind = checker::ETSChecker::TypeKind(targetType_); + + switch (typeKind) { + case checker::TypeFlag::LONG: { + Ra().Emit(node, lhs); + acc_.SetType(Checker()->GlobalLongType()); + break; + } + case checker::TypeFlag::BYTE: + case checker::TypeFlag::SHORT: + case checker::TypeFlag::INT: { + Ra().Emit(node, lhs); + acc_.SetType(Checker()->GlobalIntType()); + break; + } + case checker::TypeFlag::ETS_BOOLEAN: { + Ra().Emit(node, lhs); + acc_.SetType(Checker()->GlobalETSBooleanType()); + break; + } + default: { + UNREACHABLE(); + } + } + } +// NOLINTBEGIN(cppcoreguidelines-macro-usage, readability-container-size-empty) +#define COMPILE_ARG(idx) \ + ASSERT(idx < arguments.size()); \ + auto *paramType##idx = signature->Params()[idx]->TsType(); \ + auto ttctx##idx = TargetTypeContext(this, paramType##idx); \ + arguments[idx]->Compile(this); \ + VReg arg##idx = AllocReg(); \ + ApplyConversionAndStoreAccumulator(node, arg##idx, paramType##idx) + + template + void CallThisImpl(const ir::AstNode *node, VReg &ctor, checker::Signature *signature, + const ArenaVector &arguments) + { + RegScope rs(this); + util::StringView name = signature->InternalName(); + + switch (arguments.size()) { + case 0: { + Ra().Emit(node, name, ctor, VReg::RegStart()); + break; + } + case 1: { + COMPILE_ARG(0); + Ra().Emit(node, name, ctor, arg0); + break; + } + case 2: { + COMPILE_ARG(0); + COMPILE_ARG(1); + Ra().Emit(node, name, ctor, arg0, arg1, VReg::RegStart()); + break; + } + case 3: { + COMPILE_ARG(0); + COMPILE_ARG(1); + COMPILE_ARG(2); + Ra().Emit(node, name, ctor, arg0, arg1, arg2); + break; + } + default: { + for (const auto *arg : arguments) { + auto ttctx = TargetTypeContext(this, arg->TsType()); + VReg argReg = AllocReg(); + arg->Compile(this); + StoreAccumulator(node, argReg); + } + + Rra().Emit(node, ctor, arguments.size() + 1, name, ctor); + break; + } + } + } + + template + void CallImpl(const ir::AstNode *node, checker::Signature *signature, + const ArenaVector &arguments) + { + RegScope rs(this); + util::StringView name = signature->InternalName(); + + switch (arguments.size()) { + case 0: { + Ra().Emit(node, name, VReg::RegStart(), VReg::RegStart()); + break; + } + case 1: { + COMPILE_ARG(0); + Ra().Emit(node, name, arg0, VReg::RegStart()); + break; + } + case 2: { + COMPILE_ARG(0); + COMPILE_ARG(1); + Ra().Emit(node, name, arg0, arg1); + break; + } + case 3: { + COMPILE_ARG(0); + COMPILE_ARG(1); + COMPILE_ARG(2); + Ra().Emit(node, name, arg0, arg1, arg2, VReg::RegStart()); + break; + } + case 4: { + COMPILE_ARG(0); + COMPILE_ARG(1); + COMPILE_ARG(2); + COMPILE_ARG(3); + Ra().Emit(node, name, arg0, arg1, arg2, arg3); + break; + } + default: { + VReg argStart = NextReg(); + + for (const auto *arg : arguments) { + auto ttctx = TargetTypeContext(this, arg->TsType()); + VReg argReg = AllocReg(); + arg->Compile(this); + StoreAccumulator(node, argReg); + } + + Rra().Emit(node, argStart, arguments.size(), name, argStart); + break; + } + } + } + +#undef COMPILE_ARG + // NOLINTEND(cppcoreguidelines-macro-usage, readability-container-size-empty) + + void ToBinaryResult(const ir::AstNode *node, Label *ifFalse); + + template + void LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType); + void InitializeContainingClass(); + + friend class TargetTypeContext; + + VReg acc_ {}; + const checker::Type *targetType_ {}; + const checker::ETSObjectType *containingObjectType_ {}; +}; + +template +void ETSGen::LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType) +{ + auto typeKind = + targetType_ && !targetType_->IsETSObjectType() ? checker::ETSChecker::TypeKind(targetType_) : targetType; + + switch (typeKind) { + case checker::TypeFlag::BYTE: { + Sa().Emit(node, static_cast(number)); + acc_.SetType(Checker()->GlobalByteType()); + break; + } + case checker::TypeFlag::CHAR: { + Sa().Emit(node, static_cast(number)); + acc_.SetType(Checker()->GlobalCharType()); + break; + } + case checker::TypeFlag::SHORT: { + Sa().Emit(node, static_cast(number)); + acc_.SetType(Checker()->GlobalShortType()); + break; + } + case checker::TypeFlag::INT: { + Sa().Emit(node, static_cast(number)); + acc_.SetType(Checker()->GlobalIntType()); + break; + } + case checker::TypeFlag::LONG: { + Sa().Emit(node, static_cast(number)); + acc_.SetType(Checker()->GlobalLongType()); + break; + } + case checker::TypeFlag::FLOAT: { + Sa().Emit(node, static_cast(number)); + acc_.SetType(Checker()->GlobalFloatType()); + break; + } + case checker::TypeFlag::DOUBLE: { + Sa().Emit(node, static_cast(number)); + acc_.SetType(Checker()->GlobalDoubleType()); + break; + } + default: { + UNREACHABLE(); + } + } + + if (targetType_ && targetType_->IsETSObjectType()) { + ApplyConversion(node, targetType_); + acc_.SetType(targetType_); + } +} + +} // namespace panda::es2panda::compiler + +#endif diff --git a/compiler/core/ETSemitter.cpp b/compiler/core/ETSemitter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..55ed6a148af079281afe614e4695dc0e542b67d9 --- /dev/null +++ b/compiler/core/ETSemitter.cpp @@ -0,0 +1,645 @@ +/** + * Copyright (c) 2021 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 "ETSemitter.h" + +#include "plugins/ecmascript/es2panda/compiler/core/ETSGen.h" +#include "plugins/ecmascript/es2panda/binder/binder.h" +#include "plugins/ecmascript/es2panda/binder/ETSBinder.h" +#include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" +#include "plugins/ecmascript/es2panda/ir/base/methodDefinition.h" +#include "plugins/ecmascript/es2panda/ir/base/classDefinition.h" +#include "plugins/ecmascript/es2panda/ir/base/scriptFunction.h" +#include "plugins/ecmascript/es2panda/ir/base/classProperty.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsEnumDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsEnumMember.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsInterfaceBody.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsTypeParameterDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsTypeParameter.h" +#include "plugins/ecmascript/es2panda/ir/ets/etsTypeReference.h" +#include "plugins/ecmascript/es2panda/ir/typeNode.h" +#include "plugins/ecmascript/es2panda/parser/program/program.h" +#include "plugins/ecmascript/es2panda/compiler/core/compilerContext.h" +#include "plugins/ecmascript/es2panda/checker/checker.h" +#include "plugins/ecmascript/es2panda/checker/types/signature.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/types.h" + +#include "assembly-program.h" + +namespace panda::es2panda::compiler { + +#ifdef PANDA_WITH_ETS +static constexpr auto EXTENSION = panda_file::SourceLang::ETS; +#else +// TODO: temporary dummy gn buildfix until ETS plugin has gn build support +static constexpr auto EXTENSION = panda_file::SourceLang::PANDA_ASSEMBLY; +#endif + +static uint32_t TranslateModifierFlags(ir::ModifierFlags modifierFlags) +{ + uint32_t accessFlags = 0; + + if ((modifierFlags & ir::ModifierFlags::PRIVATE) != 0) { + accessFlags = ACC_PRIVATE; + } else if ((modifierFlags & ir::ModifierFlags::PROTECTED) != 0) { + accessFlags = ACC_PROTECTED; + } else { + accessFlags = ACC_PUBLIC; + } + + if ((modifierFlags & ir::ModifierFlags::STATIC) != 0) { + accessFlags |= ACC_STATIC; + } + + if ((modifierFlags & ir::ModifierFlags::CONST) != 0) { + accessFlags |= ACC_FINAL; + } + + if ((modifierFlags & ir::ModifierFlags::ABSTRACT) != 0) { + accessFlags |= ACC_ABSTRACT; + } + + if ((modifierFlags & ir::ModifierFlags::NATIVE) != 0) { + accessFlags |= ACC_NATIVE; + } + + return accessFlags; +} + +static pandasm::Function GenScriptFunction(const ir::ScriptFunction *scriptFunc) +{ + auto *funcScope = scriptFunc->Scope(); + auto *paramScope = funcScope->ParamScope(); + + auto func = pandasm::Function(funcScope->InternalName().Mutf8(), EXTENSION); + + func.params.reserve(paramScope->Params().size()); + + for (const auto *var : paramScope->Params()) { + std::stringstream ss; + var->TsType()->ToAssemblerType(ss); + func.params.emplace_back(pandasm::Type(ss.str(), var->TsType()->Rank()), EXTENSION); + } + + std::stringstream ss; + + if (scriptFunc->IsConstructor() || scriptFunc->IsStaticBlock()) { + func.return_type = pandasm::Type(Signatures::PRIMITIVE_VOID, 0); + } else { + auto *returnType = scriptFunc->Signature()->ReturnType(); + + returnType->ToAssemblerType(ss); + ASSERT(!ss.str().empty()); + func.return_type = pandasm::Type(ss.str(), returnType->Rank()); + } + + if (!scriptFunc->IsStaticBlock()) { + const auto *methodDef = util::Helpers::GetContainingClassMethodDefinition(scriptFunc); + func.metadata->SetAccessFlags(TranslateModifierFlags(methodDef->Modifiers())); + } + + return func; +} + +pandasm::Function *ETSFunctionEmitter::GenFunctionSignature() +{ + auto func = GenScriptFunction(Cg()->RootNode()->AsScriptFunction()); + auto *funcElement = new pandasm::Function(func.name, func.language); + *funcElement = std::move(func); + GetProgramElement()->SetFunction(funcElement); + funcElement->regs_num = VReg::REG_START - Cg()->TotalRegsNum(); + + return funcElement; +} + +void ETSFunctionEmitter::GenVariableSignature(pandasm::debuginfo::LocalVariable &variableDebug, + [[maybe_unused]] binder::LocalVariable *variable) const +{ + variableDebug.signature = Signatures::ANY; + variableDebug.signature_type = Signatures::ANY; +} + +void ETSFunctionEmitter::GenFunctionAnnotations([[maybe_unused]] pandasm::Function *func) {} + +template +static pandasm::Function GenExternalFunction(T signature, bool isCtor) +{ + auto iter = signature.begin(); + std::string name(*iter++); + + auto func = pandasm::Function(name, EXTENSION); + + while (iter != signature.end()) { + auto paramName = *iter++; + func.params.emplace_back(pandasm::Type(paramName, 0), EXTENSION); + } + + func.return_type = pandasm::Type(Signatures::PRIMITIVE_VOID, 0); + if (isCtor) { + func.metadata->SetAttribute(Signatures::CONSTRUCTOR); + } + func.metadata->SetAttribute(Signatures::EXTERNAL); + + return func; +} + +static pandasm::Function GenExternalFunction(checker::Signature *signature, bool isCtor) +{ + auto func = pandasm::Function(signature->InternalName().Mutf8(), EXTENSION); + + for (auto param : signature->Params()) { + auto *paramType = param->TsType(); + + std::stringstream ss; + paramType->ToAssemblerType(ss); + func.params.emplace_back(pandasm::Type(ss.str(), paramType->Rank()), EXTENSION); + } + + std::stringstream ss; + signature->ReturnType()->ToAssemblerType(ss); + func.return_type = pandasm::Type(ss.str(), signature->ReturnType()->Rank()); + + if (isCtor) { + func.metadata->SetAttribute(Signatures::CONSTRUCTOR); + } + func.metadata->SetAttribute(Signatures::EXTERNAL); + + return func; +} + +void ETSEmitter::GenAnnotation() +{ + Program()->lang = EXTENSION; + const auto *binder = static_cast(Context()->Binder()); + + auto *globalRecordTable = binder->GetGlobalRecordTable(); + + for (auto *classDecl : globalRecordTable->ClassDefinitions()) { + GenClassRecord(classDecl, false); + } + + for (auto *interfaceDecl : globalRecordTable->InterfaceDeclarations()) { + GenInterfaceRecord(interfaceDecl, false); + } + + for (auto *enumDecl : globalRecordTable->EnumDeclaration()) { + GenEnumRecord(enumDecl, false); + } + + for (auto *signature : globalRecordTable->Signatures()) { + auto func = GenScriptFunction(signature->Node()->AsScriptFunction()); + Program()->function_table.emplace(func.name, std::move(func)); + } + + for (auto [extProg, recordTable] : binder->GetExternalRecordTable()) { + (void)extProg; + GenExternalRecord(recordTable); + } + + const auto *checker = static_cast(Context()->Checker()); + + for (auto [arrType, signature] : checker->GlobalArrayTypes()) { + GenGlobalArrayRecord(arrType, signature); + } +} + +void ETSEmitter::GenExternalRecord(binder::RecordTable *recordTable) +{ + bool IsGenStdLib = recordTable->Program()->Binder()->IsGenStdLib(); + for (auto *classDecl : recordTable->ClassDefinitions()) { + GenClassRecord(classDecl, !IsGenStdLib); + } + + for (auto *interfaceDecl : recordTable->InterfaceDeclarations()) { + GenInterfaceRecord(interfaceDecl, !IsGenStdLib); + } + + for (auto *signature : recordTable->Signatures()) { + auto func = GenScriptFunction(signature->Node()->AsScriptFunction()); + + if (!IsGenStdLib) { + func.metadata->SetAttribute(Signatures::EXTERNAL); + } + + Program()->function_table.emplace(func.name, std::move(func)); + } +} + +void ETSEmitter::EmitDefaultFieldValue(pandasm::Field &classField, const ir::Expression *init) +{ + if (init == nullptr) { + return; + } + + const auto *type = init->TsType(); + + if (!type->HasTypeFlag(checker::TypeFlag::CONSTANT)) { + return; + } + + auto typeKind = checker::ETSChecker::TypeKind(type); + + classField.metadata->SetFieldType(classField.type); + switch (typeKind) { + case checker::TypeFlag::ETS_BOOLEAN: { + classField.metadata->SetValue(pandasm::ScalarValue::Create( + static_cast(type->AsETSBooleanType()->GetValue()))); + break; + } + case checker::TypeFlag::BYTE: { + classField.metadata->SetValue( + pandasm::ScalarValue::Create(type->AsByteType()->GetValue())); + break; + } + case checker::TypeFlag::SHORT: { + classField.metadata->SetValue( + pandasm::ScalarValue::Create(type->AsShortType()->GetValue())); + break; + } + case checker::TypeFlag::INT: { + classField.metadata->SetValue( + pandasm::ScalarValue::Create(type->AsIntType()->GetValue())); + break; + } + case checker::TypeFlag::LONG: { + classField.metadata->SetValue( + pandasm::ScalarValue::Create(type->AsLongType()->GetValue())); + break; + } + case checker::TypeFlag::FLOAT: { + classField.metadata->SetValue( + pandasm::ScalarValue::Create(type->AsFloatType()->GetValue())); + break; + } + case checker::TypeFlag::DOUBLE: { + classField.metadata->SetValue( + pandasm::ScalarValue::Create(type->AsDoubleType()->GetValue())); + break; + } + case checker::TypeFlag::CHAR: { + classField.metadata->SetValue( + pandasm::ScalarValue::Create(type->AsCharType()->GetValue())); + break; + } + case checker::TypeFlag::ETS_OBJECT: { + classField.metadata->SetValue(pandasm::ScalarValue::Create( + type->AsETSObjectType()->AsETSStringType()->GetValue().Mutf8())); + break; + } + default: { + UNREACHABLE(); + } + } +} + +void ETSEmitter::GenInterfaceMethodDefinition(const ir::MethodDefinition *methodDef, bool external) +{ + auto *scriptFunc = methodDef->Function(); + auto func = GenScriptFunction(scriptFunc); + + if (external) { + func.metadata->SetAttribute(Signatures::EXTERNAL); + } + + if (scriptFunc->Body() != nullptr) { + return; + } + + func.metadata->SetAccessFlags(func.metadata->GetAccessFlags() | ACC_ABSTRACT); + Program()->function_table.emplace(func.name, std::move(func)); +} + +void ETSEmitter::GenClassField(const ir::ClassProperty *field, pandasm::Record &classRecord, bool external) +{ + GenField(field->TsType(), field->Id()->Name(), field->Value(), TranslateModifierFlags(field->Modifiers()), + classRecord, external); +} + +void ETSEmitter::GenField(const checker::Type *tsType, const util::StringView &name, const ir::Expression *value, + uint32_t accesFlags, pandasm::Record &record, bool external) +{ + auto field = pandasm::Field(Program()->lang); + std::stringstream ss; + tsType->ToAssemblerType(ss); + + field.name = name.Mutf8(); + field.type = pandasm::Type(ss.str(), tsType->Rank()); + + field.metadata->SetAccessFlags(accesFlags); + + if (external) { + field.metadata->SetAttribute(Signatures::EXTERNAL); + } else { + EmitDefaultFieldValue(field, value); + } + + record.field_list.emplace_back(std::move(field)); +} + +void ETSEmitter::GenClassInheritedFields(const checker::ETSObjectType *baseType, pandasm::Record &classRecord) +{ + std::vector foreignProps = baseType->ForeignProperties(); + + for (const auto *foreignProp : foreignProps) { + auto *declNode = foreignProp->Declaration()->Node(); + if (!declNode->IsClassProperty()) { + continue; + } + + GenClassField(declNode->AsClassProperty(), classRecord, true); + } +} + +void ETSEmitter::GenGlobalArrayRecord(checker::ETSArrayType *arrayType, checker::Signature *signature) +{ + std::stringstream ss; + arrayType->ToAssemblerTypeWithRank(ss); + + auto arrayRecord = pandasm::Record(ss.str(), Program()->lang); + + auto func = GenExternalFunction(signature, true); + func.params.emplace(func.params.begin(), pandasm::Type(ss.str(), 0), EXTENSION); + + Program()->function_table.emplace(func.name, std::move(func)); + + arrayRecord.metadata->SetAttribute(Signatures::EXTERNAL); + Program()->record_table.emplace(arrayRecord.name, std::move(arrayRecord)); + + std::stringstream ss2; + arrayType->ElementType()->ToAssemblerTypeWithRank(ss2); + panda::pandasm::Type atype_pa(ss2.str(), 1); + Program()->array_types.emplace(std::move(atype_pa)); +} + +void ETSEmitter::GenEnumRecord(const ir::TSEnumDeclaration *enumDecl, bool external) +{ + auto enumRecord = pandasm::Record(enumDecl->InternalName().Mutf8(), Program()->lang); + + if (external) { + enumRecord.metadata->SetAttribute(Signatures::EXTERNAL); + } + + uint32_t accessFlags = ACC_PUBLIC | ACC_ENUM | ACC_STATIC; + enumRecord.metadata->SetAccessFlags(accessFlags); + enumRecord.source_file = Context()->Binder()->Program()->SourceFile().Mutf8(); + enumRecord.metadata->SetAttributeValue(Signatures::EXTENDS_ATTRIBUTE, Signatures::BUILTIN_ENUM); + + for (const auto [name, var] : enumDecl->TsType()->AsETSObjectType()->StaticFields()) { + GenField(var->TsType(), name, nullptr, accessFlags, enumRecord, external); + } + + Program()->record_table.emplace(enumRecord.name, std::move(enumRecord)); +} + +void ETSEmitter::GenInterfaceRecord(const ir::TSInterfaceDeclaration *interfaceDecl, bool external) +{ + auto *baseType = interfaceDecl->TsType()->AsETSObjectType(); + + auto interfaceRecord = pandasm::Record(baseType->Name().Mutf8(), Program()->lang); + + if (external) { + interfaceRecord.metadata->SetAttribute(Signatures::EXTERNAL); + } + + uint32_t accessFlags = ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE; + + if (interfaceDecl->IsStatic()) { + accessFlags |= ACC_STATIC; + } + + interfaceRecord.metadata->SetAccessFlags(accessFlags); + interfaceRecord.source_file = Context()->Binder()->Program()->SourceFile().Mutf8(); + interfaceRecord.metadata->SetAttributeValue(Signatures::EXTENDS_ATTRIBUTE, Signatures::BUILTIN_OBJECT); + + for (auto *it : baseType->Interfaces()) { + interfaceRecord.metadata->SetAttributeValue(Signatures::IMPLEMENTS_ATTRIBUTE, it->Name().Mutf8()); + } + + GenClassInheritedFields(baseType, interfaceRecord); + + for (const auto *prop : interfaceDecl->Body()->Body()) { + if (prop->IsClassProperty()) { + GenClassField(prop->AsClassProperty(), interfaceRecord, false); + } else if (prop->IsMethodDefinition()) { + GenInterfaceMethodDefinition(prop->AsMethodDefinition(), external); + } + } + + Program()->record_table.emplace(interfaceRecord.name, std::move(interfaceRecord)); +} + +void ETSEmitter::GenClassRecord(const ir::ClassDefinition *classDef, bool external) +{ + auto classRecord = pandasm::Record(classDef->InternalName().Mutf8(), Program()->lang); + + if (external) { + classRecord.metadata->SetAttribute(Signatures::EXTERNAL); + } + + uint32_t accessFlags = ACC_PUBLIC; + + if (classDef->IsAbstract()) { + accessFlags |= ACC_ABSTRACT; + } + + if (!classDef->IsOpen()) { + accessFlags |= ACC_FINAL; + } + + if (classDef->IsStatic()) { + accessFlags |= ACC_STATIC; + } + + classRecord.metadata->SetAccessFlags(accessFlags); + classRecord.source_file = Context()->Binder()->Program()->SourceFile().Mutf8(); + + auto *baseType = classDef->TsType()->AsETSObjectType(); + + if (baseType->SuperType() != nullptr) { + classRecord.metadata->SetAttributeValue(Signatures::EXTENDS_ATTRIBUTE, + baseType->SuperType()->AssemblerName().Mutf8()); + } else { + classRecord.metadata->SetAttributeValue(Signatures::EXTENDS_ATTRIBUTE, Signatures::BUILTIN_OBJECT); + } + + for (auto *it : baseType->Interfaces()) { + classRecord.metadata->SetAttributeValue(Signatures::IMPLEMENTS_ATTRIBUTE, it->Name().Mutf8()); + } + + if (!classDef->IsAbstract()) { + GenClassInheritedFields(baseType, classRecord); + } + + for (const auto *prop : classDef->Body()) { + if (!prop->IsClassProperty()) { + continue; + } + + GenClassField(prop->AsClassProperty(), classRecord, external); + } + + std::vector annotations; + if (classDef->TypeParams() != nullptr) { + annotations.emplace_back(GenAnnotationSignature(classDef)); + } + + const ir::AstNode *parent = classDef->Parent(); + while (parent != nullptr) { + if (parent->IsMethodDefinition()) { + annotations.emplace_back(GenAnnotationEnclosingMethod(parent->AsMethodDefinition())); + annotations.emplace_back(GenAnnotationInnerClass(classDef, parent)); + break; + } + if (parent->IsClassDefinition()) { + annotations.emplace_back(GenAnnotationEnclosingClass( + parent->AsClassDefinition()->TsType()->AsETSObjectType()->AssemblerName().Utf8())); + annotations.emplace_back(GenAnnotationInnerClass(classDef, parent)); + break; + } + parent = parent->Parent(); + } + + if (!annotations.empty()) { + classRecord.metadata->SetAnnotations(std::move(annotations)); + } + + Program()->record_table.emplace(classRecord.name, std::move(classRecord)); +} + +pandasm::AnnotationData ETSEmitter::GenAnnotationSignature(const ir::ClassDefinition *classDef) +{ + static constexpr std::string_view object = "Lstd/core/Object"; + std::vector parts {}; + std::stringstream ss {}; + const auto ¶ms = classDef->TypeParams()->Params(); + + bool firstIteration = true; + for (const auto *param : params) { + if (firstIteration) { + ss << Signatures::GENERIC_BEGIN; + firstIteration = false; + } + ss << param->Name()->Name() << Signatures::MANGLE_BEGIN; + parts.emplace_back(pandasm::ScalarValue::Create(ss.str())); + + std::stringstream {}.swap(ss); + if (param->Constraint() == nullptr) { + ss << object; + } else { + param->Constraint()->AsETSTypeReference()->TsType()->ToAssemblerTypeWithRank(ss); + auto str = ss.str(); + std::replace(str.begin(), str.end(), *Signatures::METHOD_SEPARATOR.begin(), + *Signatures::NAMESPACE_SEPARATOR.begin()); + std::stringstream {}.swap(ss); + ss << Signatures::CLASS_REF_BEGIN << str << Signatures::MANGLE_SEPARATOR; + } + + parts.emplace_back(pandasm::ScalarValue::Create(ss.str())); + std::stringstream {}.swap(ss); // cleanup + } + + ss << Signatures::GENERIC_END; + parts.emplace_back(pandasm::ScalarValue::Create(ss.str())); + + std::stringstream {}.swap(ss); + if (classDef->TsType()->AsETSObjectType()->SuperType() == nullptr) { + ss << object; + } else { + ss << Signatures::CLASS_REF_BEGIN; + auto superType = classDef->TsType()->AsETSObjectType()->SuperType()->AssemblerName().Mutf8(); + std::replace(superType.begin(), superType.end(), *Signatures::METHOD_SEPARATOR.begin(), + *Signatures::NAMESPACE_SEPARATOR.begin()); + ss << superType << Signatures::MANGLE_SEPARATOR; + } + parts.emplace_back(pandasm::ScalarValue::Create(ss.str())); + + GenAnnotationRecord(Signatures::DALVIK_ANNOTATION_SIGNATURE); + pandasm::AnnotationData signature(Signatures::DALVIK_ANNOTATION_SIGNATURE); + pandasm::AnnotationElement value( + Signatures::ANNOTATION_KEY_VALUE, + std::make_unique(pandasm::Value::Type::STRING, std::move(parts))); + signature.AddElement(std::move(value)); + return signature; +} + +pandasm::AnnotationData ETSEmitter::GenAnnotationEnclosingMethod(const ir::MethodDefinition *methodDef) +{ + GenAnnotationRecord(Signatures::DALVIK_ANNOTATION_ENCLOSING_METHOD); + pandasm::AnnotationData enclosing_method(Signatures::DALVIK_ANNOTATION_ENCLOSING_METHOD); + pandasm::AnnotationElement value( + Signatures::ANNOTATION_KEY_VALUE, + std::make_unique(pandasm::ScalarValue::Create( + methodDef->Function()->Scope()->InternalName().Mutf8()))); + enclosing_method.AddElement(std::move(value)); + return enclosing_method; +} + +pandasm::AnnotationData ETSEmitter::GenAnnotationEnclosingClass(std::string_view className) +{ + GenAnnotationRecord(Signatures::DALVIK_ANNOTATION_ENCLOSING_CLASS); + pandasm::AnnotationData enclosingClass(Signatures::DALVIK_ANNOTATION_ENCLOSING_CLASS); + pandasm::AnnotationElement value( + Signatures::ANNOTATION_KEY_VALUE, + std::make_unique( + pandasm::ScalarValue::Create(pandasm::Type::FromName(className, true)))); + enclosingClass.AddElement(std::move(value)); + return enclosingClass; +} + +pandasm::AnnotationData ETSEmitter::GenAnnotationInnerClass(const ir::ClassDefinition *classDef, + const ir::AstNode *parent) +{ + GenAnnotationRecord(Signatures::DALVIK_ANNOTATION_INNER_CLASS); + pandasm::AnnotationData innerClass(Signatures::DALVIK_ANNOTATION_INNER_CLASS); + const bool isAnonymous = (classDef->Modifiers() & ir::ClassDefinitionModifiers::ANONYMOUS) != 0; + pandasm::AnnotationElement name(Signatures::ANNOTATION_KEY_NAME, + std::make_unique( + isAnonymous + ? pandasm::ScalarValue::Create(0) + : pandasm::ScalarValue::Create( + classDef->TsType()->AsETSObjectType()->AssemblerName().Mutf8()))); + innerClass.AddElement(std::move(name)); + + pandasm::AnnotationElement accessFlags( + Signatures::ANNOTATION_KEY_ACCESS_FLAGS, + std::make_unique( + pandasm::ScalarValue::Create(TranslateModifierFlags(parent->Modifiers())))); + innerClass.AddElement(std::move(accessFlags)); + return innerClass; +} + +void ETSEmitter::GenAnnotationRecord(std::string_view recordNameView, bool isRuntime, bool isType) +{ + const std::string recordName(recordNameView); + const auto recordIt = Program()->record_table.find(recordName); + if (recordIt == Program()->record_table.end()) { + pandasm::Record record(recordName, EXTENSION); + record.metadata->SetAttribute(Signatures::EXTERNAL); + record.metadata->SetAttribute(Signatures::ANNOTATION_ATTRIBUTE); + if (isRuntime && isType) { + record.metadata->SetAttributeValue(Signatures::ANNOTATION_ATTRIBUTE_TYPE, + Signatures::RUNTIME_TYPE_ANNOTATION); + } else if (isRuntime && !isType) { + record.metadata->SetAttributeValue(Signatures::ANNOTATION_ATTRIBUTE_TYPE, Signatures::RUNTIME_ANNOTATION); + } else if (!isRuntime && isType) { + record.metadata->SetAttributeValue(Signatures::ANNOTATION_ATTRIBUTE_TYPE, Signatures::TYPE_ANNOTATION); + } + Program()->record_table.emplace(record.name, std::move(record)); + } +} +} // namespace panda::es2panda::compiler diff --git a/compiler/core/ETSemitter.h b/compiler/core/ETSemitter.h new file mode 100644 index 0000000000000000000000000000000000000000..839a2fda7eb5a95286bc4c4d0c5033c3890e68bd --- /dev/null +++ b/compiler/core/ETSemitter.h @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2021-2022 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_CORE_ETS_EMITTER_H +#define ES2PANDA_COMPILER_CORE_ETS_EMITTER_H + +#include "emitter.h" + +namespace panda::es2panda::binder { +class RecordTable; +} // namespace panda::es2panda::binder + +namespace panda::es2panda::ir { +class ClassDefinition; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::checker { +class ETSObjectType; +class ETSArrayType; +class Signature; +} // namespace panda::es2panda::checker + +namespace panda::pandasm { +struct Field; +struct Record; +class ItemMetadata; +class AnnotationData; +} // namespace panda::pandasm + +namespace panda::es2panda::compiler { + +class ETSFunctionEmitter : public FunctionEmitter { +public: + ETSFunctionEmitter(const CodeGen *cg, ProgramElement *programElement) : FunctionEmitter(cg, programElement) {} + ~ETSFunctionEmitter() = default; + NO_COPY_SEMANTIC(ETSFunctionEmitter); + NO_MOVE_SEMANTIC(ETSFunctionEmitter); + +protected: + const ETSGen *Etsg() const + { + return reinterpret_cast(Cg()); + } + + pandasm::Function *GenFunctionSignature() override; + + void GenFunctionAnnotations(pandasm::Function *func) override; + void GenVariableSignature(pandasm::debuginfo::LocalVariable &variableDebug, + binder::LocalVariable *variable) const override; +}; + +class ETSEmitter : public Emitter { +public: + explicit ETSEmitter(const CompilerContext *context) : Emitter(context) {} + ~ETSEmitter() override = default; + NO_COPY_SEMANTIC(ETSEmitter); + NO_MOVE_SEMANTIC(ETSEmitter); + + void GenAnnotation() override; + +private: + void GenExternalRecord(binder::RecordTable *recordTable); + void GenGlobalArrayRecord(checker::ETSArrayType *arrayType, checker::Signature *signature); + void GenClassRecord(const ir::ClassDefinition *classDef, bool external); + void GenEnumRecord(const ir::TSEnumDeclaration *enumDecl, bool external); + void GenAnnotationRecord(std::string_view recordNameView, bool isRuntime = false, bool isType = false); + void GenInterfaceRecord(const ir::TSInterfaceDeclaration *interfaceDecl, bool external); + void EmitDefaultFieldValue(pandasm::Field &classField, const ir::Expression *init); + void GenClassField(const ir::ClassProperty *field, pandasm::Record &classRecord, bool external); + void GenField(const checker::Type *tsType, const util::StringView &name, const ir::Expression *value, + uint32_t accesFlags, pandasm::Record &record, bool external); + void GenInterfaceMethodDefinition(const ir::MethodDefinition *methodDef, bool external); + void GenClassInheritedFields(const checker::ETSObjectType *baseType, pandasm::Record &classRecord); + pandasm::AnnotationData GenAnnotationSignature(const ir::ClassDefinition *classDef); + pandasm::AnnotationData GenAnnotationEnclosingClass(std::string_view className); + pandasm::AnnotationData GenAnnotationEnclosingMethod(const ir::MethodDefinition *methodDef); + pandasm::AnnotationData GenAnnotationInnerClass(const ir::ClassDefinition *classDef, const ir::AstNode *parent); +}; +} // namespace panda::es2panda::compiler + +#endif diff --git a/compiler/core/ETSfunction.cpp b/compiler/core/ETSfunction.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4f9e6820027623d15af8448dfae8aa1b14c8e337 --- /dev/null +++ b/compiler/core/ETSfunction.cpp @@ -0,0 +1,221 @@ +/** + * Copyright (c) 2021-2022 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 "ETSfunction.h" + +#include "plugins/ecmascript/es2panda/binder/binder.h" +#include "plugins/ecmascript/es2panda/binder/ETSBinder.h" +#include "plugins/ecmascript/es2panda/util/helpers.h" +#include "plugins/ecmascript/es2panda/binder/scope.h" +#include "plugins/ecmascript/es2panda/binder/variable.h" +#include "plugins/ecmascript/es2panda/compiler/base/lreference.h" +#include "plugins/ecmascript/es2panda/compiler/core/ETSGen.h" +#include "plugins/ecmascript/es2panda/compiler/core/envScope.h" +#include "plugins/ecmascript/es2panda/ir/base/scriptFunction.h" +#include "plugins/ecmascript/es2panda/ir/base/classDefinition.h" +#include "plugins/ecmascript/es2panda/ir/base/classProperty.h" +#include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" +#include "plugins/ecmascript/es2panda/ir/statements/blockStatement.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsEnumDeclaration.h" +#include "plugins/ecmascript/es2panda/ir/ts/tsEnumMember.h" +#include "plugins/ecmascript/es2panda/checker/types/ets/types.h" + +namespace panda::es2panda::compiler { +void ETSFunction::CallImplicitCtor(ETSGen *etsg) +{ + RegScope rs(etsg); + auto *superType = etsg->ContainingObjectType()->SuperType(); + + if (superType == nullptr) { + etsg->CallThisStatic0(etsg->RootNode(), etsg->GetThisReg(), Signatures::BUILTIN_OBJECT_CTOR); + + return; + } + + auto res = std::find_if(superType->ConstructSignatures().cbegin(), superType->ConstructSignatures().cend(), + [](const checker::Signature *sig) { return sig->Params().empty(); }); + + if (res == superType->ConstructSignatures().cend()) { + return; + } + + etsg->CallThisStatic0(etsg->RootNode(), etsg->GetThisReg(), (*res)->InternalName()); +} + +void ETSFunction::GenerateEnumMembers(ETSGen *etsg, const ir::AstNode *node, VReg arrayObj, + const ir::TSEnumMember *enumMember, int32_t index) +{ + RegScope rs(etsg); + VReg nameReg = etsg->AllocReg(); + VReg indexReg = etsg->AllocReg(); + + const auto name = enumMember->Key()->AsIdentifier()->Name(); + + etsg->LoadAccumulatorString(enumMember, name); + etsg->StoreAccumulator(enumMember, nameReg); + + etsg->LoadAccumulatorInt(enumMember, index); + etsg->StoreAccumulator(enumMember, indexReg); + + compiler::VReg enumObj = etsg->AllocReg(etsg->Checker()->GlobalETSObjectType()); + etsg->NewObject(enumMember, enumObj, etsg->ContainingObjectType()->AssemblerName()); + + etsg->CallThisStatic2(enumMember, enumObj, compiler::Signatures::BUILTIN_ENUM_CTOR, nameReg, indexReg); + + etsg->LoadAccumulator(enumMember, enumObj); + etsg->StoreStaticOwnProperty(enumMember, etsg->ContainingObjectType(), name); + + etsg->StoreArrayElement(node, arrayObj, indexReg); +} + +void ETSFunction::CompileSourceBlock(ETSGen *etsg, const ir::BlockStatement *block) +{ + auto *scriptFunc = etsg->RootNode()->AsScriptFunction(); + if (scriptFunc->IsEnum()) { + if (scriptFunc->IsConstructor()) { + auto ¶ms = scriptFunc->Scope()->ParamScope()->Params(); + etsg->CallThisStatic2(scriptFunc, params[0]->Vreg(), compiler::Signatures::BUILTIN_ENUM_CTOR, + params[1]->Vreg(), params[2]->Vreg()); + } else { + RegScope rs(etsg); + int32_t index = 0; + + const auto *enumDecl = etsg->ContainingObjectType()->GetDeclNode()->AsTSEnumDeclaration(); + etsg->LoadAccumulatorInt(enumDecl, enumDecl->Members().size()); + + const checker::ETSObjectType *enumType = enumDecl->TsType()->AsETSObjectType(); + auto var = enumType->GetProperty(compiler::Signatures::ENUM_VALUES, + checker::PropertySearchFlags::SEARCH_STATIC_FIELD); + + ASSERT(var != nullptr); + const auto *varType = var->AsLocalVariable()->TsType()->AsETSArrayType(); + VReg arrayObj = etsg->AllocReg(varType); + VReg dim = etsg->AllocReg(); + etsg->StoreAccumulator(enumDecl, dim); + etsg->NewArray(enumDecl, arrayObj, dim, varType->ElementType()); + + for (const auto *member : enumDecl->Members()) { + if (member->IsScriptFunction() || member->IsMethodDefinition()) { + continue; + } + + GenerateEnumMembers(etsg, enumDecl, arrayObj, member->AsTSEnumMember(), index++); + } + + etsg->LoadAccumulator(enumDecl, arrayObj); + etsg->StoreStaticOwnProperty(enumDecl, varType, compiler::Signatures::ENUM_VALUES); + } + } else if (scriptFunc->IsStaticBlock()) { + const auto *classDef = etsg->ContainingObjectType()->GetDeclNode()->AsClassDefinition(); + + for (const auto *prop : classDef->Body()) { + if (!prop->IsClassProperty() || !prop->IsStatic()) { + continue; + } + + prop->AsClassProperty()->Compile(etsg); + } + } else if (scriptFunc->IsConstructor()) { + if (scriptFunc->IsImplicitConstructor()) { + CallImplicitCtor(etsg); + } + + const auto *classDef = etsg->ContainingObjectType()->GetDeclNode()->AsClassDefinition(); + + for (const auto *prop : classDef->Body()) { + if (!prop->IsClassProperty() || prop->IsStatic()) { + continue; + } + + prop->AsClassProperty()->Compile(etsg); + } + } + + const auto &statements = block->Statements(); + + if (statements.empty()) { + etsg->SetFirstStmt(block); + etsg->EmitReturnVoid(block); + return; + } + + etsg->SetFirstStmt(statements.front()); + + etsg->CompileStatementList(statements); + + if (!statements.back()->IsReturnStatement()) { + if (etsg->ReturnType()->IsETSVoidType()) { + etsg->EmitReturnVoid(statements.back()); + } else { + etsg->LoadDefaultValue(statements.back(), scriptFunc->Signature()->ReturnType()); + etsg->ReturnAcc(statements.back()); + } + } +} + +void ETSFunction::CompileFunctionParameterDeclaration(ETSGen *etsg, const ir::ScriptFunction *func) +{ + ScopeContext scopeCtx(etsg, func->Scope()->ParamScope()); + + uint32_t index = 0; + + for (const auto *param : func->Params()) { + if (!param->IsRestElement()) { + index++; + continue; + } + + auto ref = JSLReference::Create(etsg, param, true); + [[maybe_unused]] binder::Variable *paramVar = ref.Variable(); + ASSERT(paramVar && paramVar->IsLocalVariable()); + + [[maybe_unused]] VReg paramReg = VReg(binder::Binder::MANDATORY_PARAMS_NUMBER + VReg::PARAM_START + index++); + ASSERT(paramVar->AsLocalVariable()->Vreg() == paramReg); + + ref.SetValue(); + index++; + } +} + +void ETSFunction::CompileFunction(ETSGen *etsg) +{ + const auto *decl = etsg->RootNode()->AsScriptFunction(); + CompileFunctionParameterDeclaration(etsg, decl); + + const ir::AstNode *body = decl->Body(); + + if (body->IsExpression()) { + // TODO(user): + } else { + CompileSourceBlock(etsg, body->AsBlockStatement()); + } +} + +void ETSFunction::Compile(ETSGen *etsg) +{ + FunctionRegScope lrs(etsg); + auto *topScope = etsg->TopScope(); + + if (topScope->IsFunctionScope()) { + CompileFunction(etsg); + } else { + ASSERT(topScope->IsGlobalScope()); + CompileSourceBlock(etsg, etsg->RootNode()->AsBlockStatement()); + } + + etsg->SortCatchTables(); +} + +} // namespace panda::es2panda::compiler diff --git a/compiler/core/ETSfunction.h b/compiler/core/ETSfunction.h new file mode 100644 index 0000000000000000000000000000000000000000..a4f22f96f43878a71ca6f914744d3b30537637b7 --- /dev/null +++ b/compiler/core/ETSfunction.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021-2022 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_CORE_ETSFUNCTION_H +#define ES2PANDA_COMPILER_CORE_ETSFUNCTION_H + +#include "plugins/ecmascript/es2panda/ir/irnode.h" + +namespace panda::es2panda::ir { +class ScriptFunction; +class BlockStatement; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::compiler { +class ETSGen; + +class ETSFunction { +public: + ETSFunction() = delete; + + static void Compile(ETSGen *etsg); + +private: + static void GenerateEnumMembers(ETSGen *etsg, const ir::AstNode *node, VReg arrayObj, + const ir::TSEnumMember *enumMember, int32_t index); + static void CompileSourceBlock(ETSGen *etsg, const ir::BlockStatement *block); + static void CompileFunctionParameterDeclaration(ETSGen *etsg, const ir::ScriptFunction *func); + static void CompileFunction(ETSGen *etsg); + static void CallImplicitCtor(ETSGen *etsg); +}; +} // namespace panda::es2panda::compiler + +#endif diff --git a/compiler/core/JSemitter.cpp b/compiler/core/JSemitter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6c59b9889592c7ed541f0b4988cb98c302b8df79 --- /dev/null +++ b/compiler/core/JSemitter.cpp @@ -0,0 +1,101 @@ +/** + * Copyright (c) 2021 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 "JSemitter.h" + +#include "plugins/ecmascript/es2panda/compiler/core/pandagen.h" +#include "plugins/ecmascript/es2panda/binder/binder.h" +#include "plugins/ecmascript/es2panda/parser/program/program.h" +#include "plugins/ecmascript/es2panda/compiler/core/compilerContext.h" +#include "assembly-program.h" + +namespace panda::es2panda::compiler { +pandasm::Function *JSFunctionEmitter::GenFunctionSignature() +{ + auto *func = new pandasm::Function(Cg()->InternalName().Mutf8(), panda_file::SourceLang::ECMASCRIPT); + GetProgramElement()->SetFunction(func); + + size_t paramCount = Cg()->InternalParamCount(); + func->params.reserve(paramCount); + + for (uint32_t i = 0; i < paramCount; ++i) { + func->params.emplace_back(pandasm::Type("any", 0), panda_file::SourceLang::ECMASCRIPT); + } + + func->regs_num = VReg::REG_START - Cg()->TotalRegsNum(); + func->return_type = pandasm::Type("any", 0); + + return func; +} + +void JSFunctionEmitter::GenVariableSignature(pandasm::debuginfo::LocalVariable &variableDebug, + [[maybe_unused]] binder::LocalVariable *variable) const +{ + variableDebug.signature = "any"; + variableDebug.signature_type = "any"; +} + +void JSFunctionEmitter::GenFunctionAnnotations(pandasm::Function *func) +{ + pandasm::AnnotationData funcAnnotationData("_ESAnnotation"); + pandasm::AnnotationElement icSizeAnnotationElement( + "icSize", std::make_unique( + pandasm::ScalarValue::Create(Pg()->IcSize()))); + funcAnnotationData.AddElement(std::move(icSizeAnnotationElement)); + + pandasm::AnnotationElement parameterLengthAnnotationElement( + "parameterLength", std::make_unique( + pandasm::ScalarValue::Create(Pg()->FormalParametersCount()))); + funcAnnotationData.AddElement(std::move(parameterLengthAnnotationElement)); + + pandasm::AnnotationElement funcNameAnnotationElement( + "funcName", std::make_unique( + pandasm::ScalarValue::Create(Pg()->FunctionName().Mutf8()))); + funcAnnotationData.AddElement(std::move(funcNameAnnotationElement)); + + func->metadata->AddAnnotations({funcAnnotationData}); +} + +void JSEmitter::GenAnnotation() +{ + Program()->lang = panda_file::SourceLang::ECMASCRIPT; + GenESAnnoatationRecord(); + GenESModuleModeRecord(Context()->Binder()->Program()->Kind() == parser::ScriptKind::MODULE); +} + +void JSEmitter::GenESAnnoatationRecord() +{ + auto annotationRecord = pandasm::Record("_ESAnnotation", Program()->lang); + annotationRecord.metadata->SetAttribute("external"); + annotationRecord.metadata->SetAccessFlags(ACC_ANNOTATION); + Program()->record_table.emplace(annotationRecord.name, std::move(annotationRecord)); +} + +void JSEmitter::GenESModuleModeRecord(bool isModule) +{ + auto modeRecord = pandasm::Record("_ESModuleMode", Program()->lang); + modeRecord.metadata->SetAccessFlags(ACC_PUBLIC); + + auto modeField = pandasm::Field(Program()->lang); + modeField.name = "isModule"; + modeField.type = pandasm::Type("u8", 0); + modeField.metadata->SetValue( + pandasm::ScalarValue::Create(static_cast(isModule))); + + modeRecord.field_list.emplace_back(std::move(modeField)); + + Program()->record_table.emplace(modeRecord.name, std::move(modeRecord)); +} +} // namespace panda::es2panda::compiler diff --git a/compiler/core/JSemitter.h b/compiler/core/JSemitter.h new file mode 100644 index 0000000000000000000000000000000000000000..6a1147082c50d2708f0b7dd1702fdb0ce428430e --- /dev/null +++ b/compiler/core/JSemitter.h @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2021-2022 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_CORE_JS_EMITTER_H +#define ES2PANDA_COMPILER_CORE_JS_EMITTER_H + +#include "emitter.h" + +namespace panda::es2panda::compiler { + +class JSFunctionEmitter : public FunctionEmitter { +public: + JSFunctionEmitter(const CodeGen *cg, ProgramElement *programElement) : FunctionEmitter(cg, programElement) {} + ~JSFunctionEmitter() = default; + NO_COPY_SEMANTIC(JSFunctionEmitter); + NO_MOVE_SEMANTIC(JSFunctionEmitter); + +protected: + const PandaGen *Pg() const + { + return reinterpret_cast(Cg()); + } + + pandasm::Function *GenFunctionSignature() override; + + void GenFunctionAnnotations(pandasm::Function *func) override; + void GenVariableSignature(pandasm::debuginfo::LocalVariable &variableDebug, + binder::LocalVariable *variable) const override; +}; + +class JSEmitter : public Emitter { +public: + explicit JSEmitter(const CompilerContext *context) : Emitter(context) {} + ~JSEmitter() override = default; + NO_COPY_SEMANTIC(JSEmitter); + NO_MOVE_SEMANTIC(JSEmitter); + + void GenAnnotation() override; + +private: + void GenESAnnoatationRecord(); + void GenESModuleModeRecord(bool isModule); +}; +} // namespace panda::es2panda::compiler + +#endif diff --git a/compiler/core/codeGen.cpp b/compiler/core/codeGen.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3c2004ce62de171a196fe27379e774900f043707 --- /dev/null +++ b/compiler/core/codeGen.cpp @@ -0,0 +1,198 @@ +/** + * Copyright (c) 2021-2022 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 "codeGen.h" + +#include "plugins/ecmascript/es2panda/compiler/core/emitter.h" +#include "plugins/ecmascript/es2panda/compiler/core/regAllocator.h" +#include "plugins/ecmascript/es2panda/compiler/core/regScope.h" +#include "plugins/ecmascript/es2panda/compiler/core/compilerContext.h" +#include "plugins/ecmascript/es2panda/compiler/core/dynamicContext.h" +#include "plugins/ecmascript/es2panda/compiler/base/catchTable.h" +#include "plugins/ecmascript/es2panda/ir/base/scriptFunction.h" +#include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" + +namespace panda::es2panda::compiler { +Label *CodeGen::AllocLabel() +{ + std::string id = std::string {Label::PREFIX} + std::to_string(labelId_++); + return sa_.AllocLabel(std::move(id)); +} + +bool CodeGen::IsDebug() const +{ + return context_->IsDebug(); +} + +uint32_t CodeGen::ParamCount() const +{ + if (rootNode_->IsProgram()) { + return 0; + } + + return rootNode_->AsScriptFunction()->Params().size(); +} + +uint32_t CodeGen::FormalParametersCount() const +{ + if (rootNode_->IsProgram()) { + return 0; + } + + ASSERT(rootNode_->IsScriptFunction()); + + return rootNode_->AsScriptFunction()->FormalParamsLength(); +} + +uint32_t CodeGen::InternalParamCount() const +{ + static const uint32_t HIDDEN_PARAMS = 3; + return ParamCount() + HIDDEN_PARAMS; +} + +const util::StringView &CodeGen::InternalName() const +{ + return topScope_->InternalName(); +} + +const util::StringView &CodeGen::FunctionName() const +{ + return topScope_->Name(); +} + +binder::Binder *CodeGen::Binder() const +{ + return context_->Binder(); +} + +int32_t CodeGen::AddLiteralBuffer(LiteralBuffer &&buf) +{ + programElement_->BuffStorage().emplace_back(std::move(buf)); + return literalBufferIdx_++; +} + +void CodeGen::LoadAccumulatorString(const ir::AstNode *node, const util::StringView &str) +{ + sa_.Emit(node, str); +} + +void CodeGen::SetLabel([[maybe_unused]] const ir::AstNode *node, Label *label) +{ + sa_.AddLabel(label); +} + +void CodeGen::Branch(const ir::AstNode *node, Label *label) +{ + sa_.Emit(node, label); +} + +bool CodeGen::CheckControlFlowChange() +{ + const auto *iter = dynamicContext_; + + while (iter != nullptr) { + if (iter->HasFinalizer()) { + return true; + } + + iter = iter->Prev(); + } + + return false; +} + +Label *CodeGen::ControlFlowChangeBreak(const ir::Identifier *label) +{ + auto *iter = dynamicContext_; + + util::StringView labelName = label != nullptr ? label->Name() : LabelTarget::BREAK_LABEL; + Label *breakTarget = nullptr; + + while (iter != nullptr) { + iter->AbortContext(ControlFlowChange::BREAK, labelName); + const auto *constIter = iter; + + const auto &labelTargetName = constIter->Target().BreakLabel(); + + if (constIter->Target().BreakTarget() != nullptr) { + breakTarget = constIter->Target().BreakTarget(); + } + + if (labelTargetName == labelName) { + break; + } + + iter = iter->Prev(); + } + + return breakTarget; +} + +Label *CodeGen::ControlFlowChangeContinue(const ir::Identifier *label) +{ + auto *iter = dynamicContext_; + util::StringView labelName = label != nullptr ? label->Name() : LabelTarget::CONTINUE_LABEL; + Label *continueTarget = nullptr; + + while (iter != nullptr) { + iter->AbortContext(ControlFlowChange::CONTINUE, labelName); + const auto *constIter = iter; + + const auto &labelTargetName = constIter->Target().ContinueLabel(); + + if (constIter->Target().ContinueTarget() != nullptr) { + continueTarget = constIter->Target().ContinueTarget(); + } + + if (labelTargetName == labelName) { + break; + } + + iter = iter->Prev(); + } + + return continueTarget; +} + +uint32_t CodeGen::TryDepth() const +{ + const auto *iter = dynamicContext_; + uint32_t depth = 0; + + while (iter != nullptr) { + if (iter->HasTryCatch()) { + depth++; + } + + iter = iter->Prev(); + } + + return depth; +} + +CatchTable *CodeGen::CreateCatchTable() +{ + auto *catchTable = allocator_->New(this, TryDepth()); + catchList_.push_back(catchTable); + return catchTable; +} + +void CodeGen::SortCatchTables() +{ + std::sort(catchList_.begin(), catchList_.end(), + [](const CatchTable *a, const CatchTable *b) { return b->Depth() < a->Depth(); }); +} + +} // namespace panda::es2panda::compiler diff --git a/compiler/core/codeGen.h b/compiler/core/codeGen.h new file mode 100644 index 0000000000000000000000000000000000000000..e56b67fe10b4b799ba65e7ea894285bb7219b2ee --- /dev/null +++ b/compiler/core/codeGen.h @@ -0,0 +1,265 @@ +/** + * Copyright (c) 2021-2022 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_CORE_CODEGEN_H +#define ES2PANDA_COMPILER_CORE_CODEGEN_H + +#include "plugins/ecmascript/es2panda/compiler/base/literals.h" +#include "plugins/ecmascript/es2panda/compiler/core/regAllocator.h" +#include "plugins/ecmascript/es2panda/compiler/core/regScope.h" + +namespace panda::es2panda::compiler { +class CatchTable; +class DynamicContext; + +enum class Constant { + JS_NAN, + JS_HOLE, + JS_INFINITY, + JS_UNDEFINED, + JS_NULL, + JS_TRUE, + JS_FALSE, + JS_SYMBOL, + JS_GLOBAL, +}; + +class DebugInfo { +public: + explicit DebugInfo(ArenaAllocator *allocator) : variableDebugInfo_(allocator->Adapter()) {}; + DEFAULT_COPY_SEMANTIC(DebugInfo); + DEFAULT_MOVE_SEMANTIC(DebugInfo); + ~DebugInfo() = default; + + ArenaVector &VariableDebugInfo() + { + return variableDebugInfo_; + } + + const ArenaVector &VariableDebugInfo() const + { + return variableDebugInfo_; + } + + const ir::Statement *FirstStatement() const + { + return firstStmt; + } + +private: + friend class CodeGen; + + ArenaVector variableDebugInfo_; + const ir::Statement *firstStmt {}; +}; + +class CodeGen { +public: + explicit CodeGen(ArenaAllocator *allocator, RegSpiller *spiller, CompilerContext *context, + binder::FunctionScope *scope, ProgramElement *programElement) + : allocator_(allocator), + context_(context), + debugInfo_(allocator_), + topScope_(scope), + scope_(topScope_), + rootNode_(scope->Node()), + insns_(allocator_->Adapter()), + catchList_(allocator_->Adapter()), + programElement_(programElement), + sa_(this), + ra_(this, spiller), + rra_(this, spiller) + { + } + ~CodeGen() = default; + NO_COPY_SEMANTIC(CodeGen); + NO_MOVE_SEMANTIC(CodeGen); + + inline ArenaAllocator *Allocator() const + { + return allocator_; + } + + const ArenaVector &CatchList() const + { + return catchList_; + } + + binder::FunctionScope *TopScope() const + { + return topScope_; + } + + binder::Scope *Scope() const + { + return scope_; + } + + const ir::AstNode *RootNode() const + { + return rootNode_; + } + + ArenaList &Insns() + { + return insns_; + } + + const ArenaList &Insns() const + { + return insns_; + } + + VReg AllocReg() + { + return VReg(usedRegs_--); + } + + VReg AllocReg(const checker::Type *type) + { + return VReg(usedRegs_--, type); + } + + VReg NextReg() const + { + return VReg(usedRegs_); + } + + uint32_t TotalRegsNum() const + { + return totalRegs_; + } + + size_t LabelCount() const + { + return labelId_; + } + + const DebugInfo &Debuginfo() const + { + return debugInfo_; + } + + uint32_t IcSize() const + { + return 0; + } + + bool IsDebug() const; + RegSpiller *GetRegSpiller() const; + uint32_t ParamCount() const; + uint32_t FormalParametersCount() const; + uint32_t InternalParamCount() const; + const util::StringView &InternalName() const; + const util::StringView &FunctionName() const; + binder::Binder *Binder() const; + + Label *AllocLabel(); + int32_t AddLiteralBuffer(LiteralBuffer &&buf); + + void LoadAccumulatorString(const ir::AstNode *node, const util::StringView &str); + + void SetLabel(const ir::AstNode *node, Label *label); + void Branch(const ir::AstNode *node, class Label *label); + bool CheckControlFlowChange(); + Label *ControlFlowChangeBreak(const ir::Identifier *label = nullptr); + Label *ControlFlowChangeContinue(const ir::Identifier *label); + + uint32_t TryDepth() const; + CatchTable *CreateCatchTable(); + void SortCatchTables(); + + void SetFirstStmt(const ir::Statement *stmt) + { + debugInfo_.firstStmt = stmt; + } + + [[noreturn]] static void Unimplemented() + { + throw Error(ErrorType::GENERIC, "", "Unimplemented code path"); + } + +protected: + SimpleAllocator &Sa() + { + return sa_; + } + + const SimpleAllocator &Sa() const + { + return sa_; + } + + RegAllocator &Ra() + { + return ra_; + } + + const RegAllocator &Ra() const + { + return ra_; + } + + RangeRegAllocator &Rra() + { + return rra_; + } + + const RangeRegAllocator &Rra() const + { + return rra_; + } + + CompilerContext *Context() const + { + return context_; + } + + ProgramElement *ProgElement() const + { + return programElement_; + } + +private: + ArenaAllocator *allocator_; + CompilerContext *context_; + DebugInfo debugInfo_; + binder::FunctionScope *topScope_; + binder::Scope *scope_; + const ir::AstNode *rootNode_; + ArenaList insns_; + ArenaVector catchList_; + ProgramElement *programElement_; + DynamicContext *dynamicContext_ {}; + + SimpleAllocator sa_; + RegAllocator ra_; + RangeRegAllocator rra_; + size_t labelId_ {0}; + int32_t literalBufferIdx_ {0}; + + uint32_t usedRegs_ {VReg::REG_START}; + uint32_t totalRegs_ {VReg::REG_START}; + friend class ScopeContext; + friend class RegScope; + friend class LocalRegScope; + friend class LoopRegScope; + friend class ParamRegScope; + friend class FunctionRegScope; + friend class DynamicContext; +}; +} // namespace panda::es2panda::compiler + +#endif diff --git a/compiler/core/compileJob.cpp b/compiler/core/compileJob.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5af7d3eed42f191c64a09a9a0fa7a2dd1f77b103 --- /dev/null +++ b/compiler/core/compileJob.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2022 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 "compileQueue.h" + +#include "plugins/ecmascript/es2panda/compiler/core/compilerContext.h" + +namespace panda::es2panda::compiler { + +void CompileJob::Run() +{ + std::unique_lock lock(m_); + cond_.wait(lock, [this] { return dependencies_ == 0; }); + + context_->GetCodeGenCb()(context_, scope_, &programElement_); + + if (dependant_ != nullptr) { + dependant_->Signal(); + } +} + +void CompileJob::DependsOn(CompileJob *job) +{ + job->dependant_ = this; + dependencies_++; +} + +void CompileJob::Signal() +{ + { + std::lock_guard lock(m_); + dependencies_--; + } + + cond_.notify_one(); +} +} // namespace panda::es2panda::compiler diff --git a/compiler/core/compileJob.h b/compiler/core/compileJob.h new file mode 100644 index 0000000000000000000000000000000000000000..687217f77f6a802857d6a0f104d7b83bb8880ed8 --- /dev/null +++ b/compiler/core/compileJob.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021-2022 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_CORE_COMPILEJOB_H +#define ES2PANDA_COMPILER_CORE_COMPILEJOB_H + +#include "macros.h" +#include "plugins/ecmascript/es2panda/es2panda.h" +#include "plugins/ecmascript/es2panda/compiler/core/programElement.h" + +#include +#include + +namespace panda::es2panda::binder { +class FunctionScope; +} // namespace panda::es2panda::binder + +namespace panda::es2panda::compiler { +class CompilerContext; +class ProgramElement; + +class CompileJob { +public: + CompileJob() = default; + NO_COPY_SEMANTIC(CompileJob); + NO_MOVE_SEMANTIC(CompileJob); + ~CompileJob() = default; + + const ProgramElement *GetProgramElement() const + { + return &programElement_; + } + + ProgramElement *GetProgramElement() + { + return &programElement_; + } + + void SetConext(CompilerContext *context, binder::FunctionScope *scope) + { + context_ = context; + scope_ = scope; + } + + void Run(); + void DependsOn(CompileJob *job); + void Signal(); + +private: + std::mutex m_; + std::condition_variable cond_; + CompilerContext *context_ {}; + binder::FunctionScope *scope_ {}; + ProgramElement programElement_; + CompileJob *dependant_ {}; + size_t dependencies_ {0}; +}; +} // namespace panda::es2panda::compiler + +#endif diff --git a/compiler/core/compileQueue.cpp b/compiler/core/compileQueue.cpp index f75127362c9f1d02fc248d49719a451da564720d..6af577190c876d2668411348d2d690199ef62b43 100644 --- a/compiler/core/compileQueue.cpp +++ b/compiler/core/compileQueue.cpp @@ -23,43 +23,6 @@ #include "plugins/ecmascript/es2panda/compiler/core/pandagen.h" namespace panda::es2panda::compiler { -CompileJob::CompileJob() : programElement_(std::make_unique()) {} -CompileJob::~CompileJob() = default; - -void CompileJob::Run() -{ - std::unique_lock lock(m_); - cond_.wait(lock, [this] { return dependencies_ == 0; }); - - ArenaAllocator allocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); - PandaGen pg(&allocator, context_, scope_, programElement_.get()); - - Function::Compile(&pg); - - FunctionEmitter funcEmitter(&pg, programElement_.get()); - funcEmitter.Generate(); - - if (dependant_ != nullptr) { - dependant_->Signal(); - } -} - -void CompileJob::DependsOn(CompileJob *job) -{ - job->dependant_ = this; - dependencies_++; -} - -void CompileJob::Signal() -{ - { - std::lock_guard lock(m_); - dependencies_--; - } - - cond_.notify_one(); -} - CompileQueue::CompileQueue(size_t threadCount) { threads_.reserve(threadCount); @@ -147,15 +110,16 @@ void CompileQueue::Wait(const JobsFinishedCb &onFinishedCb) std::unique_lock lock(m_); jobsFinished_.wait(lock, [this]() { return activeWorkers_ == 0 && jobsCount_ == 0; }); + if (!errors_.empty()) { + delete[] jobs_; + // NOLINTNEXTLINE + throw errors_.front(); + } + for (uint32_t i = 0; i < totalJobsCount_; i++) { onFinishedCb(jobs_ + i); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) } delete[] jobs_; - - if (!errors_.empty()) { - // NOLINTNEXTLINE - throw errors_.front(); - } } } // namespace panda::es2panda::compiler diff --git a/compiler/core/compileQueue.h b/compiler/core/compileQueue.h index 39d39f1bc05b1af427395add0964299981ed7071..bd31c32f550b03cb2bb715dbd5f163c095fceebf 100644 --- a/compiler/core/compileQueue.h +++ b/compiler/core/compileQueue.h @@ -19,6 +19,7 @@ #include "macros.h" #include "os/thread.h" #include "plugins/ecmascript/es2panda/es2panda.h" +#include "plugins/ecmascript/es2panda/compiler/core/compileJob.h" #include #include @@ -30,39 +31,6 @@ class FunctionScope; namespace panda::es2panda::compiler { class CompilerContext; -class ProgramElement; - -class CompileJob { -public: - CompileJob(); - NO_COPY_SEMANTIC(CompileJob); - NO_MOVE_SEMANTIC(CompileJob); - ~CompileJob(); - - ProgramElement *GetProgramElement() const - { - return programElement_.get(); - } - - void SetConext(CompilerContext *context, binder::FunctionScope *scope) - { - context_ = context; - scope_ = scope; - } - - void Run(); - void DependsOn(CompileJob *job); - void Signal(); - -private: - std::mutex m_; - std::condition_variable cond_; - CompilerContext *context_ {}; - binder::FunctionScope *scope_ {}; - std::unique_ptr programElement_; - CompileJob *dependant_ {}; - size_t dependencies_ {0}; -}; class CompileQueue { public: diff --git a/compiler/core/compilerContext.cpp b/compiler/core/compilerContext.cpp index 209d223a95a2c1545e395be5b360dcdb53820344..3e712f56a7067d35349a9e974dc491c58ccd64ee 100644 --- a/compiler/core/compilerContext.cpp +++ b/compiler/core/compilerContext.cpp @@ -14,16 +14,6 @@ */ #include "compilerContext.h" -#include - -#include "plugins/ecmascript/es2panda/compiler/core/emitter.h" -#include "plugins/ecmascript/es2panda/compiler/base/literals.h" namespace panda::es2panda::compiler { -CompilerContext::CompilerContext(binder::Binder *binder, CompilerOptions options) - : binder_(binder), emitter_(std::make_unique(this)), options_(options) -{ -} - -CompilerContext::~CompilerContext() = default; } // namespace panda::es2panda::compiler diff --git a/compiler/core/compilerContext.h b/compiler/core/compilerContext.h index 7936fc1abbe38f85d1f793b59c792362dc24a568..d7b44de730dfdaa954f1c61d6684252a68cf28de 100644 --- a/compiler/core/compilerContext.h +++ b/compiler/core/compilerContext.h @@ -21,36 +21,62 @@ #include "plugins/ecmascript/es2panda/es2panda.h" #include "plugins/ecmascript/es2panda/compiler/base/literals.h" +#include #include -namespace panda::es2panda::ir { -class Literal; -} // namespace panda::es2panda::ir - namespace panda::es2panda::binder { class Binder; +class FunctionScope; } // namespace panda::es2panda::binder +namespace panda::es2panda::checker { +class Checker; +} // namespace panda::es2panda::checker + namespace panda::es2panda::compiler { class Literal; class DebugInfo; class Emitter; +class CodeGen; +class ProgramElement; class CompilerContext { public: - CompilerContext(binder::Binder *binder, CompilerOptions options); + using CodeGenCb = + std::function; + + CompilerContext(binder::Binder *binder, checker::Checker *checker, CompilerOptions options, CodeGenCb codeGenCb) + : binder_(binder), checker_(checker), options_(std::move(options)), codeGenCb_(std::move(codeGenCb)) + { + } + NO_COPY_SEMANTIC(CompilerContext); NO_MOVE_SEMANTIC(CompilerContext); - ~CompilerContext(); + ~CompilerContext() = default; binder::Binder *Binder() const { return binder_; } + checker::Checker *Checker() const + { + return checker_; + } + Emitter *GetEmitter() const { - return emitter_.get(); + return emitter_; + } + + void SetEmitter(Emitter *emitter) + { + emitter_ = emitter; + } + + const CodeGenCb &GetCodeGenCb() const + { + return codeGenCb_; } int32_t AddContextLiteral(LiteralBuffer &&literals) @@ -69,6 +95,11 @@ public: return options_.isDebug; } + bool DumpDebugInfo() const + { + return options_.dumpDebugInfo; + } + bool IsDirectEval() const { return options_.isDirectEval; @@ -86,9 +117,11 @@ public: private: binder::Binder *binder_; + checker::Checker *checker_; + Emitter *emitter_ {}; std::vector buffStorage_; - std::unique_ptr emitter_; CompilerOptions options_; + CodeGenCb codeGenCb_ {}; }; } // namespace panda::es2panda::compiler diff --git a/compiler/core/compilerImpl.cpp b/compiler/core/compilerImpl.cpp index 1531ad8ec1150cc331b6fd4d0ca15569c585590a..2899598d8bb1d1d4800f9bf59bb1302ceb317f3f 100644 --- a/compiler/core/compilerImpl.cpp +++ b/compiler/core/compilerImpl.cpp @@ -15,26 +15,35 @@ #include "compilerImpl.h" -#include "plugins/ecmascript/es2panda/compiler/core/compileQueue.h" #include "plugins/ecmascript/es2panda/compiler/core/compilerContext.h" -#include "plugins/ecmascript/es2panda/compiler/core/emitter.h" -#include "plugins/ecmascript/es2panda/typescript/checker.h" -#include "plugins/ecmascript/es2panda/es2panda.h" +#include "plugins/ecmascript/es2panda/compiler/core/compileQueue.h" +#include "plugins/ecmascript/es2panda/compiler/core/compilerImpl.h" +#include "plugins/ecmascript/es2panda/compiler/core/pandagen.h" +#include "plugins/ecmascript/es2panda/compiler/core/ETSGen.h" +#include "plugins/ecmascript/es2panda/compiler/core/JSemitter.h" +#include "plugins/ecmascript/es2panda/compiler/core/ETSemitter.h" +#include "plugins/ecmascript/es2panda/parser/parserImpl.h" +#include "plugins/ecmascript/es2panda/parser/JSparser.h" +#include "plugins/ecmascript/es2panda/parser/ASparser.h" +#include "plugins/ecmascript/es2panda/parser/TSparser.h" +#include "plugins/ecmascript/es2panda/parser/ETSparser.h" #include "plugins/ecmascript/es2panda/parser/program/program.h" +#include "plugins/ecmascript/es2panda/binder/JSBinder.h" +#include "plugins/ecmascript/es2panda/binder/ASBinder.h" +#include "plugins/ecmascript/es2panda/binder/TSBinder.h" +#include "plugins/ecmascript/es2panda/binder/ETSBinder.h" +#include "plugins/ecmascript/es2panda/checker/TSchecker.h" +#include "plugins/ecmascript/es2panda/checker/ETSchecker.h" +#include "plugins/ecmascript/es2panda/checker/ASchecker.h" +#include "plugins/ecmascript/es2panda/checker/JSchecker.h" +#include "plugins/ecmascript/es2panda/es2panda.h" #include #include namespace panda::es2panda::compiler { -CompilerImpl::CompilerImpl(size_t threadCount) : queue_(new CompileQueue(threadCount)) {} -CompilerImpl::~CompilerImpl() -{ - delete queue_; -} - -panda::pandasm::Program *CompilerImpl::Compile(CompilerContext *context, parser::Program *program, - const es2panda::CompilerOptions &options) +void CompilerImpl::HandleContextLiterals(CompilerContext *context) { auto *emitter = context->GetEmitter(); @@ -44,27 +53,98 @@ panda::pandasm::Program *CompilerImpl::Compile(CompilerContext *context, parser: } emitter->LiteralBufferIndex() += context->ContextLiterals().size(); +} - if (program->Extension() == ScriptExtension::AS) { - /* TODO(): AS files are not yet compiled */ - return nullptr; - } +panda::pandasm::Program *CompilerImpl::Emit(CompilerContext *context) +{ + HandleContextLiterals(context); + + queue_.Schedule(context); + + /* Main thread can also be used instead of idling */ + queue_.Consume(); + auto *emitter = context->GetEmitter(); + queue_.Wait([emitter](CompileJob *job) { emitter->AddProgramElement(job->GetProgramElement()); }); + + return emitter->Finalize(context->DumpDebugInfo(), Signatures::ETS_GLOBAL); +} + +template +static CompilerContext::CodeGenCb MakeCompileJob() +{ + return + [](CompilerContext *context, binder::FunctionScope *scope, compiler::ProgramElement *programElement) -> void { + RegSpiller rspl; + ArenaAllocator allocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); + CodeGen cg(&allocator, &rspl, context, scope, programElement); + + FunctionEmitter funcEmitter(&cg, programElement); + funcEmitter.Generate(); + }; +} + +using EmitCb = std::function; + +template +static pandasm::Program *CreateCompiler(const CompilationUnit &unit, const EmitCb &emitCb) +{ + ArenaAllocator allocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); + auto program = parser::Program::NewProgram(&allocator); + auto parser = Parser(&program, unit.options.stdLib_, static_cast(unit.rawParserStatus)); + auto checker = Checker(); - if (program->Extension() == ScriptExtension::TS) { - ArenaAllocator localAllocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); - auto checker = std::make_unique(&localAllocator, context->Binder()); - checker->StartChecker(); + auto *binder = program.Binder(); + binder->SetProgram(&program); + CompilerContext context(binder, &checker, unit.options, + MakeCompileJob()); + binder->SetCompilerContext(&context); + + auto emitter = Emitter(&context); + context.SetEmitter(&emitter); + + parser.ParseScript(unit.input); + + if (!checker.StartChecker(binder, unit.options)) { return nullptr; } - queue_->Schedule(context); + emitter.GenAnnotation(); - /* Main thread can also be used instead of idling */ - queue_->Consume(); - queue_->Wait([emitter](CompileJob *job) { emitter->AddProgramElement(job->GetProgramElement()); }); + return emitCb(&context); +} + +pandasm::Program *CompilerImpl::Compile(const CompilationUnit &unit) +{ + auto emitCb = [this](CompilerContext *context) -> pandasm::Program * { return Emit(context); }; - return emitter->Finalize(options.dumpDebugInfo); + switch (unit.ext) { + case ScriptExtension::TS: { + return CreateCompiler( + unit, emitCb); + } + case ScriptExtension::AS: { + return CreateCompiler( + unit, emitCb); + } + case ScriptExtension::ETS: { + return CreateCompiler( + unit, emitCb); + } + case ScriptExtension::JS: { + return CreateCompiler( + unit, emitCb); + } + default: { + UNREACHABLE(); + return nullptr; + } + } } void CompilerImpl::DumpAsm(const panda::pandasm::Program *prog) diff --git a/compiler/core/compilerImpl.h b/compiler/core/compilerImpl.h index c81d92061a1ed393c5cc7c8dcd90f270999ac117..94629a0ee8150a6afa867eaf0825f430c18c2a0b 100644 --- a/compiler/core/compilerImpl.h +++ b/compiler/core/compilerImpl.h @@ -17,6 +17,7 @@ #define ES2PANDA_COMPILER_INCLUDE_COMPILER_IMPL_H #include "plugins/ecmascript/es2panda/es2panda.h" +#include "plugins/ecmascript/es2panda/compiler/core/compileQueue.h" #include "macros.h" #include "mem/arena_allocator.h" #include "os/thread.h" @@ -27,27 +28,38 @@ namespace panda::pandasm { struct Program; } // namespace panda::pandasm -namespace panda::es2panda::parser { -class Program; -} // namespace panda::es2panda::parser - namespace panda::es2panda::compiler { class CompileQueue; class CompilerContext; +class CompilationUnit { +public: + explicit CompilationUnit(const SourceFile &i, const CompilerOptions &o, uint32_t s, ScriptExtension e) + : input(i), options(o), rawParserStatus(s), ext(e) + { + } + const SourceFile &input; + const CompilerOptions &options; + uint32_t rawParserStatus; + ScriptExtension ext; +}; + class CompilerImpl { public: - explicit CompilerImpl(size_t threadCount); - ~CompilerImpl(); + explicit CompilerImpl(size_t threadCount) : queue_(threadCount) {} NO_COPY_SEMANTIC(CompilerImpl); NO_MOVE_SEMANTIC(CompilerImpl); + ~CompilerImpl() = default; + + pandasm::Program *Compile(const CompilationUnit &unit); - panda::pandasm::Program *Compile(CompilerContext *context, parser::Program *program, - const es2panda::CompilerOptions &options); static void DumpAsm(const panda::pandasm::Program *prog); private: - CompileQueue *queue_; + panda::pandasm::Program *Emit(CompilerContext *context); + static void HandleContextLiterals(CompilerContext *context); + + CompileQueue queue_; }; } // namespace panda::es2panda::compiler diff --git a/compiler/core/dynamicContext.cpp b/compiler/core/dynamicContext.cpp index eb936f4b1ec06f0daf1661137b5b324587afa7a8..f4dfe5e5171f0408aaadef616bef1aa064ac23ae 100644 --- a/compiler/core/dynamicContext.cpp +++ b/compiler/core/dynamicContext.cpp @@ -17,31 +17,32 @@ #include "plugins/ecmascript/es2panda/compiler/core/pandagen.h" #include "plugins/ecmascript/es2panda/compiler/base/catchTable.h" +#include "plugins/ecmascript/es2panda/compiler/core/envScope.h" #include "plugins/ecmascript/es2panda/ir/expressions/identifier.h" #include "plugins/ecmascript/es2panda/ir/statements/tryStatement.h" #include "plugins/ecmascript/es2panda/ir/statements/blockStatement.h" #include "plugins/ecmascript/es2panda/ir/statements/labelledStatement.h" namespace panda::es2panda::compiler { -DynamicContext::DynamicContext(PandaGen *pg, LabelTarget target) : pg_(pg), target_(target), prev_(pg_->dynamicContext_) +DynamicContext::DynamicContext(CodeGen *cg, LabelTarget target) : cg_(cg), target_(target), prev_(Cg()->dynamicContext_) { - pg_->dynamicContext_ = this; + Cg()->dynamicContext_ = this; } DynamicContext::~DynamicContext() { - pg_->dynamicContext_ = prev_; + Cg()->dynamicContext_ = prev_; } -LabelContext::LabelContext(PandaGen *pg, const ir::LabelledStatement *labelledStmt) - : DynamicContext(pg, LabelTarget(labelledStmt->Ident()->Name())), labelledStmt_(labelledStmt) +LabelContext::LabelContext(CodeGen *cg, const ir::LabelledStatement *labelledStmt) + : DynamicContext(cg, LabelTarget(labelledStmt->Ident()->Name())), labelledStmt_(labelledStmt) { if (!labelledStmt->Body()->IsBlockStatement()) { return; } - label_ = pg->AllocLabel(); - target_.SetBreakTarget(label_); + label_ = cg->AllocLabel(); + Target().SetBreakTarget(label_); } LabelContext::~LabelContext() @@ -50,7 +51,7 @@ LabelContext::~LabelContext() return; } - pg_->SetLabel(labelledStmt_, label_); + Cg()->SetLabel(labelledStmt_, label_); } LexEnvContext::LexEnvContext(LoopEnvScope *envScope, PandaGen *pg, LabelTarget target) @@ -60,11 +61,11 @@ LexEnvContext::LexEnvContext(LoopEnvScope *envScope, PandaGen *pg, LabelTarget t return; } - catchTable_ = pg_->CreateCatchTable(); + catchTable_ = Cg()->CreateCatchTable(); const auto &labelSet = catchTable_->LabelSet(); const auto *node = envScope_->Scope()->Node(); - pg_->SetLabel(node, labelSet.TryBegin()); + Cg()->SetLabel(node, labelSet.TryBegin()); } LexEnvContext::~LexEnvContext() @@ -76,14 +77,19 @@ LexEnvContext::~LexEnvContext() const auto &labelSet = catchTable_->LabelSet(); const auto *node = envScope_->Scope()->Node(); - pg_->SetLabel(node, labelSet.TryEnd()); - pg_->Branch(node, labelSet.CatchEnd()); + Cg()->SetLabel(node, labelSet.TryEnd()); + Cg()->Branch(node, labelSet.CatchEnd()); - pg_->SetLabel(node, labelSet.CatchBegin()); - pg_->PopLexEnv(node); - pg_->EmitThrow(node); - pg_->SetLabel(node, labelSet.CatchEnd()); - pg_->PopLexEnv(node); + Cg()->SetLabel(node, labelSet.CatchBegin()); + AsPandaGen()->PopLexEnv(node); + AsPandaGen()->EmitThrow(node); + Cg()->SetLabel(node, labelSet.CatchEnd()); + AsPandaGen()->PopLexEnv(node); +} + +PandaGen *LexEnvContext::AsPandaGen() const +{ + return static_cast(Cg()); } bool LexEnvContext::HasTryCatch() const @@ -99,14 +105,14 @@ void LexEnvContext::AbortContext([[maybe_unused]] ControlFlowChange cfc, } const auto *node = envScope_->Scope()->Node(); - pg_->PopLexEnv(node); + AsPandaGen()->PopLexEnv(node); } IteratorContext::IteratorContext(PandaGen *pg, const Iterator &iterator, LabelTarget target) : DynamicContext(pg, target), iterator_(iterator), catchTable_(pg->CreateCatchTable()) { const auto &labelSet = catchTable_->LabelSet(); - pg_->SetLabel(iterator_.Node(), labelSet.TryBegin()); + pg->SetLabel(iterator_.Node(), labelSet.TryBegin()); } IteratorContext::~IteratorContext() @@ -114,18 +120,18 @@ IteratorContext::~IteratorContext() const auto &labelSet = catchTable_->LabelSet(); const auto *node = iterator_.Node(); - pg_->SetLabel(node, labelSet.TryEnd()); - pg_->Branch(node, labelSet.CatchEnd()); + Cg()->SetLabel(node, labelSet.TryEnd()); + Cg()->Branch(node, labelSet.CatchEnd()); - pg_->SetLabel(node, labelSet.CatchBegin()); + Cg()->SetLabel(node, labelSet.CatchBegin()); iterator_.Close(true); - pg_->SetLabel(node, labelSet.CatchEnd()); + Cg()->SetLabel(node, labelSet.CatchEnd()); } void IteratorContext::AbortContext([[maybe_unused]] ControlFlowChange cfc, [[maybe_unused]] const util::StringView &targetLabel) { - if (cfc == ControlFlowChange::CONTINUE && target_.ContinueLabel() == targetLabel) { + if (cfc == ControlFlowChange::CONTINUE && Target().ContinueLabel() == targetLabel) { return; } @@ -140,16 +146,19 @@ void TryContext::InitFinalizer() return; } - finalizerRun_ = pg_->AllocReg(); - pg_->StoreConst(tryStmt_, finalizerRun_, Constant::JS_UNDEFINED); + auto *pg = static_cast(Cg()); + + finalizerRun_ = pg->AllocReg(); + pg->StoreConst(tryStmt_, finalizerRun_, Constant::JS_UNDEFINED); } -void TryContext::InitCatchTable() +void CatchContext::InitCatchTable() { - catchTable_ = pg_->CreateCatchTable(); + auto *pg = static_cast(Cg()); + catchTable_ = pg->CreateCatchTable(); } -const TryLabelSet &TryContext::LabelSet() const +const TryLabelSet &CatchContext::LabelSet() const { return catchTable_->LabelSet(); } @@ -165,14 +174,10 @@ void TryContext::EmitFinalizer() return; } + auto *pg = static_cast(Cg()); inFinalizer_ = true; - tryStmt_->FinallyBlock()->Compile(pg_); + tryStmt_->FinallyBlock()->Compile(pg); inFinalizer_ = false; } -void TryContext::AbortContext([[maybe_unused]] ControlFlowChange cfc, - [[maybe_unused]] const util::StringView &targetLabel) -{ - EmitFinalizer(); -} } // namespace panda::es2panda::compiler diff --git a/compiler/core/dynamicContext.h b/compiler/core/dynamicContext.h index 44753babdf7af61b6425dab44d7fa56713580b7b..955ea5c17ee8bd3644942766eb2704a5783b92a2 100644 --- a/compiler/core/dynamicContext.h +++ b/compiler/core/dynamicContext.h @@ -28,12 +28,12 @@ class LabelledStatement; } // namespace panda::es2panda::ir namespace panda::es2panda::compiler { -class PandaGen; +class CodeGen; class LoopEnvScope; class CatchTable; class TryLabelSet; -enum class DynamicContextType { NONE, LABEL, LEX_ENV, ITERATOR, TRY }; +enum class DynamicContextType { NONE, LABEL, LEX_ENV, ITERATOR, TRY, TRAP }; class DynamicContext { public: @@ -75,19 +75,28 @@ public: } protected: - explicit DynamicContext(PandaGen *pg, LabelTarget target); + explicit DynamicContext(CodeGen *cg, LabelTarget target); - // NOLINTBEGIN(misc-non-private-member-variables-in-classes) - PandaGen *pg_; + LabelTarget &Target() + { + return target_; + } + + CodeGen *Cg() const + { + return cg_; + } + +private: + CodeGen *cg_; LabelTarget target_; DynamicContext *prev_ {}; - // NOLINTEND(misc-non-private-member-variables-in-classes) }; class LabelContext : public DynamicContext { public: - explicit LabelContext(PandaGen *pg, LabelTarget target) : DynamicContext(pg, target) {} - explicit LabelContext(PandaGen *pg, const ir::LabelledStatement *labelledStmt); + explicit LabelContext(CodeGen *cg, LabelTarget target) : DynamicContext(cg, target) {} + explicit LabelContext(CodeGen *cg, const ir::LabelledStatement *labelledStmt); NO_COPY_SEMANTIC(LabelContext); NO_MOVE_SEMANTIC(LabelContext); ~LabelContext(); @@ -118,6 +127,9 @@ public: void AbortContext([[maybe_unused]] ControlFlowChange cfc, [[maybe_unused]] const util::StringView &targetLabel) override; +protected: + PandaGen *AsPandaGen() const; + private: LoopEnvScope *envScope_; CatchTable *catchTable_ {}; @@ -153,20 +165,46 @@ private: CatchTable *catchTable_; }; -class TryContext : public DynamicContext { +class CatchContext : public DynamicContext { public: - explicit TryContext(PandaGen *pg, const ir::TryStatement *tryStmt, bool hasFinalizer = true) - : DynamicContext(pg, {}), tryStmt_(tryStmt), hasFinalizer_(hasFinalizer) + NO_COPY_SEMANTIC(CatchContext); + NO_MOVE_SEMANTIC(CatchContext); + ~CatchContext() = default; + + CatchTable *GetCatchTable() const { - InitCatchTable(); - InitFinalizer(); + return catchTable_; } - explicit TryContext(PandaGen *pg) : DynamicContext(pg, {}) + const TryLabelSet &LabelSet() const; + + bool HasTryCatch() const override + { + return true; + } + +protected: + explicit CatchContext(CodeGen *cg) : DynamicContext(cg, {}) { InitCatchTable(); } +private: + void InitCatchTable(); + CatchTable *catchTable_ {}; +}; + +class TryContext : public CatchContext { +public: + explicit TryContext(CodeGen *cg, const ir::TryStatement *tryStmt, bool hasFinalizer = true) + : CatchContext(cg), tryStmt_(tryStmt), hasFinalizer_(hasFinalizer) + + { + InitFinalizer(); + } + + explicit TryContext(CodeGen *cg) : CatchContext(cg) {} + NO_COPY_SEMANTIC(TryContext); NO_MOVE_SEMANTIC(TryContext); ~TryContext() = default; @@ -176,38 +214,51 @@ public: return DynamicContextType::TRY; } - bool HasTryCatch() const override - { - return true; - } - VReg FinalizerRun() const { return finalizerRun_; } - CatchTable *GetCatchTable() const - { - return catchTable_; - } - - const TryLabelSet &LabelSet() const; bool HasFinalizer() const override; void InitFinalizer(); void EmitFinalizer(); void AbortContext([[maybe_unused]] ControlFlowChange cfc, - [[maybe_unused]] const util::StringView &targetLabel) override; + [[maybe_unused]] const util::StringView &targetLabel) override + { + EmitFinalizer(); + } private: - void InitCatchTable(); - const ir::TryStatement *tryStmt_ {}; - CatchTable *catchTable_ {}; VReg finalizerRun_ {}; bool hasFinalizer_ {}; bool inFinalizer_ {}; }; + +class TrapContext : public CatchContext { +public: + explicit TrapContext(CodeGen *cg) : CatchContext(cg) {} + + NO_COPY_SEMANTIC(TrapContext); + NO_MOVE_SEMANTIC(TrapContext); + ~TrapContext() = default; + + DynamicContextType Type() const override + { + return DynamicContextType::TRAP; + } + + bool HasTryCatch() const override + { + return true; + } + + bool HasFinalizer() const override + { + return false; + } +}; } // namespace panda::es2panda::compiler #endif diff --git a/compiler/core/emitter.cpp b/compiler/core/emitter.cpp index bb01bd1d3d41855f6909de606167e4decf0bc77d..d854586e297d8e57593853f89ed0292b1d04df32 100644 --- a/compiler/core/emitter.cpp +++ b/compiler/core/emitter.cpp @@ -16,21 +16,20 @@ #include "emitter.h" #include "plugins/ecmascript/es2panda/ir/irnode.h" -#include "plugins/ecmascript/es2panda/binder/binder.h" #include "plugins/ecmascript/es2panda/util/helpers.h" #include "plugins/ecmascript/es2panda/binder/scope.h" #include "plugins/ecmascript/es2panda/binder/variable.h" #include "plugins/ecmascript/es2panda/compiler/base/literals.h" #include "plugins/ecmascript/es2panda/compiler/core/compilerContext.h" -#include "plugins/ecmascript/es2panda/compiler/core/pandagen.h" +#include "plugins/ecmascript/es2panda/compiler/core/codeGen.h" +#include "plugins/ecmascript/es2panda/compiler/core/regSpiller.h" #include "plugins/ecmascript/es2panda/compiler/debugger/debuginfoDumper.h" #include "plugins/ecmascript/es2panda/compiler/base/catchTable.h" #include "plugins/ecmascript/es2panda/es2panda.h" -#include "generated/isa.h" -#include "plugins/ecmascript/es2panda/ir/expressions/literal.h" #include "plugins/ecmascript/es2panda/ir/statements/blockStatement.h" -#include "macros.h" #include "plugins/ecmascript/es2panda/parser/program/program.h" +#include "generated/isa.h" +#include "macros.h" #include #include @@ -108,33 +107,19 @@ static LiteralPair TransformLiteral(const compiler::Literal *literal) return {tagLit, valueLit}; } -constexpr const auto LANG_EXT = pandasm::extensions::Language::ECMASCRIPT; - void FunctionEmitter::Generate() { - auto *func = new panda::pandasm::Function(pg_->InternalName().Mutf8(), LANG_EXT); - programElement_->func = func; - - size_t paramCount = pg_->InternalParamCount(); - func->params.reserve(paramCount); - - for (uint32_t i = 0; i < paramCount; ++i) { - func->params.emplace_back(panda::pandasm::Type("any", 0), LANG_EXT); - } - - func->regs_num = IRNode::REG_START - pg_->TotalRegsNum(); - func->return_type = panda::pandasm::Type("any", 0); - + auto *func = GenFunctionSignature(); GenFunctionInstructions(func); GenVariablesDebugInfo(func); GenSourceFileDebugInfo(func); GenFunctionCatchTables(func); - GenFunctionICSize(func); + GenFunctionAnnotations(func); } util::StringView FunctionEmitter::SourceCode() const { - return pg_->Binder()->Program()->SourceCode(); + return cg_->Binder()->Program()->SourceCode(); } static Format MatchFormat(const IRNode *node, const Formats &formats) @@ -155,8 +140,7 @@ static Format MatchFormat(const IRNode *node, const Formats &formats) } } - if (std::all_of(registers.begin(), registers.end(), - [limit](const VReg *reg) { return *reg >= IRNode::REG_START - limit; })) { + if (std::all_of(registers.begin(), registers.end(), [limit](const VReg *reg) { return reg->IsValid(limit); })) { return format; } } @@ -194,7 +178,7 @@ void FunctionEmitter::GenInstructionDebugInfo(const IRNode *ins, pandasm::Ins *p ASSERT(astNode != nullptr); if (astNode == FIRST_NODE_OF_FUNCTION) { - astNode = pg_->Debuginfo().firstStmt; + astNode = cg_->Debuginfo().FirstStatement(); if (astNode == nullptr) { return; } @@ -202,7 +186,7 @@ void FunctionEmitter::GenInstructionDebugInfo(const IRNode *ins, pandasm::Ins *p pandaIns->ins_debug.line_number = astNode->Range().start.line + 1; - if (pg_->IsDebug()) { + if (cg_->IsDebug()) { size_t insLen = GetIRNodeWholeLength(ins); if (insLen != 0) { pandaIns->ins_debug.bound_left = offset_; @@ -214,13 +198,13 @@ void FunctionEmitter::GenInstructionDebugInfo(const IRNode *ins, pandasm::Ins *p } } -void FunctionEmitter::GenFunctionInstructions(panda::pandasm::Function *func) +void FunctionEmitter::GenFunctionInstructions(pandasm::Function *func) { - func->ins.reserve(pg_->Insns().size()); + func->ins.reserve(cg_->Insns().size()); - uint32_t totalRegs = pg_->TotalRegsNum(); + uint32_t totalRegs = cg_->TotalRegsNum(); - for (const auto *ins : pg_->Insns()) { + for (const auto *ins : cg_->Insns()) { auto &pandaIns = func->ins.emplace_back(); ins->Transform(&pandaIns, programElement_, totalRegs); @@ -228,32 +212,32 @@ void FunctionEmitter::GenFunctionInstructions(panda::pandasm::Function *func) } } -void FunctionEmitter::GenFunctionICSize(panda::pandasm::Function *func) +void FunctionEmitter::GenFunctionAnnotations(pandasm::Function *func) { pandasm::AnnotationData funcAnnotationData("_ESAnnotation"); pandasm::AnnotationElement icSizeAnnotationElement( "icSize", - std::make_unique(pandasm::ScalarValue::Create(pg_->IcSize()))); + std::make_unique(pandasm::ScalarValue::Create(cg_->IcSize()))); funcAnnotationData.AddElement(std::move(icSizeAnnotationElement)); pandasm::AnnotationElement parameterLengthAnnotationElement( "parameterLength", std::make_unique( - pandasm::ScalarValue::Create(pg_->FormalParametersCount()))); + pandasm::ScalarValue::Create(cg_->FormalParametersCount()))); funcAnnotationData.AddElement(std::move(parameterLengthAnnotationElement)); pandasm::AnnotationElement funcNameAnnotationElement( "funcName", std::make_unique( - pandasm::ScalarValue::Create(pg_->FunctionName().Mutf8()))); + pandasm::ScalarValue::Create(cg_->FunctionName().Mutf8()))); funcAnnotationData.AddElement(std::move(funcNameAnnotationElement)); func->metadata->AddAnnotations({funcAnnotationData}); } -void FunctionEmitter::GenFunctionCatchTables(panda::pandasm::Function *func) +void FunctionEmitter::GenFunctionCatchTables(pandasm::Function *func) { - func->catch_blocks.reserve(pg_->CatchList().size()); + func->catch_blocks.reserve(cg_->CatchList().size()); - for (const auto *catchBlock : pg_->CatchList()) { + for (const auto *catchBlock : cg_->CatchList()) { const auto &labelSet = catchBlock->LabelSet(); auto &pandaCatchBlock = func->catch_blocks.emplace_back(); @@ -264,15 +248,15 @@ void FunctionEmitter::GenFunctionCatchTables(panda::pandasm::Function *func) } } -void FunctionEmitter::GenSourceFileDebugInfo(panda::pandasm::Function *func) +void FunctionEmitter::GenSourceFileDebugInfo(pandasm::Function *func) { - func->source_file = std::string {pg_->Binder()->Program()->SourceFile()}; + func->source_file = std::string {cg_->Binder()->Program()->SourceFile()}; - if (!pg_->IsDebug()) { + if (!cg_->IsDebug()) { return; } - if (pg_->RootNode()->IsProgram()) { + if (cg_->RootNode()->IsProgram()) { func->source_code = SourceCode().EscapeSymbol(); } } @@ -283,12 +267,13 @@ static void GenLocalVariableInfo(pandasm::debuginfo::LocalVariable &variableDebu variableDebug.name = var->Name().Mutf8(); variableDebug.signature = "any"; variableDebug.signature_type = "any"; - variableDebug.reg = static_cast(IRNode::MapRegister(var->AsLocalVariable()->Vreg(), totalRegsNum)); + variableDebug.reg = + static_cast(IRNode::MapRegister(var->AsLocalVariable()->Vreg().GetIndex(), totalRegsNum)); variableDebug.start = start; variableDebug.length = static_cast(varsLength); } -void FunctionEmitter::GenScopeVariableInfo(panda::pandasm::Function *func, const binder::Scope *scope) const +void FunctionEmitter::GenScopeVariableInfo(pandasm::Function *func, const binder::Scope *scope) const { const auto *startIns = scope->ScopeStart(); const auto *endIns = scope->ScopeEnd(); @@ -296,7 +281,7 @@ void FunctionEmitter::GenScopeVariableInfo(panda::pandasm::Function *func, const uint32_t start = 0; uint32_t count = 0; - for (const auto *it : pg_->Insns()) { + for (const auto *it : cg_->Insns()) { if (startIns == it) { start = count; } else if (endIns == it) { @@ -305,7 +290,7 @@ void FunctionEmitter::GenScopeVariableInfo(panda::pandasm::Function *func, const if (scope->IsFunctionScope()) { for (auto *param : scope->AsFunctionScope()->ParamScope()->Params()) { auto &variableDebug = func->local_variable_debug.emplace_back(); - GenLocalVariableInfo(variableDebug, param, start, varsLength, pg_->TotalRegsNum()); + GenLocalVariableInfo(variableDebug, param, start, varsLength, cg_->TotalRegsNum()); } } @@ -317,7 +302,7 @@ void FunctionEmitter::GenScopeVariableInfo(panda::pandasm::Function *func, const } auto &variableDebug = func->local_variable_debug.emplace_back(); - GenLocalVariableInfo(variableDebug, variable, start, varsLength, pg_->TotalRegsNum()); + GenLocalVariableInfo(variableDebug, variable, start, varsLength, cg_->TotalRegsNum()); } break; @@ -327,27 +312,23 @@ void FunctionEmitter::GenScopeVariableInfo(panda::pandasm::Function *func, const } } -void FunctionEmitter::GenVariablesDebugInfo(panda::pandasm::Function *func) +void FunctionEmitter::GenVariablesDebugInfo(pandasm::Function *func) { - if (!pg_->IsDebug()) { + if (!cg_->IsDebug()) { return; } - for (const auto *scope : pg_->Debuginfo().variableDebugInfo) { + for (const auto *scope : cg_->Debuginfo().VariableDebugInfo()) { GenScopeVariableInfo(func, scope); } } // Emitter -Emitter::Emitter(const CompilerContext *context) +Emitter::Emitter(const CompilerContext *context) : context_(context) { prog_ = new pandasm::Program(); - prog_->lang = pandasm::extensions::Language::ECMASCRIPT; - prog_->function_table.reserve(context->Binder()->Functions().size()); - GenESAnnoatationRecord(); - GenESModuleModeRecord(context->Binder()->Program()->Kind() == parser::ScriptKind::MODULE); } Emitter::~Emitter() @@ -355,30 +336,6 @@ Emitter::~Emitter() delete prog_; } -void Emitter::GenESAnnoatationRecord() -{ - auto annotationRecord = pandasm::Record("_ESAnnotation", LANG_EXT); - annotationRecord.metadata->SetAttribute("external"); - annotationRecord.metadata->SetAccessFlags(ACC_ANNOTATION); - prog_->record_table.emplace(annotationRecord.name, std::move(annotationRecord)); -} - -void Emitter::GenESModuleModeRecord(bool isModule) -{ - auto modeRecord = pandasm::Record("_ESModuleMode", LANG_EXT); - modeRecord.metadata->SetAccessFlags(ACC_PUBLIC); - - auto modeField = pandasm::Field(LANG_EXT); - modeField.name = "isModule"; - modeField.type = pandasm::Type("u8", 0); - modeField.metadata->SetValue( - pandasm::ScalarValue::Create(static_cast(isModule))); - - modeRecord.field_list.emplace_back(std::move(modeField)); - - prog_->record_table.emplace(modeRecord.name, std::move(modeRecord)); -} - static void UpdateLiteralBufferId(panda::pandasm::Ins *ins, uint32_t offset) { switch (ins->opcode) { @@ -404,20 +361,20 @@ static void UpdateLiteralBufferId(panda::pandasm::Ins *ins, uint32_t offset) void Emitter::AddProgramElement(ProgramElement *programElement) { - prog_->strings.merge(programElement->strings); + prog_->strings.merge(programElement->Strings()); uint32_t newLiteralBufferIndex = literalBufferIndex_; - for (const auto &buff : programElement->buffStorage) { + for (const auto &buff : programElement->BuffStorage()) { AddLiteralBuffer(buff, newLiteralBufferIndex++); } - for (auto *ins : programElement->literalBufferIns) { + for (auto *ins : programElement->LiteralBufferIns()) { UpdateLiteralBufferId(ins, literalBufferIndex_); } literalBufferIndex_ = newLiteralBufferIndex; - auto *function = programElement->func; + auto *function = programElement->Function(); prog_->function_table.emplace(function->name, std::move(*function)); } @@ -470,13 +427,17 @@ void Emitter::AddLiteralBuffer(const LiteralBuffer &literals, uint32_t index) prog_->literalarray_table.emplace(std::to_string(index), std::move(literalArrayInstance)); } -pandasm::Program *Emitter::Finalize(bool dumpDebugInfo) +pandasm::Program *Emitter::Finalize(bool dumpDebugInfo, std::string_view globalClass) { if (dumpDebugInfo) { debuginfo::DebugInfoDumper dumper(prog_); dumper.Dump(); } + if (context_->Binder()->IsGenStdLib()) { + auto it = prog_->record_table.find(std::string(globalClass)); + prog_->record_table.erase(it); + } auto *prog = prog_; prog_ = nullptr; return prog; diff --git a/compiler/core/emitter.h b/compiler/core/emitter.h index 64f61251b34175d7dbc1d40ea91f6eaecb2c322d..c27159e7f59a8fccb4ba50b24a8646647f43dd30 100644 --- a/compiler/core/emitter.h +++ b/compiler/core/emitter.h @@ -13,12 +13,10 @@ * limitations under the License. */ -#ifndef ES2PANDA_COMPILER_IR_EMITTER_H -#define ES2PANDA_COMPILER_IR_EMITTER_H +#ifndef ES2PANDA_COMPILER_CORE_EMITTER_H +#define ES2PANDA_COMPILER_CORE_EMITTER_H #include "plugins/ecmascript/es2panda/compiler/base/literals.h" -#include "plugins/ecmascript/es2panda/lexer/token/sourceLocation.h" -#include "macros.h" #include "plugins/ecmascript/es2panda/util/ustring.h" #include @@ -32,29 +30,29 @@ namespace panda::pandasm { struct Program; struct Function; struct Ins; +namespace debuginfo { +struct LocalVariable; +} // namespace debuginfo } // namespace panda::pandasm -namespace panda::es2panda::ir { -class Statement; -class Literal; -} // namespace panda::es2panda::ir - namespace panda::es2panda::binder { class Scope; +class LocalVariable; } // namespace panda::es2panda::binder namespace panda::es2panda::compiler { -class PandaGen; +class CodeGen; class DebugInfo; class Label; class IRNode; class CompilerContext; class ProgramElement; +class RegSpiller; class FunctionEmitter { public: - explicit FunctionEmitter(const PandaGen *pg, ProgramElement *programElement) - : pg_(pg), programElement_(programElement) + explicit FunctionEmitter(const CodeGen *cg, ProgramElement *programElement) + : cg_(cg), programElement_(programElement) { } @@ -64,44 +62,70 @@ public: void Generate(); -private: +protected: + virtual pandasm::Function *GenFunctionSignature() = 0; + virtual void GenFunctionAnnotations(pandasm::Function *func) = 0; + virtual void GenVariableSignature(pandasm::debuginfo::LocalVariable &variableDebug, + binder::LocalVariable *variable) const = 0; + void GenInstructionDebugInfo(const IRNode *ins, panda::pandasm::Ins *pandaIns); - void GenFunctionInstructions(panda::pandasm::Function *func); + void GenFunctionInstructions(pandasm::Function *func); + void GenScopeVariableInfo(pandasm::Function *func, const binder::Scope *scope) const; + void GenSourceFileDebugInfo(pandasm::Function *func); void GenFunctionCatchTables(panda::pandasm::Function *func); - void GenFunctionICSize(panda::pandasm::Function *func); - void GenScopeVariableInfo(panda::pandasm::Function *func, const binder::Scope *scope) const; - void GenSourceFileDebugInfo(panda::pandasm::Function *func); - void GenVariablesDebugInfo(panda::pandasm::Function *func); + void GenVariablesDebugInfo(pandasm::Function *func); util::StringView SourceCode() const; - const PandaGen *pg_; + const CodeGen *Cg() const + { + return cg_; + } + + ProgramElement *GetProgramElement() const + { + return programElement_; + } + +private: + const CodeGen *cg_; ProgramElement *programElement_; size_t offset_ {0}; }; class Emitter { public: - explicit Emitter(const CompilerContext *context); - ~Emitter(); + virtual ~Emitter(); NO_COPY_SEMANTIC(Emitter); NO_MOVE_SEMANTIC(Emitter); - void AddFunction(FunctionEmitter *func); void AddLiteralBuffer(const LiteralBuffer &literals, uint32_t index); void AddProgramElement(ProgramElement *programElement); - static void DumpAsm(const panda::pandasm::Program *prog); - panda::pandasm::Program *Finalize(bool dumpDebugInfo); + static void DumpAsm(const pandasm::Program *prog); + pandasm::Program *Finalize(bool dumpDebugInfo, std::string_view globalClass = ""); uint32_t &LiteralBufferIndex() { return literalBufferIndex_; } -private: - void GenESAnnoatationRecord(); - void GenESModuleModeRecord(bool isModule); + virtual void GenAnnotation() = 0; + +protected: + explicit Emitter(const CompilerContext *context); + + pandasm::Program *Program() const + { + return prog_; + } - panda::pandasm::Program *prog_; + const CompilerContext *Context() const + { + return context_; + } + +private: + pandasm::Program *prog_; + const CompilerContext *context_; uint32_t literalBufferIndex_ {}; }; } // namespace panda::es2panda::compiler diff --git a/compiler/core/envScope.cpp b/compiler/core/envScope.cpp index 6dd355c886da55910d4b074e346b0d3762ed2b95..2b9b849e89dbd3dce526e5ed068123e8569be0ee 100644 --- a/compiler/core/envScope.cpp +++ b/compiler/core/envScope.cpp @@ -15,18 +15,19 @@ #include "envScope.h" +#include "plugins/ecmascript/es2panda/compiler/core/codeGen.h" #include "plugins/ecmascript/es2panda/compiler/core/pandagen.h" #include "plugins/ecmascript/es2panda/ir/statement.h" namespace panda::es2panda::compiler { -ScopeContext::ScopeContext(PandaGen *pg, binder::Scope *newScope) : pg_(pg), prevScope_(pg_->scope_) +ScopeContext::ScopeContext(CodeGen *cg, binder::Scope *newScope) : cg_(cg), prevScope_(cg_->scope_) { - pg_->scope_ = newScope; + cg->scope_ = newScope; } ScopeContext::~ScopeContext() { - pg_->scope_ = prevScope_; + cg_->scope_ = prevScope_; } void EnvScope::Initialize(PandaGen *pg, VReg lexEnv) diff --git a/compiler/core/envScope.h b/compiler/core/envScope.h index ce0548c03754e03f581f9d13072ed098fd3de38a..7de54780bc67e4ccf4091015d3c0b30c0620bdc6 100644 --- a/compiler/core/envScope.h +++ b/compiler/core/envScope.h @@ -32,14 +32,14 @@ class PandaGen; class ScopeContext { public: - explicit ScopeContext(PandaGen *pg, binder::Scope *newScope); + explicit ScopeContext(CodeGen *cg, binder::Scope *newScope); ~ScopeContext(); NO_COPY_SEMANTIC(ScopeContext); NO_MOVE_SEMANTIC(ScopeContext); private: - PandaGen *pg_; + CodeGen *cg_; binder::Scope *prevScope_; }; diff --git a/compiler/core/function.cpp b/compiler/core/function.cpp index 17d8c1d27dd33b44e6b33a6f46a2a37ea283c7a9..8a31670d3ed918624cb5fbd8bc1cc04d2be02360 100644 --- a/compiler/core/function.cpp +++ b/compiler/core/function.cpp @@ -71,7 +71,7 @@ static void CompileFunctionParameterDeclaration(PandaGen *pg, const ir::ScriptFu uint32_t index = 0; for (const auto *param : func->Params()) { - LReference ref = LReference::CreateLRef(pg, param, true); + auto ref = JSLReference::Create(pg, param, true); [[maybe_unused]] binder::Variable *paramVar = ref.Variable(); @@ -82,7 +82,7 @@ static void CompileFunctionParameterDeclaration(PandaGen *pg, const ir::ScriptFu ASSERT(paramVar && paramVar->IsLocalVariable()); - VReg paramReg = binder::Binder::MANDATORY_PARAMS_NUMBER + IRNode::PARAM_START + index++; + VReg paramReg = VReg(binder::Binder::MANDATORY_PARAMS_NUMBER + VReg::PARAM_START + index++); ASSERT(paramVar->LexicalBound() || paramVar->AsLocalVariable()->Vreg() == paramReg); if (param->IsAssignmentPattern()) { @@ -175,11 +175,11 @@ void Function::CompileInstanceFields(PandaGen *pg, const ir::ScriptFunction *dec const auto *prop = element->AsClassProperty(); - if ((prop->Modifiers() & ir::ModifierFlags::STATIC) != 0) { + if ((prop->IsStatic())) { continue; } - if (prop->IsPrivate()) { + if (prop->IsPrivateElement()) { if (prop->Value() == nullptr) { pg->LoadConst(element, Constant::JS_UNDEFINED); } else { diff --git a/compiler/core/labelTarget.cpp b/compiler/core/labelTarget.cpp index f8b39bbc6845c46d19a16adcc08f9ad8cdf9f705..b5cb85866da6b6ad51afc02d32bc13a30dddf8c7 100644 --- a/compiler/core/labelTarget.cpp +++ b/compiler/core/labelTarget.cpp @@ -15,11 +15,11 @@ #include "labelTarget.h" -#include "plugins/ecmascript/es2panda/compiler/core/pandagen.h" +#include "plugins/ecmascript/es2panda/compiler/core/codeGen.h" namespace panda::es2panda::compiler { -LabelTarget::LabelTarget(PandaGen *pg) - : LabelPair(pg->AllocLabel(), pg->AllocLabel()), breakLabel_(BREAK_LABEL), continueLabel_(CONTINUE_LABEL) +LabelTarget::LabelTarget(CodeGen *cg) + : LabelPair(cg->AllocLabel(), cg->AllocLabel()), breakLabel_(BREAK_LABEL), continueLabel_(CONTINUE_LABEL) { } } // namespace panda::es2panda::compiler diff --git a/compiler/core/labelTarget.h b/compiler/core/labelTarget.h index e31db8f01ee812f64e2b4eb5817b7e976314fbe2..870d37b667a228ee910e4a8acc202a64ed1e9d9c 100644 --- a/compiler/core/labelTarget.h +++ b/compiler/core/labelTarget.h @@ -28,7 +28,7 @@ class Identifier; namespace panda::es2panda::compiler { class LabelTarget; -class PandaGen; +class CodeGen; enum class ControlFlowChange { CONTINUE, @@ -37,7 +37,7 @@ enum class ControlFlowChange { class LabelTarget : public LabelPair { public: - explicit LabelTarget(PandaGen *pg); + explicit LabelTarget(CodeGen *cg); explicit LabelTarget(const util::StringView &label) : LabelTarget(nullptr, label) {} explicit LabelTarget(Label *target, const util::StringView &label) : LabelPair(target, nullptr), breakLabel_(label), continueLabel_(label) diff --git a/compiler/core/pandagen.cpp b/compiler/core/pandagen.cpp index 6ff9b530db893013f684c7effc59f562a8f1452d..9bacc03ce4137f4c8cc48405733ba36da112932d 100644 --- a/compiler/core/pandagen.cpp +++ b/compiler/core/pandagen.cpp @@ -39,132 +39,154 @@ #include "plugins/ecmascript/es2panda/ir/expressions/literals/stringLiteral.h" namespace panda::es2panda::compiler { -// PandaGen -Label *PandaGen::AllocLabel() +void PandaGen::FunctionInit(CatchTable *catchTable) { - std::string id = std::string {Label::PREFIX} + std::to_string(labelId_++); - return sa_.AllocLabel(std::move(id)); -} + if (RootNode()->IsProgram()) { + builder_ = Allocator()->New(this, catchTable); + return; + } -bool PandaGen::IsDebug() const -{ - return context_->IsDebug(); -} + const ir::ScriptFunction *func = RootNode()->AsScriptFunction(); -uint32_t PandaGen::ParamCount() const -{ - if (rootNode_->IsProgram()) { - return IsDirectEval() ? 1 : 0; + if (func->IsAsyncFunc()) { + if (func->IsGenerator()) { + builder_ = Allocator()->New(this, catchTable); + return; + } + + builder_ = Allocator()->New(this, catchTable); + return; } - return rootNode_->AsScriptFunction()->Params().size(); + if (func->IsGenerator()) { + builder_ = Allocator()->New(this, catchTable); + return; + } + + builder_ = Allocator()->New(this, catchTable); } -uint32_t PandaGen::FormalParametersCount() const +bool PandaGen::FunctionHasFinalizer() const { - if (rootNode_->IsProgram()) { - return 0; + if (RootNode()->IsProgram()) { + return false; } - ASSERT(rootNode_->IsScriptFunction()); + const ir::ScriptFunction *func = RootNode()->AsScriptFunction(); - return rootNode_->AsScriptFunction()->FormalParamsLength(); + return func->IsAsyncFunc() || func->IsGenerator(); } -uint32_t PandaGen::InternalParamCount() const +void PandaGen::FunctionEnter() { - static const uint32_t HIDDEN_PARAMS = 3; - return ParamCount() + HIDDEN_PARAMS; + builder_->Prepare(RootNode()->AsScriptFunction()); } -const util::StringView &PandaGen::InternalName() const +void PandaGen::FunctionExit() { - return topScope_->InternalName(); + builder_->CleanUp(RootNode()->AsScriptFunction()); } -const util::StringView &PandaGen::FunctionName() const +void PandaGen::StoreAccumulator(const ir::AstNode *node, VReg vreg) { - return topScope_->Name(); + Ra().Emit(node, vreg); } -binder::Binder *PandaGen::Binder() const +void PandaGen::LoadAccumulator(const ir::AstNode *node, VReg reg) { - return context_->Binder(); + Ra().Emit(node, reg); } -void PandaGen::FunctionInit(CatchTable *catchTable) +void PandaGen::MoveVreg(const ir::AstNode *node, VReg vd, VReg vs) { - if (rootNode_->IsProgram()) { - builder_ = allocator_->New(this, catchTable); - return; - } - - const ir::ScriptFunction *func = rootNode_->AsScriptFunction(); - - if (func->IsAsync()) { - if (func->IsGenerator()) { - builder_ = allocator_->New(this, catchTable); - return; - } - - builder_ = allocator_->New(this, catchTable); - return; - } - - if (func->IsGenerator()) { - builder_ = allocator_->New(this, catchTable); - return; - } - - builder_ = allocator_->New(this, catchTable); + Ra().Emit(node, vd, vs); } -bool PandaGen::FunctionHasFinalizer() const +void PandaGen::LoadAccumulatorFloat(const ir::AstNode *node, double num) { - if (rootNode_->IsProgram()) { - return false; - } - - const ir::ScriptFunction *func = rootNode_->AsScriptFunction(); + Sa().Emit(node, num); +} - return func->IsAsync() || func->IsGenerator(); +void PandaGen::LoadAccumulatorInt(const ir::AstNode *node, int32_t num) +{ + Sa().Emit(node, num); } -void PandaGen::FunctionEnter() +void PandaGen::LoadAccumulatorInt(const ir::AstNode *node, size_t num) { - builder_->Prepare(rootNode_->AsScriptFunction()); + Sa().Emit(node, static_cast(num)); } -void PandaGen::FunctionExit() +void PandaGen::StoreConst(const ir::AstNode *node, VReg reg, Constant id) { - builder_->CleanUp(rootNode_->AsScriptFunction()); + LoadConst(node, id); + StoreAccumulator(node, reg); } -int32_t PandaGen::AddLiteralBuffer(LiteralBuffer &&buf) +void PandaGen::LoadConst(const ir::AstNode *node, Constant id) { - programElement_->buffStorage.emplace_back(std::move(buf)); - return literalBufferIdx_++; + switch (id) { + case Constant::JS_HOLE: { + Sa().Emit(node); + break; + } + case Constant::JS_NAN: { + Sa().Emit(node); + break; + } + case Constant::JS_INFINITY: { + Sa().Emit(node); + break; + } + case Constant::JS_GLOBAL: { + Sa().Emit(node); + break; + } + case Constant::JS_UNDEFINED: { + Sa().Emit(node); + break; + } + case Constant::JS_SYMBOL: { + Sa().Emit(node); + break; + } + case Constant::JS_NULL: { + Sa().Emit(node); + break; + } + case Constant::JS_TRUE: { + Sa().Emit(node); + break; + } + case Constant::JS_FALSE: { + Sa().Emit(node); + break; + } + default: { + UNREACHABLE(); + } + } } void PandaGen::GetFunctionObject(const ir::AstNode *node) { - LoadAccFromLexEnv(node, scope_->Find(binder::Binder::MANDATORY_PARAM_FUNC)); + LoadAccFromLexEnv(node, Scope()->Find(binder::Binder::MANDATORY_PARAM_FUNC)); } void PandaGen::GetNewTarget(const ir::AstNode *node) { - LoadAccFromLexEnv(node, scope_->Find(binder::Binder::MANDATORY_PARAM_NEW_TARGET)); + LoadAccFromLexEnv(node, Scope()->Find(binder::Binder::MANDATORY_PARAM_NEW_TARGET)); } void PandaGen::GetThis(const ir::AstNode *node) { - LoadAccFromLexEnv(node, scope_->Find(binder::Binder::MANDATORY_PARAM_THIS)); + LoadAccFromLexEnv(node, Scope()->Find(binder::Binder::MANDATORY_PARAM_THIS)); } void PandaGen::SetThis(const ir::AstNode *node) { - StoreAccToLexEnv(node, scope_->Find(binder::Binder::MANDATORY_PARAM_THIS), true); + StoreAccToLexEnv(node, Scope()->Find(binder::Binder::MANDATORY_PARAM_THIS), true); } void PandaGen::LoadVar(const ir::Identifier *node, const binder::ScopeFindResult &result) @@ -217,20 +239,13 @@ void PandaGen::StoreVar(const ir::AstNode *node, const binder::ScopeFindResult & StoreAccToLexEnv(node, result, isDeclaration); } -void PandaGen::StoreAccumulator(const ir::AstNode *node, VReg vreg) -{ - ra_.Emit(node, vreg); -} - void PandaGen::LoadAccFromArgs(const ir::AstNode *node) { - const auto *varScope = scope_->AsVariableScope(); - - if (!varScope->HasFlag(binder::VariableScopeFlags::USE_ARGS)) { + if (!Scope()->HasFlag(binder::ScopeFlags::USE_ARGS)) { return; } - binder::ScopeFindResult res = scope_->Find(binder::Binder::FUNCTION_ARGUMENTS); + binder::ScopeFindResult res = Scope()->Find(binder::Binder::FUNCTION_ARGUMENTS); ASSERT(res.scope); GetUnmappedArgs(node); @@ -287,77 +302,72 @@ void PandaGen::StoreOwnProperty(const ir::AstNode *node, VReg obj, const Operand void PandaGen::TryLoadGlobalByName(const ir::AstNode *node, const util::StringView &name) { - sa_.Emit(node, name); + Sa().Emit(node, name); } void PandaGen::TryStoreGlobalByName(const ir::AstNode *node, const util::StringView &name) { - sa_.Emit(node, name); + Sa().Emit(node, name); } void PandaGen::LoadObjByName(const ir::AstNode *node, const util::StringView &prop) { - ra_.Emit(node, prop); + Ra().Emit(node, prop); } void PandaGen::StoreObjByName(const ir::AstNode *node, VReg obj, const util::StringView &prop) { - ra_.Emit(node, prop, obj); + Ra().Emit(node, prop, obj); } void PandaGen::LoadObjByIndex(const ir::AstNode *node, int64_t index) { - ra_.Emit(node, index); + Ra().Emit(node, index); } void PandaGen::LoadObjByValue(const ir::AstNode *node, VReg obj) { - ra_.Emit(node, obj); + Ra().Emit(node, obj); } void PandaGen::StoreObjByValue(const ir::AstNode *node, VReg obj, VReg prop) { - ra_.Emit(node, obj, prop); + Ra().Emit(node, obj, prop); } void PandaGen::StoreObjByIndex(const ir::AstNode *node, VReg obj, int64_t index) { - ra_.Emit(node, index, obj); + Ra().Emit(node, index, obj); } void PandaGen::StOwnByName(const ir::AstNode *node, VReg obj, const util::StringView &prop) { - ra_.Emit(node, prop, obj); + Ra().Emit(node, prop, obj); } void PandaGen::StOwnByValue(const ir::AstNode *node, VReg obj, VReg prop) { - ra_.Emit(node, obj, prop); + Ra().Emit(node, obj, prop); } void PandaGen::StOwnByIndex(const ir::AstNode *node, VReg obj, int64_t index) { - ra_.Emit(node, index, obj); + Ra().Emit(node, index, obj); } void PandaGen::DeleteObjProperty(const ir::AstNode *node, VReg obj, VReg prop) { - ra_.Emit(node, obj, prop); -} - -void PandaGen::LoadAccumulator(const ir::AstNode *node, VReg reg) -{ - ra_.Emit(node, reg); + Ra().Emit(node, obj, prop); } void PandaGen::LoadGlobalVar(const ir::AstNode *node, const util::StringView &name) { - sa_.Emit(node, name); + Sa().Emit(node, name); } void PandaGen::StoreGlobalVar(const ir::AstNode *node, const util::StringView &name) { - sa_.Emit(node, name); + Sa().Emit(node, name); } VReg PandaGen::LexEnv() const @@ -375,196 +385,44 @@ void PandaGen::StoreAccToLexEnv(const ir::AstNode *node, const binder::ScopeFind VirtualStoreVar::Expand(this, node, result, isDeclaration); } -void PandaGen::LoadAccumulatorString(const ir::AstNode *node, const util::StringView &str) -{ - sa_.Emit(node, str); -} - void PandaGen::LoadAccumulatorBigInt(const ir::AstNode *node, const util::StringView &bigInt) { - sa_.Emit(node, bigInt); -} - -void PandaGen::LoadAccumulatorFloat(const ir::AstNode *node, double num) -{ - sa_.Emit(node, num); -} - -void PandaGen::LoadAccumulatorInt(const ir::AstNode *node, int32_t num) -{ - sa_.Emit(node, num); -} - -void PandaGen::LoadAccumulatorInt(const ir::AstNode *node, size_t num) -{ - sa_.Emit(node, static_cast(num)); -} - -void PandaGen::StoreConst(const ir::AstNode *node, VReg reg, Constant id) -{ - LoadConst(node, id); - StoreAccumulator(node, reg); -} - -void PandaGen::LoadConst(const ir::AstNode *node, Constant id) -{ - switch (id) { - case Constant::JS_HOLE: { - sa_.Emit(node); - break; - } - case Constant::JS_NAN: { - sa_.Emit(node); - break; - } - case Constant::JS_INFINITY: { - sa_.Emit(node); - break; - } - case Constant::JS_GLOBAL: { - sa_.Emit(node); - break; - } - case Constant::JS_UNDEFINED: { - sa_.Emit(node); - break; - } - case Constant::JS_SYMBOL: { - sa_.Emit(node); - break; - } - case Constant::JS_NULL: { - sa_.Emit(node); - break; - } - case Constant::JS_TRUE: { - sa_.Emit(node); - break; - } - case Constant::JS_FALSE: { - sa_.Emit(node); - break; - } - default: { - UNREACHABLE(); - } - } -} - -void PandaGen::MoveVreg(const ir::AstNode *node, VReg vd, VReg vs) -{ - ra_.Emit(node, vd, vs); -} - -void PandaGen::SetLabel([[maybe_unused]] const ir::AstNode *node, Label *label) -{ - sa_.AddLabel(label); -} - -void PandaGen::Branch(const ir::AstNode *node, Label *label) -{ - sa_.Emit(node, label); -} - -bool PandaGen::CheckControlFlowChange() -{ - const auto *iter = dynamicContext_; - - while (iter != nullptr) { - if (iter->HasFinalizer()) { - return true; - } - - iter = iter->Prev(); - } - - return false; -} - -Label *PandaGen::ControlFlowChangeBreak(const ir::Identifier *label) -{ - auto *iter = dynamicContext_; - - util::StringView labelName = label != nullptr ? label->Name() : LabelTarget::BREAK_LABEL; - Label *breakTarget = nullptr; - - while (iter != nullptr) { - iter->AbortContext(ControlFlowChange::BREAK, labelName); - - const auto &labelTargetName = iter->Target().BreakLabel(); - - if (iter->Target().BreakTarget() != nullptr) { - breakTarget = iter->Target().BreakTarget(); - } - - if (labelTargetName == labelName) { - break; - } - - iter = iter->Prev(); - } - - return breakTarget; -} - -Label *PandaGen::ControlFlowChangeContinue(const ir::Identifier *label) -{ - auto *iter = dynamicContext_; - util::StringView labelName = label != nullptr ? label->Name() : LabelTarget::CONTINUE_LABEL; - Label *continueTarget = nullptr; - - while (iter != nullptr) { - iter->AbortContext(ControlFlowChange::CONTINUE, labelName); - - const auto &labelTargetName = iter->Target().ContinueLabel(); - - if (iter->Target().ContinueTarget() != nullptr) { - continueTarget = iter->Target().ContinueTarget(); - } - - if (labelTargetName == labelName) { - break; - } - - iter = iter->Prev(); - } - - return continueTarget; + Sa().Emit(node, bigInt); } void PandaGen::Condition(const ir::AstNode *node, lexer::TokenType op, VReg lhs, Label *ifFalse) { switch (op) { case lexer::TokenType::PUNCTUATOR_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_LESS_THAN: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_GREATER_THAN: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } default: { @@ -579,27 +437,27 @@ void PandaGen::Unary(const ir::AstNode *node, lexer::TokenType op, VReg operand) { switch (op) { case lexer::TokenType::PUNCTUATOR_PLUS: { - ra_.Emit(node, operand); + Ra().Emit(node, operand); break; } case lexer::TokenType::PUNCTUATOR_MINUS: { - ra_.Emit(node, operand); + Ra().Emit(node, operand); break; } case lexer::TokenType::PUNCTUATOR_TILDE: { - ra_.Emit(node, operand); + Ra().Emit(node, operand); break; } case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: { - sa_.Emit(node); + Sa().Emit(node); break; } case lexer::TokenType::PUNCTUATOR_PLUS_PLUS: { - ra_.Emit(node, operand); + Ra().Emit(node, operand); break; } case lexer::TokenType::PUNCTUATOR_MINUS_MINUS: { - ra_.Emit(node, operand); + Ra().Emit(node, operand); break; } case lexer::TokenType::KEYW_VOID: @@ -617,103 +475,103 @@ void PandaGen::Binary(const ir::AstNode *node, lexer::TokenType op, VReg lhs) { switch (op) { case lexer::TokenType::PUNCTUATOR_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_LESS_THAN: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_GREATER_THAN: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_PLUS: case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_MINUS: case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_MULTIPLY: case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_DIVIDE: case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_MOD: case lexer::TokenType::PUNCTUATOR_MOD_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_EXPONENTIATION_EQUAL: case lexer::TokenType::PUNCTUATOR_EXPONENTIATION: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT: case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT: case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT: case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_BITWISE_AND: case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_BITWISE_OR: case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::KEYW_IN: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::KEYW_INSTANCEOF: { - ra_.Emit(node, lhs); + Ra().Emit(node, lhs); break; } case lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING: @@ -729,56 +587,56 @@ void PandaGen::Binary(const ir::AstNode *node, lexer::TokenType op, VReg lhs) void PandaGen::BranchIfUndefined(const ir::AstNode *node, Label *target) { - sa_.Emit(node); + Sa().Emit(node); BranchIfTrue(node, target); } void PandaGen::BranchIfNotUndefined(const ir::AstNode *node, Label *target) { - sa_.Emit(node); + Sa().Emit(node); BranchIfFalse(node, target); } void PandaGen::BranchIfTrue(const ir::AstNode *node, Label *target) { - sa_.Emit(node, target); + Sa().Emit(node, target); } void PandaGen::BranchIfNotTrue(const ir::AstNode *node, Label *target) { - sa_.Emit(node); + Sa().Emit(node); BranchIfFalse(node, target); } void PandaGen::BranchIfFalse(const ir::AstNode *node, Label *target) { - sa_.Emit(node, target); + Sa().Emit(node, target); } void PandaGen::BranchIfCoercible(const ir::AstNode *node, Label *target) { - sa_.Emit(node); + Sa().Emit(node); BranchIfTrue(node, target); } void PandaGen::EmitThrow(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::EmitRethrow(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::EmitReturn(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::EmitReturnUndefined(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::ImplicitReturn(const ir::AstNode *node) @@ -825,25 +683,25 @@ void PandaGen::EmitAwait(const ir::AstNode *node) void PandaGen::Call0This(const ir::AstNode *node, VReg callee, VReg thisReg) { LoadAccumulator(node, thisReg); - ra_.Emit(node, callee); + Ra().Emit(node, callee); } void PandaGen::Call1This(const ir::AstNode *node, VReg callee, VReg thisReg, VReg arg0) { LoadAccumulator(node, arg0); - ra_.Emit(node, callee, thisReg); + Ra().Emit(node, callee, thisReg); } void PandaGen::Call(const ir::AstNode *node, VReg callee, VReg thisReg, const ArenaVector &arguments) { - bool hasThis = thisReg != INVALID_VREG; + bool hasThis = !thisReg.IsInvalid(); switch (arguments.size()) { case 0: { // 0 args if (hasThis) { Call0This(node, callee, thisReg); } else { - sa_.Emit(node); + Sa().Emit(node); } return; } @@ -852,9 +710,9 @@ void PandaGen::Call(const ir::AstNode *node, VReg callee, VReg thisReg, const Ar arg0->Compile(this); if (hasThis) { - ra_.Emit(node, callee, thisReg); + Ra().Emit(node, callee, thisReg); } else { - ra_.Emit(node, callee); + Ra().Emit(node, callee); } return; } @@ -868,9 +726,9 @@ void PandaGen::Call(const ir::AstNode *node, VReg callee, VReg thisReg, const Ar arg1->Compile(this); if (hasThis) { - ra_.Emit(node, callee, thisReg, arg0Reg); + Ra().Emit(node, callee, thisReg, arg0Reg); } else { - ra_.Emit(node, callee, arg0Reg); + Ra().Emit(node, callee, arg0Reg); } return; } @@ -889,9 +747,9 @@ void PandaGen::Call(const ir::AstNode *node, VReg callee, VReg thisReg, const Ar arg2->Compile(this); if (hasThis) { - ra_.Emit(node, callee, thisReg, arg0Reg, arg1Reg); + Ra().Emit(node, callee, thisReg, arg0Reg, arg1Reg); } else { - ra_.Emit(node, callee, arg0Reg, arg1Reg); + Ra().Emit(node, callee, arg0Reg, arg1Reg); } return; } @@ -909,26 +767,26 @@ void PandaGen::Call(const ir::AstNode *node, VReg callee, VReg thisReg, const Ar if (hasThis) { size_t argCount = arguments.size() + 1; auto constexpr extraArgs = 2; - rra_.Emit(node, callee, argCount + extraArgs, static_cast(argCount), callee); + Rra().Emit(node, callee, argCount + extraArgs, static_cast(argCount), callee); } else { size_t argCount = arguments.size(); - rra_.Emit(node, callee, argCount + 1, static_cast(argCount), callee); + Rra().Emit(node, callee, argCount + 1, static_cast(argCount), callee); } } void PandaGen::CallTagged(const ir::AstNode *node, VReg callee, VReg thisReg, const ArenaVector &arguments) { - bool hasThis = thisReg != INVALID_VREG; + bool hasThis = !thisReg.IsInvalid(); StoreAccumulator(node, callee); Literals::GetTemplateObject(this, node->AsTaggedTemplateExpression()); if (arguments.empty()) { if (hasThis) { - ra_.Emit(node, callee, thisReg); + Ra().Emit(node, callee, thisReg); } else { - sa_.Emit(node, callee); + Sa().Emit(node, callee); } return; } @@ -942,9 +800,9 @@ void PandaGen::CallTagged(const ir::AstNode *node, VReg callee, VReg thisReg, arg->Compile(this); if (hasThis) { - ra_.Emit(node, callee, thisReg, arg0Reg); + Ra().Emit(node, callee, thisReg, arg0Reg); } else { - ra_.Emit(node, callee, arg0Reg); + Ra().Emit(node, callee, arg0Reg); } return; } @@ -958,9 +816,9 @@ void PandaGen::CallTagged(const ir::AstNode *node, VReg callee, VReg thisReg, arg2->Compile(this); if (hasThis) { - ra_.Emit(node, callee, thisReg, arg0Reg, arg1Reg); + Ra().Emit(node, callee, thisReg, arg0Reg, arg1Reg); } else { - ra_.Emit(node, callee, arg0Reg, arg1Reg); + Ra().Emit(node, callee, arg0Reg, arg1Reg); } return; } @@ -978,237 +836,237 @@ void PandaGen::CallTagged(const ir::AstNode *node, VReg callee, VReg thisReg, if (hasThis) { auto constexpr extraArgs = 2; size_t argCount = arguments.size() + extraArgs; - rra_.Emit(node, callee, argCount + extraArgs, static_cast(argCount), callee); + Rra().Emit(node, callee, argCount + extraArgs, static_cast(argCount), callee); } else { size_t argCount = arguments.size() + 1; - rra_.Emit(node, callee, argCount + 1, static_cast(argCount), callee); + Rra().Emit(node, callee, argCount + 1, static_cast(argCount), callee); } } void PandaGen::SuperCall(const ir::AstNode *node, VReg startReg, size_t argCount) { - rra_.Emit(node, startReg, argCount, static_cast(argCount), startReg); + Rra().Emit(node, startReg, argCount, static_cast(argCount), startReg); } void PandaGen::SuperCallSpread(const ir::AstNode *node, VReg vs) { - ra_.Emit(node, vs); + Ra().Emit(node, vs); } void PandaGen::NewObject(const ir::AstNode *node, VReg startReg, size_t argCount) { - rra_.Emit(node, startReg, argCount, static_cast(argCount), startReg); + Rra().Emit(node, startReg, argCount, static_cast(argCount), startReg); } void PandaGen::LoadHomeObject(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::DefineMethod(const ir::AstNode *node, const util::StringView &name) { - ra_.Emit(node, name, LexEnv()); + Ra().Emit(node, name, LexEnv()); } void PandaGen::DefineFunction(const ir::AstNode *node, const ir::ScriptFunction *realNode, const util::StringView &name) { - if (realNode->IsAsync()) { + if (realNode->IsAsyncFunc()) { if (realNode->IsGenerator()) { - ra_.Emit(node, name, LexEnv()); + Ra().Emit(node, name, LexEnv()); } else { - ra_.Emit(node, name, LexEnv()); + Ra().Emit(node, name, LexEnv()); } } else if (realNode->IsGenerator()) { - ra_.Emit(node, name, LexEnv()); + Ra().Emit(node, name, LexEnv()); } else if (realNode->IsArrow()) { LoadHomeObject(node); - ra_.Emit(node, name, LexEnv()); + Ra().Emit(node, name, LexEnv()); } else if (realNode->IsMethod()) { DefineMethod(node, name); } else { - ra_.Emit(node, name, LexEnv()); + Ra().Emit(node, name, LexEnv()); } } void PandaGen::TypeOf(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::CallSpread(const ir::AstNode *node, VReg func, VReg thisReg, VReg args) { - ra_.Emit(node, func, thisReg, args); + Ra().Emit(node, func, thisReg, args); } void PandaGen::NewObjSpread(const ir::AstNode *node, VReg obj, VReg target) { - ra_.Emit(node, obj, target); + Ra().Emit(node, obj, target); } void PandaGen::GetUnmappedArgs(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::Negate(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::ToBoolean(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::ToNumber(const ir::AstNode *node, VReg arg) { - ra_.Emit(node, arg); + Ra().Emit(node, arg); } void PandaGen::GetMethod(const ir::AstNode *node, VReg obj, const util::StringView &name) { - ra_.Emit(node, name, obj); + Ra().Emit(node, name, obj); } void PandaGen::CreateGeneratorObj(const ir::AstNode *node, VReg funcObj) { - ra_.Emit(node, funcObj); + Ra().Emit(node, funcObj); } void PandaGen::CreateAsyncGeneratorObj(const ir::AstNode *node, VReg funcObj) { - ra_.Emit(node, funcObj); + Ra().Emit(node, funcObj); } void PandaGen::CreateIterResultObject(const ir::AstNode *node, bool done) { - ra_.Emit(node, static_cast(done)); + Ra().Emit(node, static_cast(done)); } void PandaGen::SuspendGenerator(const ir::AstNode *node, VReg genObj) { - ra_.Emit(node, genObj); + Ra().Emit(node, genObj); } void PandaGen::SuspendAsyncGenerator(const ir::AstNode *node, VReg asyncGenObj) { - ra_.Emit(node, asyncGenObj); + Ra().Emit(node, asyncGenObj); } void PandaGen::GeneratorYield(const ir::AstNode *node, VReg genObj) { - ra_.Emit(node, genObj, static_cast(GeneratorState::SUSPENDED_YIELD)); + Ra().Emit(node, genObj, static_cast(GeneratorState::SUSPENDED_YIELD)); } void PandaGen::GeneratorComplete(const ir::AstNode *node, VReg genObj) { - ra_.Emit(node, genObj, static_cast(GeneratorState::COMPLETED)); + Ra().Emit(node, genObj, static_cast(GeneratorState::COMPLETED)); } void PandaGen::ResumeGenerator(const ir::AstNode *node, VReg genObj) { - ra_.Emit(node, genObj); + Ra().Emit(node, genObj); } void PandaGen::GetResumeMode(const ir::AstNode *node, VReg genObj) { - ra_.Emit(node, genObj); + Ra().Emit(node, genObj); } void PandaGen::AsyncFunctionEnter(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::AsyncFunctionAwait(const ir::AstNode *node, VReg asyncFuncObj) { - ra_.Emit(node, asyncFuncObj); + Ra().Emit(node, asyncFuncObj); } void PandaGen::AsyncFunctionResolve(const ir::AstNode *node, VReg asyncFuncObj) { - ra_.Emit(node, asyncFuncObj); + Ra().Emit(node, asyncFuncObj); } void PandaGen::AsyncFunctionReject(const ir::AstNode *node, VReg asyncFuncObj) { - ra_.Emit(node, asyncFuncObj); + Ra().Emit(node, asyncFuncObj); } void PandaGen::AsyncGeneratorResolve(const ir::AstNode *node, VReg asyncGenObj) { - ra_.Emit(node, asyncGenObj); + Ra().Emit(node, asyncGenObj); } void PandaGen::AsyncGeneratorReject(const ir::AstNode *node, VReg asyncGenObj) { - ra_.Emit(node, asyncGenObj); + Ra().Emit(node, asyncGenObj); } void PandaGen::GetTemplateObject(const ir::AstNode *node, VReg value) { - ra_.Emit(node, value); + Ra().Emit(node, value); } void PandaGen::CopyRestArgs(const ir::AstNode *node, uint32_t index) { - sa_.Emit(node, index); + Sa().Emit(node, index); } void PandaGen::GetPropIterator(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::GetNextPropName(const ir::AstNode *node, VReg iter) { - ra_.Emit(node, iter); + Ra().Emit(node, iter); } void PandaGen::CreateEmptyObject(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::CreateObjectWithBuffer(const ir::AstNode *node, uint32_t idx) { ASSERT(util::Helpers::IsInteger(idx)); - sa_.Emit(node, util::Helpers::ToStringView(allocator_, idx)); + Sa().Emit(node, util::Helpers::ToStringView(Allocator(), idx)); } void PandaGen::CreateObjectHavingMethod(const ir::AstNode *node, uint32_t idx) { ASSERT(util::Helpers::IsInteger(idx)); LoadAccumulator(node, LexEnv()); - sa_.Emit(node, util::Helpers::ToStringView(allocator_, idx)); + Sa().Emit(node, util::Helpers::ToStringView(Allocator(), idx)); } void PandaGen::SetObjectWithProto(const ir::AstNode *node, VReg proto, VReg obj) { - ra_.Emit(node, proto, obj); + Ra().Emit(node, proto, obj); } void PandaGen::CopyDataProperties(const ir::AstNode *node, VReg dst, VReg src) { - ra_.Emit(node, dst, src); + Ra().Emit(node, dst, src); } void PandaGen::DefineGetterSetterByValue(const ir::AstNode *node, VReg obj, VReg name, VReg getter, VReg setter, bool setName) { LoadConst(node, setName ? Constant::JS_TRUE : Constant::JS_FALSE); - ra_.Emit(node, obj, name, getter, setter); + Ra().Emit(node, obj, name, getter, setter); } void PandaGen::CreateEmptyArray(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::CreateArrayWithBuffer(const ir::AstNode *node, uint32_t idx) { ASSERT(util::Helpers::IsInteger(idx)); - sa_.Emit(node, util::Helpers::ToStringView(allocator_, idx)); + Sa().Emit(node, util::Helpers::ToStringView(Allocator(), idx)); } void PandaGen::CreateArray(const ir::AstNode *node, const ArenaVector &elements, VReg obj) @@ -1312,144 +1170,144 @@ void PandaGen::CreateArray(const ir::AstNode *node, const ArenaVector(node, array, index); + Ra().Emit(node, array, index); } void PandaGen::CreateRegExpWithLiteral(const ir::AstNode *node, const util::StringView &pattern, uint8_t flags) { - sa_.Emit(node, pattern, flags); + Sa().Emit(node, pattern, flags); } void PandaGen::ThrowIfNotObject(const ir::AstNode *node) { - ra_.Emit(node); + Ra().Emit(node); } void PandaGen::ThrowThrowNotExist(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::GetIterator(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::GetAsyncIterator(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::CreateObjectWithExcludedKeys(const ir::AstNode *node, VReg obj, VReg argStart, size_t argCount) { - ASSERT(argStart == obj - 1); + ASSERT(argStart.GetIndex() == obj.GetIndex() - 1); if (argCount == 0) { // Do not emit undefined register argStart = obj; } - rra_.Emit(node, argStart, argCount, static_cast(argCount), obj, - argStart); + Rra().Emit(node, argStart, argCount, static_cast(argCount), obj, + argStart); } void PandaGen::ThrowObjectNonCoercible(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::CloseIterator(const ir::AstNode *node, VReg iter) { - ra_.Emit(node, iter); + Ra().Emit(node, iter); } void PandaGen::ImportModule(const ir::AstNode *node, const util::StringView &name) { - sa_.Emit(node, name); + Sa().Emit(node, name); } void PandaGen::SetClassComputedFields(const ir::AstNode *node, VReg classReg, VReg computedInstanceFieldArray) { - ra_.Emit(node, classReg, computedInstanceFieldArray); + Ra().Emit(node, classReg, computedInstanceFieldArray); } void PandaGen::DefineClassWithBuffer(const ir::AstNode *node, const util::StringView &ctorId, int32_t litIdx, VReg lexenv, VReg base) { - ra_.Emit(node, ctorId, litIdx, lexenv, base); + Ra().Emit(node, ctorId, litIdx, lexenv, base); } void PandaGen::LoadClassComputedInstanceFields(const ir::AstNode *node, VReg ctor) { - sa_.Emit(node, ctor); + Sa().Emit(node, ctor); } void PandaGen::DefineClassPrivateFields(const ir::AstNode *node, int32_t privateBufIdx) { - sa_.Emit(node, util::Helpers::ToStringView(allocator_, privateBufIdx), LexEnv()); + Sa().Emit(node, util::Helpers::ToStringView(Allocator(), privateBufIdx), LexEnv()); } void PandaGen::ClassFieldAdd(const ir::AstNode *node, VReg obj, VReg prop) { - ra_.Emit(node, obj, prop); + Ra().Emit(node, obj, prop); } void PandaGen::ClassPrivateFieldAdd(const ir::AstNode *node, VReg ctor, VReg obj, const util::StringView &prop) { - ra_.Emit(node, prop, ctor, obj); + Ra().Emit(node, prop, ctor, obj); } void PandaGen::ClassPrivateMethodOrAccessorAdd(const ir::AstNode *node, VReg ctor, VReg obj) { - ra_.Emit(node, ctor, obj); + Ra().Emit(node, ctor, obj); } void PandaGen::ClassPrivateFieldGet(const ir::AstNode *node, VReg ctor, VReg obj, const util::StringView &prop) { - ra_.Emit(node, prop, ctor, obj); + Ra().Emit(node, prop, ctor, obj); } void PandaGen::ClassPrivateFieldSet(const ir::AstNode *node, VReg ctor, VReg obj, const util::StringView &prop) { - ra_.Emit(node, prop, ctor, obj); + Ra().Emit(node, prop, ctor, obj); } void PandaGen::ClassPrivateFieldIn(const ir::AstNode *node, VReg ctor, const util::StringView &prop) { - ra_.Emit(node, prop, ctor); + Ra().Emit(node, prop, ctor); } void PandaGen::LoadModuleVariable(const ir::AstNode *node, VReg module, const util::StringView &name) { - ra_.Emit(node, name, module); + Ra().Emit(node, name, module); } void PandaGen::StoreModuleVar(const ir::AstNode *node, const util::StringView &name) { - sa_.Emit(node, name); + Sa().Emit(node, name); } void PandaGen::CopyModule(const ir::AstNode *node, VReg module) { - ra_.Emit(node, module); + Ra().Emit(node, module); } void PandaGen::StSuperByName(const ir::AstNode *node, VReg obj, const util::StringView &key) { - ra_.Emit(node, key, obj); + Ra().Emit(node, key, obj); } void PandaGen::LdSuperByName(const ir::AstNode *node, const util::StringView &key) { - ra_.Emit(node, key); + Ra().Emit(node, key); } void PandaGen::StSuperByValue(const ir::AstNode *node, VReg obj, VReg prop) { - ra_.Emit(node, obj, prop); + Ra().Emit(node, obj, prop); } void PandaGen::LdSuperByValue(const ir::AstNode *node, VReg obj) { - ra_.Emit(node, obj); + Ra().Emit(node, obj); } void PandaGen::StoreSuperProperty(const ir::AstNode *node, VReg obj, const Operand &prop) @@ -1476,91 +1334,62 @@ void PandaGen::LoadSuperProperty(const ir::AstNode *node, const Operand &prop) void PandaGen::LoadLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot) { - sa_.Emit(node, level, slot); + Sa().Emit(node, level, slot); } void PandaGen::LoadLexical(const ir::AstNode *node, const util::StringView &name, uint32_t level, uint32_t slot) { - sa_.Emit(node, name, level, slot); + Sa().Emit(node, name, level, slot); } void PandaGen::StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot) { - ra_.Emit(node, level, slot); + Ra().Emit(node, level, slot); } void PandaGen::StoreLexical(const ir::AstNode *node, const util::StringView &name, uint32_t level, uint32_t slot) { - ra_.Emit(node, name, level, slot); + Ra().Emit(node, name, level, slot); } void PandaGen::ThrowIfSuperNotCorrectCall(const ir::AstNode *node, int64_t num) { - sa_.Emit(node, num); + Sa().Emit(node, num); } void PandaGen::ThrowTdz(const ir::AstNode *node, const util::StringView &name) { - sa_.Emit(node, name); + Sa().Emit(node, name); } void PandaGen::ThrowConstAssignment(const ir::AstNode *node, const util::StringView &name) { - ra_.Emit(node, name); + Ra().Emit(node, name); } void PandaGen::PopLexEnv(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::CopyLexEnv(const ir::AstNode *node) { - sa_.Emit(node); + Sa().Emit(node); } void PandaGen::NewLexEnv(const ir::AstNode *node, uint32_t num) { - sa_.Emit(node, num); + Sa().Emit(node, num); } void PandaGen::LdLexEnv(const ir::AstNode *node) { - sa_.Emit(node); -} - -uint32_t PandaGen::TryDepth() const -{ - const auto *iter = dynamicContext_; - uint32_t depth = 0; - - while (iter != nullptr) { - if (iter->HasTryCatch()) { - depth++; - } - - iter = iter->Prev(); - } - - return depth; -} - -CatchTable *PandaGen::CreateCatchTable() -{ - auto *catchTable = allocator_->New(this, TryDepth()); - catchList_.push_back(catchTable); - return catchTable; -} - -void PandaGen::SortCatchTables() -{ - std::sort(catchList_.begin(), catchList_.end(), - [](const CatchTable *a, const CatchTable *b) { return b->Depth() < a->Depth(); }); + Sa().Emit(node); } Operand PandaGen::ToNamedPropertyKey(const ir::Expression *prop, bool isComputed) { - VReg res {IRNode::REG_START}; + VReg res {VReg::REG_START}; if (!isComputed) { if (prop->IsIdentifier()) { @@ -1586,7 +1415,7 @@ Operand PandaGen::ToNamedPropertyKey(const ir::Expression *prop, bool isComputed } if (prop->IsNumberLiteral()) { - auto num = prop->AsNumberLiteral()->Number(); + auto num = prop->AsNumberLiteral()->Number().GetDouble(); if (util::Helpers::IsIndex(num)) { return static_cast(num); } @@ -1660,7 +1489,7 @@ void PandaGen::LoadEvalVariable(const ir::AstNode *node, const util::StringView { RegScope rs(this); LoadLexicalContext(node); - ra_.Emit(node, name); + Ra().Emit(node, name); } void PandaGen::StoreEvalVariable(const ir::AstNode *node, const util::StringView &name) @@ -1669,7 +1498,7 @@ void PandaGen::StoreEvalVariable(const ir::AstNode *node, const util::StringView VReg value = AllocReg(); StoreAccumulator(node, value); LoadLexicalContext(node); - ra_.Emit(node, name, value); + Ra().Emit(node, name, value); } void PandaGen::DirectEval(const ir::AstNode *node, uint32_t parserStatus) @@ -1698,12 +1527,12 @@ void PandaGen::DirectEval(const ir::AstNode *node, uint32_t parserStatus) LoadAccumulator(node, LexEnv()); StOwnByIndex(node, evalBindings, evalBindingsIndex++); - const auto *iter = scope_->EnclosingVariableScope(); + const auto *iter = Scope()->EnclosingVariableScope(); while (true) { uint32_t scopeBindingsBuf = iter->EvalBindings(); if (scopeBindingsBuf != INVALID_LITERAL_BUFFER_ID) { - sa_.Emit(node, util::Helpers::ToStringView(allocator_, scopeBindingsBuf)); + Sa().Emit(node, util::Helpers::ToStringView(Allocator(), scopeBindingsBuf)); StOwnByIndex(node, evalBindings, evalBindingsIndex++); } @@ -1717,22 +1546,22 @@ void PandaGen::DirectEval(const ir::AstNode *node, uint32_t parserStatus) LoadAccumulator(node, evalBindings); StOwnByIndex(node, bindings, index++); - sa_.Emit(node, static_cast(parserStatus), arg0, bindings); + Sa().Emit(node, static_cast(parserStatus), arg0, bindings); } void PandaGen::LoadLexicalContext(const ir::AstNode *node) { - auto result = scope_->Find(binder::Binder::LEXICAL_CONTEXT_PARAM); + auto result = Scope()->Find(binder::Binder::LEXICAL_CONTEXT_PARAM); LoadLexicalVar(node, result.lexLevel, result.variable->AsLocalVariable()->LexIdx()); } bool PandaGen::IsDirectEval() const { - return context_->IsDirectEval(); + return Context()->IsDirectEval(); } bool PandaGen::IsEval() const { - return context_->IsEval(); + return Context()->IsEval(); } } // namespace panda::es2panda::compiler diff --git a/compiler/core/pandagen.h b/compiler/core/pandagen.h index 03b65f9270499c7785175f9a96b150d80ba3d127..0c9aff7de03f957b8b4bbde9a5da2f1d7e9d7e2d 100644 --- a/compiler/core/pandagen.h +++ b/compiler/core/pandagen.h @@ -16,10 +16,11 @@ #ifndef ES2PANDA_COMPILER_CORE_PANDAGEN_H #define ES2PANDA_COMPILER_CORE_PANDAGEN_H -#include "plugins/ecmascript/es2panda/compiler/base/literals.h" +#include "plugins/ecmascript/es2panda/compiler/core/codeGen.h" #include "plugins/ecmascript/es2panda/compiler/base/optionalChain.h" #include "plugins/ecmascript/es2panda/compiler/core/envScope.h" -#include "plugins/ecmascript/es2panda/compiler/core/inlineCache.h" +#include "plugins/ecmascript/es2panda/compiler/core/function.h" + #include "plugins/ecmascript/es2panda/compiler/core/regAllocator.h" #include "plugins/ecmascript/es2panda/compiler/core/regScope.h" #include "plugins/ecmascript/es2panda/ir/irnode.h" @@ -46,115 +47,20 @@ namespace panda::es2panda::compiler { class FunctionBuilder; class CompilerContext; class DynamicContext; -class CatchTable; - -enum class Constant { - JS_NAN, - JS_HOLE, - JS_INFINITY, - JS_UNDEFINED, - JS_NULL, - JS_TRUE, - JS_FALSE, - JS_SYMBOL, - JS_GLOBAL, -}; - -class DebugInfo { -public: - explicit DebugInfo(ArenaAllocator *allocator) : variableDebugInfo(allocator->Adapter()) {}; - DEFAULT_COPY_SEMANTIC(DebugInfo); - DEFAULT_MOVE_SEMANTIC(DebugInfo); - ~DebugInfo() = default; - - // NOLINTBEGIN(misc-non-private-member-variables-in-classes) - ArenaVector variableDebugInfo; - const ir::Statement *firstStmt {}; - // NOLINTEND(misc-non-private-member-variables-in-classes) -}; -class PandaGen { +class PandaGen : public CodeGen { public: - explicit PandaGen(ArenaAllocator *allocator, CompilerContext *context, binder::FunctionScope *scope, - ProgramElement *programElement) - : allocator_(allocator), - context_(context), - debugInfo_(allocator_), - topScope_(scope), - scope_(topScope_), - rootNode_(scope->Node()), - insns_(allocator_->Adapter()), - catchList_(allocator_->Adapter()), - programElement_(programElement), - sa_(this), - ra_(this), - rra_(this) + explicit PandaGen(ArenaAllocator *allocator, RegSpiller *spiller, CompilerContext *context, + binder::FunctionScope *scope, ProgramElement *programElement) + : CodeGen(allocator, spiller, context, scope, programElement) { + Function::Compile(this); } + ~PandaGen() = default; NO_COPY_SEMANTIC(PandaGen); NO_MOVE_SEMANTIC(PandaGen); - inline ArenaAllocator *Allocator() const - { - return allocator_; - } - - const ArenaVector &CatchList() const - { - return catchList_; - } - - binder::FunctionScope *TopScope() const - { - return topScope_; - } - - binder::Scope *Scope() const - { - return scope_; - } - - const ir::AstNode *RootNode() const - { - return rootNode_; - } - - ArenaList &Insns() - { - return insns_; - } - - const ArenaList &Insns() const - { - return insns_; - } - - VReg AllocReg() - { - return usedRegs_--; - } - - VReg NextReg() const - { - return usedRegs_; - } - - uint32_t TotalRegsNum() const - { - return totalRegs_; - } - - size_t LabelCount() const - { - return labelId_; - } - - const DebugInfo &Debuginfo() const - { - return debugInfo_; - } - FunctionBuilder *FuncBuilder() const { return builder_; @@ -172,21 +78,6 @@ public: } } - uint32_t IcSize() const - { - return ic_.Size(); - } - - bool IsDebug() const; - uint32_t ParamCount() const; - uint32_t FormalParametersCount() const; - uint32_t InternalParamCount() const; - const util::StringView &InternalName() const; - const util::StringView &FunctionName() const; - binder::Binder *Binder() const; - - Label *AllocLabel(); - VReg LexEnv() const; bool FunctionHasFinalizer() const; @@ -194,7 +85,16 @@ public: void FunctionEnter(); void FunctionExit(); - int32_t AddLiteralBuffer(LiteralBuffer &&buf); + void StoreAccumulator(const ir::AstNode *node, VReg vreg); + void LoadAccumulator(const ir::AstNode *node, VReg reg); + void MoveVreg(const ir::AstNode *node, VReg vd, VReg vs); + + void LoadAccumulatorFloat(const ir::AstNode *node, double num); + void LoadAccumulatorInt(const ir::AstNode *node, int32_t num); + void LoadAccumulatorInt(const ir::AstNode *node, size_t num); + + void LoadConst(const ir::AstNode *node, Constant id); + void StoreConst(const ir::AstNode *node, VReg reg, Constant id); void GetFunctionObject(const ir::AstNode *node); void GetNewTarget(const ir::AstNode *node); @@ -203,7 +103,6 @@ public: void LoadVar(const ir::Identifier *node, const binder::ScopeFindResult &result); void StoreVar(const ir::AstNode *node, const binder::ScopeFindResult &result, bool isDeclaration); - void StoreAccumulator(const ir::AstNode *node, VReg vreg); void LoadAccFromArgs(const ir::AstNode *node); void LoadObjProperty(const ir::AstNode *node, const Operand &prop); @@ -212,7 +111,6 @@ public: void StoreObjProperty(const ir::AstNode *node, VReg obj, const Operand &prop); void StoreOwnProperty(const ir::AstNode *node, VReg obj, const Operand &prop); void DeleteObjProperty(const ir::AstNode *node, VReg obj, VReg prop); - void LoadAccumulator(const ir::AstNode *node, VReg reg); void LoadGlobalVar(const ir::AstNode *node, const util::StringView &name); void StoreGlobalVar(const ir::AstNode *node, const util::StringView &name); void StoreGlobalLet(const ir::AstNode *node, const util::StringView &name); @@ -225,21 +123,7 @@ public: void LoadAccFromLexEnv(const ir::AstNode *node, const binder::ScopeFindResult &result); void StoreAccToLexEnv(const ir::AstNode *node, const binder::ScopeFindResult &result, bool isDeclaration); - void LoadAccumulatorString(const ir::AstNode *node, const util::StringView &str); void LoadAccumulatorBigInt(const ir::AstNode *node, const util::StringView &bigInt); - void LoadAccumulatorFloat(const ir::AstNode *node, double num); - void LoadAccumulatorInt(const ir::AstNode *node, int32_t num); - void LoadAccumulatorInt(const ir::AstNode *node, size_t num); - - void LoadConst(const ir::AstNode *node, Constant id); - void StoreConst(const ir::AstNode *node, VReg reg, Constant id); - void MoveVreg(const ir::AstNode *node, VReg vd, VReg vs); - - void SetLabel(const ir::AstNode *node, Label *label); - void Branch(const ir::AstNode *node, class Label *label); - bool CheckControlFlowChange(); - Label *ControlFlowChangeBreak(const ir::Identifier *label = nullptr); - Label *ControlFlowChangeContinue(const ir::Identifier *label); void Condition(const ir::AstNode *node, lexer::TokenType op, VReg lhs, class Label *ifFalse); void Unary(const ir::AstNode *node, lexer::TokenType op, VReg operand); @@ -248,6 +132,7 @@ public: void BranchIfUndefined(const ir::AstNode *node, class Label *target); void BranchIfNotUndefined(const ir::AstNode *node, class Label *target); void BranchIfHole(const ir::AstNode *node, class Label *target); + void BranchIfTrue(const ir::AstNode *node, class Label *target); void BranchIfNotTrue(const ir::AstNode *node, class Label *target); void BranchIfFalse(const ir::AstNode *node, class Label *target); @@ -369,10 +254,6 @@ public: void ThrowTdz(const ir::AstNode *node, const util::StringView &name); void ThrowConstAssignment(const ir::AstNode *node, const util::StringView &name); - uint32_t TryDepth() const; - CatchTable *CreateCatchTable(); - void SortCatchTables(); - void LoadObjByIndex(const ir::AstNode *node, int64_t index); void LoadObjByValue(const ir::AstNode *node, VReg obj); @@ -397,52 +278,17 @@ public: bool IsDirectEval() const; bool IsEval() const; - void SetFirstStmt(const ir::Statement *stmt) - { - debugInfo_.firstStmt = stmt; - } - - [[noreturn]] static void Unimplemented() - { - throw Error(ErrorType::GENERIC, "Unimplemented code path"); - } - private: void LoadEvalBindings(const ir::AstNode *node); - ArenaAllocator *allocator_; - CompilerContext *context_; FunctionBuilder *builder_ {}; - DebugInfo debugInfo_; - binder::FunctionScope *topScope_; - binder::Scope *scope_; - const ir::AstNode *rootNode_; - ArenaList insns_; - ArenaVector catchList_; - ProgramElement *programElement_; - EnvScope *envScope_ {}; - DynamicContext *dynamicContext_ {}; OptionalChain *optionalChain_ {}; - InlineCache ic_; - SimpleAllocator sa_; - RegAllocator ra_; - RangeRegAllocator rra_; - - uint32_t usedRegs_ {IRNode::REG_START}; - uint32_t totalRegs_ {IRNode::REG_START}; - int32_t literalBufferIdx_ {0}; - friend class ScopeContext; - friend class RegScope; - friend class LocalRegScope; - friend class LoopRegScope; - friend class ParamRegScope; - friend class FunctionRegScope; + friend class EnvScope; friend class LoopEnvScope; friend class DynamicContext; friend class OptionalChain; - size_t labelId_ {0}; }; } // namespace panda::es2panda::compiler diff --git a/compiler/core/programElement.cpp b/compiler/core/programElement.cpp new file mode 100644 index 0000000000000000000000000000000000000000..64a9951a165039dc4dddfc67cba5b3f2f6f570aa --- /dev/null +++ b/compiler/core/programElement.cpp @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2021-2022 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 "programElement.h" + +#include + +namespace panda::es2panda::compiler { +std::unordered_set &ProgramElement::Strings() +{ + return strings_; +} + +std::vector &ProgramElement::LiteralBufferIns() +{ + return literalBufferIns_; +} + +std::vector &ProgramElement::BuffStorage() +{ + return buffStorage_; +} + +pandasm::Function *ProgramElement::Function() +{ + return func_; +} + +void ProgramElement::SetFunction(pandasm::Function *func) +{ + func_ = func; +} + +ProgramElement::~ProgramElement() +{ + delete func_; +} +} // namespace panda::es2panda::compiler diff --git a/compiler/core/programElement.h b/compiler/core/programElement.h new file mode 100644 index 0000000000000000000000000000000000000000..4a750dc7b7add5a3d494076f93064dfe64d7a84e --- /dev/null +++ b/compiler/core/programElement.h @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2021-2022 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_CORE_PROGRAM_ELEMENT_H +#define ES2PANDA_COMPILER_CORE_PROGRAM_ELEMENT_H + +#include "macros.h" +#include "plugins/ecmascript/es2panda/compiler/base/literals.h" + +namespace panda::pandasm { +struct Ins; +struct Function; +} // namespace panda::pandasm + +namespace panda::es2panda::compiler { +class ProgramElement { +public: + explicit ProgramElement() = default; + ~ProgramElement(); + NO_COPY_SEMANTIC(ProgramElement); + NO_MOVE_SEMANTIC(ProgramElement); + + std::unordered_set &Strings(); + std::vector &LiteralBufferIns(); + std::vector &BuffStorage(); + pandasm::Function *Function(); + void SetFunction(pandasm::Function *func); + +private: + std::unordered_set strings_; + std::vector literalBufferIns_; + std::vector buffStorage_; + pandasm::Function *func_ {}; +}; +} // namespace panda::es2panda::compiler +#endif diff --git a/compiler/core/regAllocator.cpp b/compiler/core/regAllocator.cpp index 13a40894731448ad05b709c788bb229c23d1658d..e95c6c7d48447dcd5cfc0fa0d236b3f10bc5987f 100644 --- a/compiler/core/regAllocator.cpp +++ b/compiler/core/regAllocator.cpp @@ -15,70 +15,76 @@ #include "regAllocator.h" -#include "plugins/ecmascript/es2panda/ir/irnode.h" -#include "plugins/ecmascript/es2panda/compiler/core/pandagen.h" +#include "plugins/ecmascript/es2panda/compiler/core/codeGen.h" +#include "plugins/ecmascript/es2panda/checker/types/type.h" #include namespace panda::es2panda::compiler { -// RegAllocatorBase void AllocatorBase::PushBack(IRNode *ins) { - pg_->Insns().push_back(ins); + cg_->Insns().push_back(ins); } ArenaAllocator *AllocatorBase::Allocator() const { - return pg_->Allocator(); + return cg_->Allocator(); } // SimpleAllocator Label *SimpleAllocator::AllocLabel(std::string &&id) { - const auto *lastInsNode = pg_->Insns().empty() ? FIRST_NODE_OF_FUNCTION : pg_->Insns().back()->Node(); + const auto *lastInsNode = cg_->Insns().empty() ? FIRST_NODE_OF_FUNCTION : cg_->Insns().back()->Node(); return Alloc