diff --git a/es2panda/CMakeLists.txt b/es2panda/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..58cbe2c8239a7cbb78c70a5dbc43b452fb6f2587 --- /dev/null +++ b/es2panda/CMakeLists.txt @@ -0,0 +1,337 @@ +# 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. + +cmake_minimum_required (VERSION 3.5.0) + +project (es2panda) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +set(OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/out) +set(GENERATED_DIR ${OUTPUT_DIR}/gen) +set(GENERATED_STAMP ${OUTPUT_DIR}/gen_dir.stamp) +add_custom_target(es2panda-gen) + +add_custom_command( + OUTPUT ${GENERATED_STAMP} + COMMAND ${CMAKE_COMMAND} -E make_directory ${OUTPUT_DIR} + COMMAND ${CMAKE_COMMAND} -E make_directory ${GENERATED_DIR} + COMMAND ${CMAKE_COMMAND} -E touch ${GENERATED_STAMP} +) + +set(TEMPLATES + isa.h.erb + formats.h.erb +) + +panda_isa_gen( + TEMPLATES ${TEMPLATES} + SOURCE ${CMAKE_CURRENT_LIST_DIR}/compiler/templates + DESTINATION ${GENERATED_DIR} + 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() + +gen_keywords(keywords.h.erb ${GENERATED_DIR}) +gen_keywords(keywordsMap.h.erb ${GENERATED_DIR}) + +set(ES2PANDA_LIB_SRC + es2panda.cpp + binder/binder.cpp + binder/declaration.cpp + binder/scope.cpp + binder/variable.cpp + compiler/base/catchTable.cpp + compiler/base/condition.cpp + compiler/base/destructuring.cpp + compiler/base/hoisting.cpp + compiler/base/iterators.cpp + compiler/base/lexenv.cpp + compiler/base/literals.cpp + compiler/base/lreference.cpp + compiler/core/compileQueue.cpp + compiler/core/compilerContext.cpp + compiler/core/compilerImpl.cpp + compiler/core/dynamicContext.cpp + compiler/core/emitter.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/regAllocator.cpp + compiler/core/regScope.cpp + compiler/core/switchBuilder.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/base/catchClause.cpp + ir/base/classDefinition.cpp + ir/base/classProperty.cpp + ir/base/decorator.cpp + ir/base/metaProperty.cpp + ir/base/methodDefinition.cpp + ir/base/property.cpp + ir/base/scriptFunction.cpp + ir/base/spreadElement.cpp + ir/base/templateElement.cpp + ir/expression.cpp + ir/expressions/arrayExpression.cpp + ir/expressions/arrowFunctionExpression.cpp + ir/expressions/assignmentExpression.cpp + ir/expressions/awaitExpression.cpp + ir/expressions/binaryExpression.cpp + ir/expressions/callExpression.cpp + ir/expressions/chainExpression.cpp + ir/expressions/classExpression.cpp + ir/expressions/conditionalExpression.cpp + ir/expressions/functionExpression.cpp + ir/expressions/identifier.cpp + ir/expressions/importExpression.cpp + ir/expressions/literal.cpp + ir/expressions/literals/bigIntLiteral.cpp + ir/expressions/literals/booleanLiteral.cpp + ir/expressions/literals/nullLiteral.cpp + ir/expressions/literals/numberLiteral.cpp + ir/expressions/literals/regExpLiteral.cpp + ir/expressions/literals/stringLiteral.cpp + ir/expressions/literals/taggedLiteral.cpp + ir/expressions/memberExpression.cpp + ir/expressions/newExpression.cpp + ir/expressions/objectExpression.cpp + ir/expressions/omittedExpression.cpp + ir/expressions/sequenceExpression.cpp + ir/expressions/superExpression.cpp + ir/expressions/taggedTemplateExpression.cpp + ir/expressions/templateLiteral.cpp + ir/expressions/thisExpression.cpp + ir/expressions/unaryExpression.cpp + ir/expressions/updateExpression.cpp + ir/expressions/yieldExpression.cpp + ir/module/exportAllDeclaration.cpp + ir/module/exportDefaultDeclaration.cpp + ir/module/exportNamedDeclaration.cpp + ir/module/exportSpecifier.cpp + ir/module/importDeclaration.cpp + ir/module/importDefaultSpecifier.cpp + ir/module/importNamespaceSpecifier.cpp + ir/module/importSpecifier.cpp + ir/statement.cpp + ir/statements/blockStatement.cpp + ir/statements/breakStatement.cpp + ir/statements/classDeclaration.cpp + ir/statements/continueStatement.cpp + ir/statements/debuggerStatement.cpp + ir/statements/doWhileStatement.cpp + ir/statements/emptyStatement.cpp + ir/statements/expressionStatement.cpp + ir/statements/forInStatement.cpp + ir/statements/forOfStatement.cpp + ir/statements/forUpdateStatement.cpp + ir/statements/functionDeclaration.cpp + ir/statements/ifStatement.cpp + ir/statements/labelledStatement.cpp + ir/statements/loopStatement.cpp + ir/statements/returnStatement.cpp + ir/statements/switchCaseStatement.cpp + ir/statements/switchStatement.cpp + ir/statements/throwStatement.cpp + ir/statements/tryStatement.cpp + ir/statements/variableDeclaration.cpp + ir/statements/variableDeclarator.cpp + ir/statements/whileStatement.cpp + ir/ts/tsAnyKeyword.cpp + ir/ts/tsArrayType.cpp + ir/ts/tsAsExpression.cpp + ir/ts/tsBigintKeyword.cpp + ir/ts/tsBooleanKeyword.cpp + ir/ts/tsClassImplements.cpp + ir/ts/tsConditionalType.cpp + ir/ts/tsConstructorType.cpp + ir/ts/tsEnumDeclaration.cpp + ir/ts/tsEnumMember.cpp + ir/ts/tsExternalModuleReference.cpp + 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 + ir/ts/tsInterfaceDeclaration.cpp + ir/ts/tsInterfaceHeritage.cpp + 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 + ir/ts/tsNeverKeyword.cpp + ir/ts/tsNonNullExpression.cpp + ir/ts/tsNullKeyword.cpp + ir/ts/tsNumberKeyword.cpp + ir/ts/tsObjectKeyword.cpp + ir/ts/tsParameterProperty.cpp + ir/ts/tsParenthesizedType.cpp + ir/ts/tsPrivateIdentifier.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 + ir/ts/tsTypeAliasDeclaration.cpp + ir/ts/tsTypeAssertion.cpp + ir/ts/tsTypeLiteral.cpp + ir/ts/tsTypeOperator.cpp + ir/ts/tsTypeParameter.cpp + ir/ts/tsTypeParameterDeclaration.cpp + ir/ts/tsTypeParameterInstantiation.cpp + ir/ts/tsTypePredicate.cpp + ir/ts/tsTypeQuery.cpp + ir/ts/tsTypeReference.cpp + ir/ts/tsUndefinedKeyword.cpp + ir/ts/tsUnionType.cpp + ir/ts/tsUnknownKeyword.cpp + ir/ts/tsVoidKeyword.cpp + lexer/keywordsUtil.cpp + lexer/lexer.cpp + lexer/regexp/regexp.cpp + lexer/token/sourceLocation.cpp + lexer/token/token.cpp + parser/context/parserContext.cpp + parser/expressionParser.cpp + parser/parserImpl.cpp + parser/program/program.cpp + parser/statementParser.cpp + typescript/checker.cpp + typescript/core/binaryLikeExpression.cpp + typescript/core/destructuring.cpp + typescript/core/function.cpp + typescript/core/generics.cpp + typescript/core/helpers.cpp + typescript/core/object.cpp + typescript/core/typeCreation.cpp + typescript/core/typeElaboration.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 + 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) + +set(ICU_INCLUDE_DIRS + ${PANDA_THIRD_PARTY_SOURCES_DIR}/icu/icu4c/source/common + ${PANDA_THIRD_PARTY_SOURCES_DIR}/icu/icu4c/source/i18n + ${PANDA_THIRD_PARTY_SOURCES_DIR}/icu/icu4c/source +) + +target_include_directories(es2panda-lib + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE ${OUTPUT_DIR} + PRIVATE ${ICU_INCLUDE_DIRS} +) + +target_compile_options(es2panda-lib + PRIVATE -fexceptions -Werror=shadow +) + +target_link_libraries(es2panda-lib + PUBLIC arkbase hmicuuc.z + PRIVATE arkassembler +) + +if (PANDA_FUZZILLI) + target_compile_options(es2panda-lib + PRIVATE -fPIC + ) +endif() + +panda_add_sanitizers(TARGET es2panda-lib SANITIZERS ${PANDA_SANITIZERS_LIST}) +panda_add_to_clang_tidy(TARGET es2panda-lib CHECKS + "-modernize-use-trailing-return-type" + "-llvmlibc-restrict-system-libc-headers" + "-llvmlibc-callee-namespace" + "-cert-dcl21-cpp" + "-cppcoreguidelines-macro-usage" + "-cppcoreguidelines-pro-bounds-pointer-arithmetic" + "-fuchsia-default-arguments-calls" + "-fuchsia-default-arguments-declarations" + "-readability-implicit-bool-conversion" + "-misc-non-private-member-variables-in-classes" + "-hicpp-signed-bitwise" + "-altera-struct-pack-align" +) + +add_subdirectory(aot) diff --git a/es2panda/README.md b/es2panda/README.md new file mode 100644 index 0000000000000000000000000000000000000000..43a3d5500db85b11cc4cf8d434e6e7b577e043c8 --- /dev/null +++ b/es2panda/README.md @@ -0,0 +1,43 @@ +# Es2panda + +All in one JavaScript/TypeScript parser and compiler. + +## Usage +```sh +es2panda [OPTIONS] [input file] -- [arguments] +``` + +## Optional arguments + - `--debug-info`: Compile with debug info + - `--dump-assembly`: Dump pandasm + - `--dump-ast`: Dump the parsed AST + - `--dump-debug-info`: Dump debug info + - `--dump-size-stat`: Dump binary size statistics + - `--extension`: Parse the input as the given extension (options: js | ts | as) + - `--module`: Parse the input as module + - `--opt-level`: Compiler optimization level (options: 0 | 1 | 2) + - `--output`: Compiler binary output (.abc) + - `--parse-only`: Parse the input only + - `--strict`: Parse the input in strict mode + +## Tail arguments + - `input`: input file + +## Running the tests +```sh +pip install tqdm +``` +```sh +python3 test/runner.py [OPTIONS] [build_directory] +``` + +### Optional arguments + - `--regression`: Run regression tests + - `--test262`: Run test262 + - `--no-progress`: Don't show progress bar + +### Tail arguments + - `build_directory`: Path to panda build directory + +### Skip list +Skip list for the runtime: `test/test262skiplist.txt, test/test262skiplist-long.txt`. diff --git a/es2panda/aot/CMakeLists.txt b/es2panda/aot/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..f4c39424742a97aa185d5510950319a0d7cf48b9 --- /dev/null +++ b/es2panda/aot/CMakeLists.txt @@ -0,0 +1,47 @@ +# 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. + +set(ES2PANDA_AOT_SRC + options.cpp + main.cpp +) + +panda_add_executable(es2panda ${ES2PANDA_AOT_SRC}) +target_link_libraries(es2panda es2panda-lib arkassembler arkbytecodeopt) +target_include_directories(es2panda PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +if (PANDA_FUZZILLI) + target_compile_options(es2panda + PRIVATE -fPIC + ) +endif() + +target_compile_options(es2panda + PRIVATE -Werror=shadow +) + +panda_add_sanitizers(TARGET es2panda SANITIZERS ${PANDA_SANITIZERS_LIST}) +panda_add_to_clang_tidy(TARGET es2panda CHECKS + "-modernize-use-trailing-return-type" + "-llvmlibc-restrict-system-libc-headers" + "-llvmlibc-callee-namespace" + "-cert-dcl21-cpp" + "-cppcoreguidelines-macro-usage" + "-cppcoreguidelines-pro-bounds-pointer-arithmetic" + "-fuchsia-default-arguments-calls" + "-fuchsia-default-arguments-declarations" + "-readability-implicit-bool-conversion" + "-misc-non-private-member-variables-in-classes" + "-hicpp-signed-bitwise" + "-altera-struct-pack-align" +) diff --git a/es2panda/aot/main.cpp b/es2panda/aot/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8dbd680977fa6e901ca1e41d7f6dbc8235458125 --- /dev/null +++ b/es2panda/aot/main.cpp @@ -0,0 +1,146 @@ +/** + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace panda::es2panda::aot { + +using mem::MemConfig; + +class MemManager { +public: + explicit MemManager() + { + constexpr auto COMPILER_SIZE = 128_MB; + + MemConfig::Initialize(0, 0, COMPILER_SIZE, 0); + PoolManager::Initialize(PoolType::MMAP); + } + + NO_COPY_SEMANTIC(MemManager); + NO_MOVE_SEMANTIC(MemManager); + + ~MemManager() + { + PoolManager::Finalize(); + MemConfig::Finalize(); + } +}; + +static int GenerateProgram(panda::pandasm::Program *prog, const std::string &output, int optLevel, bool dumpAsm, + bool dumpSize) +{ + std::map stat; + std::map *statp = optLevel != 0 ? &stat : nullptr; + panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps {}; + panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps *mapsp = optLevel != 0 ? &maps : nullptr; + +#ifdef PANDA_WITH_BYTECODE_OPTIMIZER + if (optLevel != 0) { + const uint32_t COMPONENT_MASK = panda::Logger::Component::ASSEMBLER | + panda::Logger::Component::BYTECODE_OPTIMIZER | + panda::Logger::Component::COMPILER; + panda::Logger::InitializeStdLogging(panda::Logger::Level::ERROR, COMPONENT_MASK); + + if (!panda::pandasm::AsmEmitter::Emit(output, *prog, statp, mapsp, true)) { + return 1; + } + + panda::bytecodeopt::options.SetOptLevel(optLevel); + panda::bytecodeopt::OptimizeBytecode(prog, mapsp, output, true, true); + } +#endif + + if (dumpAsm) { + es2panda::Compiler::DumpAsm(prog); + } + + if (!panda::pandasm::AsmEmitter::Emit(output, *prog, statp, mapsp, true)) { + return 1; + } + + if (dumpSize && optLevel != 0) { + size_t totalSize = 0; + std::cout << "Panda file size statistic:" << std::endl; + constexpr std::array INFO_STATS = {"instructions_number", "codesize"}; + + for (const auto &[name, size] : stat) { + if (find(INFO_STATS.begin(), INFO_STATS.end(), name) != INFO_STATS.end()) { + continue; + } + std::cout << name << " section: " << size << std::endl; + totalSize += size; + } + + for (const auto &name : INFO_STATS) { + std::cout << name << ": " << stat.at(std::string(name)) << std::endl; + } + + std::cout << "total: " << totalSize << std::endl; + } + + return 0; +} + +int Run(int argc, const char **argv) +{ + auto options = std::make_unique(); + + if (!options->Parse(argc, argv)) { + std::cerr << options->ErrorMsg() << std::endl; + return 1; + } + + es2panda::Compiler compiler(options->Extension(), options->ThreadCount()); + es2panda::SourceFile input(options->SourceFile(), options->ParserInput(), options->ParseModule()); + + auto *program = compiler.Compile(input, options->CompilerOptions()); + + if (!program) { + const auto &err = compiler.GetError(); + + if (err.Message().empty() && options->ParseOnly()) { + return 0; + } + + std::cout << err.TypeString() << ": " << err.Message(); + std::cout << " [" << options->SourceFile() << ":" << err.Line() << ":" << err.Col() << "]" << std::endl; + + return err.ErrorCode(); + } + + GenerateProgram(program, options->CompilerOutput(), options->OptLevel(), options->CompilerOptions().dumpAsm, + options->SizeStat()); + delete program; + + return 0; +} + +} // namespace panda::es2panda::aot + +int main(int argc, const char **argv) +{ + panda::es2panda::aot::MemManager mm; + return panda::es2panda::aot::Run(argc, argv); +} diff --git a/es2panda/aot/options.cpp b/es2panda/aot/options.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9be3f001c5b1f2f8a6c5e7e5cd796649fe38f70d --- /dev/null +++ b/es2panda/aot/options.cpp @@ -0,0 +1,163 @@ +/* + * 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 "options.h" + +#include + +#include + +namespace panda::es2panda::aot { + +template +T BaseName(T const &path, T const &delims = "/") +{ + return path.substr(path.find_last_of(delims) + 1); +} + +template +T RemoveExtension(T const &filename) +{ + typename T::size_type const P(filename.find_last_of('.')); + return P > 0 && P != T::npos ? filename.substr(0, P) : filename; +} + +// Options + +Options::Options() : argparser_(new panda::PandArgParser()) {} + +Options::~Options() +{ + delete argparser_; +} + +bool Options::Parse(int argc, const char **argv) +{ + panda::PandArg opHelp("help", false, "Print this message and exit"); + + // parser + panda::PandArg inputExtension("extension", "js", + "Parse the input as the given extension (options: js | ts | as)"); + 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"); + + // compiler + panda::PandArg opDumpAssembly("dump-assembly", false, "Dump pandasm"); + panda::PandArg opDebugInfo("debug-info", false, "Compile with debug info"); + panda::PandArg opDumpDebugInfo("dump-debug-info", false, "Dump debug info"); + panda::PandArg opOptLevel("opt-level", 0, "Compiler optimization level (options: 0 | 1 | 2)"); + panda::PandArg opThreadCount("thread", 0, "Number of worker theads"); + panda::PandArg opSizeStat("dump-size-stat", false, "Dump size statistics"); + panda::PandArg outputFile("output", "", "Compiler binary output (.abc)"); + + // tail arguments + panda::PandArg inputFile("input", "", "input file"); + + argparser_->Add(&opHelp); + argparser_->Add(&opModule); + argparser_->Add(&opDumpAst); + argparser_->Add(&opParseOnly); + argparser_->Add(&opDumpAssembly); + argparser_->Add(&opDebugInfo); + argparser_->Add(&opDumpDebugInfo); + + argparser_->Add(&opOptLevel); + argparser_->Add(&opThreadCount); + argparser_->Add(&opSizeStat); + + argparser_->Add(&inputExtension); + argparser_->Add(&outputFile); + + argparser_->PushBackTail(&inputFile); + argparser_->EnableTail(); + argparser_->EnableRemainder(); + + if (!argparser_->Parse(argc, argv) || inputFile.GetValue().empty() || opHelp.GetValue()) { + std::stringstream ss; + + ss << argparser_->GetErrorString() << std::endl; + ss << "Usage: " + << "es2panda" + << " [OPTIONS] [input file] -- [arguments]" << std::endl; + ss << std::endl; + ss << "optional arguments:" << std::endl; + ss << argparser_->GetHelpString() << std::endl; + + errorMsg_ = ss.str(); + return false; + } + + sourceFile_ = inputFile.GetValue(); + std::ifstream inputStream(sourceFile_.c_str()); + + if (inputStream.fail()) { + errorMsg_ = "Failed to open file: "; + errorMsg_.append(sourceFile_); + return false; + } + + std::stringstream ss; + ss << inputStream.rdbuf(); + parserInput_ = ss.str(); + + sourceFile_ = BaseName(sourceFile_); + + if (!outputFile.GetValue().empty()) { + compilerOutput_ = outputFile.GetValue(); + } else { + compilerOutput_ = RemoveExtension(sourceFile_).append(".abc"); + } + + std::string extension = inputExtension.GetValue(); + + if (!extension.empty()) { + if (extension == "js") { + extension_ = es2panda::ScriptExtension::JS; + } else if (extension == "ts") { + extension_ = es2panda::ScriptExtension::TS; + } else if (extension == "as") { + extension_ = es2panda::ScriptExtension::AS; + } else { + errorMsg_ = "Invalid extension (available options: js, ts, as)"; + return false; + } + } + + optLevel_ = opOptLevel.GetValue(); + threadCount_ = opThreadCount.GetValue(); + + if (opParseOnly.GetValue()) { + options_ |= OptionFlags::PARSE_ONLY; + } + + if (opModule.GetValue()) { + options_ |= OptionFlags::PARSE_MODULE; + } + + if (opSizeStat.GetValue()) { + options_ |= OptionFlags::SIZE_STAT; + } + + compilerOptions_.dumpAsm = opDumpAssembly.GetValue(); + compilerOptions_.dumpAst = opDumpAst.GetValue(); + compilerOptions_.dumpDebugInfo = opDumpDebugInfo.GetValue(); + compilerOptions_.isDebug = opDebugInfo.GetValue(); + compilerOptions_.parseOnly = opParseOnly.GetValue(); + + return true; +} + +} // namespace panda::es2panda::aot diff --git a/es2panda/aot/options.h b/es2panda/aot/options.h new file mode 100644 index 0000000000000000000000000000000000000000..af8c8e3093a40394bd8679a28407a28d4dd16708 --- /dev/null +++ b/es2panda/aot/options.h @@ -0,0 +1,134 @@ +/** + * 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_AOT_OPTIONS_H +#define ES2PANDA_AOT_OPTIONS_H + +#include +#include + +#include +#include +#include + +namespace panda { +class PandArgParser; +class PandaArg; +} // namespace panda + +namespace panda::es2panda::aot { + +enum class OptionFlags { + DEFAULT = 0, + PARSE_ONLY = 1 << 1, + PARSE_MODULE = 1 << 2, + SIZE_STAT = 1 << 3, +}; + +inline std::underlying_type_t operator&(OptionFlags a, OptionFlags b) +{ + using utype = std::underlying_type_t; + /* NOLINTNEXTLINE(hicpp-signed-bitwise) */ + return static_cast(static_cast(a) & static_cast(b)); +} + +inline OptionFlags &operator|=(OptionFlags &a, OptionFlags b) +{ + using utype = std::underlying_type_t; + /* NOLINTNEXTLINE(hicpp-signed-bitwise) */ + return a = static_cast(static_cast(a) | static_cast(b)); +} + +class Options { +public: + Options(); + NO_COPY_SEMANTIC(Options); + NO_MOVE_SEMANTIC(Options); + ~Options(); + + bool Parse(int argc, const char **argv); + + es2panda::ScriptExtension Extension() const + { + return extension_; + } + + const es2panda::CompilerOptions &CompilerOptions() const + { + return compilerOptions_; + } + + const std::string &ParserInput() const + { + return parserInput_; + } + + const std::string &CompilerOutput() const + { + return compilerOutput_; + } + + const std::string &SourceFile() const + { + return sourceFile_; + } + + const std::string &ErrorMsg() const + { + return errorMsg_; + } + + int OptLevel() const + { + return optLevel_; + } + + int ThreadCount() const + { + return threadCount_; + } + + bool ParseModule() const + { + return (options_ & OptionFlags::PARSE_MODULE) != 0; + } + + bool ParseOnly() const + { + return (options_ & OptionFlags::PARSE_ONLY) != 0; + } + + bool SizeStat() const + { + return (options_ & OptionFlags::SIZE_STAT) != 0; + } + +private: + es2panda::ScriptExtension extension_ {es2panda::ScriptExtension::JS}; + es2panda::CompilerOptions compilerOptions_ {}; + OptionFlags options_ {OptionFlags::DEFAULT}; + panda::PandArgParser *argparser_; + std::string parserInput_; + std::string compilerOutput_; + std::string result_; + std::string sourceFile_; + std::string errorMsg_; + int optLevel_ {0}; + int threadCount_ {0}; +}; + +} // namespace panda::es2panda::aot + +#endif // AOT_OPTIONS_H diff --git a/es2panda/binder/binder.cpp b/es2panda/binder/binder.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d2a8b2ad397c8be9b3a5a17ca306bd6354d2d199 --- /dev/null +++ b/es2panda/binder/binder.cpp @@ -0,0 +1,509 @@ +/** + * 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 "binder.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace panda::es2panda::binder { +void Binder::InitTopScope() +{ + if (program_->Kind() == parser::ScriptKind::MODULE) { + topScope_ = Allocator()->New(Allocator()); + } else { + topScope_ = Allocator()->New(Allocator()); + } + + scope_ = topScope_; +} + +ParameterDecl *Binder::AddParamDecl(const ir::AstNode *param) +{ + ASSERT(scope_->IsFunctionParamScope() || scope_->IsCatchParamScope()); + auto [decl, node] = static_cast(scope_)->AddParamDecl(Allocator(), param); + + if (!node) { + return decl; + } + + ThrowRedeclaration(node->Start(), decl->Name()); +} + +void Binder::ThrowRedeclaration(const lexer::SourcePosition &pos, const util::StringView &name) +{ + 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); +} + +void Binder::IdentifierAnalysis() +{ + ASSERT(program_->Ast()); + ASSERT(scope_ == topScope_); + + BuildFunction(topScope_, "main"); + ResolveReferences(program_->Ast()); + AddMandatoryParams(); +} + +void Binder::LookupReference(const util::StringView &name) +{ + ScopeFindResult res = scope_->Find(name); + if (res.level == 0) { + return; + } + + ASSERT(res.variable); + res.variable->SetLexical(res.scope); +} + +void Binder::InstantiateArguments() +{ + auto *iter = scope_; + while (true) { + Scope *scope = iter->IsFunctionParamScope() ? iter : iter->EnclosingVariableScope(); + + const auto *node = scope->Node(); + + if (scope->IsLoopScope()) { + iter = scope->Parent(); + continue; + } + + if (!node->IsScriptFunction()) { + break; + } + + if (!node->AsScriptFunction()->IsArrow()) { + auto *argumentsVariable = + scope->AddDecl(Allocator(), FUNCTION_ARGUMENTS, VariableFlags::INITIALIZED); + + if (iter->IsFunctionParamScope()) { + if (!argumentsVariable) { + break; + } + + scope = iter->AsFunctionParamScope()->GetFunctionScope(); + scope->Bindings().insert({argumentsVariable->Name(), argumentsVariable}); + } + + scope->AsVariableScope()->AddFlag(VariableScopeFlags::USE_ARGS); + + break; + } + + iter = scope->Parent(); + } +} + +void Binder::LookupIdentReference(ir::Identifier *ident) +{ + if (ident->Name().Is(FUNCTION_ARGUMENTS)) { + InstantiateArguments(); + } + + ScopeFindResult res = scope_->Find(ident->Name()); + if (res.level != 0) { + ASSERT(res.variable); + res.variable->SetLexical(res.scope); + } + + if (!res.variable) { + return; + } + + if (res.variable->Declaration()->IsLetOrConstDecl() && !res.variable->HasFlag(VariableFlags::INITIALIZED)) { + ident->SetTdz(); + } + + ident->SetVariable(res.variable); +} + +void Binder::BuildFunction(FunctionScope *funcScope, util::StringView name) +{ + 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()); +} + +void Binder::BuildScriptFunction(Scope *outerScope, const ir::ScriptFunction *scriptFunc) +{ + if (scriptFunc->IsArrow()) { + VariableScope *outerVarScope = outerScope->EnclosingVariableScope(); + outerVarScope->AddFlag(VariableScopeFlags::INNER_ARROW); + } + + BuildFunction(scope_->AsFunctionScope(), util::Helpers::FunctionName(scriptFunc)); +} + +void Binder::BuildVarDeclaratorId(const ir::AstNode *parent, ir::AstNode *childNode) +{ + childNode->SetParent(parent); + + switch (childNode->Type()) { + case ir::AstNodeType::IDENTIFIER: { + const auto &name = childNode->AsIdentifier()->Name(); + if (util::Helpers::IsGlobalIdentifier(childNode->AsIdentifier()->Name())) { + break; + } + + auto *variable = scope_->FindLocal(name); + variable->AddFlag(VariableFlags::INITIALIZED); + break; + } + case ir::AstNodeType::OBJECT_PATTERN: { + for (auto *prop : childNode->AsObjectPattern()->Properties()) { + BuildVarDeclaratorId(childNode, prop); + } + break; + } + case ir::AstNodeType::ARRAY_PATTERN: { + for (auto *element : childNode->AsArrayPattern()->Elements()) { + BuildVarDeclaratorId(childNode, element); + } + break; + } + case ir::AstNodeType::ASSIGNMENT_PATTERN: { + ResolveReference(childNode, childNode->AsAssignmentPattern()->Right()); + BuildVarDeclaratorId(childNode, childNode->AsAssignmentPattern()->Left()); + break; + } + case ir::AstNodeType::PROPERTY: { + ResolveReference(childNode, childNode->AsProperty()->Key()); + BuildVarDeclaratorId(childNode, childNode->AsProperty()->Value()); + break; + } + case ir::AstNodeType::REST_ELEMENT: { + BuildVarDeclaratorId(childNode, childNode->AsRestElement()->Argument()); + break; + } + default: + break; + } +} + +void Binder::BuildVarDeclarator(ir::VariableDeclarator *varDecl) +{ + if (varDecl->Parent()->AsVariableDeclaration()->Kind() == ir::VariableDeclaration::VariableDeclarationKind::VAR) { + ResolveReferences(varDecl); + return; + } + + if (varDecl->Init()) { + ResolveReference(varDecl, varDecl->Init()); + } + + BuildVarDeclaratorId(varDecl, varDecl->Id()); +} + +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); + } + + auto scopeCtx = LexicalScope::Enter(this, classDef->Scope()); + + if (classDef->Super()) { + ResolveReference(classDef, classDef->Super()); + } + + if (classDef->Ident()) { + ScopeFindResult res = scope_->Find(classDef->Ident()->Name()); + + ASSERT(res.variable && res.variable->Declaration()->IsConstDecl()); + res.variable->AddFlag(VariableFlags::INITIALIZED); + } + + ResolveReference(classDef, classDef->Ctor()); + + for (auto *stmt : classDef->Body()) { + ResolveReference(classDef, stmt); + } +} + +void Binder::BuildForUpdateLoop(ir::ForUpdateStatement *forUpdateStmt) +{ + auto *loopScope = forUpdateStmt->Scope(); + + auto declScopeCtx = LexicalScope::Enter(this, loopScope->DeclScope()); + + if (forUpdateStmt->Init()) { + ResolveReference(forUpdateStmt, forUpdateStmt->Init()); + } + + if (forUpdateStmt->Update()) { + ResolveReference(forUpdateStmt, forUpdateStmt->Update()); + } + + auto loopCtx = LexicalScope::Enter(this, loopScope); + + if (forUpdateStmt->Test()) { + ResolveReference(forUpdateStmt, forUpdateStmt->Test()); + } + + ResolveReference(forUpdateStmt, 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) +{ + auto declScopeCtx = LexicalScope::Enter(this, loopScope->DeclScope()); + + ResolveReference(parent, right); + ResolveReference(parent, left); + + auto loopCtx = LexicalScope::Enter(this, loopScope); + + ResolveReference(parent, body); + loopCtx.GetScope()->ConvertToVariableScope(Allocator()); +} + +void Binder::BuildCatchClause(ir::CatchClause *catchClauseStmt) +{ + if (catchClauseStmt->Param()) { + auto paramScopeCtx = LexicalScope::Enter(this, catchClauseStmt->Scope()->ParamScope()); + ResolveReference(catchClauseStmt, catchClauseStmt->Param()); + } + + auto scopeCtx = LexicalScope::Enter(this, catchClauseStmt->Scope()); + ResolveReference(catchClauseStmt, catchClauseStmt->Body()); +} + +void Binder::ResolveReference(const ir::AstNode *parent, ir::AstNode *childNode) +{ + childNode->SetParent(parent); + + switch (childNode->Type()) { + case ir::AstNodeType::IDENTIFIER: { + auto *ident = childNode->AsIdentifier(); + + if (ident->IsReference()) { + LookupIdentReference(ident); + } + + ResolveReferences(childNode); + break; + } + case ir::AstNodeType::SUPER_EXPRESSION: { + VariableScope *varScope = scope_->EnclosingVariableScope(); + varScope->AddFlag(VariableScopeFlags::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); + } + } + + auto scopeCtx = LexicalScope::Enter(this, funcScope); + + BuildScriptFunction(outerScope, scriptFunc); + + ResolveReference(scriptFunc, scriptFunc->Body()); + 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); + break; + } + case ir::AstNodeType::BLOCK_STATEMENT: { + auto scopeCtx = LexicalScope::Enter(this, childNode->AsBlockStatement()->Scope()); + + ResolveReferences(childNode); + break; + } + case ir::AstNodeType::SWITCH_STATEMENT: { + auto scopeCtx = LexicalScope::Enter(this, childNode->AsSwitchStatement()->Scope()); + + ResolveReferences(childNode); + break; + } + case ir::AstNodeType::DO_WHILE_STATEMENT: { + auto *doWhileStatement = childNode->AsDoWhileStatement(); + + { + auto loopScopeCtx = LexicalScope::Enter(this, doWhileStatement->Scope()); + ResolveReference(doWhileStatement, doWhileStatement->Body()); + } + + ResolveReference(doWhileStatement, doWhileStatement->Test()); + break; + } + case ir::AstNodeType::WHILE_STATEMENT: { + auto *whileStatement = childNode->AsWhileStatement(); + ResolveReference(whileStatement, whileStatement->Test()); + + auto loopScopeCtx = LexicalScope::Enter(this, whileStatement->Scope()); + ResolveReference(whileStatement, whileStatement->Body()); + + break; + } + case ir::AstNodeType::FOR_UPDATE_STATEMENT: { + BuildForUpdateLoop(childNode->AsForUpdateStatement()); + break; + } + case ir::AstNodeType::FOR_IN_STATEMENT: { + auto *forInStmt = childNode->AsForInStatement(); + BuildForInOfLoop(forInStmt, 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()); + break; + } + case ir::AstNodeType::CATCH_CLAUSE: { + BuildCatchClause(childNode->AsCatchClause()); + break; + } + default: { + ResolveReferences(childNode); + break; + } + } +} +void Binder::ResolveReferences(const ir::AstNode *parent) +{ + parent->Iterate([this, parent](auto *childNode) { ResolveReference(parent, childNode); }); +} + +void Binder::AddMandatoryParam(const std::string_view &name) +{ + ASSERT(scope_->IsFunctionVariableScope()); + + auto *decl = Allocator()->New(name); + auto *param = Allocator()->New(decl, VariableFlags::VAR); + + auto &funcParams = scope_->AsFunctionVariableScope()->ParamScope()->Params(); + funcParams.insert(funcParams.begin(), param); + scope_->AsFunctionVariableScope()->Bindings().insert({decl->Name(), param}); +} + +void Binder::AddMandatoryParams(const MandatoryParams ¶ms) +{ + for (auto iter = params.rbegin(); iter != params.rend(); iter++) { + AddMandatoryParam(*iter); + } +} + +void Binder::AddMandatoryParams() +{ + ASSERT(scope_ == topScope_); + ASSERT(!functionScopes_.empty()); + auto iter = functionScopes_.begin(); + [[maybe_unused]] auto *funcScope = *iter++; + + ASSERT(funcScope->IsGlobalScope() || funcScope->IsModuleScope()); + AddMandatoryParams(FUNCTION_MANDATORY_PARAMS); + + for (; iter != functionScopes_.end(); iter++) { + funcScope = *iter; + const auto *scriptFunc = funcScope->Node()->AsScriptFunction(); + + auto scopeCtx = LexicalScope::Enter(this, funcScope); + + if (!scriptFunc->IsArrow()) { + AddMandatoryParams(FUNCTION_MANDATORY_PARAMS); + continue; + } + + const ir::ScriptFunction *ctor = util::Helpers::GetContainingConstructor(scriptFunc); + bool lexicalFunctionObject {}; + + if (ctor && util::Helpers::GetClassDefiniton(ctor)->Super() && + funcScope->HasFlag(VariableScopeFlags::USE_SUPER)) { + ASSERT(ctor->Scope()->HasFlag(VariableScopeFlags::INNER_ARROW)); + ctor->Scope()->AddFlag(VariableScopeFlags::SET_LEXICAL_FUNCTION); + lexicalFunctionObject = true; + AddMandatoryParams(CTOR_ARROW_MANDATORY_PARAMS); + } else { + AddMandatoryParams(ARROW_MANDATORY_PARAMS); + } + + LookupReference(MANDATORY_PARAM_NEW_TARGET); + LookupReference(MANDATORY_PARAM_THIS); + + if (funcScope->HasFlag(VariableScopeFlags::USE_ARGS)) { + LookupReference(FUNCTION_ARGUMENTS); + } + + if (lexicalFunctionObject) { + LookupReference(MANDATORY_PARAM_FUNC); + } + } +} +} // namespace panda::es2panda::binder diff --git a/es2panda/binder/binder.h b/es2panda/binder/binder.h new file mode 100644 index 0000000000000000000000000000000000000000..28f4b038d2ecfe000b020cd6552496d5d681d9d0 --- /dev/null +++ b/es2panda/binder/binder.h @@ -0,0 +1,210 @@ +/** + * 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_BINDER_H +#define ES2PANDA_BINDER_BINDER_H + +#include +#include +#include +#include +#include + +namespace panda::es2panda::ir { +class AstNode; +class BlockStatement; +class CatchClause; +class ClassDefinition; +class Expression; +class ForUpdateStatement; +class Identifier; +class ScriptFunction; +class Statement; +class VariableDeclarator; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::binder { +class Scope; +class VariableScope; + +class Binder { +public: + explicit Binder(parser::Program *program) : program_(program), functionScopes_(Allocator()->Adapter()) {} + NO_COPY_SEMANTIC(Binder); + DEFAULT_MOVE_SEMANTIC(Binder); + ~Binder() = default; + + void InitTopScope(); + void IdentifierAnalysis(); + + template + T *AddDecl(const lexer::SourcePosition &pos, Args &&... args); + + template + T *AddTsDecl(const lexer::SourcePosition &pos, Args &&... args); + + ParameterDecl *AddParamDecl(const ir::AstNode *param); + + Scope *GetScope() const + { + return scope_; + } + + GlobalScope *TopScope() const + { + return topScope_; + } + + [[noreturn]] void ThrowRedeclaration(const lexer::SourcePosition &pos, const util::StringView &name); + + template + friend class LexicalScope; + + inline ArenaAllocator *Allocator() const + { + return program_->Allocator(); + } + + const ArenaVector &Functions() const + { + return functionScopes_; + } + + ArenaVector Functions() + { + return functionScopes_; + } + + const parser::Program *Program() const + { + return program_; + } + + static constexpr std::string_view FUNCTION_ARGUMENTS = "arguments"; + static constexpr std::string_view MANDATORY_PARAM_FUNC = "=f"; + static constexpr std::string_view MANDATORY_PARAM_NEW_TARGET = "=nt"; + static constexpr std::string_view MANDATORY_PARAM_THIS = "=t"; + + static constexpr uint32_t MANDATORY_PARAM_FUNC_REG = 0; + static constexpr uint32_t MANDATORY_PARAMS_NUMBER = 3; + + static constexpr std::string_view LEXICAL_MANDATORY_PARAM_FUNC = "!f"; + static constexpr std::string_view LEXICAL_MANDATORY_PARAM_NEW_TARGET = "!nt"; + static constexpr std::string_view LEXICAL_MANDATORY_PARAM_THIS = "!t"; + +private: + using MandatoryParams = std::array; + + static constexpr MandatoryParams FUNCTION_MANDATORY_PARAMS = {MANDATORY_PARAM_FUNC, MANDATORY_PARAM_NEW_TARGET, + MANDATORY_PARAM_THIS}; + + static constexpr MandatoryParams ARROW_MANDATORY_PARAMS = {MANDATORY_PARAM_FUNC, LEXICAL_MANDATORY_PARAM_NEW_TARGET, + LEXICAL_MANDATORY_PARAM_THIS}; + + static constexpr MandatoryParams CTOR_ARROW_MANDATORY_PARAMS = { + LEXICAL_MANDATORY_PARAM_FUNC, LEXICAL_MANDATORY_PARAM_NEW_TARGET, LEXICAL_MANDATORY_PARAM_THIS}; + + void AddMandatoryParam(const std::string_view &name); + 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 BuildVarDeclarator(ir::VariableDeclarator *varDecl); + void BuildVarDeclaratorId(const ir::AstNode *parent, 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 BuildCatchClause(ir::CatchClause *catchClauseStmt); + void LookupIdentReference(ir::Identifier *ident); + void ResolveReference(const ir::AstNode *parent, ir::AstNode *childNode); + void ResolveReferences(const ir::AstNode *parent); + + parser::Program *program_ {}; + GlobalScope *topScope_ {}; + Scope *scope_ {}; + ArenaVector functionScopes_; +}; + +template +class LexicalScope { +public: + template + explicit LexicalScope(Binder *binder, Args &&... args) + : LexicalScope(binder->Allocator()->New(binder->Allocator(), binder->scope_, std::forward(args)...), + binder) + { + } + + T *GetScope() const + { + return scope_; + } + + ~LexicalScope() + { + ASSERT(binder_); + binder_->scope_ = prevScope_; + } + + [[nodiscard]] static LexicalScope Enter(Binder *binder, T *scope) + { + LexicalScope lexScope(scope, binder); + return lexScope; + } + + DEFAULT_MOVE_SEMANTIC(LexicalScope); + +private: + NO_COPY_SEMANTIC(LexicalScope); + + explicit LexicalScope(T *scope, Binder *binder) : binder_(binder), scope_(scope), prevScope_(binder->scope_) + { + binder_->scope_ = scope_; + } + + Binder *binder_ {}; + T *scope_ {}; + Scope *prevScope_ {}; +}; + +template +T *Binder::AddTsDecl(const lexer::SourcePosition &pos, Args &&... args) +{ + T *decl = Allocator()->New(std::forward(args)...); + + if (scope_->AddTsDecl(Allocator(), decl, program_->Extension())) { + return decl; + } + + ThrowRedeclaration(pos, decl->Name()); +} + +template +T *Binder::AddDecl(const lexer::SourcePosition &pos, Args &&... args) +{ + T *decl = Allocator()->New(std::forward(args)...); + + if (scope_->AddDecl(Allocator(), decl, program_->Extension())) { + return decl; + } + + ThrowRedeclaration(pos, decl->Name()); +} +} // namespace panda::es2panda::binder + +#endif diff --git a/es2panda/binder/declaration.cpp b/es2panda/binder/declaration.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0af567336506887c4beb0d376507d1762615de4b --- /dev/null +++ b/es2panda/binder/declaration.cpp @@ -0,0 +1,20 @@ +/** + * 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 "declaration.h" + +namespace panda::es2panda::binder { + +} // namespace panda::es2panda::binder diff --git a/es2panda/binder/declaration.h b/es2panda/binder/declaration.h new file mode 100644 index 0000000000000000000000000000000000000000..ab65a7579ac6db9768e837f80d82e57f8cbcdd62 --- /dev/null +++ b/es2panda/binder/declaration.h @@ -0,0 +1,313 @@ +/** + * 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_SCOPES_DECLARATION_H +#define ES2PANDA_COMPILER_SCOPES_DECLARATION_H + +#include +#include +#include + +namespace panda::es2panda::ir { +class AstNode; +class FunctionDeclaration; +class TSInterfaceDeclaration; +class ImportDeclaration; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::binder { + +class Scope; +class LocalScope; + +#define DECLARE_CLASSES(decl_kind, className) class className; +DECLARATION_KINDS(DECLARE_CLASSES) +#undef DECLARE_CLASSES + +class Decl { +public: + virtual ~Decl() = default; + NO_COPY_SEMANTIC(Decl); + NO_MOVE_SEMANTIC(Decl); + + virtual DeclType Type() const = 0; + + const util::StringView &Name() const + { + return name_; + } + + const ir::AstNode *Node() const + { + return node_; + } + +#define DECLARE_CHECKS_CASTS(declKind, className) \ + bool Is##className() const \ + { \ + return Type() == DeclType::declKind; \ + } \ + className *As##className() \ + { \ + ASSERT(Is##className()); \ + return reinterpret_cast(this); \ + } \ + const className *As##className() const \ + { \ + ASSERT(Is##className()); \ + return reinterpret_cast(this); \ + } + DECLARATION_KINDS(DECLARE_CHECKS_CASTS) +#undef DECLARE_CHECKS_CASTS + + void BindNode(const ir::AstNode *node) + { + node_ = node; + } + + bool IsLetOrConstDecl() const + { + return IsLetDecl() || IsConstDecl(); + } + +protected: + explicit Decl(util::StringView name) : name_(name) {} + + util::StringView name_; + const ir::AstNode *node_ {}; +}; + +template +class MultiDecl : public Decl { +public: + explicit MultiDecl(ArenaAllocator *allocator, util::StringView name) + : Decl(name), declarations_(allocator->Adapter()) + { + } + + const ArenaVector &Decls() const + { + return declarations_; + } + + void Add(T *decl) + { + declarations_.push_back(decl); + } + +private: + ArenaVector declarations_; +}; + +class EnumLiteralDecl : public Decl { +public: + explicit EnumLiteralDecl(util::StringView name, bool isConst) : Decl(name), isConst_(isConst) {} + + DeclType Type() const override + { + return DeclType::ENUM_LITERAL; + } + + bool IsConst() const + { + return isConst_; + } + + void BindScope(LocalScope *scope) + { + scope_ = scope; + } + + LocalScope *Scope() + { + return scope_; + } + +private: + LocalScope *scope_ {}; + bool isConst_ {}; +}; + +class InterfaceDecl : public MultiDecl { +public: + explicit InterfaceDecl(ArenaAllocator *allocator, util::StringView name) : MultiDecl(allocator, name) {} + + DeclType Type() const override + { + return DeclType::INTERFACE; + } +}; + +class FunctionDecl : public MultiDecl { +public: + explicit FunctionDecl(ArenaAllocator *allocator, util::StringView name, const ir::AstNode *node) + : MultiDecl(allocator, name) + { + node_ = node; + } + + DeclType Type() const override + { + return DeclType::FUNC; + } +}; + +class TypeParameterDecl : public Decl { +public: + explicit TypeParameterDecl(util::StringView name, const ir::AstNode *node); + + DeclType Type() const override + { + return DeclType::TYPE_PARAMETER; + } +}; + +class EnumDecl : public Decl { +public: + explicit EnumDecl(util::StringView name) : Decl(name) {} + + DeclType Type() const override + { + return DeclType::ENUM; + } +}; + +class TypeAliasDecl : public Decl { +public: + explicit TypeAliasDecl(util::StringView name) : Decl(name) {} + + DeclType Type() const override + { + return DeclType::TYPE_ALIAS; + } +}; + +class NameSpaceDecl : public Decl { +public: + explicit NameSpaceDecl(util::StringView name) : Decl(name) {} + + DeclType Type() const override + { + return DeclType::NAMESPACE; + } +}; + +class VarDecl : public Decl { +public: + explicit VarDecl(util::StringView name) : Decl(name) {} + + DeclType Type() const override + { + return DeclType::VAR; + } +}; + +class LetDecl : public Decl { +public: + explicit LetDecl(util::StringView name) : Decl(name) {} + + DeclType Type() const override + { + return DeclType::LET; + } +}; + +class ConstDecl : public Decl { +public: + explicit ConstDecl(util::StringView name) : Decl(name) {} + + DeclType Type() const override + { + return DeclType::CONST; + } +}; + +class ParameterDecl : public Decl { +public: + explicit ParameterDecl(util::StringView name) : Decl(name) {} + + DeclType Type() const override + { + return DeclType::PARAM; + } +}; + +class ImportDecl : public Decl { +public: + explicit ImportDecl(util::StringView importName, util::StringView localName) + : Decl(localName), importName_(importName) + { + } + + explicit ImportDecl(util::StringView importName, util::StringView localName, const ir::AstNode *node) + : Decl(localName), importName_(importName) + { + BindNode(node); + } + + const util::StringView &ImportName() const + { + return importName_; + } + + const util::StringView &LocalName() const + { + return name_; + } + + DeclType Type() const override + { + return DeclType::IMPORT; + } + +private: + util::StringView importName_; +}; + +class ExportDecl : public Decl { +public: + explicit ExportDecl(util::StringView exportName, util::StringView localName) + : Decl(localName), exportName_(exportName) + { + } + + explicit ExportDecl(util::StringView exportName, util::StringView localName, const ir::AstNode *node) + : Decl(localName), exportName_(exportName) + { + BindNode(node); + } + + const util::StringView &ExportName() const + { + return exportName_; + } + + const util::StringView &LocalName() const + { + return name_; + } + + DeclType Type() const override + { + return DeclType::EXPORT; + } + +private: + util::StringView exportName_; +}; + +} // namespace panda::es2panda::binder + +#endif diff --git a/es2panda/binder/enumMemberResult.h b/es2panda/binder/enumMemberResult.h new file mode 100644 index 0000000000000000000000000000000000000000..e0acb7709c25e8dd9bebaf82a7f38d95ef93ea1a --- /dev/null +++ b/es2panda/binder/enumMemberResult.h @@ -0,0 +1,30 @@ +/** + * 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_SCOPES_ENUM_MEMBER_RESULT_H +#define ES2PANDA_COMPILER_SCOPES_ENUM_MEMBER_RESULT_H + +#include + +#include + +namespace panda::es2panda::binder { + +// Note: if the bool operand is present, the expression cannot be evaluated during compliation +using EnumMemberResult = std::variant; + +} // namespace panda::es2panda::binder + +#endif diff --git a/es2panda/binder/scope.cpp b/es2panda/binder/scope.cpp new file mode 100644 index 0000000000000000000000000000000000000000..402bdd7a0c77989d8eb0451300936e38afe78d8d --- /dev/null +++ b/es2panda/binder/scope.cpp @@ -0,0 +1,505 @@ +/** + * 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 "scope.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace panda::es2panda::binder { + +VariableScope *Scope::EnclosingVariableScope() +{ + Scope *iter = this; + + while (iter) { + if (iter->IsVariableScope()) { + return iter->AsVariableScope(); + } + + iter = iter->Parent(); + } + + return nullptr; +} + +Variable *Scope::FindLocal(const util::StringView &name, ResolveBindingOptions options) const +{ + if (options & ResolveBindingOptions::INTERFACES) { + util::StringView interfaceNameView(binder::TSBinding::ToTSBinding(name)); + + auto res = bindings_.find(interfaceNameView); + if (res != bindings_.end()) { + return res->second; + } + + if (!(options & ResolveBindingOptions::BINDINGS)) { + return nullptr; + } + } + + auto res = bindings_.find(name); + if (res == bindings_.end()) { + return nullptr; + } + + return res->second; +} + +ScopeFindResult Scope::Find(const util::StringView &name, ResolveBindingOptions options) const +{ + uint32_t level = 0; + uint32_t lexLevel = 0; + const auto *iter = this; + + while (iter != nullptr) { + Variable *v = iter->FindLocal(name, options); + + if (v != nullptr) { + return {name, const_cast(iter), level, lexLevel, v}; + } + + if (iter->IsVariableScope()) { + level++; + + if (iter->AsVariableScope()->NeedLexEnv()) { + lexLevel++; + } + } + + iter = iter->Parent(); + } + + return {name, nullptr, 0, 0, nullptr}; +} + +Decl *Scope::FindDecl(const util::StringView &name) const +{ + for (auto *it : decls_) { + if (it->Name() == name) { + return it; + } + } + + return nullptr; +} + +std::tuple Scope::IterateShadowedVariables(const util::StringView &name, const VariableVisitior &visitor) +{ + auto *iter = this; + + while (true) { + auto *v = iter->FindLocal(name); + + if (v && visitor(v)) { + return {iter, true}; + } + + if (iter->IsFunctionVariableScope()) { + break; + } + + iter = iter->Parent(); + } + + return {iter, false}; +} + +bool Scope::AddLocal(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) +{ + VariableFlags flags = VariableFlags::NONE; + switch (newDecl->Type()) { + case DeclType::VAR: { + auto [scope, shadowed] = IterateShadowedVariables( + newDecl->Name(), [](const Variable *v) { return !v->HasFlag(VariableFlags::VAR); }); + + if (shadowed) { + return false; + } + + 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 true; + } + case DeclType::ENUM: { + bindings_.insert({newDecl->Name(), allocator->New(newDecl, false)}); + return true; + } + case DeclType::ENUM_LITERAL: { + bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::ENUM_LITERAL)}); + return true; + } + case DeclType::INTERFACE: { + bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::INTERFACE)}); + return true; + } + case DeclType::FUNC: { + flags = VariableFlags::HOIST; + [[fallthrough]]; + } + default: { + if (currentVariable) { + return false; + } + + auto [_, shadowed] = IterateShadowedVariables( + newDecl->Name(), [](const Variable *v) { return v->HasFlag(VariableFlags::LEXICAL_VAR); }); + (void)_; + + if (shadowed) { + return false; + } + + bindings_.insert({newDecl->Name(), allocator->New(newDecl, flags)}); + return true; + } + } +} + +bool ParamScope::AddParam(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, VariableFlags flags) +{ + ASSERT(newDecl->IsParameterDecl()); + + if (currentVariable) { + return false; + } + + auto *param = allocator->New(newDecl, flags); + + params_.push_back(param); + bindings_.insert({newDecl->Name(), param}); + return true; +} + +std::tuple ParamScope::AddParamDecl(ArenaAllocator *allocator, + const ir::AstNode *param) +{ + const auto [name, pattern] = util::Helpers::ParamName(allocator, param, params_.size()); + + auto *decl = NewDecl(allocator, name); + + if (!AddParam(allocator, FindLocal(name), decl, VariableFlags::VAR)) { + return {decl, param}; + } + + if (!pattern) { + return {decl, nullptr}; + } + + std::vector bindings = util::Helpers::CollectBindingNames(param); + + for (const auto *binding : bindings) { + auto *varDecl = NewDecl(allocator, binding->Name()); + varDecl->BindNode(binding); + + if (FindLocal(varDecl->Name())) { + return {decl, binding}; + } + + auto *paramVar = allocator->New(varDecl, VariableFlags::VAR); + bindings_.insert({varDecl->Name(), paramVar}); + } + + return {decl, nullptr}; +} + +void FunctionParamScope::BindName(ArenaAllocator *allocator, util::StringView name) +{ + nameVar_ = AddDecl(allocator, name, VariableFlags::INITIALIZED); + functionScope_->Bindings().insert({name, nameVar_}); +} + +bool 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) +{ + switch (newDecl->Type()) { + case DeclType::VAR: { + return AddVar(allocator, currentVariable, newDecl); + } + case DeclType::FUNC: { + return AddFunction(allocator, currentVariable, newDecl, extension); + } + case DeclType::ENUM: { + bindings_.insert({newDecl->Name(), allocator->New(newDecl, false)}); + return true; + } + case DeclType::ENUM_LITERAL: { + return AddTSBinding(allocator, currentVariable, newDecl, VariableFlags::ENUM_LITERAL); + } + case DeclType::INTERFACE: { + return AddTSBinding(allocator, currentVariable, newDecl, VariableFlags::INTERFACE); + } + default: { + return AddLexical(allocator, currentVariable, newDecl); + } + } +} + +bool GlobalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) +{ + switch (newDecl->Type()) { + case DeclType::VAR: { + return AddVar(allocator, currentVariable, newDecl); + } + case DeclType::FUNC: { + return AddFunction(allocator, currentVariable, newDecl, extension); + } + case DeclType::ENUM: { + bindings_.insert({newDecl->Name(), allocator->New(newDecl, false)}); + return true; + } + case DeclType::ENUM_LITERAL: { + return AddTSBinding(allocator, currentVariable, newDecl, VariableFlags::ENUM_LITERAL); + } + case DeclType::INTERFACE: { + return AddTSBinding(allocator, currentVariable, newDecl, VariableFlags::INTERFACE); + } + default: { + return AddLexical(allocator, currentVariable, newDecl); + } + } + + return true; +} + +// ModuleScope + +bool ModuleScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) +{ + switch (newDecl->Type()) { + case DeclType::VAR: { + return AddVar(allocator, currentVariable, newDecl); + } + case DeclType::FUNC: { + return AddFunction(allocator, currentVariable, newDecl, extension); + } + case DeclType::ENUM: { + bindings_.insert({newDecl->Name(), allocator->New(newDecl, false)}); + return true; + } + case DeclType::ENUM_LITERAL: { + return AddTSBinding(allocator, currentVariable, newDecl, VariableFlags::ENUM_LITERAL); + } + case DeclType::INTERFACE: { + return AddTSBinding(allocator, currentVariable, newDecl, VariableFlags::INTERFACE); + } + case DeclType::IMPORT: { + return AddImport(allocator, currentVariable, newDecl); + } + case DeclType::EXPORT: { + return true; + } + default: { + return AddLexical(allocator, currentVariable, newDecl); + } + } +} + +void ModuleScope::AddImportDecl(const ir::ImportDeclaration *importDecl, ImportDeclList &&decls) +{ + auto res = imports_.emplace_back(importDecl, decls); + + for (auto &decl : res.second) { + decl->BindNode(importDecl); + } +} + +void ModuleScope::AddExportDecl(const ir::AstNode *exportDecl, ExportDecl *decl) +{ + decl->BindNode(exportDecl); + + ArenaVector decls(allocator_->Adapter()); + decls.push_back(decl); + + AddExportDecl(exportDecl, std::move(decls)); +} + +void ModuleScope::AddExportDecl(const ir::AstNode *exportDecl, ExportDeclList &&decls) +{ + auto res = exports_.emplace_back(exportDecl, decls); + + for (auto &decl : res.second) { + decl->BindNode(exportDecl); + } +} + +bool ModuleScope::AddImport(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl) +{ + if (currentVariable && currentVariable->Declaration()->Type() != DeclType::VAR) { + return false; + } + + 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 true; +} + +bool ModuleScope::ExportAnalysis() +{ + std::set exportedNames; + + for (const auto &[exportDecl, decls] : exports_) { + if (exportDecl->IsExportAllDeclaration()) { + const auto *exportAllDecl = exportDecl->AsExportAllDeclaration(); + + if (exportAllDecl->Exported() != nullptr) { + auto result = exportedNames.insert(exportAllDecl->Exported()->Name()); + if (!result.second) { + return false; + } + } + + continue; + } + + if (exportDecl->IsExportNamedDeclaration()) { + const auto *exportNamedDecl = exportDecl->AsExportNamedDeclaration(); + + if (exportNamedDecl->Source()) { + continue; + } + } + + for (const auto *decl : decls) { + binder::Variable *variable = FindLocal(decl->LocalName()); + + if (!variable) { + continue; + } + + auto result = exportedNames.insert(decl->ExportName()); + if (!result.second) { + return false; + } + + if (!variable->IsModuleVariable()) { + variable->AddFlag(VariableFlags::LOCAL_EXPORT); + localExports_.insert({variable, decl->ExportName()}); + } + } + } + + return true; +} + +// LocalScope + +bool LocalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) +{ + return AddLocal(allocator, currentVariable, newDecl, extension); +} + +void LoopDeclarationScope::ConvertToVariableScope(ArenaAllocator *allocator) +{ + if (NeedLexEnv()) { + return; + } + + for (auto &[name, var] : bindings_) { + if (!var->LexicalBound() || !var->Declaration()->IsLetOrConstDecl()) { + continue; + } + + slotIndex_++; + loopType_ = ScopeType::LOOP_DECL; + auto *copiedVar = var->AsLocalVariable()->Copy(allocator, var->Declaration()); + copiedVar->AddFlag(VariableFlags::INITIALIZED | VariableFlags::PER_ITERATION); + var->AddFlag(VariableFlags::LOOP_DECL); + loopScope_->Bindings().insert({name, copiedVar}); + } + + if (loopType_ == ScopeType::LOOP_DECL) { + slotIndex_ = std::max(slotIndex_, parent_->EnclosingVariableScope()->LexicalSlots()); + initScope_ = allocator->New(allocator, parent_); + initScope_->BindNode(node_); + initScope_->Bindings() = bindings_; + } +} + +void LoopScope::ConvertToVariableScope(ArenaAllocator *allocator) +{ + declScope_->ConvertToVariableScope(allocator); + + if (loopType_ != ScopeType::LOCAL) { + return; + } + + for (const auto &[_, var] : bindings_) { + (void)_; + if (var->LexicalBound() && var->Declaration()->IsLetDecl()) { + ASSERT(declScope_->NeedLexEnv()); + loopType_ = ScopeType::LOOP; + break; + } + } + + if (loopType_ == ScopeType::LOOP) { + slotIndex_ = std::max(slotIndex_, declScope_->LexicalSlots()); + } +} + +bool 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) +{ + if (!newDecl->IsVarDecl() && paramScope_->FindLocal(newDecl->Name())) { + return false; + } + + return AddLocal(allocator, currentVariable, newDecl, extension); +} + +} // namespace panda::es2panda::binder diff --git a/es2panda/binder/scope.h b/es2panda/binder/scope.h new file mode 100644 index 0000000000000000000000000000000000000000..9621858f36bc2b887ed1fcbdbfc61b26cdb6b729 --- /dev/null +++ b/es2panda/binder/scope.h @@ -0,0 +1,742 @@ +/** + * 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_SCOPES_SCOPE_H +#define ES2PANDA_COMPILER_SCOPES_SCOPE_H + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace panda::es2panda::compiler { +class IRNode; +} // namespace panda::es2panda::compiler + +namespace panda::es2panda::binder { + +#define DECLARE_CLASSES(type, className) class className; +SCOPE_TYPES(DECLARE_CLASSES) +#undef DECLARE_CLASSES + +class Scope; +class VariableScope; +class Variable; + +using VariableMap = ArenaUnorderedMap; + +class ScopeFindResult { +public: + ScopeFindResult() = default; + ScopeFindResult(util::StringView n, Scope *s, uint32_t l, Variable *v) : ScopeFindResult(n, s, l, l, v) {} + ScopeFindResult(Scope *s, uint32_t l, uint32_t ll, Variable *v) : scope(s), level(l), lexLevel(ll), variable(v) {} + ScopeFindResult(util::StringView n, Scope *s, uint32_t l, uint32_t ll, Variable *v) + : name(n), scope(s), level(l), lexLevel(ll), variable(v) + { + } + + util::StringView name {}; + Scope *scope {}; + uint32_t level {}; + uint32_t lexLevel {}; + Variable *variable {}; +}; + +class Scope { +public: + virtual ~Scope() = default; + NO_COPY_SEMANTIC(Scope); + NO_MOVE_SEMANTIC(Scope); + + virtual ScopeType Type() const = 0; + +#define DECLARE_CHECKS_CASTS(scopeType, className) \ + bool Is##className() const \ + { \ + return Type() == ScopeType::scopeType; \ + } \ + className *As##className() \ + { \ + ASSERT(Is##className()); \ + return reinterpret_cast(this); \ + } \ + const className *As##className() const \ + { \ + ASSERT(Is##className()); \ + return reinterpret_cast(this); \ + } + SCOPE_TYPES(DECLARE_CHECKS_CASTS) +#undef DECLARE_CHECKS_CASTS + + bool IsVariableScope() const + { + return Type() > ScopeType::LOCAL; + } + + bool IsFunctionVariableScope() const + { + return Type() >= ScopeType::FUNCTION; + } + + FunctionScope *AsFunctionVariableScope() + { + ASSERT(IsFunctionVariableScope()); + return reinterpret_cast(this); + } + + const FunctionScope *AsFunctionVariableScope() const + { + ASSERT(IsFunctionVariableScope()); + return reinterpret_cast(this); + } + + VariableScope *AsVariableScope() + { + ASSERT(IsVariableScope()); + return reinterpret_cast(this); + } + + const VariableScope *AsVariableScope() const + { + ASSERT(IsVariableScope()); + return reinterpret_cast(this); + } + + VariableScope *EnclosingVariableScope(); + + const ArenaVector &Decls() const + { + return decls_; + } + + Scope *Parent() + { + return parent_; + } + + const Scope *Parent() const + { + return parent_; + } + + const compiler::IRNode *ScopeStart() const + { + return startIns_; + } + + const compiler::IRNode *ScopeEnd() const + { + return endIns_; + } + + void SetScopeStart(const compiler::IRNode *ins) + { + startIns_ = ins; + } + + void SetScopeEnd(const compiler::IRNode *ins) + { + endIns_ = ins; + } + + const ir::AstNode *Node() const + { + return node_; + } + + void BindNode(const ir::AstNode *node) + { + node_ = node; + } + + bool 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) + { + decls_.push_back(decl); + return AddBinding(allocator, FindLocal(decl->Name(), ResolveBindingOptions::ALL), decl, extension); + } + + template + T *NewDecl(ArenaAllocator *allocator, Args &&... args); + + template + VariableType *AddDecl(ArenaAllocator *allocator, util::StringView name, VariableFlags flags); + + template + static VariableType *CreateVar(ArenaAllocator *allocator, util::StringView name, VariableFlags flags, + const ir::AstNode *node); + + template + void PropagateBinding(ArenaAllocator *allocator, util::StringView name, Args &&... args); + + VariableMap &Bindings() + { + return bindings_; + } + + const VariableMap &Bindings() const + { + return bindings_; + } + + virtual bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) = 0; + + Variable *FindLocal(const util::StringView &name, + ResolveBindingOptions options = ResolveBindingOptions::BINDINGS) const; + + ScopeFindResult Find(const util::StringView &name, + ResolveBindingOptions options = ResolveBindingOptions::BINDINGS) const; + + Decl *FindDecl(const util::StringView &name) const; + +protected: + explicit Scope(ArenaAllocator *allocator, Scope *parent) + : parent_(parent), decls_(allocator->Adapter()), bindings_(allocator->Adapter()) + { + } + + /** + * @return true - if the variable is shadowed + * false - otherwise + */ + using VariableVisitior = std::function; + + /** + * @return true - if the variable is shadowed + * false - otherwise + */ + std::tuple IterateShadowedVariables(const util::StringView &name, const VariableVisitior &visitor); + + bool AddLocal(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension); + + Scope *parent_ {}; + ArenaVector decls_; + VariableMap bindings_; + const ir::AstNode *node_ {}; + const compiler::IRNode *startIns_ {}; + const compiler::IRNode *endIns_ {}; +}; + +class VariableScope : public Scope { +public: + ~VariableScope() override = default; + 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_++; + } + + uint32_t LexicalSlots() const + { + return slotIndex_; + } + + bool NeedLexEnv() const + { + return slotIndex_ != 0; + } + +protected: + explicit VariableScope(ArenaAllocator *allocator, Scope *parent) : Scope(allocator, parent) {} + + template + bool AddVar(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl); + + template + bool AddFunction(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension); + + template + bool AddTSBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, VariableFlags flags); + + template + bool AddLexical(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl); + + VariableScopeFlags flags_ {}; + uint32_t slotIndex_ {}; +}; + +class ParamScope : public Scope { +public: + ScopeType Type() const override + { + return ScopeType::PARAM; + } + + ArenaVector &Params() + { + return params_; + } + + const ArenaVector &Params() const + { + return params_; + } + + std::tuple AddParamDecl(ArenaAllocator *allocator, const ir::AstNode *param); + +protected: + explicit ParamScope(ArenaAllocator *allocator, Scope *parent) + : Scope(allocator, parent), params_(allocator->Adapter()) + { + } + + bool AddParam(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, VariableFlags flags); + + ArenaVector params_; +}; + +class FunctionScope; + +class FunctionParamScope : public ParamScope { +public: + explicit FunctionParamScope(ArenaAllocator *allocator, Scope *parent) : ParamScope(allocator, parent) {} + + FunctionScope *GetFunctionScope() const + { + return functionScope_; + } + + void BindFunctionScope(FunctionScope *funcScope) + { + functionScope_ = funcScope; + } + + LocalVariable *NameVar() const + { + return nameVar_; + } + + void BindName(ArenaAllocator *allocator, util::StringView name); + + ScopeType Type() const override + { + return ScopeType::FUNCTION_PARAM; + } + + bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override; + + friend class FunctionScope; + template + friend class ScopeWithParamScope; + +private: + FunctionScope *functionScope_ {}; + LocalVariable *nameVar_ {}; +}; + +template +class ScopeWithParamScope : public E { +public: + explicit ScopeWithParamScope(ArenaAllocator *allocator, Scope *parent) : E(allocator, parent) {} + + void BindParamScope(T *paramScope) + { + AssignParamScope(paramScope); + this->bindings_ = paramScope->Bindings(); + } + + void AssignParamScope(T *paramScope) + { + ASSERT(this->parent_ == paramScope); + ASSERT(this->bindings_.empty()); + + paramScope_ = paramScope; + } + + T *ParamScope() + { + return paramScope_; + } + + const T *ParamScope() const + { + return paramScope_; + } + +protected: + T *paramScope_; +}; + +class FunctionScope : public ScopeWithParamScope { +public: + explicit FunctionScope(ArenaAllocator *allocator, Scope *parent) : ScopeWithParamScope(allocator, parent) {} + + ScopeType Type() const override + { + return ScopeType::FUNCTION; + } + + void BindName(util::StringView name, util::StringView internalName) + { + name_ = name; + internalName_ = internalName; + } + + const util::StringView &Name() const + { + return name_; + } + + const util::StringView &InternalName() const + { + return internalName_; + } + + bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override; + +private: + util::StringView name_ {}; + util::StringView internalName_ {}; +}; + +class LocalScope : public Scope { +public: + explicit LocalScope(ArenaAllocator *allocator, Scope *parent) : Scope(allocator, parent) {} + + ScopeType Type() const override + { + return ScopeType::LOCAL; + } + + bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override; +}; + +class CatchParamScope : public ParamScope { +public: + explicit CatchParamScope(ArenaAllocator *allocator, Scope *parent) : ParamScope(allocator, parent) {} + + ScopeType Type() const override + { + return ScopeType::CATCH_PARAM; + } + + bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override; + + friend class CatchScope; +}; + +class CatchScope : public ScopeWithParamScope { +public: + explicit CatchScope(ArenaAllocator *allocator, Scope *parent) : ScopeWithParamScope(allocator, parent) {} + + ScopeType Type() const override + { + return ScopeType::CATCH; + } + + bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override; +}; + +class LoopScope; + +class LoopDeclarationScope : public VariableScope { +public: + explicit LoopDeclarationScope(ArenaAllocator *allocator, Scope *parent) : VariableScope(allocator, parent) {} + + ScopeType Type() const override + { + return loopType_; + } + + bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override + { + return AddLocal(allocator, currentVariable, newDecl, extension); + } + + Scope *InitScope() + { + if (NeedLexEnv()) { + return initScope_; + } + + return this; + } + + void ConvertToVariableScope(ArenaAllocator *allocator); + +private: + friend class LoopScope; + LoopScope *loopScope_ {}; + LocalScope *initScope_ {}; + ScopeType loopType_ {ScopeType::LOCAL}; +}; + +class LoopScope : public VariableScope { +public: + explicit LoopScope(ArenaAllocator *allocator, Scope *parent) : VariableScope(allocator, parent) {} + + LoopDeclarationScope *DeclScope() + { + return declScope_; + } + + void BindDecls(LoopDeclarationScope *declScope) + { + declScope_ = declScope; + declScope_->loopScope_ = this; + } + + ScopeType Type() const override + { + return loopType_; + } + + void ConvertToVariableScope(ArenaAllocator *allocator); + + bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override + { + return AddLocal(allocator, currentVariable, newDecl, extension); + } + +protected: + LoopDeclarationScope *declScope_ {}; + ScopeType loopType_ {ScopeType::LOCAL}; +}; + +class GlobalScope : public FunctionScope { +public: + explicit GlobalScope(ArenaAllocator *allocator) : FunctionScope(allocator, nullptr) + { + auto *paramScope = allocator->New(allocator, this); + paramScope_ = paramScope; + } + + ScopeType Type() const override + { + return ScopeType::GLOBAL; + } + + bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override; +}; + +class ModuleScope : public GlobalScope { +public: + template + using ModuleEntry = ArenaVector>; + using ImportDeclList = ArenaVector; + using ExportDeclList = ArenaVector; + using LocalExportNameMap = ArenaMultiMap; + + explicit ModuleScope(ArenaAllocator *allocator) + : GlobalScope(allocator), + allocator_(allocator), + imports_(allocator_->Adapter()), + exports_(allocator_->Adapter()), + localExports_(allocator_->Adapter()) + { + } + + ScopeType Type() const override + { + return ScopeType::MODULE; + } + + const ModuleEntry &Imports() const + { + return imports_; + } + + const ModuleEntry &Exports() const + { + return exports_; + } + + const LocalExportNameMap &LocalExports() const + { + return localExports_; + } + + void AddImportDecl(const ir::ImportDeclaration *importDecl, ImportDeclList &&decls); + + void AddExportDecl(const ir::AstNode *exportDecl, ExportDecl *decl); + + void AddExportDecl(const ir::AstNode *exportDecl, ExportDeclList &&decls); + + bool AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, + [[maybe_unused]] ScriptExtension extension) override; + + bool ExportAnalysis(); + +private: + bool AddImport(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl); + + ArenaAllocator *allocator_; + ModuleEntry imports_; + ModuleEntry exports_; + LocalExportNameMap localExports_; +}; + +template +bool VariableScope::AddVar(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl) +{ + if (!currentVariable) { + bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::HOIST_VAR)}); + return true; + } + + switch (currentVariable->Declaration()->Type()) { + case DeclType::VAR: { + currentVariable->Reset(newDecl, VariableFlags::HOIST_VAR); + break; + } + case DeclType::PARAM: + case DeclType::FUNC: { + break; + } + default: { + return false; + } + } + + return true; +} + +template +bool 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; + } + + if (extension != ScriptExtension::JS || IsModuleScope()) { + return false; + } + + switch (currentVariable->Declaration()->Type()) { + case DeclType::VAR: + case DeclType::FUNC: { + currentVariable->Reset(newDecl, VariableFlags::HOIST_VAR); + break; + } + default: { + return false; + } + } + + return true; +} + +template +bool VariableScope::AddTSBinding(ArenaAllocator *allocator, [[maybe_unused]] Variable *currentVariable, Decl *newDecl, + VariableFlags flags) +{ + ASSERT(!currentVariable); + bindings_.insert({newDecl->Name(), allocator->New(newDecl, flags)}); + return true; +} + +template +bool VariableScope::AddLexical(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl) +{ + if (currentVariable) { + return false; + } + + bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::NONE)}); + return true; +} + +template +T *Scope::NewDecl(ArenaAllocator *allocator, Args &&... args) +{ + T *decl = allocator->New(std::forward(args)...); + decls_.push_back(decl); + + return decl; +} + +template +VariableType *Scope::AddDecl(ArenaAllocator *allocator, util::StringView name, VariableFlags flags) +{ + if (FindLocal(name)) { + return nullptr; + } + + auto *decl = allocator->New(name); + auto *variable = allocator->New(decl, flags); + + decls_.push_back(decl); + bindings_.insert({decl->Name(), variable}); + + return variable; +} + +template +VariableType *Scope::CreateVar(ArenaAllocator *allocator, util::StringView name, VariableFlags flags, + const ir::AstNode *node) +{ + auto *decl = allocator->New(name); + auto *variable = allocator->New(decl, flags); + decl->BindNode(node); + return variable; +} + +template +void 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; + } + + res->second->Reset(std::forward(args)...); +} + +} // namespace panda::es2panda::binder + +#endif diff --git a/es2panda/binder/tsBinding.h b/es2panda/binder/tsBinding.h new file mode 100644 index 0000000000000000000000000000000000000000..4d586ceaf481ce0ef30f02d82e494f8f3732e50d --- /dev/null +++ b/es2panda/binder/tsBinding.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_BINDING_H +#define ES2PANDA_BINDER_TS_BINDING_H + +#include + +#include + +namespace panda::es2panda::binder { +// Note: if the bool operand is present, the expression cannot be evaluated during compliation +using EnumMemberResult = std::variant; + +class TSBinding : public util::UString { +public: + explicit TSBinding(ArenaAllocator *allocator, util::StringView name) + : util::UString(std::string {TS_PREFIX}, allocator) + { + Append(name); + } + + static std::string ToTSBinding(util::StringView name) + { + return std::string {TS_PREFIX}.append(name.Utf8()); + } + + static constexpr std::string_view TS_PREFIX = "#"; +}; +} // namespace panda::es2panda::binder + +#endif diff --git a/es2panda/binder/variable.cpp b/es2panda/binder/variable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fb57097d38c93a584423ed6ca4f3937b10ea614a --- /dev/null +++ b/es2panda/binder/variable.cpp @@ -0,0 +1,63 @@ +/** + * 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 "variable.h" + +#include + +#include + +namespace panda::es2panda::binder { + +LocalVariable::LocalVariable(Decl *decl, VariableFlags flags) : Variable(decl, flags) +{ + if (decl->IsConstDecl()) { + flags_ |= VariableFlags::READONLY; + } +} + +const util::StringView &Variable::Name() const +{ + return decl_->Name(); +} + +LocalVariable *LocalVariable::Copy(ArenaAllocator *allocator, Decl *decl) const +{ + auto *var = allocator->New(decl, flags_); + var->vreg_ = vreg_; + return var; +} + +void LocalVariable::SetLexical(Scope *scope) +{ + if (LexicalBound()) { + return; + } + + VariableScope *varScope = scope->EnclosingVariableScope(); + + BindLexEnvSlot(varScope->NextSlot()); +} + +void GlobalVariable::SetLexical([[maybe_unused]] Scope *scope) {} +void ModuleVariable::SetLexical([[maybe_unused]] Scope *scope) {} +void EnumVariable::SetLexical([[maybe_unused]] Scope *scope) {} + +void EnumVariable::ResetDecl(Decl *decl) +{ + decl_ = decl; +} + +} // namespace panda::es2panda::binder diff --git a/es2panda/binder/variable.h b/es2panda/binder/variable.h new file mode 100644 index 0000000000000000000000000000000000000000..4302048a8bf2ccafe9887e3574da5bfd7be16f1c --- /dev/null +++ b/es2panda/binder/variable.h @@ -0,0 +1,254 @@ +/** + * 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_SCOPES_VARIABLE_H +#define ES2PANDA_COMPILER_SCOPES_VARIABLE_H + +#include +#include +#include +#include +#include + +#include + +namespace panda::es2panda::checker { +class Type; +} // namespace panda::es2panda::checker + +namespace panda::es2panda::binder { + +class Decl; +class Scope; +class VariableScope; + +#define DECLARE_CLASSES(type, className) class className; +VARIABLE_TYPES(DECLARE_CLASSES) +#undef DECLARE_CLASSES + +class Variable { +public: + virtual ~Variable() = default; + NO_COPY_SEMANTIC(Variable); + NO_MOVE_SEMANTIC(Variable); + + VariableType virtual Type() const = 0; + +#define DECLARE_CHECKS_CASTS(variableType, className) \ + bool Is##className() const \ + { \ + return Type() == VariableType::variableType; \ + } \ + className *As##className() \ + { \ + ASSERT(Is##className()); \ + return reinterpret_cast(this); \ + } \ + const className *As##className() const \ + { \ + ASSERT(Is##className()); \ + return reinterpret_cast(this); \ + } + VARIABLE_TYPES(DECLARE_CHECKS_CASTS) +#undef DECLARE_CHECKS_CASTS + + Decl *Declaration() const + { + return decl_; + } + + VariableFlags Flags() const + { + return flags_; + } + + checker::Type *TsType() const + { + return tsType_; + } + + void SetTsType(checker::Type *tsType) + { + tsType_ = tsType; + } + + void AddFlag(VariableFlags flag) + { + flags_ |= flag; + } + + bool HasFlag(VariableFlags flag) const + { + return (flags_ & flag) != 0; + } + + void RemoveFlag(VariableFlags flag) + { + flags_ &= ~flag; + } + + void Reset(Decl *decl, VariableFlags flags) + { + decl_ = decl; + flags_ = flags; + } + + bool LexicalBound() const + { + return HasFlag(VariableFlags::LEXICAL_BOUND); + } + + const util::StringView &Name() const; + virtual void SetLexical(Scope *scope) = 0; + +protected: + explicit Variable(Decl *decl, VariableFlags flags) : decl_(decl), flags_(flags) {} + + Decl *decl_; + VariableFlags flags_ {}; + checker::Type *tsType_ {}; +}; + +class LocalVariable : public Variable { +public: + explicit LocalVariable(Decl *decl, VariableFlags flags); + + VariableType Type() const override + { + return VariableType::LOCAL; + } + + void BindVReg(compiler::VReg vreg) + { + ASSERT(!LexicalBound()); + vreg_ = vreg; + } + + void BindLexEnvSlot(uint32_t slot) + { + ASSERT(!LexicalBound()); + AddFlag(VariableFlags::LEXICAL_BOUND); + vreg_ = slot; + } + + compiler::VReg Vreg() const + { + return vreg_; + } + + uint32_t LexIdx() const + { + ASSERT(LexicalBound()); + return vreg_; + } + + void SetLexical([[maybe_unused]] Scope *scope) override; + LocalVariable *Copy(ArenaAllocator *allocator, Decl *decl) const; + +private: + uint32_t vreg_ {}; +}; + +class GlobalVariable : public Variable { +public: + explicit GlobalVariable(Decl *decl, VariableFlags flags) : Variable(decl, flags) {} + + VariableType Type() const override + { + return VariableType::GLOBAL; + } + + void SetLexical([[maybe_unused]] Scope *scope) override; +}; + +class ModuleVariable : public Variable { +public: + explicit ModuleVariable(Decl *decl, VariableFlags flags) : Variable(decl, flags) {} + + VariableType Type() const override + { + return VariableType::MODULE; + } + + compiler::VReg &ModuleReg() + { + return moduleReg_; + } + + compiler::VReg ModuleReg() const + { + return moduleReg_; + } + + const util::StringView &ExoticName() const + { + return exoticName_; + } + + util::StringView &ExoticName() + { + return exoticName_; + } + + void SetLexical([[maybe_unused]] Scope *scope) override; + +private: + compiler::VReg moduleReg_ {}; + util::StringView exoticName_ {}; +}; + +class EnumVariable : public Variable { +public: + explicit EnumVariable(Decl *decl, bool backReference = false) + : Variable(decl, VariableFlags::NONE), backReference_(backReference) + { + } + + VariableType Type() const override + { + return VariableType::ENUM; + } + + void SetValue(EnumMemberResult value) + { + value_ = value; + } + + const EnumMemberResult &Value() const + { + return value_; + } + + bool BackReference() const + { + return backReference_; + } + + void SetBackReference() + { + backReference_ = true; + } + + void ResetDecl(Decl *decl); + + void SetLexical([[maybe_unused]] Scope *scope) override; + +private: + EnumMemberResult value_ {false}; + bool backReference_ {}; +}; + +} // namespace panda::es2panda::binder +#endif diff --git a/es2panda/binder/variableFlags.h b/es2panda/binder/variableFlags.h new file mode 100644 index 0000000000000000000000000000000000000000..dee8b1475741544e27355e61683873cafe375fb3 --- /dev/null +++ b/es2panda/binder/variableFlags.h @@ -0,0 +1,143 @@ +/** + * 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_SCOPES_VARIABLE_FLAGS_H +#define ES2PANDA_COMPILER_SCOPES_VARIABLE_FLAGS_H + +#include + +namespace panda::es2panda::binder { + +#define DECLARATION_KINDS(_) \ + _(VAR, VarDecl) \ + _(LET, LetDecl) \ + _(CONST, ConstDecl) \ + _(FUNC, FunctionDecl) \ + _(PARAM, ParameterDecl) \ + _(IMPORT, ImportDecl) \ + _(EXPORT, ExportDecl) \ + /* TS */ \ + _(TYPE_ALIAS, TypeAliasDecl) \ + _(NAMESPACE, NameSpaceDecl) \ + _(INTERFACE, InterfaceDecl) \ + _(ENUM_LITERAL, EnumLiteralDecl) \ + _(TYPE_PARAMETER, TypeParameterDecl) \ + _(ENUM, EnumDecl) + +enum class DeclType { + NONE, +#define DECLARE_TYPES(decl_kind, class_name) decl_kind, + DECLARATION_KINDS(DECLARE_TYPES) +#undef DECLARE_TYPES +}; + +#define SCOPE_TYPES(_) \ + _(PARAM, ParamScope) \ + _(CATCH_PARAM, CatchParamScope) \ + _(FUNCTION_PARAM, FunctionParamScope) \ + _(CATCH, CatchScope) \ + _(LOCAL, LocalScope) \ + /* Variable Scopes */ \ + _(LOOP, LoopScope) \ + _(LOOP_DECL, LoopDeclarationScope) \ + _(FUNCTION, FunctionScope) \ + _(GLOBAL, GlobalScope) \ + _(MODULE, ModuleScope) + +enum class ScopeType { +#define GEN_SCOPE_TYPES(type, class_name) type, + SCOPE_TYPES(GEN_SCOPE_TYPES) +#undef GEN_SCOPE_TYPES +}; + +enum class ResolveBindingOptions { + BINDINGS = 1U << 0U, + INTERFACES = 1U << 1U, + + ALL = BINDINGS | INTERFACES, +}; + +DEFINE_BITOPS(ResolveBindingOptions) + +#define VARIABLE_TYPES(_) \ + _(LOCAL, LocalVariable) \ + _(GLOBAL, GlobalVariable) \ + _(MODULE, ModuleVariable) \ + _(ENUM, EnumVariable) + +enum class VariableType { +#define GEN_VARIABLE_TYPES(type, class_name) type, + VARIABLE_TYPES(GEN_VARIABLE_TYPES) +#undef GEN_VARIABLE_TYPES +}; + +enum class VariableKind { + NONE, + VAR, + LEXICAL, + FUNCTION, + MODULE, +}; + +enum class VariableFlags { + NONE = 0, + OPTIONAL = 1 << 0, + PROPERTY = 1 << 1, + METHOD = 1 << 2, + TYPE_ALIAS = 1 << 3, + INTERFACE = 1 << 4, + ENUM_LITERAL = 1 << 5, + READONLY = 1 << 6, + COMPUTED = 1 << 7, + COMPUTED_IDENT = 1 << 8, + COMPUTED_INDEX = 1 << 9, + INDEX_NAME = 1 << 10, + LOCAL_EXPORT = 1 << 12, + INFERED_IN_PATTERN = 1 << 13, + REST_ARG = 1 << 14, + + INDEX_LIKE = COMPUTED_INDEX | INDEX_NAME, + + LOOP_DECL = 1 << 25, + PER_ITERATION = 1 << 26, + LEXICAL_VAR = 1 << 27, + HOIST = 1 << 28, + VAR = 1 << 29, + INITIALIZED = 1 << 30, + LEXICAL_BOUND = 1 << 31, + + HOIST_VAR = HOIST | VAR, +}; + +DEFINE_BITOPS(VariableFlags) + +enum class LetOrConstStatus { + INITIALIZED, + UNINITIALIZED, +}; + +enum class VariableScopeFlags { + NONE = 0, + SET_LEXICAL_FUNCTION = 1U << 0U, + USE_ARGS = 1U << 2U, + USE_SUPER = 1U << 3U, + INNER_ARROW = 1U << 4U, +}; + +DEFINE_BITOPS(VariableScopeFlags) + +} // namespace panda::es2panda::binder + +#endif diff --git a/es2panda/compiler/base/catchTable.cpp b/es2panda/compiler/base/catchTable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2bafa48df03d8dba1bf3f0b8c0751e40a208a176 --- /dev/null +++ b/es2panda/compiler/base/catchTable.cpp @@ -0,0 +1,27 @@ +/* + * 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 "catchTable.h" + +#include + +namespace panda::es2panda::compiler { + +TryLabelSet::TryLabelSet(PandaGen *pg) + : try_(pg->AllocLabel(), pg->AllocLabel()), catch_(pg->AllocLabel(), pg->AllocLabel()) +{ +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/base/catchTable.h b/es2panda/compiler/base/catchTable.h new file mode 100644 index 0000000000000000000000000000000000000000..9954ae9ff79d2901a4fb61cd0ce0848c5e67dca7 --- /dev/null +++ b/es2panda/compiler/base/catchTable.h @@ -0,0 +1,94 @@ +/** + * 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_BASE_CATCH_TABLE_H +#define ES2PANDA_COMPILER_BASE_CATCH_TABLE_H + +#include +#include + +namespace panda::es2panda::compiler { + +class PandaGen; + +class TryLabelSet { +public: + explicit TryLabelSet(PandaGen *pg); + + ~TryLabelSet() = default; + DEFAULT_COPY_SEMANTIC(TryLabelSet); + DEFAULT_MOVE_SEMANTIC(TryLabelSet); + + const LabelPair &TryLabelPair() const + { + return try_; + } + + const LabelPair &CatchLabelPair() const + { + return catch_; + } + + Label *TryBegin() const + { + return try_.Begin(); + } + + Label *TryEnd() const + { + return try_.End(); + } + + Label *CatchBegin() const + { + return catch_.Begin(); + } + + Label *CatchEnd() const + { + return catch_.End(); + } + +private: + LabelPair try_; + LabelPair catch_; +}; + +class CatchTable { +public: + CatchTable(PandaGen *pg, uint32_t depth) : labelSet_(pg), depth_(depth) {} + + ~CatchTable() = default; + NO_COPY_SEMANTIC(CatchTable); + NO_MOVE_SEMANTIC(CatchTable); + + const TryLabelSet &LabelSet() const + { + return labelSet_; + } + + uint32_t Depth() const + { + return depth_; + } + +private: + TryLabelSet labelSet_; + uint32_t depth_; +}; + +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/base/condition.cpp b/es2panda/compiler/base/condition.cpp new file mode 100644 index 0000000000000000000000000000000000000000..644e9195fecef0aaa030ac05f26f19ebe9c77636 --- /dev/null +++ b/es2panda/compiler/base/condition.cpp @@ -0,0 +1,94 @@ +/* + * 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 "condition.h" + +#include +#include +#include + +namespace panda::es2panda::compiler { + +void Condition::Compile(PandaGen *pg, 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_STRICT_EQUAL: + case lexer::TokenType::PUNCTUATOR_NOT_STRICT_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: { + // This is a special case + // These operators are expressed via cmp instructions and the following + // if-else branches. Condition also expressed via cmp instruction and + // the following if-else. + // the goal of this method is to merge these two sequences of instructions. + RegScope rs(pg); + VReg lhs = pg->AllocReg(); + + binExpr->Left()->Compile(pg); + pg->StoreAccumulator(binExpr, lhs); + binExpr->Right()->Compile(pg); + pg->Condition(binExpr, binExpr->OperatorType(), lhs, falseLabel); + return; + } + case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: { + binExpr->Left()->Compile(pg); + pg->ToBoolean(binExpr); + pg->BranchIfFalse(binExpr, falseLabel); + + binExpr->Right()->Compile(pg); + pg->ToBoolean(binExpr); + pg->BranchIfFalse(binExpr, falseLabel); + return; + } + case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: { + auto *endLabel = pg->AllocLabel(); + + binExpr->Left()->Compile(pg); + pg->ToBoolean(binExpr); + pg->BranchIfTrue(binExpr, endLabel); + + binExpr->Right()->Compile(pg); + pg->ToBoolean(binExpr); + pg->BranchIfFalse(binExpr, falseLabel); + pg->SetLabel(binExpr, endLabel); + return; + } + default: { + break; + } + } + } else if (expr->IsUnaryExpression() && + expr->AsUnaryExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK) { + expr->AsUnaryExpression()->Argument()->Compile(pg); + + pg->Negate(expr); + pg->BranchIfFalse(expr, falseLabel); + return; + } + + // General case including some binExpr i.E.(a+b) + expr->Compile(pg); + pg->ToBoolean(expr); + pg->BranchIfFalse(expr, falseLabel); +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/base/condition.h b/es2panda/compiler/base/condition.h new file mode 100644 index 0000000000000000000000000000000000000000..e6ad51d835e7314bf0b1826ffdf414c889627db5 --- /dev/null +++ b/es2panda/compiler/base/condition.h @@ -0,0 +1,35 @@ +/** + * 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_BASE_CONDITION_H +#define ES2PANDA_COMPILER_BASE_CONDITION_H + +#include + +namespace panda::es2panda::compiler { + +class PandaGen; +class Label; + +class Condition { +public: + Condition() = delete; + + static void Compile(PandaGen *pg, const ir::Expression *expr, Label *falseLabel); +}; + +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/base/destructuring.cpp b/es2panda/compiler/base/destructuring.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7ff756529f3b4f1b528f3245a35b19636af46963 --- /dev/null +++ b/es2panda/compiler/base/destructuring.cpp @@ -0,0 +1,262 @@ +/** + * 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 "destructuring.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace panda::es2panda::compiler { + +static void GenRestElement(PandaGen *pg, const ir::SpreadElement *restElement, + const DestructuringIterator &destIterator, bool isDeclaration) +{ + VReg array = pg->AllocReg(); + VReg index = pg->AllocReg(); + + auto *next = pg->AllocLabel(); + auto *done = pg->AllocLabel(); + + DestructuringRestIterator iterator(destIterator); + + // create left reference for rest element + LReference lref = LReference::CreateLRef(pg, restElement, isDeclaration); + + // create an empty array first + pg->CreateEmptyArray(restElement); + pg->StoreAccumulator(restElement, array); + + // index = 0 + pg->LoadAccumulatorInt(restElement, 0); + pg->StoreAccumulator(restElement, index); + + pg->SetLabel(restElement, next); + + iterator.Step(done); + pg->StoreObjByValue(restElement, array, index); + + // index++ + pg->LoadAccumulatorInt(restElement, 1); + pg->Binary(restElement, lexer::TokenType::PUNCTUATOR_PLUS, index); + pg->StoreAccumulator(restElement, index); + + pg->Branch(restElement, next); + + pg->SetLabel(restElement, done); + pg->LoadAccumulator(restElement, array); + + lref.SetValue(); +} + +static void GenArray(PandaGen *pg, const ir::ArrayExpression *array) +{ + // RegScope rs(pg); + DestructuringIterator iterator(pg, array); + + if (array->Elements().empty()) { + iterator.Close(false); + return; + } + + TryContext tryCtx(pg); + const auto &labelSet = tryCtx.LabelSet(); + pg->SetLabel(array, labelSet.TryBegin()); + + for (const auto *element : array->Elements()) { + RegScope ers(pg); + + if (element->IsRestElement()) { + GenRestElement(pg, element->AsRestElement(), iterator, array->IsDeclaration()); + break; + } + + // if a hole exist, just let the iterator step ahead + if (element->IsOmittedExpression()) { + iterator.Step(); + continue; + } + + const ir::Expression *init = nullptr; + const ir::Expression *target = element; + + if (element->IsAssignmentPattern()) { + target = element->AsAssignmentPattern()->Left(); + init = element->AsAssignmentPattern()->Right(); + } + + LReference lref = LReference::CreateLRef(pg, target, array->IsDeclaration()); + iterator.Step(); + + if (init) { + auto *assingValue = pg->AllocLabel(); + auto *defaultInit = pg->AllocLabel(); + pg->BranchIfUndefined(element, defaultInit); + pg->LoadAccumulator(element, iterator.Result()); + pg->Branch(element, assingValue); + + pg->SetLabel(element, defaultInit); + init->Compile(pg); + pg->SetLabel(element, assingValue); + } + + lref.SetValue(); + } + + pg->SetLabel(array, labelSet.TryEnd()); + + // Normal completion + pg->LoadAccumulator(array, iterator.Done()); + pg->BranchIfTrue(array, labelSet.CatchEnd()); + iterator.Close(false); + + pg->Branch(array, labelSet.CatchEnd()); + + Label *end = pg->AllocLabel(); + pg->SetLabel(array, labelSet.CatchBegin()); + pg->StoreAccumulator(array, iterator.Result()); + pg->LoadAccumulator(array, iterator.Done()); + + pg->BranchIfTrue(array, end); + pg->LoadAccumulator(array, iterator.Result()); + iterator.Close(true); + pg->SetLabel(array, end); + pg->LoadAccumulator(array, iterator.Result()); + pg->EmitThrow(array); + pg->SetLabel(array, labelSet.CatchEnd()); +} + +static void GenObjectProperty(PandaGen *pg, const ir::ObjectExpression *object, const ir::Expression *element, + VReg value, VReg propReg) +{ + RegScope propScope(pg); + VReg loadedValue = pg->AllocReg(); + + const ir::Property *propExpr = element->AsProperty(); + + const ir::Expression *init = nullptr; + const ir::Expression *key = propExpr->Key(); + const ir::Expression *target = propExpr->Value(); + + if (target->IsAssignmentPattern()) { + init = target->AsAssignmentPattern()->Right(); + target = target->AsAssignmentPattern()->Left(); + } + + // compile key + if (key->IsIdentifier()) { + pg->LoadAccumulatorString(key, key->AsIdentifier()->Name()); + } else { + key->Compile(pg); + } + + pg->StoreAccumulator(key, propReg); + + LReference lref = LReference::CreateLRef(pg, target, object->IsDeclaration()); + + // load obj property from rhs, return undefined if no corresponding property exists + pg->LoadObjByValue(element, value, propReg); + pg->StoreAccumulator(element, loadedValue); + + if (init != nullptr) { + auto *getDefault = pg->AllocLabel(); + auto *store = pg->AllocLabel(); + + pg->BranchIfUndefined(element, getDefault); + pg->LoadAccumulator(element, loadedValue); + pg->Branch(element, store); + + // load default value + pg->SetLabel(element, getDefault); + init->Compile(pg); + + pg->SetLabel(element, store); + } + + lref.SetValue(); +} + +static void GenObjectWithRest(PandaGen *pg, const ir::ObjectExpression *object, VReg rhs) +{ + const auto &properties = object->Properties(); + + RegScope rs(pg); + VReg propStart = pg->NextReg(); + + for (const auto *element : properties) { + if (element->IsRestElement()) { + RegScope restScope(pg); + LReference lref = LReference::CreateLRef(pg, element, object->IsDeclaration()); + pg->CreateObjectWithExcludedKeys(element, rhs, propStart, properties.size() - 1); + lref.SetValue(); + break; + } + + VReg propReg = pg->AllocReg(); + GenObjectProperty(pg, object, element, rhs, propReg); + } +} + +static void GenObject(PandaGen *pg, const ir::ObjectExpression *object, VReg rhs) +{ + const auto &properties = object->Properties(); + + if (properties.empty() || properties.back()->IsRestElement()) { + auto *notNullish = pg->AllocLabel(); + + pg->LoadAccumulator(object, rhs); + pg->BranchIfCoercible(object, notNullish); + pg->ThrowObjectNonCoercible(object); + + pg->SetLabel(object, notNullish); + + if (!properties.empty()) { + return GenObjectWithRest(pg, object, rhs); + } + } + + for (const auto *element : properties) { + RegScope rs(pg); + VReg propReg = pg->AllocReg(); + + GenObjectProperty(pg, object, element, rhs, propReg); + } +} + +void Destructuring::Compile(PandaGen *pg, const ir::Expression *pattern) +{ + RegScope rs(pg); + + VReg rhs = pg->AllocReg(); + pg->StoreAccumulator(pattern, rhs); + + if (pattern->IsArrayPattern()) { + GenArray(pg, pattern->AsArrayPattern()); + } else { + GenObject(pg, pattern->AsObjectPattern(), rhs); + } + + pg->LoadAccumulator(pattern, rhs); +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/base/destructuring.h b/es2panda/compiler/base/destructuring.h new file mode 100644 index 0000000000000000000000000000000000000000..081d7801b3d9415457ab05dd8605d258fc0f1d91 --- /dev/null +++ b/es2panda/compiler/base/destructuring.h @@ -0,0 +1,34 @@ +/* + * 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_BASE_DESTRUCTURING_H +#define ES2PANDA_COMPILER_BASE_DESTRUCTURING_H + +#include + +namespace panda::es2panda::compiler { + +class PandaGen; + +class Destructuring { +public: + Destructuring() = delete; + + static void Compile(PandaGen *pg, const ir::Expression *pattern); +}; + +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/base/hoisting.cpp b/es2panda/compiler/base/hoisting.cpp new file mode 100644 index 0000000000000000000000000000000000000000..07621e8d630c5793019c81bf2641fc2da9f5b9e2 --- /dev/null +++ b/es2panda/compiler/base/hoisting.cpp @@ -0,0 +1,81 @@ +/** + * 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 "hoisting.h" + +#include +#include +#include + +namespace panda::es2panda::compiler { + +static void HoistVar(PandaGen *pg, binder::Variable *var, const binder::VarDecl *decl) +{ + auto *scope = pg->Scope(); + + if (scope->IsGlobalScope()) { + pg->LoadConst(decl->Node(), Constant::JS_UNDEFINED); + pg->StoreGlobalVar(decl->Node(), decl->Name()); + return; + } + + binder::ScopeFindResult result(decl->Name(), scope, 0, var); + + pg->LoadConst(decl->Node(), Constant::JS_UNDEFINED); + pg->StoreAccToLexEnv(decl->Node(), result, true); +} + +static void HoistFunction(PandaGen *pg, binder::Variable *var, const binder::FunctionDecl *decl) +{ + const ir::ScriptFunction *scriptFunction = decl->Node()->AsScriptFunction(); + auto *scope = pg->Scope(); + + const auto &internalName = scriptFunction->Scope()->InternalName(); + + if (scope->IsGlobalScope()) { + pg->DefineFunction(decl->Node(), scriptFunction, internalName); + pg->StoreGlobalVar(decl->Node(), var->Declaration()->Name()); + return; + } + + ASSERT(scope->IsFunctionScope() || scope->IsCatchScope() || scope->IsLocalScope() || scope->IsModuleScope()); + binder::ScopeFindResult result(decl->Name(), scope, 0, var); + + pg->DefineFunction(decl->Node(), scriptFunction, internalName); + pg->StoreAccToLexEnv(decl->Node(), result, true); +} + +void Hoisting::Hoist(PandaGen *pg) +{ + const auto *scope = pg->Scope(); + + for (const auto &[_, var] : scope->Bindings()) { + (void)_; + if (!var->HasFlag(binder::VariableFlags::HOIST)) { + continue; + } + + const auto *decl = var->Declaration(); + + if (decl->IsVarDecl()) { + HoistVar(pg, var, decl->AsVarDecl()); + } else { + ASSERT(decl->IsFunctionDecl()); + HoistFunction(pg, var, decl->AsFunctionDecl()); + } + } +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/base/hoisting.h b/es2panda/compiler/base/hoisting.h new file mode 100644 index 0000000000000000000000000000000000000000..2448cf22281e170eda6c89b77aaea136c0f6804b --- /dev/null +++ b/es2panda/compiler/base/hoisting.h @@ -0,0 +1,30 @@ +/** + * 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_BASE_HOISTING_H +#define ES2PANDA_COMPILER_BASE_HOISTING_H + +namespace panda::es2panda::compiler { + +class PandaGen; + +class Hoisting { +public: + static void Hoist(PandaGen *pg); +}; + +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/base/iterators.cpp b/es2panda/compiler/base/iterators.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1bf7ad888797b1fb5f2c829aab527b4979a09c8a --- /dev/null +++ b/es2panda/compiler/base/iterators.cpp @@ -0,0 +1,216 @@ +/* + * 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 "iterators.h" + +#include +#include +#include + +namespace panda::es2panda::compiler { + +// Iterator + +Iterator::Iterator(PandaGen *pg, const ir::AstNode *node, IteratorType type) + : pg_(pg), node_(node), method_(pg->AllocReg()), iterator_(pg->AllocReg()), nextResult_(pg->AllocReg()), type_(type) +{ + if (type_ == IteratorType::ASYNC) { + pg_->GetAsyncIterator(node); + } else { + pg_->GetIterator(node); + } + + pg_->StoreAccumulator(node, iterator_); + pg_->LoadObjByName(node_, iterator_, "next"); + pg_->StoreAccumulator(node_, method_); + + pg_->ThrowIfNotObject(node_); +} + +void Iterator::GetMethod(util::StringView name) const +{ + pg_->GetMethod(node_, iterator_, name); + pg_->StoreAccumulator(node_, method_); +} + +void Iterator::CallMethodWithValue() const +{ + pg_->CallThis(node_, method_, 2); +} + +void Iterator::CallMethod() const +{ + pg_->CallThis(node_, method_, 1); +} + +void Iterator::Next() const +{ + CallMethod(); + + if (type_ == IteratorType::ASYNC) { + pg_->FuncBuilder()->Await(node_); + } + + pg_->ThrowIfNotObject(node_); + pg_->StoreAccumulator(node_, nextResult_); +} + +void Iterator::Complete() const +{ + pg_->LoadObjByName(node_, nextResult_, "done"); + pg_->ToBoolean(node_); +} + +void Iterator::Value() const +{ + pg_->LoadObjByName(node_, nextResult_, "value"); +} + +void Iterator::Close(bool abruptCompletion) const +{ + if (type_ == IteratorType::SYNC) { + if (!abruptCompletion) { + pg_->LoadConst(node_, Constant::JS_HOLE); + } + pg_->CloseIterator(node_, iterator_); + return; + } + + RegScope rs(pg_); + VReg completion = pg_->AllocReg(); + VReg innerResult = pg_->AllocReg(); + VReg innerResultType = pg_->AllocReg(); + + pg_->StoreAccumulator(node_, completion); + pg_->StoreConst(node_, innerResultType, Constant::JS_HOLE); + + TryContext tryCtx(pg_); + const auto &labelSet = tryCtx.LabelSet(); + Label *returnExits = pg_->AllocLabel(); + + pg_->SetLabel(node_, labelSet.TryBegin()); + + // 4. Let innerResult be GetMethod(iterator, "return"). + GetMethod("return"); + + // 5. If innerResult.[[Type]] is normal, then + { + // b. If return is undefined, return Completion(completion). + pg_->BranchIfNotUndefined(node_, returnExits); + // a. Let return be innerResult.[[Value]]. + pg_->LoadAccumulator(node_, completion); + + if (abruptCompletion) { + pg_->EmitThrow(node_); + } else { + pg_->DirectReturn(node_); + } + + pg_->SetLabel(node_, returnExits); + + { + TryContext innerTryCtx(pg_); + const auto &innerLabelSet = innerTryCtx.LabelSet(); + + pg_->SetLabel(node_, innerLabelSet.TryBegin()); + // c. Set innerResult to Call(return, iterator). + CallMethod(); + // d. If innerResult.[[Type]] is normal, set innerResult to Await(innerResult.[[Value]]). + pg_->FuncBuilder()->Await(node_); + pg_->StoreAccumulator(node_, innerResult); + pg_->SetLabel(node_, innerLabelSet.TryEnd()); + pg_->Branch(node_, innerLabelSet.CatchEnd()); + + pg_->SetLabel(node_, innerLabelSet.CatchBegin()); + pg_->StoreAccumulator(node_, innerResult); + pg_->StoreAccumulator(node_, innerResultType); + pg_->SetLabel(node_, innerLabelSet.CatchEnd()); + } + } + + pg_->SetLabel(node_, labelSet.TryEnd()); + pg_->Branch(node_, labelSet.CatchEnd()); + + pg_->SetLabel(node_, labelSet.CatchBegin()); + pg_->StoreAccumulator(node_, innerResult); + pg_->StoreAccumulator(node_, innerResultType); + pg_->SetLabel(node_, labelSet.CatchEnd()); + + // 6. If completion.[[Type]] is throw, return Completion(completion). + if (abruptCompletion) { + pg_->LoadAccumulator(node_, completion); + pg_->EmitThrow(node_); + } else { + // 7. If innerResult.[[Type]] is throw, return Completion(innerResult). + pg_->LoadAccumulator(node_, innerResultType); + pg_->EmitRethrow(node_); + } + + // 8. If Type(innerResult.[[Value]]) is not Object, throw a TypeError exception. + pg_->LoadAccumulator(node_, innerResult); + pg_->ThrowIfNotObject(node_); +} + +DestructuringIterator::DestructuringIterator(PandaGen *pg, const ir::AstNode *node) + : Iterator(pg, node, IteratorType::SYNC), done_(pg->AllocReg()), result_(pg->AllocReg()) +{ + pg_->StoreConst(node, done_, Constant::JS_FALSE); + pg_->StoreConst(node, result_, Constant::JS_UNDEFINED); +} + +void DestructuringIterator::Step(Label *doneTarget) const +{ + TryContext tryCtx(pg_); + const auto &labelSet = tryCtx.LabelSet(); + Label *normalClose = pg_->AllocLabel(); + Label *noClose = pg_->AllocLabel(); + + pg_->SetLabel(node_, labelSet.TryBegin()); + Next(); + Complete(); + pg_->StoreAccumulator(node_, done_); + pg_->BranchIfFalse(node_, normalClose); + pg_->StoreConst(node_, done_, Constant::JS_TRUE); + pg_->LoadConst(node_, Constant::JS_UNDEFINED); + OnIterDone(doneTarget); + pg_->Branch(node_, noClose); + + pg_->SetLabel(node_, normalClose); + Value(); + pg_->StoreAccumulator(node_, result_); + pg_->SetLabel(node_, noClose); + + pg_->SetLabel(node_, labelSet.TryEnd()); + pg_->Branch(node_, labelSet.CatchEnd()); + + pg_->SetLabel(node_, labelSet.CatchBegin()); + pg_->StoreAccumulator(node_, result_); + pg_->StoreConst(node_, done_, Constant::JS_TRUE); + pg_->LoadAccumulator(node_, result_); + pg_->EmitThrow(node_); + pg_->SetLabel(node_, labelSet.CatchEnd()); +} + +void DestructuringIterator::OnIterDone([[maybe_unused]] Label *doneTarget) const +{ + pg_->LoadConst(node_, Constant::JS_UNDEFINED); +} + +void DestructuringRestIterator::OnIterDone([[maybe_unused]] Label *doneTarget) const +{ + pg_->Branch(node_, doneTarget); +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/base/iterators.h b/es2panda/compiler/base/iterators.h new file mode 100644 index 0000000000000000000000000000000000000000..0f23818d9ed2015bc8db01e274ba63b5a064dea1 --- /dev/null +++ b/es2panda/compiler/base/iterators.h @@ -0,0 +1,119 @@ +/** + * 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_BASE_ITERATORS_H +#define ES2PANDA_COMPILER_BASE_ITERATORS_H + +#include + +namespace panda::es2panda::ir { +class AstNode; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::compiler { + +class PandaGen; + +enum class IteratorType { SYNC, ASYNC }; + +class Iterator { +public: + Iterator(PandaGen *pg, const ir::AstNode *node, IteratorType type); + DEFAULT_COPY_SEMANTIC(Iterator); + DEFAULT_MOVE_SEMANTIC(Iterator); + ~Iterator() = default; + + IteratorType Type() const + { + return type_; + } + + VReg Method() const + { + return method_; + } + + VReg NextResult() const + { + return nextResult_; + } + + const ir::AstNode *Node() const + { + return node_; + } + + void GetMethod(util::StringView name) const; + void CallMethod() const; + void CallMethodWithValue() const; + + void Next() const; + void Complete() const; + void Value() const; + void Close(bool abruptCompletion) const; + +protected: + PandaGen *pg_; + const ir::AstNode *node_; + // These 3 regs must be allocated continously + VReg method_; + VReg iterator_; + VReg nextResult_; + IteratorType type_; +}; + +class DestructuringIterator : public Iterator { +public: + explicit DestructuringIterator(PandaGen *pg, const ir::AstNode *node); + + DEFAULT_COPY_SEMANTIC(DestructuringIterator); + DEFAULT_MOVE_SEMANTIC(DestructuringIterator); + ~DestructuringIterator() = default; + + VReg Done() const + { + return done_; + } + + VReg Result() const + { + return result_; + } + + void Step(Label *doneTarget = nullptr) const; + + virtual void OnIterDone([[maybe_unused]] Label *doneTarget) const; + + friend class DestructuringRestIterator; + +protected: + VReg done_; + VReg result_; +}; + +class DestructuringRestIterator : public DestructuringIterator { +public: + explicit DestructuringRestIterator(const DestructuringIterator &iterator) : DestructuringIterator(iterator) {} + + DEFAULT_COPY_SEMANTIC(DestructuringRestIterator); + DEFAULT_MOVE_SEMANTIC(DestructuringRestIterator); + ~DestructuringRestIterator() = default; + + void OnIterDone([[maybe_unused]] Label *doneTarget) const override; +}; + +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/base/lexenv.cpp b/es2panda/compiler/base/lexenv.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f3410f06c2ff81e8e0a5a098f5909111720f2970 --- /dev/null +++ b/es2panda/compiler/base/lexenv.cpp @@ -0,0 +1,142 @@ +/** + * 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 "lexenv.h" + +#include +#include +#include +#include +#include + +namespace panda::es2panda::compiler { + +// Helpers + +static bool CheckTdz(const ir::AstNode *node) +{ + return node->IsIdentifier() && node->AsIdentifier()->IsTdz(); +} + +static void CheckConstAssignment(PandaGen *pg, const ir::AstNode *node, binder::Variable *variable) +{ + if (!variable->Declaration()->IsConstDecl()) { + return; + } + + pg->ThrowConstAssignment(node, variable->Name()); +} + +// VirtualLoadVar + +static void ExpandLoadLexVar(PandaGen *pg, const ir::AstNode *node, const binder::ScopeFindResult &result) +{ + pg->LoadLexicalVar(node, result.lexLevel, result.variable->AsLocalVariable()->LexIdx()); + pg->ThrowUndefinedIfHole(node, result.variable->Name()); +} + +static void ExpandLoadNormalVar(PandaGen *pg, const ir::AstNode *node, const binder::ScopeFindResult &result) +{ + auto *local = result.variable->AsLocalVariable(); + + if (CheckTdz(node)) { + pg->LoadConst(node, Constant::JS_HOLE); + pg->ThrowUndefinedIfHole(node, local->Name()); + } else { + pg->LoadAccumulator(node, local->Vreg()); + } +} + +void VirtualLoadVar::Expand(PandaGen *pg, const ir::AstNode *node, const binder::ScopeFindResult &result) +{ + if (result.variable->LexicalBound()) { + ExpandLoadLexVar(pg, node, result); + } else { + ExpandLoadNormalVar(pg, node, result); + } +} + +// VirtualStoreVar + +static void StoreLocalExport(PandaGen *pg, const ir::AstNode *node, binder::Variable *variable) +{ + if (!variable->HasFlag(binder::VariableFlags::LOCAL_EXPORT) || !pg->Scope()->IsModuleScope()) { + return; + } + + auto range = pg->Scope()->AsModuleScope()->LocalExports().equal_range(variable); + + for (auto it = range.first; it != range.second; ++it) { + if (it->second != "default") { + pg->StoreModuleVar(node, it->second); + } + } +} + +static void ExpandStoreLexVar(PandaGen *pg, const ir::AstNode *node, const binder::ScopeFindResult &result, bool isDecl) +{ + binder::LocalVariable *local = result.variable->AsLocalVariable(); + + const auto *decl = result.variable->Declaration(); + + if (decl->IsLetOrConstDecl() && !isDecl) { + RegScope rs(pg); + + VReg valueReg = pg->AllocReg(); + pg->StoreAccumulator(node, valueReg); + + ExpandLoadLexVar(pg, node, result); + + if (decl->IsConstDecl()) { + pg->ThrowConstAssignment(node, local->Name()); + } + + pg->LoadAccumulator(node, valueReg); + } + + pg->StoreLexicalVar(node, result.lexLevel, local->LexIdx()); + + StoreLocalExport(pg, node, local); +} + +static void ExpandStoreNormalVar(PandaGen *pg, const ir::AstNode *node, const binder::ScopeFindResult &result, + bool isDecl) +{ + auto *local = result.variable->AsLocalVariable(); + VReg localReg = local->Vreg(); + + if (!isDecl) { + if (CheckTdz(node)) { + pg->LoadConst(node, Constant::JS_HOLE); + pg->ThrowUndefinedIfHole(node, local->Name()); + } + + CheckConstAssignment(pg, node, local); + } + + pg->StoreAccumulator(node, localReg); + StoreLocalExport(pg, node, local); +} + +void VirtualStoreVar::Expand(PandaGen *pg, const ir::AstNode *node, const binder::ScopeFindResult &result, bool isDecl) +{ + if (result.variable->LexicalBound()) { + ExpandStoreLexVar(pg, node, result, isDecl); + } else { + ExpandStoreNormalVar(pg, node, result, isDecl); + } +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/base/lexenv.h b/es2panda/compiler/base/lexenv.h new file mode 100644 index 0000000000000000000000000000000000000000..128b8c45791cdabfad19d5672b6b744af7fd365c --- /dev/null +++ b/es2panda/compiler/base/lexenv.h @@ -0,0 +1,41 @@ +/** + * 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_SCOPES_LEXENV_H +#define ES2PANDA_COMPILER_SCOPES_LEXENV_H + +#include +#include + +namespace panda::es2panda::compiler { + +class PandaGen; + +class VirtualLoadVar { +public: + VirtualLoadVar() = delete; + + static void Expand(PandaGen *pg, const ir::AstNode *node, const binder::ScopeFindResult &result); +}; + +class VirtualStoreVar { +public: + VirtualStoreVar() = delete; + + static void Expand(PandaGen *pg, const ir::AstNode *node, const binder::ScopeFindResult &result, bool isDecl); +}; +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/base/literals.cpp b/es2panda/compiler/base/literals.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0690bb6602f2737e4568ab5dc1f3b9fad29f758f --- /dev/null +++ b/es2panda/compiler/base/literals.cpp @@ -0,0 +1,78 @@ +/* + * 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 "literals.h" + +#include +#include +#include +#include + +namespace panda::es2panda::compiler { + +// Literals + +void Literals::GetTemplateObject(PandaGen *pg, const ir::TaggedTemplateExpression *lit) +{ + RegScope rs(pg); + VReg templateArg = pg->AllocReg(); + VReg indexReg = pg->AllocReg(); + VReg rawArr = pg->AllocReg(); + VReg cookedArr = pg->AllocReg(); + + const ir::TemplateLiteral *templateLit = lit->Quasi(); + + pg->CreateEmptyArray(templateLit); + pg->StoreAccumulator(templateLit, rawArr); + + pg->CreateEmptyArray(templateLit); + pg->StoreAccumulator(templateLit, cookedArr); + + size_t elemIndex = 0; + + for (const auto *element : templateLit->Quasis()) { + pg->LoadAccumulatorInt(element, elemIndex); + pg->StoreAccumulator(element, indexReg); + + pg->LoadAccumulatorString(element, element->Raw()); + pg->StoreObjByValue(element, rawArr, indexReg); + + pg->LoadAccumulatorString(element, element->Cooked()); + pg->StoreObjByValue(element, cookedArr, indexReg); + + elemIndex++; + } + + pg->CreateEmptyArray(lit); + pg->StoreAccumulator(lit, templateArg); + + elemIndex = 0; + pg->LoadAccumulatorInt(lit, elemIndex); + pg->StoreAccumulator(lit, indexReg); + + pg->LoadAccumulator(lit, rawArr); + pg->StoreObjByValue(lit, templateArg, indexReg); + + elemIndex++; + pg->LoadAccumulatorInt(lit, elemIndex); + pg->StoreAccumulator(lit, indexReg); + + pg->LoadAccumulator(lit, cookedArr); + pg->StoreObjByValue(lit, templateArg, indexReg); + + pg->GetTemplateObject(lit, templateArg); +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/base/literals.h b/es2panda/compiler/base/literals.h new file mode 100644 index 0000000000000000000000000000000000000000..ddda8fc23861ab1be8805d722dbca9ed12cce94c --- /dev/null +++ b/es2panda/compiler/base/literals.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_BASE_LITERALS_H +#define ES2PANDA_COMPILER_BASE_LITERALS_H + +#include +#include + +#include + +namespace panda::es2panda::ir { +class Literal; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::checker { +class Checker; +class Type; +} // namespace panda::es2panda::checker + +namespace panda::es2panda::compiler { + +class PandaGen; + +class LiteralBuffer { +public: + explicit LiteralBuffer(ArenaAllocator *allocator) : literals_(allocator->Adapter()) {} + ~LiteralBuffer() = default; + NO_COPY_SEMANTIC(LiteralBuffer); + NO_MOVE_SEMANTIC(LiteralBuffer); + + void Add(ir::Literal *lit) + { + literals_.push_back(lit); + } + + bool IsEmpty() const + { + return literals_.empty(); + } + + size_t Size() const + { + return literals_.size(); + } + + int32_t Index() const + { + return index_; + } + + void ResetLiteral(size_t index, const ir::Literal *literal) + { + literals_[index] = literal; + } + + const ArenaVector &Literals() const + { + return literals_; + } + + void Insert(LiteralBuffer *other) + { + literals_.insert(literals_.end(), other->literals_.begin(), other->literals_.end()); + other->literals_.clear(); + } + + void SetIndex(int32_t index) + { + index_ = index; + } + +private: + ArenaVector literals_; + int32_t index_ {}; +}; + +class Literals { +public: + Literals() = delete; + + static void GetTemplateObject(PandaGen *pg, const ir::TaggedTemplateExpression *lit); +}; + +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/base/lreference.cpp b/es2panda/compiler/base/lreference.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6fd21e14eedd2b4be322b5746b6411bd6f70a279 --- /dev/null +++ b/es2panda/compiler/base/lreference.cpp @@ -0,0 +1,132 @@ +/* + * 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 "lreference.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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) +{ + if (refKind == ReferenceKind::MEMBER) { + obj_ = pg_->AllocReg(); + + node_->AsMemberExpression()->CompileObject(pg_, obj_); + prop_ = node->AsMemberExpression()->CompileKey(pg_); + } +} + +void LReference::GetValue() +{ + switch (refKind_) { + case ReferenceKind::VAR_OR_GLOBAL: { + pg_->LoadVar(node_->AsIdentifier(), res_); + break; + } + case ReferenceKind::MEMBER: { + pg_->LoadObjProperty(node_, obj_, prop_); + break; + } + default: { + UNREACHABLE(); + } + } +} + +void LReference::SetValue() +{ + switch (refKind_) { + case ReferenceKind::VAR_OR_GLOBAL: { + pg_->StoreVar(node_, res_, isDeclaration_); + break; + } + case ReferenceKind::MEMBER: { + if (node_->AsMemberExpression()->Object()->IsSuperExpression()) { + pg_->StoreSuperProperty(node_, obj_, prop_); + } else { + pg_->StoreObjProperty(node_, obj_, prop_); + } + + break; + } + case ReferenceKind::DESTRUCTURING: { + Destructuring::Compile(pg_, node_->AsExpression()); + break; + } + default: { + UNREACHABLE(); + } + } +} + +ReferenceKind LReference::Kind() const +{ + return refKind_; +} + +binder::Variable *LReference::Variable() const +{ + return res_.variable; +} + +LReference LReference::CreateLRef(PandaGen *pg, const ir::AstNode *node, bool isDeclaration) +{ + switch (node->Type()) { + case ir::AstNodeType::IDENTIFIER: { + const util::StringView &name = node->AsIdentifier()->Name(); + binder::ScopeFindResult res = pg->Scope()->Find(name); + + return {node, pg, isDeclaration, ReferenceKind::VAR_OR_GLOBAL, res}; + } + case ir::AstNodeType::MEMBER_EXPRESSION: { + return {node, pg, false, ReferenceKind::MEMBER, {}}; + } + case ir::AstNodeType::VARIABLE_DECLARATION: { + ASSERT(node->AsVariableDeclaration()->Declarators().size() == 1); + return LReference::CreateLRef(pg, node->AsVariableDeclaration()->Declarators()[0]->Id(), true); + } + case ir::AstNodeType::VARIABLE_DECLARATOR: { + return LReference::CreateLRef(pg, node->AsVariableDeclarator()->Id(), true); + } + case ir::AstNodeType::ARRAY_PATTERN: + case ir::AstNodeType::OBJECT_PATTERN: { + return {node, pg, isDeclaration, ReferenceKind::DESTRUCTURING, {}}; + } + case ir::AstNodeType::ASSIGNMENT_PATTERN: { + return LReference::CreateLRef(pg, node->AsAssignmentPattern()->Left(), true); + } + case ir::AstNodeType::REST_ELEMENT: { + return LReference::CreateLRef(pg, node->AsRestElement()->Argument(), true); + } + default: { + UNREACHABLE(); + } + } +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/base/lreference.h b/es2panda/compiler/base/lreference.h new file mode 100644 index 0000000000000000000000000000000000000000..e7edf06f7b90d4f08a94bce2f7faaa85dea5c880 --- /dev/null +++ b/es2panda/compiler/base/lreference.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_BASE_LREFERENCE_H +#define ES2PANDA_COMPILER_BASE_LREFERENCE_H + +#include +#include + +namespace panda::es2panda::ir { +class AstNode; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::compiler { + +enum class ReferenceKind { + MEMBER, + VAR_OR_GLOBAL, + DESTRUCTURING, +}; + +enum class ReferenceFlags { + NONE = 0, + DECLARATION = 1U << 0U, +}; + +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); + + void GetValue(); + void SetValue(); + binder::Variable *Variable() const; + ReferenceKind Kind() const; + + static LReference CreateLRef(PandaGen *pg, const ir::AstNode *node, bool isDeclaration); + +private: + const ir::AstNode *node_; + PandaGen *pg_; + ReferenceKind refKind_; + binder::ScopeFindResult res_; + VReg obj_; + Operand prop_; + bool isDeclaration_; +}; + +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/core/compileQueue.cpp b/es2panda/compiler/core/compileQueue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..29460a7b3b4a59143e2c994747b7587c334251af --- /dev/null +++ b/es2panda/compiler/core/compileQueue.cpp @@ -0,0 +1,155 @@ +/* + * 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 "compileQueue.h" + +#include +#include +#include +#include +#include +#include + +namespace panda::es2panda::compiler { + +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_); + + Function::Compile(&pg); + + FunctionEmitter funcEmitter(&allocator, &pg); + funcEmitter.Generate(); + + context_->GetEmitter()->AddFunction(&funcEmitter); + + if (dependant_) { + 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); + + for (size_t i = 0; i < threadCount; i++) { + threads_.push_back(os::thread::ThreadStart(Worker, this)); + } +} + +CompileQueue::~CompileQueue() +{ + void *retval = nullptr; + + std::unique_lock lock(m_); + terminate_ = true; + lock.unlock(); + jobsAvailable_.notify_all(); + + for (const auto handle_id : threads_) { + os::thread::ThreadJoin(handle_id, &retval); + } +} + +void CompileQueue::Schedule(CompilerContext *context) +{ + ASSERT(jobsCount_ == 0); + std::unique_lock lock(m_); + const auto &functions = context->Binder()->Functions(); + jobs_ = new CompileJob[functions.size()](); + + for (auto *function : functions) { + jobs_[jobsCount_++].SetConext(context, function); + } + + lock.unlock(); + jobsAvailable_.notify_all(); +} + +void CompileQueue::Worker(CompileQueue *queue) +{ + while (true) { + std::unique_lock lock(queue->m_); + queue->jobsAvailable_.wait(lock, [queue]() { return queue->terminate_ || queue->jobsCount_ != 0; }); + + if (queue->terminate_) { + return; + } + + lock.unlock(); + + queue->Consume(); + queue->jobsFinished_.notify_one(); + } +} + +void CompileQueue::Consume() +{ + std::unique_lock lock(m_); + activeWorkers_++; + + while (jobsCount_ > 0) { + --jobsCount_; + auto &job = jobs_[jobsCount_]; + + lock.unlock(); + + try { + job.Run(); + } catch (const Error &e) { + lock.lock(); + errors_.push_back(e); + lock.unlock(); + } + + lock.lock(); + } + + activeWorkers_--; +} + +void CompileQueue::Wait() +{ + std::unique_lock lock(m_); + jobsFinished_.wait(lock, [this]() { return activeWorkers_ == 0 && jobsCount_ == 0; }); + delete[] jobs_; + + if (!errors_.empty()) { + // NOLINTNEXTLINE + throw errors_.front(); + } +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/compileQueue.h b/es2panda/compiler/core/compileQueue.h new file mode 100644 index 0000000000000000000000000000000000000000..5e227475ba26c314078b5d5f4108a8b5c069ee63 --- /dev/null +++ b/es2panda/compiler/core/compileQueue.h @@ -0,0 +1,92 @@ +/* + * 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_COMPILEQUEUE_H +#define ES2PANDA_COMPILER_CORE_COMPILEQUEUE_H + +#include +#include +#include + +#include +#include + +namespace panda::es2panda::binder { +class FunctionScope; +} // namespace panda::es2panda::binder + +namespace panda::es2panda::compiler { + +class CompilerContext; + +class CompileJob { +public: + CompileJob() = default; + NO_COPY_SEMANTIC(CompileJob); + NO_MOVE_SEMANTIC(CompileJob); + ~CompileJob() = default; + + binder::FunctionScope *Scope() const + { + return scope_; + } + + 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_ {}; + CompileJob *dependant_ {}; + size_t dependencies_ {0}; +}; + +class CompileQueue { +public: + explicit CompileQueue(size_t threadCount); + NO_COPY_SEMANTIC(CompileQueue); + NO_MOVE_SEMANTIC(CompileQueue); + ~CompileQueue(); + + void Schedule(CompilerContext *context); + void Consume(); + void Wait(); + +private: + static void Worker(CompileQueue *queue); + + std::vector threads_; + std::vector errors_; + std::mutex m_; + std::condition_variable jobsAvailable_; + std::condition_variable jobsFinished_; + CompileJob *jobs_ {}; + size_t jobsCount_ {0}; + size_t activeWorkers_ {0}; + bool terminate_ {false}; +}; + +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/core/compilerContext.cpp b/es2panda/compiler/core/compilerContext.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a97c410fdd32e9a6b48274c4700ec069553893c --- /dev/null +++ b/es2panda/compiler/core/compilerContext.cpp @@ -0,0 +1,27 @@ +/* + * 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 "compilerContext.h" + +#include + +namespace panda::es2panda::compiler { + +CompilerContext::CompilerContext(binder::Binder *binder, bool isDebug) + : binder_(binder), emitter_(std::make_unique(this)), isDebug_(isDebug) +{ +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/compilerContext.h b/es2panda/compiler/core/compilerContext.h new file mode 100644 index 0000000000000000000000000000000000000000..517163ae62d861c45600c38321c9780a62103e3a --- /dev/null +++ b/es2panda/compiler/core/compilerContext.h @@ -0,0 +1,82 @@ +/** + * 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_COMPILER_CONTEXT_H +#define ES2PANDA_COMPILER_CORE_COMPILER_CONTEXT_H + +#include +#include + +#include +#include + +namespace panda::es2panda::binder { +class Binder; +} // namespace panda::es2panda::binder + +namespace panda::es2panda::compiler { + +class DebugInfo; +class Emitter; + +class CompilerContext { +public: + CompilerContext(binder::Binder *binder, bool isDebug); + NO_COPY_SEMANTIC(CompilerContext); + NO_MOVE_SEMANTIC(CompilerContext); + ~CompilerContext() = default; + + binder::Binder *Binder() const + { + return binder_; + } + + Emitter *GetEmitter() const + { + return emitter_.get(); + } + + int32_t LiteralCount() const + { + return literalBufferIdx_; + } + + int32_t NewLiteralIndex() + { + std::lock_guard lock(m_); + return literalBufferIdx_++; + } + + std::mutex &Mutex() + { + return m_; + } + + bool IsDebug() const + { + return isDebug_; + } + +private: + binder::Binder *binder_; + std::unique_ptr emitter_; + int32_t literalBufferIdx_ {0}; + std::mutex m_; + bool isDebug_; +}; + +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/core/compilerImpl.cpp b/es2panda/compiler/core/compilerImpl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c4ff569b2a7da04f7405cbb0f76ded8ea043606e --- /dev/null +++ b/es2panda/compiler/core/compilerImpl.cpp @@ -0,0 +1,64 @@ +/** + * 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 "compilerImpl.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace panda::es2panda::compiler { + +CompilerImpl::CompilerImpl(size_t threadCount) : queue_(new CompileQueue(threadCount)) {} + +CompilerImpl::~CompilerImpl() +{ + delete queue_; +} + +panda::pandasm::Program *CompilerImpl::Compile(parser::Program *program, const es2panda::CompilerOptions &options) +{ + CompilerContext context(program->Binder(), options.isDebug); + + if (program->Extension() == ScriptExtension::TS) { + ArenaAllocator localAllocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); + auto checker = std::make_unique(&localAllocator, context.Binder()); + checker->StartChecker(); + + /* TODO(): TS files are not yet compiled */ + return nullptr; + } + + queue_->Schedule(&context); + + /* Main thread can also be used instead of idling */ + queue_->Consume(); + queue_->Wait(); + + return context.GetEmitter()->Finalize(options.dumpDebugInfo); +} + +void CompilerImpl::DumpAsm(const panda::pandasm::Program *prog) +{ + Emitter::DumpAsm(prog); +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/compilerImpl.h b/es2panda/compiler/core/compilerImpl.h new file mode 100644 index 0000000000000000000000000000000000000000..19c6061347f643e46a5de26d170a800251e019ce --- /dev/null +++ b/es2panda/compiler/core/compilerImpl.h @@ -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. + */ + +#ifndef ES2PANDA_COMPILER_INCLUDE_COMPILER_IMPL_H +#define ES2PANDA_COMPILER_INCLUDE_COMPILER_IMPL_H + +#include +#include +#include +#include + +#include + +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 CompilerImpl { +public: + explicit CompilerImpl(size_t threadCount); + ~CompilerImpl(); + NO_COPY_SEMANTIC(CompilerImpl); + NO_MOVE_SEMANTIC(CompilerImpl); + + panda::pandasm::Program *Compile(parser::Program *program, const es2panda::CompilerOptions &options); + static void DumpAsm(const panda::pandasm::Program *prog); + +private: + CompileQueue *queue_; +}; +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/core/dynamicContext.cpp b/es2panda/compiler/core/dynamicContext.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73e9e9ddcb08b589c09a48c9d822fe1497086fb4 --- /dev/null +++ b/es2panda/compiler/core/dynamicContext.cpp @@ -0,0 +1,178 @@ +/** + * 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 "dynamicContext.h" + +#include +#include +#include +#include +#include +#include + +namespace panda::es2panda::compiler { +DynamicContext::DynamicContext(PandaGen *pg, LabelTarget target) : pg_(pg), target_(target), prev_(pg_->dynamicContext_) +{ + pg_->dynamicContext_ = this; +} + +DynamicContext::~DynamicContext() +{ + pg_->dynamicContext_ = prev_; +} + +LabelContext::LabelContext(PandaGen *pg, const ir::LabelledStatement *labelledStmt) + : DynamicContext(pg, LabelTarget(labelledStmt->Ident()->Name())), labelledStmt_(labelledStmt) +{ + if (!labelledStmt->Body()->IsBlockStatement()) { + return; + } + + label_ = pg->AllocLabel(); + target_.SetBreakTarget(label_); +} + +LabelContext::~LabelContext() +{ + if (!label_) { + return; + } + + pg_->SetLabel(labelledStmt_, label_); +} + +LexEnvContext::LexEnvContext(LoopEnvScope *envScope, PandaGen *pg, LabelTarget target) + : DynamicContext(pg, target), envScope_(envScope) +{ + if (!envScope_->HasEnv()) { + return; + } + + catchTable_ = pg_->CreateCatchTable(); + const auto &labelSet = catchTable_->LabelSet(); + const auto *node = envScope_->Scope()->Node(); + + pg_->SetLabel(node, labelSet.TryBegin()); +} + +LexEnvContext::~LexEnvContext() +{ + if (!envScope_->HasEnv()) { + return; + } + + const auto &labelSet = catchTable_->LabelSet(); + const auto *node = envScope_->Scope()->Node(); + + pg_->SetLabel(node, labelSet.TryEnd()); + pg_->Branch(node, labelSet.CatchEnd()); + + pg_->SetLabel(node, labelSet.CatchBegin()); + pg_->PopLexEnv(node); + pg_->EmitThrow(node); + pg_->SetLabel(node, labelSet.CatchEnd()); + pg_->PopLexEnv(node); +} + +bool LexEnvContext::HasTryCatch() const +{ + return envScope_->HasEnv(); +} + +void LexEnvContext::AbortContext([[maybe_unused]] ControlFlowChange cfc, + [[maybe_unused]] const util::StringView &targetLabel) +{ + if (cfc == ControlFlowChange::CONTINUE || !envScope_->HasEnv()) { + return; + } + + const auto *node = envScope_->Scope()->Node(); + pg_->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()); +} + +IteratorContext::~IteratorContext() +{ + const auto &labelSet = catchTable_->LabelSet(); + const auto *node = iterator_.Node(); + + pg_->SetLabel(node, labelSet.TryEnd()); + pg_->Branch(node, labelSet.CatchEnd()); + + pg_->SetLabel(node, labelSet.CatchBegin()); + iterator_.Close(true); + pg_->SetLabel(node, labelSet.CatchEnd()); +} + +void IteratorContext::AbortContext([[maybe_unused]] ControlFlowChange cfc, + [[maybe_unused]] const util::StringView &targetLabel) +{ + if (cfc == ControlFlowChange::CONTINUE && target_.ContinueLabel() == targetLabel) { + return; + } + + iterator_.Close(false); +} + +void TryContext::InitFinalizer() +{ + ASSERT(tryStmt_); + + if (!hasFinalizer_ || !tryStmt_->FinallyBlock()) { + return; + } + + finalizerRun_ = pg_->AllocReg(); + pg_->StoreConst(tryStmt_, finalizerRun_, Constant::JS_UNDEFINED); +} + +void TryContext::InitCatchTable() +{ + catchTable_ = pg_->CreateCatchTable(); +} + +const TryLabelSet &TryContext::LabelSet() const +{ + return catchTable_->LabelSet(); +} + +bool TryContext::HasFinalizer() const +{ + return hasFinalizer_; +} + +void TryContext::EmitFinalizer() +{ + if (!hasFinalizer_ || inFinalizer_ || !tryStmt_->FinallyBlock()) { + return; + } + + inFinalizer_ = true; + 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/es2panda/compiler/core/dynamicContext.h b/es2panda/compiler/core/dynamicContext.h new file mode 100644 index 0000000000000000000000000000000000000000..2e13faab6e9ab022393fa06168150f4af0fa2590 --- /dev/null +++ b/es2panda/compiler/core/dynamicContext.h @@ -0,0 +1,211 @@ +/** + * 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_CORE_DYNAMIC_CONTEXT_H +#define ES2PANDA_COMPILER_CORE_DYNAMIC_CONTEXT_H + +#include +#include +#include +#include + +namespace panda::es2panda::ir { +class TryStatement; +class ForOfStatement; +class LabelledStatement; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::compiler { +class PandaGen; +class LoopEnvScope; +class CatchTable; +class TryLabelSet; + +enum class DynamicContextType { NONE, LABEL, LEX_ENV, ITERATOR, TRY }; + +class DynamicContext { +public: + NO_COPY_SEMANTIC(DynamicContext); + NO_MOVE_SEMANTIC(DynamicContext); + ~DynamicContext(); + + void *operator new(size_t) = delete; + void *operator new[](size_t) = delete; + + virtual void AbortContext([[maybe_unused]] ControlFlowChange cfc, + [[maybe_unused]] const util::StringView &targetLabel) {}; + + virtual bool HasTryCatch() const + { + return false; + } + + virtual bool HasFinalizer() const + { + return HasTryCatch(); + } + + virtual DynamicContextType Type() const = 0; + + DynamicContext *Prev() + { + return prev_; + } + + const DynamicContext *Prev() const + { + return prev_; + } + + const LabelTarget &Target() const + { + return target_; + } + +protected: + explicit DynamicContext(PandaGen *pg, LabelTarget target); + + PandaGen *pg_; + LabelTarget target_; + DynamicContext *prev_ {}; +}; + +class LabelContext : public DynamicContext { +public: + explicit LabelContext(PandaGen *pg, LabelTarget target) : DynamicContext(pg, target) {} + explicit LabelContext(PandaGen *pg, const ir::LabelledStatement *labelledStmt); + NO_COPY_SEMANTIC(LabelContext); + NO_MOVE_SEMANTIC(LabelContext); + ~LabelContext(); + + DynamicContextType Type() const override + { + return DynamicContextType::LABEL; + } + +private: + Label *label_ {}; + const ir::LabelledStatement *labelledStmt_ {}; +}; + +class LexEnvContext : public DynamicContext { +public: + explicit LexEnvContext(LoopEnvScope *envScope, PandaGen *pg, LabelTarget target); + NO_COPY_SEMANTIC(LexEnvContext); + NO_MOVE_SEMANTIC(LexEnvContext); + ~LexEnvContext(); + + DynamicContextType Type() const override + { + return DynamicContextType::LEX_ENV; + } + + bool HasTryCatch() const override; + void AbortContext([[maybe_unused]] ControlFlowChange cfc, + [[maybe_unused]] const util::StringView &targetLabel) override; + +private: + LoopEnvScope *envScope_; + CatchTable *catchTable_ {}; +}; + +class IteratorContext : public DynamicContext { +public: + explicit IteratorContext(PandaGen *pg, const Iterator &iterator, LabelTarget target); + NO_COPY_SEMANTIC(IteratorContext); + NO_MOVE_SEMANTIC(IteratorContext); + ~IteratorContext(); + + DynamicContextType Type() const override + { + return DynamicContextType::ITERATOR; + } + + const Iterator &GetIterator() const + { + return iterator_; + } + + bool HasTryCatch() const override + { + return true; + } + + void AbortContext([[maybe_unused]] ControlFlowChange cfc, + [[maybe_unused]] const util::StringView &targetLabel) override; + +private: + const Iterator &iterator_; + CatchTable *catchTable_; +}; + +class TryContext : public DynamicContext { +public: + explicit TryContext(PandaGen *pg, const ir::TryStatement *tryStmt, bool hasFinalizer = true) + : DynamicContext(pg, {}), tryStmt_(tryStmt), hasFinalizer_(hasFinalizer) + { + InitCatchTable(); + InitFinalizer(); + } + + explicit TryContext(PandaGen *pg) : DynamicContext(pg, {}) + { + InitCatchTable(); + } + + NO_COPY_SEMANTIC(TryContext); + NO_MOVE_SEMANTIC(TryContext); + ~TryContext() = default; + + DynamicContextType Type() const override + { + 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; + +private: + void InitCatchTable(); + + const ir::TryStatement *tryStmt_ {}; + CatchTable *catchTable_ {}; + VReg finalizerRun_ {}; + bool hasFinalizer_ {}; + bool inFinalizer_ {}; +}; +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/core/emitter.cpp b/es2panda/compiler/core/emitter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6e617cb41f196d3b1ae3fdc361eed462f28af155 --- /dev/null +++ b/es2panda/compiler/core/emitter.cpp @@ -0,0 +1,446 @@ +/** + * 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 "emitter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace panda::es2panda::compiler { + +constexpr const auto LANG_EXT = panda::pandasm::extensions::Language::ECMASCRIPT; + +FunctionEmitter::FunctionEmitter(ArenaAllocator *allocator, const PandaGen *pg) + : pg_(pg), literalBuffers_(allocator->Adapter()) +{ + func_ = allocator->New(pg->InternalName().Mutf8(), LANG_EXT); + + 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 = pg->TotalRegsNum(); + func_->return_type = panda::pandasm::Type("any", 0); +} + +void FunctionEmitter::Generate() +{ + GenFunctionInstructions(); + GenVariablesDebugInfo(); + GenSourceFileDebugInfo(); + GenFunctionCatchTables(); + GenFunctionICSize(); + GenLiteralBuffers(); +} + +const ArenaSet &FunctionEmitter::Strings() const +{ + return pg_->Strings(); +} + +void FunctionEmitter::GenBufferLiterals(const LiteralBuffer *buff) +{ + auto &[idx, array] = literalBuffers_.emplace_back(); + idx = buff->Index(); + array.reserve(buff->Literals().size() * 2); + + for (const auto *literal : buff->Literals()) { + panda::pandasm::LiteralArray::Literal valueLit; + panda::pandasm::LiteralArray::Literal tagLit; + + ir::LiteralTag tag = literal->Tag(); + + switch (tag) { + case ir::LiteralTag::BOOLEAN: { + valueLit.tag_ = panda::panda_file::LiteralTag::BOOL; + valueLit.value_ = literal->GetBoolean(); + break; + } + case ir::LiteralTag::INTEGER: { + valueLit.tag_ = panda::panda_file::LiteralTag::INTEGER; + valueLit.value_ = literal->GetInt(); + break; + } + case ir::LiteralTag::DOUBLE: { + valueLit.tag_ = panda::panda_file::LiteralTag::DOUBLE; + valueLit.value_ = literal->GetDouble(); + break; + } + case ir::LiteralTag::STRING: { + valueLit.tag_ = panda::panda_file::LiteralTag::STRING; + valueLit.value_ = literal->GetString().Mutf8(); + break; + } + case ir::LiteralTag::ACCESSOR: { + valueLit.tag_ = panda::panda_file::LiteralTag::ACCESSOR; + valueLit.value_ = static_cast(0); + break; + } + case ir::LiteralTag::METHOD: { + valueLit.tag_ = panda::panda_file::LiteralTag::METHOD; + valueLit.value_ = literal->GetMethod().Mutf8(); + break; + } + case ir::LiteralTag::GENERATOR_METHOD: { + valueLit.tag_ = panda::panda_file::LiteralTag::GENERATORMETHOD; + valueLit.value_ = literal->GetMethod().Mutf8(); + break; + } + case ir::LiteralTag::ASYNC_GENERATOR_METHOD: { + valueLit.tag_ = panda::panda_file::LiteralTag::ASYNCGENERATORMETHOD; + valueLit.value_ = literal->GetMethod().Mutf8(); + break; + } + case ir::LiteralTag::NULL_VALUE: { + valueLit.tag_ = panda::panda_file::LiteralTag::NULLVALUE; + valueLit.value_ = static_cast(0); + break; + } + default: + break; + } + + tagLit.tag_ = panda::panda_file::LiteralTag::TAGVALUE; + tagLit.value_ = static_cast(valueLit.tag_); + + array.emplace_back(tagLit); + array.emplace_back(valueLit); + } +} + +util::StringView FunctionEmitter::SourceCode() const +{ + return pg_->Binder()->Program()->SourceCode(); +} + +static Format MatchFormat(const IRNode *node, const Formats &formats) +{ + std::array regs {}; + auto regCnt = node->Registers(®s); + auto registers = Span(regs.data(), regs.data() + regCnt); + + const auto *iter = formats.begin(); + + for (; iter != formats.end(); iter++) { + auto format = *iter; + size_t limit = 0; + for (const auto &formatItem : format.GetFormatItem()) { + if (formatItem.IsVReg()) { + limit = 1 << formatItem.Bitwidth(); + break; + } + } + + if (std::all_of(registers.begin(), registers.end(), [limit](const VReg *reg) { return *reg < limit; })) { + return format; + } + } + + UNREACHABLE(); + return *iter; +} + +static size_t GetIRNodeWholeLength(const IRNode *node) +{ + Formats formats = node->GetFormats(); + if (formats.empty()) { + return 0; + } + + size_t len = 1; + const auto format = MatchFormat(node, formats); + + for (auto fi : format.GetFormatItem()) { + len += fi.Bitwidth() / 8; + } + + return len; +} + +static std::string WholeLine(const util::StringView &source, lexer::SourceRange range) +{ + return source.Substr(range.start.index, range.end.index).EscapeSymbol(); +} + +void FunctionEmitter::GenInstructionDebugInfo(const IRNode *ins, panda::pandasm::Ins *pandaIns) +{ + const ir::AstNode *astNode = ins->Node(); + + ASSERT(astNode != nullptr); + + if (astNode == FIRST_NODE_OF_FUNCTION) { + astNode = pg_->Debuginfo().firstStmt; + if (!astNode) { + return; + } + } + + pandaIns->ins_debug.line_number = astNode->Range().start.line + 1; + + if (pg_->IsDebug()) { + size_t insLen = GetIRNodeWholeLength(ins); + if (insLen != 0) { + pandaIns->ins_debug.bound_left = offset_; + pandaIns->ins_debug.bound_right = offset_ + insLen; + } + + offset_ += insLen; + pandaIns->ins_debug.whole_line = WholeLine(SourceCode(), astNode->Range()); + } +} + +void FunctionEmitter::GenFunctionInstructions() +{ + func_->ins.reserve(pg_->Insns().size()); + + for (const auto *ins : pg_->Insns()) { + auto &pandaIns = func_->ins.emplace_back(); + + ins->Transform(&pandaIns); + GenInstructionDebugInfo(ins, &pandaIns); + } +} + +void FunctionEmitter::GenFunctionICSize() +{ + panda::pandasm::AnnotationData funcAnnotationData("_ESAnnotation"); + panda::pandasm::AnnotationElement icSizeAnnotationElement( + "icSize", std::make_unique( + panda::pandasm::ScalarValue::Create(pg_->IcSize()))); + funcAnnotationData.AddElement(std::move(icSizeAnnotationElement)); + + panda::pandasm::AnnotationElement parameterLengthAnnotationElement( + "parameterLength", + std::make_unique( + panda::pandasm::ScalarValue::Create(pg_->FormalParametersCount()))); + funcAnnotationData.AddElement(std::move(parameterLengthAnnotationElement)); + + panda::pandasm::AnnotationElement funcNameAnnotationElement( + "funcName", + std::make_unique( + panda::pandasm::ScalarValue::Create(pg_->FunctionName().Mutf8()))); + funcAnnotationData.AddElement(std::move(funcNameAnnotationElement)); + + func_->metadata->AddAnnotations({funcAnnotationData}); +} + +void FunctionEmitter::GenFunctionCatchTables() +{ + func_->catch_blocks.reserve(pg_->CatchList().size()); + + for (const auto *catchBlock : pg_->CatchList()) { + const auto &labelSet = catchBlock->LabelSet(); + + auto &pandaCatchBlock = func_->catch_blocks.emplace_back(); + pandaCatchBlock.try_begin_label = labelSet.TryBegin()->Id(); + pandaCatchBlock.try_end_label = labelSet.TryEnd()->Id(); + pandaCatchBlock.catch_begin_label = labelSet.CatchBegin()->Id(); + pandaCatchBlock.catch_end_label = labelSet.CatchEnd()->Id(); + } +} + +void FunctionEmitter::GenLiteralBuffers() +{ + for (const auto *buff : pg_->BuffStorage()) { + GenBufferLiterals(buff); + } +} + +void FunctionEmitter::GenSourceFileDebugInfo() +{ + func_->source_file = std::string {pg_->Binder()->Program()->SourceFile()}; + + if (!pg_->IsDebug()) { + return; + } + + if (pg_->RootNode()->IsProgram()) { + func_->source_code = SourceCode().EscapeSymbol(); + } +} + +void FunctionEmitter::GenScopeVariableInfo(const binder::Scope *scope) +{ + const auto *startIns = scope->ScopeStart(); + const auto *endIns = scope->ScopeEnd(); + + uint32_t start = 0; + uint32_t count = 0; + + for (const auto *it : pg_->Insns()) { + if (startIns == it) { + start = count; + } else if (endIns == it) { + auto varsLength = static_cast(count - start + 1); + + for (const auto &[name, variable] : scope->Bindings()) { + if (!variable->IsLocalVariable() || variable->LexicalBound()) { + continue; + } + + auto &variableDebug = func_->local_variable_debug.emplace_back(); + variableDebug.name = name.Mutf8(); + variableDebug.signature = "any"; + variableDebug.signature_type = "any"; + variableDebug.reg = static_cast(variable->AsLocalVariable()->Vreg()); + variableDebug.start = start; + variableDebug.length = static_cast(varsLength); + } + + break; + } + + count++; + } +} + +void FunctionEmitter::GenVariablesDebugInfo() +{ + if (!pg_->IsDebug()) { + return; + } + + for (const auto *scope : pg_->Debuginfo().variableDebugInfo) { + GenScopeVariableInfo(scope); + } +} + +// Emitter + +Emitter::Emitter(const CompilerContext *context) +{ + prog_ = new panda::pandasm::Program(); + prog_->lang = panda::pandasm::extensions::Language::ECMASCRIPT; + + prog_->function_table.reserve(context->Binder()->Functions().size()); + GenESAnnoatationRecord(); + GenESModuleModeRecord(context->Binder()->Program()->Kind() == parser::ScriptKind::MODULE); +} + +Emitter::~Emitter() +{ + delete prog_; +} + +void Emitter::GenESAnnoatationRecord() +{ + auto annotationRecord = panda::pandasm::Record("_ESAnnotation", LANG_EXT); + annotationRecord.metadata->SetAttribute("external"); + annotationRecord.metadata->SetAccessFlags(panda::ACC_ANNOTATION); + prog_->record_table.emplace(annotationRecord.name, std::move(annotationRecord)); +} + +void Emitter::GenESModuleModeRecord(bool isModule) +{ + auto modeRecord = panda::pandasm::Record("_ESModuleMode", LANG_EXT); + modeRecord.metadata->SetAccessFlags(panda::ACC_PUBLIC); + + auto modeField = panda::pandasm::Field(LANG_EXT); + modeField.name = "isModule"; + modeField.type = panda::pandasm::Type("u8", 0); + modeField.metadata->SetValue( + panda::pandasm::ScalarValue::Create(static_cast(isModule))); + + modeRecord.field_list.emplace_back(std::move(modeField)); + + prog_->record_table.emplace(modeRecord.name, std::move(modeRecord)); +} + +void Emitter::AddFunction(FunctionEmitter *func) +{ + std::lock_guard lock(m_); + + for (const auto &str : func->Strings()) { + prog_->strings.insert(str.Mutf8()); + } + + for (auto &[idx, buf] : func->LiteralBuffers()) { + auto literalArrayInstance = panda::pandasm::LiteralArray(std::move(buf)); + prog_->literalarray_table.emplace(std::to_string(idx), std::move(literalArrayInstance)); + } + + auto *function = func->Function(); + prog_->function_table.emplace(function->name, std::move(*function)); +} + +void Emitter::DumpAsm(const panda::pandasm::Program *prog) +{ + auto &ss = std::cout; + + ss << ".language ECMAScript" << std::endl << std::endl; + + for (auto &[name, func] : prog->function_table) { + ss << ".function any " << name << '('; + + for (uint32_t i = 0; i < func.GetParamsNum(); i++) { + ss << "any a" << std::to_string(i); + + if (i != func.GetParamsNum() - 1) { + ss << ", "; + } + } + + ss << ") {" << std::endl; + + for (const auto &ins : func.ins) { + ss << (ins.set_label ? "" : "\t") << ins.ToString("", true, func.GetTotalRegs()) << std::endl; + } + + ss << "}" << std::endl << std::endl; + + for (const auto &ct : func.catch_blocks) { + ss << ".catchall " << ct.try_begin_label << ", " << ct.try_end_label << ", " << ct.catch_begin_label + << std::endl + << std::endl; + } + } + + ss << std::endl; +} + +panda::pandasm::Program *Emitter::Finalize(bool dumpDebugInfo) +{ + if (dumpDebugInfo) { + debuginfo::DebugInfoDumper dumper(prog_); + dumper.Dump(); + } + + auto *prog = prog_; + prog_ = nullptr; + return prog; +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/emitter.h b/es2panda/compiler/core/emitter.h new file mode 100644 index 0000000000000000000000000000000000000000..addf4b142b4daaeb21c86c5edcf424de01ea2c0b --- /dev/null +++ b/es2panda/compiler/core/emitter.h @@ -0,0 +1,115 @@ +/** + * 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_IR_EMITTER_H +#define ES2PANDA_COMPILER_IR_EMITTER_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace panda::pandasm { +struct Program; +struct Function; +struct Ins; +} // namespace panda::pandasm + +namespace panda::es2panda::ir { +class Statement; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::binder { +class Scope; +} // namespace panda::es2panda::binder + +namespace panda::es2panda::compiler { + +class PandaGen; +class LiteralBuffer; +class DebugInfo; +class Label; +class IRNode; +class CompilerContext; + +class FunctionEmitter { +public: + explicit FunctionEmitter(ArenaAllocator *allocator, const PandaGen *pg); + ~FunctionEmitter() = default; + NO_COPY_SEMANTIC(FunctionEmitter); + NO_MOVE_SEMANTIC(FunctionEmitter); + + panda::pandasm::Function *Function() + { + return func_; + } + + auto &LiteralBuffers() + { + return literalBuffers_; + } + + void Generate(); + const ArenaSet &Strings() const; + +private: + void GenInstructionDebugInfo(const IRNode *ins, panda::pandasm::Ins *pandaIns); + void GenFunctionInstructions(); + void GenFunctionCatchTables(); + void GenFunctionICSize(); + void GenScopeVariableInfo(const binder::Scope *scope); + void GenSourceFileDebugInfo(); + void GenVariablesDebugInfo(); + util::StringView SourceCode() const; + + void GenLiteralBuffers(); + void GenBufferLiterals(const LiteralBuffer *buff); + + const PandaGen *pg_; + panda::pandasm::Function *func_ {}; + ArenaVector>> literalBuffers_; + size_t offset_ {0}; +}; + +class Emitter { +public: + explicit Emitter(const CompilerContext *context); + ~Emitter(); + NO_COPY_SEMANTIC(Emitter); + NO_MOVE_SEMANTIC(Emitter); + + void AddFunction(FunctionEmitter *func); + static void DumpAsm(const panda::pandasm::Program *prog); + panda::pandasm::Program *Finalize(bool dumpDebugInfo); + +private: + void GenESAnnoatationRecord(); + void GenESModuleModeRecord(bool isModule); + + std::mutex m_; + panda::pandasm::Program *prog_; +}; + +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/core/envScope.cpp b/es2panda/compiler/core/envScope.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6e2fa13c78c7038bb1a0d4e2a0eb7405af141868 --- /dev/null +++ b/es2panda/compiler/core/envScope.cpp @@ -0,0 +1,84 @@ +/* + * 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 "envScope.h" + +#include +#include + +namespace panda::es2panda::compiler { + +ScopeContext::ScopeContext(PandaGen *pg, binder::Scope *newScope) : pg_(pg), prevScope_(pg_->scope_) +{ + pg_->scope_ = newScope; +} + +ScopeContext::~ScopeContext() +{ + pg_->scope_ = prevScope_; +} + +void EnvScope::Initialize(PandaGen *pg, VReg lexEnv) +{ + pg_ = pg; + prev_ = pg_->envScope_; + lexEnv_ = lexEnv; + pg_->envScope_ = this; +} + +EnvScope::~EnvScope() +{ + if (!pg_) { + return; + } + + pg_->envScope_ = prev_; +} + +void LoopEnvScope::CopyBindings(PandaGen *pg, binder::VariableScope *scope, binder::VariableFlags flag) +{ + if (!HasEnv()) { + return; + } + + Initialize(pg, pg->AllocReg()); + + pg_->NewLexEnv(scope_->Node(), scope->LexicalSlots()); + pg_->StoreAccumulator(scope_->Node(), lexEnv_); + + ASSERT(scope->NeedLexEnv()); + + for (const auto &[_, variable] : scope_->Bindings()) { + (void)_; + if (!variable->HasFlag(flag)) { + continue; + } + + pg->LoadLexicalVar(scope_->Node(), 1, variable->AsLocalVariable()->LexIdx()); + pg->StoreLexicalVar(scope_->Parent()->Node(), 0, variable->AsLocalVariable()->LexIdx()); + } +} + +void LoopEnvScope::CopyPetIterationCtx() +{ + if (!HasEnv()) { + return; + } + + pg_->CopyLexEnv(scope_->Node()); + pg_->StoreAccumulator(scope_->Node(), lexEnv_); +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/envScope.h b/es2panda/compiler/core/envScope.h new file mode 100644 index 0000000000000000000000000000000000000000..55b801a7a1f3e026a9403344d896fdbedc68ecbe --- /dev/null +++ b/es2panda/compiler/core/envScope.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_CORE_ENV_SCOPE_H +#define ES2PANDA_COMPILER_CORE_ENV_SCOPE_H + +#include +#include +#include +#include +#include + +namespace panda::es2panda::ir { +class AstNode; +class Statement; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::compiler { + +class PandaGen; + +class ScopeContext { +public: + explicit ScopeContext(PandaGen *pg, binder::Scope *newScope); + ~ScopeContext(); + + NO_COPY_SEMANTIC(ScopeContext); + NO_MOVE_SEMANTIC(ScopeContext); + +private: + PandaGen *pg_; + binder::Scope *prevScope_; +}; + +class EnvScope { +public: + explicit EnvScope() = default; + + NO_COPY_SEMANTIC(EnvScope); + NO_MOVE_SEMANTIC(EnvScope); + ~EnvScope(); + + void Initialize(PandaGen *pg, VReg lexEnv); + + VReg LexEnv() const + { + return lexEnv_; + } + + EnvScope *Prev() const + { + return prev_; + } + +protected: + friend class PandaGen; + + PandaGen *pg_ {}; + EnvScope *prev_ {}; + VReg lexEnv_ {}; +}; + +class LoopEnvScope : public EnvScope { +public: + explicit LoopEnvScope(PandaGen *pg, binder::LoopScope *scope, LabelTarget target) + : scope_(NeedEnv(scope) ? scope : nullptr), regScope_(pg, scope), lexEnvCtx_(this, pg, target) + { + CopyBindings(pg, scope, binder::VariableFlags::PER_ITERATION); + } + + explicit LoopEnvScope(PandaGen *pg, LabelTarget target, binder::LoopScope *scope) + : scope_(NeedEnv(scope) ? scope : nullptr), regScope_(pg), lexEnvCtx_(this, pg, target) + { + CopyBindings(pg, scope, binder::VariableFlags::PER_ITERATION); + } + + explicit LoopEnvScope(PandaGen *pg, binder::LoopDeclarationScope *scope) + : scope_(NeedEnv(scope) ? scope : nullptr), regScope_(pg), lexEnvCtx_(this, pg, {}) + { + CopyBindings(pg, scope, binder::VariableFlags::LOOP_DECL); + } + + binder::VariableScope *Scope() const + { + ASSERT(HasEnv()); + return scope_; + } + + bool HasEnv() const + { + return scope_ != nullptr; + } + + void CopyPetIterationCtx(); + +private: + static bool NeedEnv(binder::VariableScope *scope) + { + return scope->IsVariableScope() && scope->AsVariableScope()->NeedLexEnv(); + } + + void CopyBindings(PandaGen *pg, binder::VariableScope *scope, binder::VariableFlags flag); + + binder::VariableScope *scope_ {}; + LocalRegScope regScope_; + LexEnvContext lexEnvCtx_; +}; + +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/core/function.cpp b/es2panda/compiler/core/function.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8625e3f28155c571f83e1abbde96feac25844436 --- /dev/null +++ b/es2panda/compiler/core/function.cpp @@ -0,0 +1,208 @@ +/** + * 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 "function.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace panda::es2panda::compiler { + +static void CompileSourceBlock(PandaGen *pg, const ir::BlockStatement *block) +{ + bool hasReturn = false; + + const auto &statements = block->Statements(); + pg->SetFirstStmt(statements.empty() ? block : statements.front()); + + for (const auto *stmt : statements) { + stmt->Compile(pg); + + if (stmt->IsReturnStatement()) { + hasReturn = true; + } + } + + if (hasReturn) { + return; + } + + pg->ImplicitReturn(statements.empty() ? block : statements.back()); +} + +static void CompileFunctionParameterDeclaration(PandaGen *pg, const ir::ScriptFunction *func) +{ + ScopeContext scopeCtx(pg, func->Scope()->ParamScope()); + + uint32_t index = 0; + + for (const auto *param : func->Params()) { + LReference ref = LReference::CreateLRef(pg, param, true); + + [[maybe_unused]] binder::Variable *paramVar = ref.Variable(); + + if (ref.Kind() == ReferenceKind::DESTRUCTURING) { + util::StringView name = util::Helpers::ToStringView(pg->Allocator(), index); + paramVar = pg->Scope()->FindLocal(name, binder::ResolveBindingOptions::BINDINGS); + } + + ASSERT(paramVar && paramVar->IsLocalVariable()); + + VReg paramReg = binder::Binder::MANDATORY_PARAMS_NUMBER + index++; + ASSERT(paramVar->LexicalBound() || paramVar->AsLocalVariable()->Vreg() == paramReg); + + if (param->IsAssignmentPattern()) { + RegScope rs(pg); + pg->LoadAccumulator(func, paramReg); + auto *nonDefaultLabel = pg->AllocLabel(); + + if (ref.Kind() == ReferenceKind::DESTRUCTURING) { + auto *loadParamLabel = pg->AllocLabel(); + + pg->BranchIfNotUndefined(func, loadParamLabel); + param->AsAssignmentPattern()->Right()->Compile(pg); + pg->Branch(func, nonDefaultLabel); + + pg->SetLabel(func, loadParamLabel); + pg->LoadAccumulator(func, paramReg); + + pg->SetLabel(func, nonDefaultLabel); + ref.SetValue(); + } else { + pg->BranchIfNotUndefined(func, nonDefaultLabel); + + param->AsAssignmentPattern()->Right()->Compile(pg); + ref.SetValue(); + pg->SetLabel(func, nonDefaultLabel); + } + + continue; + } + + if (param->IsRestElement()) { + pg->CopyRestArgs(param, func->Params().size() - 1); + } else if (ref.Kind() == ReferenceKind::DESTRUCTURING) { + pg->LoadAccumulator(func, paramReg); + } else { + continue; + } + ref.SetValue(); + } +} + +static void CompileInstanceFields(PandaGen *pg, const ir::ScriptFunction *decl) +{ + const auto &statements = decl->Parent()->Parent()->Parent()->AsClassDefinition()->Body(); + + RegScope rs(pg); + auto thisReg = pg->AllocReg(); + pg->GetThis(decl); + pg->StoreAccumulator(decl, thisReg); + + for (auto const &stmt : statements) { + if (stmt->IsClassProperty()) { + const auto *prop = stmt->AsClassProperty(); + if (!prop->Value()) { + pg->LoadConst(stmt, Constant::JS_UNDEFINED); + } else { + RegScope rsProp(pg); + prop->Value()->Compile(pg); + } + + if (!prop->Key()->IsIdentifier()) { + PandaGen::Unimplemented(); + } + + pg->StoreObjByName(stmt, thisReg, prop->Key()->AsIdentifier()->Name()); + } + } +} + +static void CompileFunction(PandaGen *pg) +{ + const auto *decl = pg->RootNode()->AsScriptFunction(); + + // TODO(szilagyia): move after super call + if (decl->IsConstructor()) { + CompileInstanceFields(pg, decl); + } + + auto *funcParamScope = pg->TopScope()->ParamScope(); + if (funcParamScope->NameVar()) { + RegScope rs(pg); + pg->GetFunctionObject(pg->RootNode()); + pg->StoreAccToLexEnv(pg->RootNode(), funcParamScope->Find(funcParamScope->NameVar()->Name()), true); + } + + CompileFunctionParameterDeclaration(pg, decl); + + pg->FunctionEnter(); + const ir::AstNode *body = decl->Body(); + + if (body->IsExpression()) { + body->Compile(pg); + pg->DirectReturn(decl); + } else { + CompileSourceBlock(pg, body->AsBlockStatement()); + } + + pg->FunctionExit(); +} + +static VReg CompileFunctionOrProgram(PandaGen *pg) +{ + FunctionRegScope lrs(pg); + const auto *topScope = pg->TopScope(); + + if (pg->FunctionHasFinalizer()) { + ASSERT(topScope->IsFunctionScope()); + + TryContext tryCtx(pg); + pg->FunctionInit(tryCtx.GetCatchTable()); + + CompileFunction(pg); + } else { + pg->FunctionInit(nullptr); + + if (topScope->IsFunctionScope()) { + CompileFunction(pg); + } else { + ASSERT(topScope->IsGlobalScope() || topScope->IsModuleScope()); + CompileSourceBlock(pg, pg->RootNode()->AsBlockStatement()); + } + } + + return pg->GetEnvScope()->LexEnv(); +} + +void Function::Compile(PandaGen *pg) +{ + VReg lexEnv = CompileFunctionOrProgram(pg); + pg->CopyFunctionArguments(pg->RootNode()); + pg->InitializeLexEnv(pg->RootNode(), lexEnv); + pg->SortCatchTables(); +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/function.h b/es2panda/compiler/core/function.h new file mode 100644 index 0000000000000000000000000000000000000000..4be6f755aacd317538303d5daeaf062a0896d898 --- /dev/null +++ b/es2panda/compiler/core/function.h @@ -0,0 +1,32 @@ +/* + * 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_FUNCTION_H +#define ES2PANDA_COMPILER_CORE_FUNCTION_H + +namespace panda::es2panda::compiler { + +class PandaGen; + +class Function { +public: + Function() = delete; + + static void Compile(PandaGen *pg); +}; + +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/core/inlineCache.cpp b/es2panda/compiler/core/inlineCache.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0570e0f8977ce5746355358998eecf2a0cb858d5 --- /dev/null +++ b/es2panda/compiler/core/inlineCache.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 "inlineCache.h" + +#include + +namespace panda::es2panda::compiler { + +size_t InlineCache::Size() const +{ + return size_; +} + +uint32_t InlineCache::Offset(uint32_t slotSize) +{ + uint32_t offset = size_ + slotSize; + + constexpr uint32_t LIMIT = std::numeric_limits::max(); + if (offset > LIMIT) { + return LIMIT; + } + + size_ = offset; + return offset; +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/inlineCache.h b/es2panda/compiler/core/inlineCache.h new file mode 100644 index 0000000000000000000000000000000000000000..3b8ee40827cd9e1be980527a5a842e34dc61a7e9 --- /dev/null +++ b/es2panda/compiler/core/inlineCache.h @@ -0,0 +1,36 @@ +/** + * 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_IR_INLINE_CACHE_H +#define ES2PANDA_COMPILER_IR_INLINE_CACHE_H + +#include + +namespace panda::es2panda::compiler { + +class InlineCache { +public: + explicit InlineCache() = default; + + size_t Size() const; + uint32_t Offset(uint32_t slotSize); + +private: + size_t size_ {}; +}; + +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/core/labelPair.h b/es2panda/compiler/core/labelPair.h new file mode 100644 index 0000000000000000000000000000000000000000..ec85351877c1ec7ad878b108e7e0c39baa5de882 --- /dev/null +++ b/es2panda/compiler/core/labelPair.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_CORE_LABEL_PAIR_H +#define ES2PANDA_COMPILER_CORE_LABEL_PAIR_H + +#include +#include + +namespace panda::es2panda::compiler { +class LabelPair { +public: + LabelPair(Label *begin, Label *end) : begin_(begin), end_(end) {} + + Label *Begin() const + { + return begin_; + } + + Label *End() const + { + return end_; + } + +protected: + Label *begin_ {}; + Label *end_ {}; +}; +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/core/labelTarget.cpp b/es2panda/compiler/core/labelTarget.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3fc9530fa07e3b66cd8c6e982274efeee2cfc3b1 --- /dev/null +++ b/es2panda/compiler/core/labelTarget.cpp @@ -0,0 +1,27 @@ +/** + * 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 "labelTarget.h" + +#include + +namespace panda::es2panda::compiler { + +LabelTarget::LabelTarget(PandaGen *pg) + : LabelPair(pg->AllocLabel(), pg->AllocLabel()), breakLabel_(BREAK_LABEL), continueLabel_(CONTINUE_LABEL) +{ +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/labelTarget.h b/es2panda/compiler/core/labelTarget.h new file mode 100644 index 0000000000000000000000000000000000000000..6c9ee9792b458ad55d9721d3b82c47d7786d2e14 --- /dev/null +++ b/es2panda/compiler/core/labelTarget.h @@ -0,0 +1,88 @@ +/** + * 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_CORE_LABEL_TARGET_H +#define ES2PANDA_COMPILER_CORE_LABEL_TARGET_H + +#include +#include + +#include + +namespace panda::es2panda::ir { +class AstNode; +class Identifier; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::compiler { + +class LabelTarget; +class PandaGen; + +enum class ControlFlowChange { + CONTINUE, + BREAK, +}; + +class LabelTarget : public LabelPair { +public: + explicit LabelTarget(PandaGen *pg); + explicit LabelTarget(const util::StringView &label) : LabelTarget(nullptr, label) {} + explicit LabelTarget(Label *target, const util::StringView &label) + : LabelPair(target, nullptr), breakLabel_(label), continueLabel_(label) + { + } + LabelTarget() : LabelPair(nullptr, nullptr) {}; + + ~LabelTarget() = default; + DEFAULT_COPY_SEMANTIC(LabelTarget); + DEFAULT_MOVE_SEMANTIC(LabelTarget); + + const util::StringView &BreakLabel() const + { + return breakLabel_; + } + + Label *BreakTarget() const + { + return begin_; + } + + void SetBreakTarget(Label *label) + { + begin_ = label; + } + + const util::StringView &ContinueLabel() const + { + return continueLabel_; + } + + Label *ContinueTarget() const + { + return end_; + } + + static constexpr std::string_view BREAK_LABEL = "#b"; + static constexpr std::string_view CONTINUE_LABEL = "#c"; + +private: + util::StringView breakLabel_ {}; + util::StringView continueLabel_ {}; +}; + +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/core/moduleContext.cpp b/es2panda/compiler/core/moduleContext.cpp new file mode 100644 index 0000000000000000000000000000000000000000..272a319c361a78ee4436ee60cf84cc30152e5f8f --- /dev/null +++ b/es2panda/compiler/core/moduleContext.cpp @@ -0,0 +1,84 @@ +/** + * 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 "moduleContext.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace panda::es2panda::compiler { +void CompileImports(PandaGen *pg, binder::ModuleScope *scope) +{ + for (const auto &[importDecl, decls] : scope->Imports()) { + pg->ImportModule(importDecl, importDecl->Source()->Str()); + + VReg moduleReg = pg->AllocReg(); + pg->StoreAccumulator(importDecl, moduleReg); + + for (const auto *decl : decls) { + binder::Variable *v = scope->FindLocal(decl->LocalName()); + + if (!v->IsModuleVariable()) { + ASSERT(decl->ImportName() == "*"); + + binder::ScopeFindResult result(decl->LocalName(), scope, 0, v); + pg->StoreAccToLexEnv(decl->Node(), result, true); + } else { + v->AsModuleVariable()->ModuleReg() = moduleReg; + } + } + } +} + +void CompileExports(PandaGen *pg, const binder::ModuleScope *scope) +{ + for (const auto &[exportDecl, decls] : scope->Exports()) { + if (exportDecl->IsExportAllDeclaration()) { + pg->ImportModule(exportDecl, exportDecl->AsExportAllDeclaration()->Source()->Str()); + } else if (exportDecl->IsExportNamedDeclaration() && exportDecl->AsExportNamedDeclaration()->Source()) { + pg->ImportModule(exportDecl, exportDecl->AsExportNamedDeclaration()->Source()->Str()); + } else { + continue; + } + + VReg moduleReg = pg->AllocReg(); + pg->StoreAccumulator(exportDecl, moduleReg); + + if (exportDecl->IsExportAllDeclaration()) { + pg->StoreModuleVar(exportDecl, decls.front()->ExportName()); + continue; + } + + pg->CopyModule(exportDecl, moduleReg); + + for (const auto *decl : decls) { + pg->LoadObjByName(decl->Node(), moduleReg, decl->LocalName()); + pg->StoreModuleVar(decl->Node(), decl->ExportName()); + } + } +} + +void ModuleContext::Compile(PandaGen *pg, binder::ModuleScope *scope) +{ + CompileImports(pg, scope); + CompileExports(pg, scope); +} +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/moduleContext.h b/es2panda/compiler/core/moduleContext.h new file mode 100644 index 0000000000000000000000000000000000000000..da15b229feb4aa0fcc51b2146d6cc00bcb488010 --- /dev/null +++ b/es2panda/compiler/core/moduleContext.h @@ -0,0 +1,39 @@ +/** + * 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_MODULE_CONTEXT_H +#define ES2PANDA_COMPILER_CORE_MODULE_CONTEXT_H + +#include + +#include + +namespace panda::es2panda::binder { +class Variable; +class ModuleScope; +} // namespace panda::es2panda::binder + +namespace panda::es2panda::compiler { +class PandaGen; + +class ModuleContext { +public: + ModuleContext() = delete; + + static void Compile(PandaGen *pg, binder::ModuleScope *scope); +}; +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/core/pandagen.cpp b/es2panda/compiler/core/pandagen.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d58fa71c45b4622695a76884a4c7035854e2eac --- /dev/null +++ b/es2panda/compiler/core/pandagen.cpp @@ -0,0 +1,1526 @@ +/* + * 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 "pandagen.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace panda::es2panda::compiler { + +// PandaGen + +Label *PandaGen::AllocLabel() +{ + std::string id = std::string {Label::PREFIX} + std::to_string(labelId_++); + return sa_.AllocLabel(std::move(id)); +} + +bool PandaGen::IsDebug() const +{ + return context_->IsDebug(); +} + +uint32_t PandaGen::ParamCount() const +{ + if (rootNode_->IsProgram()) { + return 0; + } + + return rootNode_->AsScriptFunction()->Params().size(); +} + +uint32_t PandaGen::FormalParametersCount() const +{ + if (rootNode_->IsProgram()) { + return 0; + } + + ASSERT(rootNode_->IsScriptFunction()); + + return rootNode_->AsScriptFunction()->FormalParamsLength(); +} + +uint32_t PandaGen::InternalParamCount() const +{ + static const uint32_t HIDDEN_PARAMS = 3; + return ParamCount() + HIDDEN_PARAMS; +} + +const util::StringView &PandaGen::InternalName() const +{ + return topScope_->InternalName(); +} + +const util::StringView &PandaGen::FunctionName() const +{ + return topScope_->Name(); +} + +binder::Binder *PandaGen::Binder() const +{ + return context_->Binder(); +} + +void PandaGen::FunctionInit(CatchTable *catchTable) +{ + 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); +} + +bool PandaGen::FunctionHasFinalizer() const +{ + if (rootNode_->IsProgram()) { + return false; + } + + const ir::ScriptFunction *func = rootNode_->AsScriptFunction(); + + return func->IsAsync() || func->IsGenerator(); +} + +void PandaGen::FunctionEnter() +{ + builder_->Prepare(rootNode_->AsScriptFunction()); +} + +void PandaGen::FunctionExit() +{ + builder_->CleanUp(rootNode_->AsScriptFunction()); +} + +void PandaGen::InitializeLexEnv(const ir::AstNode *node, VReg lexEnv) +{ + FrontAllocator fa(this); + + if (topScope_->NeedLexEnv()) { + NewLexEnv(node, topScope_->LexicalSlots()); + } else { + LdLexEnv(node); + } + + StoreAccumulator(node, lexEnv); +} + +void PandaGen::CopyFunctionArguments(const ir::AstNode *node) +{ + FrontAllocator fa(this); + VReg targetReg = totalRegs_; + + for (const auto *param : topScope_->ParamScope()->Params()) { + if (param->LexicalBound()) { + LoadAccumulator(node, targetReg++); + StoreLexicalVar(node, 0, param->LexIdx()); + } else { + ra_.Emit(node, param->Vreg(), targetReg++); + } + } +} + +LiteralBuffer *PandaGen::NewLiteralBuffer() +{ + return allocator_->New(allocator_); +} + +int32_t PandaGen::AddLiteralBuffer(LiteralBuffer *buf) +{ + buffStorage_.push_back(buf); + buf->SetIndex(context_->NewLiteralIndex()); + return buf->Index(); +} + +void PandaGen::GetFunctionObject(const ir::AstNode *node) +{ + 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)); +} + +void PandaGen::GetThis(const ir::AstNode *node) +{ + 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); +} + +void PandaGen::LoadVar(const ir::Identifier *node, const binder::ScopeFindResult &result) +{ + auto *var = result.variable; + + if (!var) { + TryLoadGlobalByName(node, result.name); + return; + } + + if (var->IsGlobalVariable()) { + LoadGlobalVar(node, var->Name()); + return; + } + + if (var->IsModuleVariable()) { + LoadModuleVariable(node, var->AsModuleVariable()->ModuleReg(), var->AsModuleVariable()->ExoticName()); + return; + } + + ASSERT(var->IsLocalVariable()); + LoadAccFromLexEnv(node, result); +} + +void PandaGen::StoreVar(const ir::AstNode *node, const binder::ScopeFindResult &result, bool isDeclaration) +{ + binder::Variable *var = result.variable; + + if (!var) { + TryStoreGlobalByName(node, result.name); + return; + } + + if (var->IsGlobalVariable()) { + StoreGlobalVar(node, var->Name()); + return; + } + + if (var->IsModuleVariable()) { + ThrowConstAssignment(node, var->Name()); + return; + } + + ASSERT(var->IsLocalVariable()); + 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)) { + return; + } + + binder::ScopeFindResult res = scope_->Find(binder::Binder::FUNCTION_ARGUMENTS); + ASSERT(res.scope); + + GetUnmappedArgs(node); + StoreAccToLexEnv(node, res, true); +} + +void PandaGen::LoadObjProperty(const ir::AstNode *node, VReg obj, const Operand &prop) +{ + if (std::holds_alternative(prop)) { + LoadObjByValue(node, obj, std::get(prop)); + return; + } + + if (std::holds_alternative(prop)) { + LoadObjByIndex(node, obj, std::get(prop)); + return; + } + + ASSERT(std::holds_alternative(prop)); + LoadObjByName(node, obj, std::get(prop)); +} + +void PandaGen::StoreObjProperty(const ir::AstNode *node, VReg obj, const Operand &prop) +{ + if (std::holds_alternative(prop)) { + StoreObjByValue(node, obj, std::get(prop)); + return; + } + + if (std::holds_alternative(prop)) { + StoreObjByIndex(node, obj, std::get(prop)); + return; + } + + ASSERT(std::holds_alternative(prop)); + StoreObjByName(node, obj, std::get(prop)); +} + +void PandaGen::StoreOwnProperty(const ir::AstNode *node, VReg obj, const Operand &prop) +{ + if (std::holds_alternative(prop)) { + StOwnByValue(node, obj, std::get(prop)); + return; + } + + if (std::holds_alternative(prop)) { + StOwnByIndex(node, obj, std::get(prop)); + return; + } + + ASSERT(std::holds_alternative(prop)); + StOwnByName(node, obj, std::get(prop)); +} + +void PandaGen::TryLoadGlobalByName(const ir::AstNode *node, const util::StringView &name) +{ + sa_.Emit(node, name); + strings_.insert(name); +} + +void PandaGen::TryStoreGlobalByName(const ir::AstNode *node, const util::StringView &name) +{ + sa_.Emit(node, name); + strings_.insert(name); +} + +void PandaGen::LoadObjByName(const ir::AstNode *node, VReg obj, const util::StringView &prop) +{ + ra_.Emit(node, prop, obj); + strings_.insert(prop); +} + +void PandaGen::StoreObjByName(const ir::AstNode *node, VReg obj, const util::StringView &prop) +{ + ra_.Emit(node, prop, obj); + strings_.insert(prop); +} + +void PandaGen::LoadObjByIndex(const ir::AstNode *node, VReg obj, int64_t index) +{ + ra_.Emit(node, index, obj); +} + +void PandaGen::LoadObjByValue(const ir::AstNode *node, VReg obj, VReg prop) +{ + ra_.Emit(node, obj, prop); +} + +void PandaGen::StoreObjByValue(const ir::AstNode *node, VReg obj, VReg prop) +{ + ra_.Emit(node, obj, prop); +} + +void PandaGen::StoreObjByIndex(const ir::AstNode *node, VReg obj, int64_t index) +{ + ra_.Emit(node, index, obj); +} + +void PandaGen::StOwnByName(const ir::AstNode *node, VReg obj, const util::StringView &prop) +{ + ra_.Emit(node, prop, obj); + strings_.insert(prop); +} + +void PandaGen::StOwnByValue(const ir::AstNode *node, VReg obj, VReg prop) +{ + ra_.Emit(node, obj, prop); +} + +void PandaGen::StOwnByIndex(const ir::AstNode *node, VReg obj, int64_t index) +{ + ra_.Emit(node, index, obj); +} + +void PandaGen::DeleteObjProperty(const ir::AstNode *node, VReg obj, const Operand &prop) +{ + VReg property {}; + + if (std::holds_alternative(prop)) { + property = std::get(prop); + } else if (std::holds_alternative(prop)) { + LoadAccumulatorInt(node, static_cast(std::get(prop))); + property = AllocReg(); + StoreAccumulator(node, property); + } else { + ASSERT(std::holds_alternative(prop)); + LoadAccumulatorString(node, std::get(prop)); + property = AllocReg(); + StoreAccumulator(node, property); + } + + ra_.Emit(node, obj, property); +} + +void PandaGen::LoadAccumulator(const ir::AstNode *node, VReg reg) +{ + ra_.Emit(node, reg); +} + +void PandaGen::LoadGlobalVar(const ir::AstNode *node, const util::StringView &name) +{ + sa_.Emit(node, name); + strings_.insert(name); +} + +void PandaGen::StoreGlobalVar(const ir::AstNode *node, const util::StringView &name) +{ + sa_.Emit(node, name); + strings_.insert(name); +} + +void PandaGen::StoreGlobalLet(const ir::AstNode *node, const util::StringView &name) +{ + sa_.Emit(node, name); + strings_.insert(name); +} + +VReg PandaGen::LexEnv() const +{ + return envScope_->LexEnv(); +} + +void PandaGen::LoadAccFromLexEnv(const ir::AstNode *node, const binder::ScopeFindResult &result) +{ + VirtualLoadVar::Expand(this, node, result); +} + +void PandaGen::StoreAccToLexEnv(const ir::AstNode *node, const binder::ScopeFindResult &result, bool isDeclaration) +{ + VirtualStoreVar::Expand(this, node, result, isDeclaration); +} + +void PandaGen::LoadAccumulatorString(const ir::AstNode *node, const util::StringView &str) +{ + sa_.Emit(node, str); + strings_.insert(str); +} + +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) { + if (iter->HasFinalizer()) { + return true; + } + + iter = iter->Prev(); + } + + return false; +} + +Label *PandaGen::ControlFlowChangeBreak(const ir::Identifier *label) +{ + auto *iter = dynamicContext_; + + util::StringView labelName = label ? label->Name() : LabelTarget::BREAK_LABEL; + Label *breakTarget = nullptr; + + while (iter) { + iter->AbortContext(ControlFlowChange::BREAK, labelName); + + const auto &labelTargetName = iter->Target().BreakLabel(); + + if (iter->Target().BreakTarget()) { + 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 ? label->Name() : LabelTarget::CONTINUE_LABEL; + Label *continueTarget = nullptr; + + while (iter) { + iter->AbortContext(ControlFlowChange::CONTINUE, labelName); + + const auto &labelTargetName = iter->Target().ContinueLabel(); + + if (iter->Target().ContinueTarget()) { + continueTarget = iter->Target().ContinueTarget(); + } + + if (labelTargetName == labelName) { + break; + } + + iter = iter->Prev(); + } + + return continueTarget; +} + +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); + break; + } + case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_LESS_THAN: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_GREATER_THAN: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: { + ra_.Emit(node, lhs); + break; + } + default: { + UNREACHABLE(); + } + } + + BranchIfFalse(node, ifFalse); +} + +void PandaGen::Unary(const ir::AstNode *node, lexer::TokenType op, VReg operand) +{ + switch (op) { + case lexer::TokenType::PUNCTUATOR_PLUS: { + ra_.Emit(node, operand); + break; + } + case lexer::TokenType::PUNCTUATOR_MINUS: { + ra_.Emit(node, operand); + break; + } + case lexer::TokenType::PUNCTUATOR_TILDE: { + ra_.Emit(node, operand); + break; + } + case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: { + sa_.Emit(node); + break; + } + case lexer::TokenType::PUNCTUATOR_PLUS_PLUS: { + ra_.Emit(node, operand); + break; + } + case lexer::TokenType::PUNCTUATOR_MINUS_MINUS: { + ra_.Emit(node, operand); + break; + } + case lexer::TokenType::KEYW_VOID: + case lexer::TokenType::KEYW_DELETE: { + LoadConst(node, Constant::JS_UNDEFINED); + break; + } + default: { + UNREACHABLE(); + } + } +} + +void PandaGen::Binary(const ir::AstNode *node, lexer::TokenType op, VReg lhs) +{ + switch (op) { + case lexer::TokenType::PUNCTUATOR_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_LESS_THAN: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_GREATER_THAN: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_PLUS: + case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_MINUS: + case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_MULTIPLY: + case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_DIVIDE: + case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_MOD: + case lexer::TokenType::PUNCTUATOR_MOD_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_EXPONENTIATION_EQUAL: + case lexer::TokenType::PUNCTUATOR_EXPONENTIATION: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT: + case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT: + case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT: + case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_BITWISE_AND: + case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_BITWISE_OR: + case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: + case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::KEYW_IN: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::KEYW_INSTANCEOF: { + ra_.Emit(node, lhs); + break; + } + case lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING: + case lexer::TokenType::PUNCTUATOR_LOGICAL_NULLISH_EQUAL: { + Unimplemented(); + break; + } + default: { + UNREACHABLE(); + } + } +} + +void PandaGen::BranchIfUndefined(const ir::AstNode *node, Label *target) +{ + sa_.Emit(node); + BranchIfTrue(node, target); +} + +void PandaGen::BranchIfNotUndefined(const ir::AstNode *node, Label *target) +{ + sa_.Emit(node); + BranchIfFalse(node, target); +} + +void PandaGen::BranchIfTrue(const ir::AstNode *node, Label *target) +{ + sa_.Emit(node, target); +} + +void PandaGen::BranchIfNotTrue(const ir::AstNode *node, Label *target) +{ + sa_.Emit(node); + BranchIfFalse(node, target); +} + +void PandaGen::BranchIfFalse(const ir::AstNode *node, Label *target) +{ + sa_.Emit(node, target); +} + +void PandaGen::BranchIfCoercible(const ir::AstNode *node, Label *target) +{ + sa_.Emit(node); + BranchIfTrue(node, target); +} + +void PandaGen::EmitThrow(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::EmitRethrow(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::EmitReturn(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::EmitReturnUndefined(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::ImplicitReturn(const ir::AstNode *node) +{ + builder_->ImplicitReturn(node); +} + +void PandaGen::DirectReturn(const ir::AstNode *node) +{ + builder_->DirectReturn(node); +} + +void PandaGen::ValidateClassDirectReturn(const ir::AstNode *node) +{ + const ir::ScriptFunction *func = util::Helpers::GetContainingFunction(node); + + if (!func || !func->IsConstructor()) { + return; + } + + RegScope rs(this); + VReg value = AllocReg(); + StoreAccumulator(node, value); + + auto *notUndefined = AllocLabel(); + auto *condEnd = AllocLabel(); + + BranchIfNotUndefined(node, notUndefined); + GetThis(func); + ThrowIfSuperNotCorrectCall(func, 0); + Branch(node, condEnd); + + SetLabel(node, notUndefined); + LoadAccumulator(node, value); + + SetLabel(node, condEnd); +} + +void PandaGen::EmitAwait(const ir::AstNode *node) +{ + builder_->Await(node); +} + +void PandaGen::CallThis(const ir::AstNode *node, VReg startReg, size_t argCount) +{ + rra_.Emit(node, startReg, argCount + 2, static_cast(argCount), startReg); +} + +void PandaGen::Call(const ir::AstNode *node, VReg startReg, size_t argCount) +{ + VReg callee = startReg; + + switch (argCount) { + case 0: { // 0 args + ra_.Emit(node, callee); + break; + } + case 1: { // 1 arg + VReg arg0 = callee + 1; + ra_.Emit(node, callee, arg0); + break; + } + case 2: { // 2 args + VReg arg0 = callee + 1; + VReg arg1 = arg0 + 1; + ra_.Emit(node, callee, arg0, arg1); + break; + } + case 3: { // 3 args + VReg arg0 = callee + 1; + VReg arg1 = arg0 + 1; + VReg arg2 = arg1 + 1; + ra_.Emit(node, callee, arg0, arg1, arg2); + break; + } + default: { + rra_.Emit(node, startReg, argCount + 1, static_cast(argCount), startReg); + break; + } + } +} + +void PandaGen::SuperCall(const ir::AstNode *node, VReg startReg, size_t argCount) +{ + rra_.Emit(node, startReg, argCount, static_cast(argCount), startReg); +} + +void PandaGen::SuperCallSpread(const ir::AstNode *node, VReg 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); +} + +void PandaGen::LoadHomeObject(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::DefineFunction(const ir::AstNode *node, const ir::ScriptFunction *realNode, const util::StringView &name) +{ + if (realNode->IsAsync()) { + if (realNode->IsGenerator()) { + ra_.Emit(node, name, LexEnv()); + } else { + ra_.Emit(node, name, LexEnv()); + } + } else if (realNode->IsGenerator()) { + ra_.Emit(node, name, LexEnv()); + } else if (realNode->IsArrow()) { + LoadHomeObject(node); + ra_.Emit(node, name, LexEnv()); + } else if (realNode->IsMethod()) { + ra_.Emit(node, name, LexEnv()); + } else { + ra_.Emit(node, name, LexEnv()); + } + + strings_.insert(name); +} + +void PandaGen::TypeOf(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::CallSpread(const ir::AstNode *node, VReg func, VReg thisReg, VReg args) +{ + ra_.Emit(node, func, thisReg, args); +} + +void PandaGen::NewObjSpread(const ir::AstNode *node, VReg obj, VReg target) +{ + ra_.Emit(node, obj, target); +} + +void PandaGen::GetUnmappedArgs(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::Negate(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::ToBoolean(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::ToNumber(const ir::AstNode *node, VReg arg) +{ + ra_.Emit(node, arg); +} + +void PandaGen::GetMethod(const ir::AstNode *node, VReg obj, const util::StringView &name) +{ + ra_.Emit(node, name, obj); + strings_.insert(name); +} + +void PandaGen::CreateGeneratorObj(const ir::AstNode *node, VReg funcObj) +{ + ra_.Emit(node, funcObj); +} + +void PandaGen::CreateAsyncGeneratorObj(const ir::AstNode *node, VReg funcObj) +{ + ra_.Emit(node, funcObj); +} + +void PandaGen::CreateIterResultObject(const ir::AstNode *node, bool done) +{ + ra_.Emit(node, static_cast(done)); +} + +void PandaGen::SuspendGenerator(const ir::AstNode *node, VReg genObj) +{ + ra_.Emit(node, genObj); +} + +void PandaGen::SuspendAsyncGenerator(const ir::AstNode *node, VReg asyncGenObj) +{ + ra_.Emit(node, asyncGenObj); +} + +void PandaGen::GeneratorYield(const ir::AstNode *node, VReg genObj) +{ + 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)); +} + +void PandaGen::ResumeGenerator(const ir::AstNode *node, VReg genObj) +{ + ra_.Emit(node, genObj); +} + +void PandaGen::GetResumeMode(const ir::AstNode *node, VReg genObj) +{ + ra_.Emit(node, genObj); +} + +void PandaGen::AsyncFunctionEnter(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::AsyncFunctionAwait(const ir::AstNode *node, VReg asyncFuncObj) +{ + ra_.Emit(node, asyncFuncObj); +} + +void PandaGen::AsyncFunctionResolve(const ir::AstNode *node, VReg asyncFuncObj) +{ + ra_.Emit(node, asyncFuncObj); +} + +void PandaGen::AsyncFunctionReject(const ir::AstNode *node, VReg asyncFuncObj) +{ + ra_.Emit(node, asyncFuncObj); +} + +void PandaGen::AsyncGeneratorResolve(const ir::AstNode *node, VReg asyncGenObj) +{ + ra_.Emit(node, asyncGenObj); +} + +void PandaGen::AsyncGeneratorReject(const ir::AstNode *node, VReg asyncGenObj) +{ + ra_.Emit(node, asyncGenObj); +} + +void PandaGen::GetTemplateObject(const ir::AstNode *node, VReg value) +{ + ra_.Emit(node, value); +} + +void PandaGen::CopyRestArgs(const ir::AstNode *node, uint32_t index) +{ + sa_.Emit(node, index); +} + +void PandaGen::GetPropIterator(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::GetNextPropName(const ir::AstNode *node, VReg iter) +{ + ra_.Emit(node, iter); +} + +void PandaGen::CreateEmptyObject(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::CreateObjectWithBuffer(const ir::AstNode *node, uint32_t idx) +{ + ASSERT(util::Helpers::IsInteger(idx)); + sa_.Emit(node, idx); +} + +void PandaGen::CreateObjectHavingMethod(const ir::AstNode *node, uint32_t idx) +{ + ASSERT(util::Helpers::IsInteger(idx)); + LoadAccumulator(node, LexEnv()); + sa_.Emit(node, idx); +} + +void PandaGen::SetObjectWithProto(const ir::AstNode *node, VReg proto, VReg obj) +{ + ra_.Emit(node, proto, obj); +} + +void PandaGen::CopyDataProperties(const ir::AstNode *node, VReg dst, VReg 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); +} + +void PandaGen::CreateEmptyArray(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::CreateArrayWithBuffer(const ir::AstNode *node, uint32_t idx) +{ + ASSERT(util::Helpers::IsInteger(idx)); + sa_.Emit(node, idx); +} + +void PandaGen::CreateArray(const ir::AstNode *node, const ArenaVector &elements, VReg obj) +{ + if (elements.empty()) { + CreateEmptyArray(node); + StoreAccumulator(node, obj); + return; + } + + auto *buf = NewLiteralBuffer(); + + size_t i = 0; + // This loop handles constant literal data by collecting it into a literal buffer + // until a non-constant element is encountered. + while (i < elements.size() && util::Helpers::IsConstantExpr(elements[i])) { + buf->Add(elements[i]->AsLiteral()); + i++; + } + + if (buf->IsEmpty()) { + CreateEmptyArray(node); + } else { + uint32_t bufIdx = AddLiteralBuffer(buf); + CreateArrayWithBuffer(node, bufIdx); + } + + StoreAccumulator(node, obj); + + if (i == elements.size()) { + return; + } + + bool hasSpread = false; + + // This loop handles array elements until a spread element is encountered + for (; i < elements.size(); i++) { + const ir::Expression *elem = elements[i]; + + if (elem->IsOmittedExpression()) { + continue; + } + + if (elem->IsSpreadElement()) { + // The next loop will handle arrays that have a spread element + hasSpread = true; + break; + } + + elem->Compile(this); + StOwnByIndex(elem, obj, i); + } + + RegScope rs(this); + VReg idxReg {}; + + if (hasSpread) { + idxReg = AllocReg(); + LoadAccumulatorInt(node, i); + StoreAccumulator(node, idxReg); + } + + // This loop handles arrays that contain spread elements + for (; i < elements.size(); i++) { + const ir::Expression *elem = elements[i]; + + if (elem->IsSpreadElement()) { + elem->AsSpreadElement()->Argument()->Compile(this); + + StoreArraySpread(elem, obj, idxReg); + + LoadObjByName(node, obj, "length"); + StoreAccumulator(elem, idxReg); + continue; + } + + if (!elem->IsOmittedExpression()) { + elem->Compile(this); + StOwnByValue(elem, obj, idxReg); + } + + Unary(elem, lexer::TokenType::PUNCTUATOR_PLUS_PLUS, idxReg); + StoreAccumulator(elem, idxReg); + } + + // If the last element is omitted, we also have to update the length property + if (elements.back()->IsOmittedExpression()) { + // if there was a spread value then acc already contains the length + if (!hasSpread) { + LoadAccumulatorInt(node, i); + } + + StOwnByName(node, obj, "length"); + } + + LoadAccumulator(node, obj); +} + +void PandaGen::StoreArraySpread(const ir::AstNode *node, VReg array, VReg index) +{ + ra_.Emit(node, array, index); +} + +void PandaGen::ThrowIfNotObject(const ir::AstNode *node) +{ + ra_.Emit(node); +} + +void PandaGen::ThrowThrowNotExist(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::GetIterator(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::GetAsyncIterator(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::CreateObjectWithExcludedKeys(const ir::AstNode *node, VReg obj, VReg argStart, size_t argCount) +{ + ASSERT(argStart == obj + 1); + if (argCount == 0) { // Do not emit undefined register + argStart = obj; + } + + rra_.Emit(node, argStart, argCount, static_cast(argCount), obj, + argStart); +} + +void PandaGen::ThrowObjectNonCoercible(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::CloseIterator(const ir::AstNode *node, VReg iter) +{ + ra_.Emit(node, iter); +} + +void PandaGen::ImportModule(const ir::AstNode *node, const util::StringView &name) +{ + sa_.Emit(node, name); + strings_.insert(name); +} + +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); + strings_.insert(ctorId); +} + +void PandaGen::LoadModuleVariable(const ir::AstNode *node, VReg module, const util::StringView &name) +{ + ra_.Emit(node, name, module); + strings_.insert(name); +} + +void PandaGen::StoreModuleVar(const ir::AstNode *node, const util::StringView &name) +{ + sa_.Emit(node, name); + strings_.insert(name); +} + +void PandaGen::CopyModule(const ir::AstNode *node, VReg module) +{ + ra_.Emit(node, module); +} + +void PandaGen::StSuperByName(const ir::AstNode *node, VReg obj, const util::StringView &key) +{ + ra_.Emit(node, key, obj); + strings_.insert(key); +} + +void PandaGen::LdSuperByName(const ir::AstNode *node, VReg obj, const util::StringView &key) +{ + ra_.Emit(node, key, obj); + strings_.insert(key); +} + +void PandaGen::StSuperByValue(const ir::AstNode *node, VReg obj, VReg prop) +{ + ra_.Emit(node, obj, prop); +} + +void PandaGen::LdSuperByValue(const ir::AstNode *node, VReg obj, VReg prop) +{ + ra_.Emit(node, obj, prop); +} + +void PandaGen::StoreSuperProperty(const ir::AstNode *node, VReg obj, const Operand &prop) +{ + if (std::holds_alternative(prop)) { + StSuperByName(node, obj, std::get(prop)); + return; + } + + if (std::holds_alternative(prop)) { + StSuperByValue(node, obj, std::get(prop)); + return; + } + + ASSERT(std::holds_alternative(prop)); + RegScope rs(this); + VReg property = AllocReg(); + VReg value = AllocReg(); + + StoreAccumulator(node, value); + LoadAccumulatorInt(node, static_cast(std::get(prop))); + StoreAccumulator(node, property); + LoadAccumulator(node, value); + StSuperByValue(node, obj, property); +} + +void PandaGen::LoadSuperProperty(const ir::AstNode *node, VReg obj, const Operand &prop) +{ + if (std::holds_alternative(prop)) { + LdSuperByName(node, obj, std::get(prop)); + return; + } + + if (std::holds_alternative(prop)) { + LdSuperByValue(node, obj, std::get(prop)); + return; + } + + ASSERT(std::holds_alternative(prop)); + RegScope rs(this); + + LoadAccumulatorInt(node, static_cast(std::get(prop))); + VReg property = AllocReg(); + StoreAccumulator(node, property); + LdSuperByValue(node, obj, property); +} + +void PandaGen::LoadLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot) +{ + sa_.Emit(node, level, slot); +} + +void PandaGen::StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot) +{ + ra_.Emit(node, level, slot); +} + +void PandaGen::ThrowIfSuperNotCorrectCall(const ir::AstNode *node, int64_t num) +{ + sa_.Emit(node, num); +} + +void PandaGen::ThrowUndefinedIfHole(const ir::AstNode *node, const util::StringView &name) +{ + ra_.Emit(node, name); + strings_.insert(name); +} + +void PandaGen::ThrowConstAssignment(const ir::AstNode *node, const util::StringView &name) +{ + ra_.Emit(node, name); + strings_.insert(name); +} + +void PandaGen::PopLexEnv(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::CopyLexEnv(const ir::AstNode *node) +{ + sa_.Emit(node); +} + +void PandaGen::NewLexEnv(const ir::AstNode *node, uint32_t 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) { + 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(); }); +} + +Operand PandaGen::ToNamedPropertyKey(const ir::Expression *prop, bool isComputed) +{ + VReg res {0}; + + if (!isComputed) { + if (prop->IsIdentifier()) { + return prop->AsIdentifier()->Name(); + } + } else if (prop->IsStringLiteral()) { + const util::StringView &str = prop->AsStringLiteral()->Str(); + + /* TODO(dbatyai): remove this when runtime handles __proto__ as property name correctly */ + if (str.Is("__proto__")) { + return res; + } + + int64_t index = util::Helpers::GetIndex(str); + if (index != util::Helpers::INVALID_INDEX) { + return index; + } + + return str; + } else if (prop->IsNumberLiteral()) { + auto num = prop->AsNumberLiteral()->Number(); + if (util::Helpers::IsIndex(num)) { + return static_cast(num); + } + + return prop->AsNumberLiteral()->Str(); + } + + return res; +} + +Operand PandaGen::ToPropertyKey(const ir::Expression *prop, bool isComputed) +{ + Operand op = ToNamedPropertyKey(prop, isComputed); + if (!std::holds_alternative(op)) { + ASSERT(std::holds_alternative(op) || std::holds_alternative(op)); + return op; + } + + VReg propReg = AllocReg(); + + prop->Compile(this); + StoreAccumulator(prop, propReg); + + return propReg; +} + +VReg PandaGen::LoadPropertyKey(const ir::Expression *prop, bool isComputed) +{ + Operand op = ToNamedPropertyKey(prop, isComputed); + + if (std::holds_alternative(op)) { + LoadAccumulatorString(prop, std::get(op)); + } else if (std::holds_alternative(op)) { + LoadAccumulatorInt(prop, static_cast(std::get(op))); + } else { + prop->Compile(this); + } + + VReg propReg = AllocReg(); + StoreAccumulator(prop, propReg); + + return propReg; +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/pandagen.h b/es2panda/compiler/core/pandagen.h new file mode 100644 index 0000000000000000000000000000000000000000..eefd2e4b24180c09cf99b988a6a7f24cd2927186 --- /dev/null +++ b/es2panda/compiler/core/pandagen.h @@ -0,0 +1,421 @@ +/** + * 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_PANDAGEN_H +#define ES2PANDA_COMPILER_CORE_PANDAGEN_H + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace panda::es2panda::binder { +class FunctionScope; +class ScopeFindResult; +class Scope; +} // namespace panda::es2panda::binder + +namespace panda::es2panda::ir { +class AstNode; +class ScriptFunction; +class Statement; +class Expression; +class Identifier; +} // namespace panda::es2panda::ir + +namespace panda::es2panda::compiler { + +class FunctionBuilder; +class CompilerContext; +class LiteralBuffer; +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; + + ArenaVector variableDebugInfo; + const ir::Statement *firstStmt {}; +}; + +class PandaGen { +public: + explicit PandaGen(ArenaAllocator *allocator, CompilerContext *context, binder::FunctionScope *scope) + : allocator_(allocator), + context_(context), + builder_(nullptr), + debugInfo_(allocator_), + topScope_(scope), + scope_(topScope_), + rootNode_(scope->Node()), + insns_(allocator_->Adapter()), + catchList_(allocator_->Adapter()), + strings_(allocator_->Adapter()), + buffStorage_(allocator_->Adapter()), + sa_(this), + ra_(this), + rra_(this) + { + } + ~PandaGen() = default; + NO_COPY_SEMANTIC(PandaGen); + NO_MOVE_SEMANTIC(PandaGen); + + inline ArenaAllocator *Allocator() const + { + return allocator_; + } + + const ArenaSet &Strings() const + { + return strings_; + } + + 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_; + } + + EnvScope *GetEnvScope() const + { + return envScope_; + } + + const ArenaVector &BuffStorage() const + { + return buffStorage_; + } + + 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; + void FunctionInit(CatchTable* catchTable); + void FunctionEnter(); + void FunctionExit(); + + LiteralBuffer *NewLiteralBuffer(); + int32_t AddLiteralBuffer(LiteralBuffer *buf); + + void InitializeLexEnv(const ir::AstNode *node, VReg lexEnv); + void CopyFunctionArguments(const ir::AstNode *node); + void GetFunctionObject(const ir::AstNode *node); + void GetNewTarget(const ir::AstNode *node); + void GetThis(const ir::AstNode *node); + void SetThis(const ir::AstNode *node); + 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, VReg obj, const Operand &prop); + + void LoadObjByName(const ir::AstNode *node, VReg obj, const util::StringView &prop); + + 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, const Operand &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); + + void TryLoadGlobalByValue(const ir::AstNode *node, VReg key); + void TryStoreGlobalByValue(const ir::AstNode *node, VReg key); + void TryLoadGlobalByName(const ir::AstNode *node, const util::StringView &name); + void TryStoreGlobalByName(const ir::AstNode *node, const util::StringView &name); + + 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 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); + void Binary(const ir::AstNode *node, lexer::TokenType op, VReg lhs); + + 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); + void BranchIfCoercible(const ir::AstNode *node, class Label *target); + + void EmitThrow(const ir::AstNode *node); + void EmitRethrow(const ir::AstNode *node); + void EmitReturn(const ir::AstNode *node); + void EmitReturnUndefined(const ir::AstNode *node); + void ValidateClassDirectReturn(const ir::AstNode *node); + void DirectReturn(const ir::AstNode *node); + void ImplicitReturn(const ir::AstNode *node); + void EmitAwait(const ir::AstNode *node); + + void CallThis(const ir::AstNode *node, VReg startReg, size_t argCount); + void Call(const ir::AstNode *node, VReg startReg, size_t argCount); + void CallSpread(const ir::AstNode *node, VReg func, VReg thisReg, VReg args); + void SuperCall(const ir::AstNode *node, VReg startReg, size_t argCount); + void SuperCallSpread(const ir::AstNode *node, VReg vs); + + void LoadHomeObject(const ir::AstNode *node); + void NewObject(const ir::AstNode *node, VReg startReg, size_t argCount); + void DefineFunction(const ir::AstNode *node, const ir::ScriptFunction *realNode, const util::StringView &name); + + void TypeOf(const ir::AstNode *node); + void NewObjSpread(const ir::AstNode *node, VReg obj, VReg target); + void GetUnmappedArgs(const ir::AstNode *node); + + void Negate(const ir::AstNode *node); + void ToBoolean(const ir::AstNode *node); + void ToNumber(const ir::AstNode *node, VReg arg); + + void CreateGeneratorObj(const ir::AstNode *node, VReg funcObj); + void ResumeGenerator(const ir::AstNode *node, VReg genObj); + void GetResumeMode(const ir::AstNode *node, VReg genObj); + + void AsyncFunctionEnter(const ir::AstNode *node); + void AsyncFunctionAwait(const ir::AstNode *node, VReg asyncFuncObj); + void AsyncFunctionResolve(const ir::AstNode *node, VReg asyncFuncObj); + void AsyncFunctionReject(const ir::AstNode *node, VReg asyncFuncObj); + + void GetMethod(const ir::AstNode *node, VReg obj, const util::StringView &name); + void GeneratorYield(const ir::AstNode *node, VReg genObj); + void GeneratorComplete(const ir::AstNode *node, VReg genObj); + void CreateAsyncGeneratorObj(const ir::AstNode *node, VReg funcObj); + void CreateIterResultObject(const ir::AstNode *node, bool done); + void SuspendGenerator(const ir::AstNode *node, VReg genObj); + void SuspendAsyncGenerator(const ir::AstNode *node, VReg asyncGenObj); + + void AsyncGeneratorResolve(const ir::AstNode *node, VReg asyncGenObj); + void AsyncGeneratorReject(const ir::AstNode *node, VReg asyncGenObj); + + void GetTemplateObject(const ir::AstNode *node, VReg value); + void CopyRestArgs(const ir::AstNode *node, uint32_t index); + + void GetPropIterator(const ir::AstNode *node); + void GetNextPropName(const ir::AstNode *node, VReg iter); + void CreateEmptyObject(const ir::AstNode *node); + void CreateObjectWithBuffer(const ir::AstNode *node, uint32_t idx); + void CreateObjectHavingMethod(const ir::AstNode *node, uint32_t idx); + void SetObjectWithProto(const ir::AstNode *node, VReg proto, VReg obj); + void CopyDataProperties(const ir::AstNode *node, VReg dst, VReg src); + void DefineGetterSetterByValue(const ir::AstNode *node, VReg obj, VReg name, VReg getter, VReg setter, + bool setName); + void CreateEmptyArray(const ir::AstNode *node); + void CreateArray(const ir::AstNode *node, const ArenaVector &elements, VReg obj); + void CreateArrayWithBuffer(const ir::AstNode *node, uint32_t idx); + void StoreArraySpread(const ir::AstNode *node, VReg array, VReg index); + + void ThrowIfNotObject(const ir::AstNode *node); + void ThrowThrowNotExist(const ir::AstNode *node); + void GetIterator(const ir::AstNode *node); + void GetAsyncIterator(const ir::AstNode *node); + + void CreateObjectWithExcludedKeys(const ir::AstNode *node, VReg obj, VReg argStart, size_t argCount); + void ThrowObjectNonCoercible(const ir::AstNode *node); + void CloseIterator(const ir::AstNode *node, VReg iter); + void DefineClassWithBuffer(const ir::AstNode *node, const util::StringView &ctorId, int32_t litIdx, VReg lexenv, + VReg base); + + void ImportModule(const ir::AstNode *node, const util::StringView &name); + void LoadModuleVariable(const ir::AstNode *node, VReg module, const util::StringView &name); + void StoreModuleVar(const ir::AstNode *node, const util::StringView &name); + void CopyModule(const ir::AstNode *node, VReg module); + + void StSuperByName(const ir::AstNode *node, VReg obj, const util::StringView &key); + void LdSuperByName(const ir::AstNode *node, VReg obj, const util::StringView &key); + void StSuperByValue(const ir::AstNode *node, VReg obj, VReg prop); + void LdSuperByValue(const ir::AstNode *node, VReg obj, VReg prop); + void StoreSuperProperty(const ir::AstNode *node, VReg obj, const Operand &prop); + void LoadSuperProperty(const ir::AstNode *node, VReg obj, const Operand &prop); + + void LdLexEnv(const ir::AstNode *node); + void PopLexEnv(const ir::AstNode *node); + void CopyLexEnv(const ir::AstNode *node); + void NewLexEnv(const ir::AstNode *node, uint32_t num); + void LoadLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot); + void StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot); + + void ThrowIfSuperNotCorrectCall(const ir::AstNode *node, int64_t num); + void ThrowUndefinedIfHole(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, VReg obj, int64_t index); + void LoadObjByValue(const ir::AstNode *node, VReg obj, VReg prop); + + void StoreObjByName(const ir::AstNode *node, VReg obj, const util::StringView &prop); + void StoreObjByIndex(const ir::AstNode *node, VReg obj, int64_t index); + void StoreObjByValue(const ir::AstNode *node, VReg obj, VReg prop); + + void StOwnByName(const ir::AstNode *node, VReg obj, const util::StringView &prop); + void StOwnByValue(const ir::AstNode *node, VReg obj, VReg prop); + void StOwnByIndex(const ir::AstNode *node, VReg obj, int64_t index); + + static Operand ToNamedPropertyKey(const ir::Expression *prop, bool isComputed); + Operand ToPropertyKey(const ir::Expression *prop, bool isComputed); + VReg LoadPropertyKey(const ir::Expression *prop, bool isComputed); + + void SetFirstStmt(const ir::Statement *stmt) + { + debugInfo_.firstStmt = stmt; + } + + [[noreturn]] static void Unimplemented() + { + throw Error(ErrorType::GENERIC, "Unimplemented code path"); + } + +private: + ArenaAllocator *allocator_; + CompilerContext *context_; + FunctionBuilder *builder_; + DebugInfo debugInfo_; + binder::FunctionScope *topScope_; + binder::Scope *scope_; + const ir::AstNode *rootNode_; + ArenaList insns_; + ArenaVector catchList_; + ArenaSet strings_; + ArenaVector buffStorage_; + EnvScope *envScope_ {}; + DynamicContext *dynamicContext_ {}; + InlineCache ic_; + SimpleAllocator sa_; + RegAllocator ra_; + RangeRegAllocator rra_; + + uint32_t usedRegs_ {0}; + uint32_t totalRegs_ {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; + size_t labelId_ {0}; +}; +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/core/regAllocator.cpp b/es2panda/compiler/core/regAllocator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7ed5763f5499b520da2aff1128a28b73cacc06a7 --- /dev/null +++ b/es2panda/compiler/core/regAllocator.cpp @@ -0,0 +1,159 @@ +/* + * 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 "regAllocator.h" + +#include + +#include + +namespace panda::es2panda::compiler { + +// RegAllocatorBase + +void AllocatorBase::PushBack(IRNode *ins) +{ + pg_->Insns().push_back(ins); +} + +ArenaAllocator *AllocatorBase::Allocator() const +{ + return pg_->Allocator(); +} + +// SimpleAllocator + +Label *SimpleAllocator::AllocLabel(std::string &&id) +{ + const auto *lastInsNode = pg_->Insns().empty() ? FIRST_NODE_OF_FUNCTION : pg_->Insns().back()->Node(); + return Alloc