diff --git a/es2panda/BUILD.gn b/es2panda/BUILD.gn index 474b1604ead5fd781687a3642aaab202ca9674fe..8bd142ccb16bb94069e9605678690181bf2e145f 100644 --- a/es2panda/BUILD.gn +++ b/es2panda/BUILD.gn @@ -239,6 +239,7 @@ es2panda_src = [ "util/bitset.cpp", "util/dumper.cpp", "util/helpers.cpp", + "util/hotfix.cpp", "util/moduleHelpers.cpp", "util/ustring.cpp", ] diff --git a/es2panda/CMakeLists.txt b/es2panda/CMakeLists.txt index c96649e60d19a3e3d386269896ee4fcfa3f82027..1e791deeeacac4109d6d0e531ea5e420306cfa5b 100644 --- a/es2panda/CMakeLists.txt +++ b/es2panda/CMakeLists.txt @@ -284,6 +284,7 @@ set(ES2PANDA_LIB_SRC typescript/types/voidType.cpp util/bitset.cpp util/helpers.cpp + util/hotfix.cpp util/ustring.cpp ) diff --git a/es2panda/aot/options.cpp b/es2panda/aot/options.cpp index fd09afa9999ce9b5163b9415c38e199ddaef0736..b74e593eb952d94245309bb1109ee159b1bc19b2 100644 --- a/es2panda/aot/options.cpp +++ b/es2panda/aot/options.cpp @@ -144,6 +144,11 @@ bool Options::Parse(int argc, const char **argv) panda::PandArg opFileThreadCount("file-threads", 0, "Number of worker threads to compile file"); panda::PandArg opSizeStat("dump-size-stat", false, "Dump size statistics"); panda::PandArg opDumpLiteralBuffer("dump-literal-buffer", false, "Dump literal buffer"); + + // hotfix + panda::PandArg opDumpBytecodeMap("dump-map", "", "Dump function bytecode info to file"); + panda::PandArg opMapFile("ref-map", "", "Generate patch with this map file"); + panda::PandArg outputFile("output", "", "Compiler binary output (.abc)"); panda::PandArg recordName("record-name", "", "Specify the record name"); panda::PandArg debuggerEvaluateExpression("debugger-evaluate-expression", false, @@ -179,6 +184,9 @@ bool Options::Parse(int argc, const char **argv) argparser_->Add(&opSizeStat); argparser_->Add(&opDumpLiteralBuffer); + argparser_->Add(&opDumpBytecodeMap); + argparser_->Add(&opMapFile); + argparser_->Add(&inputExtension); argparser_->Add(&outputFile); argparser_->Add(&sourceFile); @@ -339,6 +347,9 @@ bool Options::Parse(int argc, const char **argv) compilerOptions_.sourceFiles = sourceFiles_; compilerOptions_.mergeAbc = opMergeAbc.GetValue(); + compilerOptions_.hotfixOptions.dumpBytecodeMap = opDumpBytecodeMap.GetValue(); + compilerOptions_.hotfixOptions.mapFile = opMapFile.GetValue(); + return true; } diff --git a/es2panda/binder/binder.cpp b/es2panda/binder/binder.cpp index 63f359f987bc7885c95bc2abe78ef452a0073608..eee18948d53f9796372bbdb0bcca9956caee0b6d 100644 --- a/es2panda/binder/binder.cpp +++ b/es2panda/binder/binder.cpp @@ -16,6 +16,7 @@ #include "binder.h" #include +#include #include #include #include diff --git a/es2panda/binder/scope.h b/es2panda/binder/scope.h index a6046c098a5dae268673a2529d7ced4935b2954e..d3dc7391696e71147696f1521a0ab0f3fc3ffc75 100644 --- a/es2panda/binder/scope.h +++ b/es2panda/binder/scope.h @@ -291,9 +291,20 @@ public: return lexicalVarNames_; } + void AddLexicalVarTypes(uint32_t slot, int type) + { + lexicalVarTypes_.emplace(slot, type); + } + + ArenaMap &GetLexicalVarTypes() + { + return lexicalVarTypes_; + } + protected: explicit VariableScope(ArenaAllocator *allocator, Scope *parent) : Scope(allocator, parent), - lexicalVarNames_(allocator->Adapter()) {} + lexicalVarNames_(allocator->Adapter()), + lexicalVarTypes_(allocator->Adapter()) {} inline VariableFlags DeclFlagToVariableFlag(DeclarationFlags declFlag); @@ -313,6 +324,7 @@ protected: VariableScopeFlags flags_ {}; uint32_t slotIndex_ {}; ArenaMap lexicalVarNames_; // for debuginfo + ArenaMap lexicalVarTypes_; }; class ParamScope : public Scope { diff --git a/es2panda/binder/variable.cpp b/es2panda/binder/variable.cpp index fecc73e586b120aa9269a43d5364a150a9a463d8..66d9fb4c1e8c8d292cdec8a5e343bb5e03936aa6 100644 --- a/es2panda/binder/variable.cpp +++ b/es2panda/binder/variable.cpp @@ -16,6 +16,7 @@ #include "variable.h" #include +#include #include @@ -47,10 +48,19 @@ void LocalVariable::SetLexical(Scope *scope) } VariableScope *varScope = scope->EnclosingVariableScope(); - uint32_t slot = varScope->NextSlot(); + uint32_t slot = 0; auto name = Declaration()->Name(); - varScope->AddLexicalVarName(slot, name); // gather lexical variables for debuginfo + + if (util::Hotfix::GetInstance()->NeedSetLexicalForPatch(varScope)) { + slot = util::Hotfix::GetInstance()->SetLexicalForPatch(varScope, std::string(name)); + } else { + slot = varScope->NextSlot(); + } + BindLexEnvSlot(slot); + varScope->AddLexicalVarName(slot, name); // gather lexical variables for debuginfo + varScope->AddLexicalVarTypes(slot, + static_cast::type>(Declaration()->Type())); } void GlobalVariable::SetLexical([[maybe_unused]] Scope *scope) {} diff --git a/es2panda/compiler/base/lexenv.cpp b/es2panda/compiler/base/lexenv.cpp index bd75ca475abe4247b840bc47ec479b3adcfb3a8f..f2142e3e4550ad060e935be4063ed63917ba9b8f 100644 --- a/es2panda/compiler/base/lexenv.cpp +++ b/es2panda/compiler/base/lexenv.cpp @@ -42,7 +42,7 @@ static void CheckConstAssignment(PandaGen *pg, const ir::AstNode *node, binder:: static void ExpandLoadLexVar(PandaGen *pg, const ir::AstNode *node, const binder::ScopeFindResult &result) { - pg->LoadLexicalVar(node, result.lexLevel, result.variable->AsLocalVariable()->LexIdx()); + pg->LoadLexicalVar(node, result.lexLevel, result.variable->AsLocalVariable()->LexIdx(), result.variable->Name()); const auto *decl = result.variable->Declaration(); if (decl->IsLetOrConstOrClassDecl()) { pg->ThrowUndefinedIfHole(node, result.variable->Name()); @@ -93,7 +93,7 @@ static void ExpandStoreLexVar(PandaGen *pg, const ir::AstNode *node, const binde pg->LoadAccumulator(node, valueReg); } - pg->StoreLexicalVar(node, result.lexLevel, local->LexIdx()); + pg->StoreLexicalVar(node, result.lexLevel, local->LexIdx(), local->Name()); } static void ExpandStoreNormalVar(PandaGen *pg, const ir::AstNode *node, const binder::ScopeFindResult &result, diff --git a/es2panda/compiler/core/emitter/emitter.cpp b/es2panda/compiler/core/emitter/emitter.cpp index e767eab9af1695fe0bd027f41ff7407d4ebdb0d2..27fd512fe5d7c83eafb84340976b5bc17d05caf2 100644 --- a/es2panda/compiler/core/emitter/emitter.cpp +++ b/es2panda/compiler/core/emitter/emitter.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -62,6 +63,7 @@ void FunctionEmitter::Generate() GenSourceFileDebugInfo(); GenFunctionCatchTables(); GenLiteralBuffers(); + util::Hotfix::GetInstance()->ProcessFunction(pg_, func_, literalBuffers_); } const ArenaSet &FunctionEmitter::Strings() const @@ -385,6 +387,7 @@ void Emitter::AddSourceTextModuleRecord(ModuleRecordEmitter *module, const Compi moduleIdxField.metadata->SetValue(panda::pandasm::ScalarValue::Create( static_cast(module->Index()))); rec_->field_list.emplace_back(std::move(moduleIdxField)); + util::Hotfix::GetInstance()->ProcessModule(rec_->name, module->Buffer()); } else { auto ecmaModuleRecord = panda::pandasm::Record("_ESModuleRecord", LANG_EXT); ecmaModuleRecord.metadata->SetAccessFlags(panda::ACC_PUBLIC); @@ -395,11 +398,14 @@ void Emitter::AddSourceTextModuleRecord(ModuleRecordEmitter *module, const Compi moduleIdxField.metadata->SetValue(panda::pandasm::ScalarValue::Create( static_cast(module->Index()))); ecmaModuleRecord.field_list.emplace_back(std::move(moduleIdxField)); + + util::Hotfix::GetInstance()->ProcessModule(ecmaModuleRecord.name, module->Buffer()); prog_->record_table.emplace(ecmaModuleRecord.name, std::move(ecmaModuleRecord)); } auto &moduleLiteralsBuffer = module->Buffer(); auto literalArrayInstance = panda::pandasm::LiteralArray(std::move(moduleLiteralsBuffer)); prog_->literalarray_table.emplace(std::to_string(module->Index()), std::move(literalArrayInstance)); + } void Emitter::DumpAsm(const panda::pandasm::Program *prog) @@ -439,6 +445,8 @@ void Emitter::DumpAsm(const panda::pandasm::Program *prog) panda::pandasm::Program *Emitter::Finalize(bool dumpDebugInfo) { + util::Hotfix::GetInstance()->Finalize(&prog_); + if (dumpDebugInfo) { debuginfo::DebugInfoDumper dumper(prog_); dumper.Dump(); diff --git a/es2panda/compiler/core/pandagen.cpp b/es2panda/compiler/core/pandagen.cpp index 6103be37a8e58ec4a8e4304eaae661fb494bec29..07fb9ddbb8b9d86d9dc1fe752ab0ed1995e3cdc5 100644 --- a/es2panda/compiler/core/pandagen.cpp +++ b/es2panda/compiler/core/pandagen.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -1563,6 +1564,16 @@ void PandaGen::LoadLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t sa_.Emit(node, level, slot); } +void PandaGen::LoadLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot, const util::StringView &name) +{ + if (slot != UINT32_MAX) { + sa_.Emit(node, level, slot); + } else { + uint32_t patchSlot = util::Hotfix::GetInstance()->GetPatchLexicalIdx(std::string(name)); + sa_.Emit(node, patchSlot); + } +} + void PandaGen::StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot) { RegScope rs(this); @@ -1576,6 +1587,19 @@ void PandaGen::StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t ra_.Emit(node, level, slot, value); } +void PandaGen::StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot, const util::StringView &name) +{ + if (slot != UINT32_MAX) { + RegScope rs(this); + VReg value = AllocReg(); + StoreAccumulator(node, value); + ra_.Emit(node, level, slot, value); + } else { + uint32_t patchSlot = util::Hotfix::GetInstance()->GetPatchLexicalIdx(std::string(name)); + sa_.Emit(node, patchSlot); + } +} + void PandaGen::ThrowIfSuperNotCorrectCall(const ir::AstNode *node, int64_t num) { sa_.Emit(node, num); diff --git a/es2panda/compiler/core/pandagen.h b/es2panda/compiler/core/pandagen.h index 053146a871c222f3a6b19199718f383266f9d4ad..04ec600e50b310a91b32e75320f3faf1ca54bb15 100644 --- a/es2panda/compiler/core/pandagen.h +++ b/es2panda/compiler/core/pandagen.h @@ -368,7 +368,9 @@ public: void NewLexEnv(const ir::AstNode *node, uint32_t num); void NewLexEnvWithScopeInfo(const ir::AstNode *node, uint32_t num, int32_t scopeInfoIdx); void LoadLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot); + void LoadLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot, const util::StringView &name); void StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot); + void StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot, const util::StringView &name); void StoreLexicalVar(const ir::AstNode *node, uint32_t level, uint32_t slot, VReg value); void ThrowIfSuperNotCorrectCall(const ir::AstNode *node, int64_t num); diff --git a/es2panda/es2panda.cpp b/es2panda/es2panda.cpp index 0dac82455ae0f606f8ade069dcaa7f59c835ba98..229c14861a68b35639f1a5bfa7eb8b6cfd8faeb5 100644 --- a/es2panda/es2panda.cpp +++ b/es2panda/es2panda.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -52,6 +53,11 @@ panda::pandasm::Program *Compiler::Compile(const SourceFile &input, const Compil std::string rname(input.recordName); parser::ScriptKind kind(input.scriptKind); + if (!util::Hotfix::GetInstance()->Initialize(options.hotfixOptions.mapFile, options.hotfixOptions.dumpBytecodeMap)) { + std::cerr << "Exits due to hot fix initialize failed!" << std::endl; + return nullptr; + } + try { auto ast = parser_->Parse(fname, src, rname, kind); diff --git a/es2panda/es2panda.h b/es2panda/es2panda.h index cddf15d10e458ae1780b943ee09c3f46f6d703ea..14efd222730016bb530ba0d96d909e61944c485c 100644 --- a/es2panda/es2panda.h +++ b/es2panda/es2panda.h @@ -56,6 +56,11 @@ struct SourceFile { uint32_t hash {0}; }; +struct HotfixOptions { + std::string dumpBytecodeMap; + std::string mapFile; +}; + struct CompilerOptions { bool isDebug {false}; bool dumpAst {false}; @@ -73,6 +78,7 @@ struct CompilerOptions { std::string output {}; std::string debugInfoSourceFile {}; std::vector sourceFiles; + HotfixOptions hotfixOptions; }; enum class ErrorType { diff --git a/es2panda/test/hotfix/hotfix-throwerror/add-anon-function/base.js b/es2panda/test/hotfix/hotfix-throwerror/add-anon-function/base.js new file mode 100644 index 0000000000000000000000000000000000000000..8e3becac7f6df2d48f1b0061d1b19cbf8694642c --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/add-anon-function/base.js @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +(()=>{ + print("anonymous: 1"); +})() diff --git a/es2panda/test/hotfix/hotfix-throwerror/add-anon-function/base_mod.js b/es2panda/test/hotfix/hotfix-throwerror/add-anon-function/base_mod.js new file mode 100644 index 0000000000000000000000000000000000000000..d61ccc58328f159e2d1106a487c8c4b8ead21ff0 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/add-anon-function/base_mod.js @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Test scenario: add anonymous function, es2abc can specify and throw error. + +(()=>{ + print("anonymous: 1"); +})() + +(()=>{ + print("anonymous: 2"); // add anonymous function +})() \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/add-anon-function/expected.txt b/es2panda/test/hotfix/hotfix-throwerror/add-anon-function/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..988281a38e3da89c8c35754cd32ad092fbd512f4 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/add-anon-function/expected.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Found new anonymous or duplicate name function .#2# not supported! +Found unsupported change in file, will not generate patch! +Error: [base_mod.js:0:0] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/hotfix/hotfix-throwerror/add-dup-name-function/base.js b/es2panda/test/hotfix/hotfix-throwerror/add-dup-name-function/base.js new file mode 100644 index 0000000000000000000000000000000000000000..35b57466b5f3fbdd77db2cf2505f2fb94b16a82c --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/add-dup-name-function/base.js @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function A() { + print("A"); +} + +function B() { + print("B"); +} \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/add-dup-name-function/base_mod.js b/es2panda/test/hotfix/hotfix-throwerror/add-dup-name-function/base_mod.js new file mode 100644 index 0000000000000000000000000000000000000000..004bdcd79411112cd4cfbb1a709e909dfc3a6847 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/add-dup-name-function/base_mod.js @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Test scenario: add function which has same name as exits functions, es2abc can specify and throw error. + +function A() { + print("A"); + function B() { // add function B, which has a duplicate name + print("A::B"); + } +} + +function B() { + print("B"); +} \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/add-dup-name-function/expected.txt b/es2panda/test/hotfix/hotfix-throwerror/add-dup-name-function/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..411b1e31d752c54777cc86f88502aa6f5a8e72af --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/add-dup-name-function/expected.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Found new anonymous or duplicate name function .#1#B not supported! +Found unsupported change in file, will not generate patch! +Error: [base_mod.js:0:0] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/hotfix/hotfix-throwerror/add-lexenv-1/base.js b/es2panda/test/hotfix/hotfix-throwerror/add-lexenv-1/base.js new file mode 100644 index 0000000000000000000000000000000000000000..aa5b7b7a108bd256d9df9d9354ce9566ed4e4369 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/add-lexenv-1/base.js @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function A() { + print("base A()"); +} diff --git a/es2panda/test/hotfix/hotfix-throwerror/add-lexenv-1/base_mod.js b/es2panda/test/hotfix/hotfix-throwerror/add-lexenv-1/base_mod.js new file mode 100644 index 0000000000000000000000000000000000000000..761a9cca5e660d3179f339f3d19ed76c50f8de63 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/add-lexenv-1/base_mod.js @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Test scenario: add new lexenv function, function is not in top scope, es2abc can specify and throw error + +function A() { + print("base A()"); + function B() { + print("patch B()"); + } + + function C() { + B(); // make B become a lexenv, and B is not in top scope + } + + C(); +} diff --git a/es2panda/test/hotfix/hotfix-throwerror/add-lexenv-1/expected.txt b/es2panda/test/hotfix/hotfix-throwerror/add-lexenv-1/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..9cd1db2abf37c30a5b60b9df88160a34bed47be7 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/add-lexenv-1/expected.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Found lexenv size changed, not supported! +Found unsupported change in file, will not generate patch! +Error: [base_mod.js:0:0] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-class-1/base.js b/es2panda/test/hotfix/hotfix-throwerror/modify-class-1/base.js new file mode 100644 index 0000000000000000000000000000000000000000..eac1d12632657ce51e1a85f4a489c1bdb53a3d92 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-class-1/base.js @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Car { + constructor(brand) { + this.carname = brand; + } + present() { + return 'I have a ' + this.carname; + } +} + +class Model extends Car { + constructor(brand, mod) { + super(brand); + this.model = mod; + } + show() { + return this.present() + ', it is a ' + this.model; + } + + ["123"]() { // computed properties + return 0; + } +} + +let myCar = new Model("Ford", "Mustang"); \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-class-1/base_mod.js b/es2panda/test/hotfix/hotfix-throwerror/modify-class-1/base_mod.js new file mode 100644 index 0000000000000000000000000000000000000000..e480883174450c6e7d54f703248600f6253049e3 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-class-1/base_mod.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Test scenario: add class attributes, which is before computed properties, es2abc can specify and throw error + +class Car { + constructor(brand) { + this.carname = brand; + } + present() { + return 'I have a ' + this.carname; + } +} + +class Model extends Car { + constructor(brand, mod) { + super(brand); + this.model = mod; + } + + show() { + return this.present() + ', it is a ' + this.model; + } + + a() { // an attribute added before computed properties + + } + + ["123"]() { // computed properties + return 0; + } +} + +let myCar = new Model("Ford", "Mustang"); \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-class-1/expected.txt b/es2panda/test/hotfix/hotfix-throwerror/modify-class-1/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..510375b27c93498b18489bb774539d4c6aff2f1a --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-class-1/expected.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Found class .Model changed, not supported! +Found unsupported change in file, will not generate patch! +Error: [base_mod.js:0:0] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-export-1/base.js b/es2panda/test/hotfix/hotfix-throwerror/modify-export-1/base.js new file mode 100644 index 0000000000000000000000000000000000000000..eb2bc2d8b32eeda6f58d8425f88565449a9e6aaf --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-export-1/base.js @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function v1() { print("v1") } +function v2() { print("v2") } + +export { + v1 as streamV1, + v2 as base, +}; \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-export-1/base_mod.js b/es2panda/test/hotfix/hotfix-throwerror/modify-export-1/base_mod.js new file mode 100644 index 0000000000000000000000000000000000000000..9f4410f9d136a9efd31b1c589cf6c3de7e1fb998 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-export-1/base_mod.js @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Test scenario: modify export variable's name, es2abc can specify and throw error + +function v1() { print("v1") } +function v2() { print("v2") } + +export { + v1 as streamV1, + v2 as patch, // modify export variable's name from 'base' to 'patch' +}; \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-export-1/expected.txt b/es2panda/test/hotfix/hotfix-throwerror/modify-export-1/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..c98efa2c37b9ce58e515efb55c3466b783d2dfcf --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-export-1/expected.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Found import/export expression changed in _ESModuleRecord, not supported! +Found unsupported change in file, will not generate patch! +Error: [base_mod.js:0:0] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-export-2/base.js b/es2panda/test/hotfix/hotfix-throwerror/modify-export-2/base.js new file mode 100644 index 0000000000000000000000000000000000000000..eb2bc2d8b32eeda6f58d8425f88565449a9e6aaf --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-export-2/base.js @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function v1() { print("v1") } +function v2() { print("v2") } + +export { + v1 as streamV1, + v2 as base, +}; \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-export-2/base_mod.js b/es2panda/test/hotfix/hotfix-throwerror/modify-export-2/base_mod.js new file mode 100644 index 0000000000000000000000000000000000000000..8a8f752ac802c7ec0b96c4da78aff29111020850 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-export-2/base_mod.js @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Test scenario: delete export variables, es2abc can specify and throw error + +function v1() { print("v1") } +function v2() { print("v2") } + +export { + v1 as streamV1, // delete an export variable +}; \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-export-2/expected.txt b/es2panda/test/hotfix/hotfix-throwerror/modify-export-2/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..c98efa2c37b9ce58e515efb55c3466b783d2dfcf --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-export-2/expected.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Found import/export expression changed in _ESModuleRecord, not supported! +Found unsupported change in file, will not generate patch! +Error: [base_mod.js:0:0] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-export-3/base.js b/es2panda/test/hotfix/hotfix-throwerror/modify-export-3/base.js new file mode 100644 index 0000000000000000000000000000000000000000..6fc070752567bd67b9b061d458fcc871544144b8 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-export-3/base.js @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function v1() { print("v1") } +function v2() { print("v2") } + +export {v1, v2}; \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-export-3/base_mod.js b/es2panda/test/hotfix/hotfix-throwerror/modify-export-3/base_mod.js new file mode 100644 index 0000000000000000000000000000000000000000..7fd234d67b89e2f485a4cf6656214d985721e0b4 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-export-3/base_mod.js @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Test scenario: delete export variables, es2abc can specify and throw error + +function v1() { print("v1") } +function v2() { print("v2") } + +export {v1}; // delete export variables \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-export-3/expected.txt b/es2panda/test/hotfix/hotfix-throwerror/modify-export-3/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..c98efa2c37b9ce58e515efb55c3466b783d2dfcf --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-export-3/expected.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Found import/export expression changed in _ESModuleRecord, not supported! +Found unsupported change in file, will not generate patch! +Error: [base_mod.js:0:0] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-import/base.js b/es2panda/test/hotfix/hotfix-throwerror/modify-import/base.js new file mode 100644 index 0000000000000000000000000000000000000000..30392e920d91ee5622e2e8c3fb533ebef7d794d7 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-import/base.js @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import base from '../hotfix-throwerror/modify-export-1/base.js' \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-import/base_mod.js b/es2panda/test/hotfix/hotfix-throwerror/modify-import/base_mod.js new file mode 100644 index 0000000000000000000000000000000000000000..f26549b196d213a77f1ea446c5b1e4e32797dbb0 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-import/base_mod.js @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Test scenario: modify import variables, es2abc can specify and throw error + +import streamV1 from '../hotfix-throwerror/modify-export-1/base.js' \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-import/expected.txt b/es2panda/test/hotfix/hotfix-throwerror/modify-import/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..c98efa2c37b9ce58e515efb55c3466b783d2dfcf --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-import/expected.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Found import/export expression changed in _ESModuleRecord, not supported! +Found unsupported change in file, will not generate patch! +Error: [base_mod.js:0:0] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-1/base.js b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-1/base.js new file mode 100644 index 0000000000000000000000000000000000000000..be7c1a59b075e136eb620e0b6c4586e709839e9a --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-1/base.js @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function A() { + var a1 = 1; + var a2 = 2; + function B() { + print(a1); + } + B(); + } + +A() \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-1/base_mod.js b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-1/base_mod.js new file mode 100644 index 0000000000000000000000000000000000000000..efbe6e583aa0658a0f7b737d45e2c0834814187f --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-1/base_mod.js @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Test scenario: modify lexenvs, es2abc can specify and throw error + +function A() { + var a1 = 1; + var a2 = 2; + function B() { + print(a2); // modify lexenv that used by function B(from a1 to a2) + } + B(); + } + +A() \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-1/expected.txt b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-1/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..87f27bd72a4698bf46c40ff7d0d2509ca37568b8 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-1/expected.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Found new lex env added, not supported! +Found unsupported change in file, will not generate patch! +Error: [base_mod.js:0:0] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-2/base.js b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-2/base.js new file mode 100644 index 0000000000000000000000000000000000000000..19e98ab1b007465f134e9001eb9d1fee7faefaa8 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-2/base.js @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function A() { + var a1 = 1; + var a2 = 2; + function B() { + print(a1 + a2); + } + B(); + } + +A() \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-2/base_mod.js b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-2/base_mod.js new file mode 100644 index 0000000000000000000000000000000000000000..e5dd4e1f068c93819faf2a652a374e13c7ec991f --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-2/base_mod.js @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Test scenario: modify lexenv, es2abc can specify and throw error + +function A() { + var a1 = 1; + var a3 = 2; // modify lexenv (from a2 to a3) + function B() { + print(a1 + a3); + } + B(); + } + +A() \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-2/expected.txt b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-2/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..87f27bd72a4698bf46c40ff7d0d2509ca37568b8 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-2/expected.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Found new lex env added, not supported! +Found unsupported change in file, will not generate patch! +Error: [base_mod.js:0:0] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-3/base.js b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-3/base.js new file mode 100644 index 0000000000000000000000000000000000000000..19e98ab1b007465f134e9001eb9d1fee7faefaa8 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-3/base.js @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function A() { + var a1 = 1; + var a2 = 2; + function B() { + print(a1 + a2); + } + B(); + } + +A() \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-3/base_mod.js b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-3/base_mod.js new file mode 100644 index 0000000000000000000000000000000000000000..ccdec8362de45ef4a26f4086d4dbb71de380e2e5 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-3/base_mod.js @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Test scenario: modify lexenv type, es2abc can specify and throw error + +function A() { + var a1 = 1; + function a2() { // modify lexenv a2's type(from var to function) + return 2; + } + function B() { + print(a1 + a2()); + } + B(); + } + +A() \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-3/expected.txt b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-3/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..fbdc371ef999038ad246dddedf7b1ec4108e67aa --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-3/expected.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Found new lex env changed(slot or type), not supported! +Found unsupported change in file, will not generate patch! +Error: [base_mod.js:0:0] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-4/base.js b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-4/base.js new file mode 100644 index 0000000000000000000000000000000000000000..d57694842c74af8639306d27726588bb638bd789 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-4/base.js @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function makeAdder(x) { + return function(y) { + return x + y; + }; + } + + var add5 = makeAdder(5); + var add10 = makeAdder(10); + + console.log(add5(2)); // 7 + console.log(add10(2)); // 12 \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-4/base_mod.js b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-4/base_mod.js new file mode 100644 index 0000000000000000000000000000000000000000..c274979a4c5a9ad702a1164c53f0e8aeb2d0acf6 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-4/base_mod.js @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Test scenario: modify lexenv, es2abc can specify and throw error + +function makeAdder() { // In base, use function parameter as lexenv, in patch, lexenv deleted + return function(y) { + return y; + }; + } + +var add5 = makeAdder(5); +var add10 = makeAdder(10); + +print(add5(2)); // 2 +print(add10(2)); // 10 \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-4/expected.txt b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-4/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..9cd1db2abf37c30a5b60b9df88160a34bed47be7 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-4/expected.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Found lexenv size changed, not supported! +Found unsupported change in file, will not generate patch! +Error: [base_mod.js:0:0] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-5/base.js b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-5/base.js new file mode 100644 index 0000000000000000000000000000000000000000..e18a0f0bd0a171965566e0a79977a683b3aca09e --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-5/base.js @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function a() { + var c = 9; + for (let i = 1; i < 10; i++){ + function b() { + return i + c; + } + } +} \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-5/base_mod.js b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-5/base_mod.js new file mode 100644 index 0000000000000000000000000000000000000000..cde2fa05d4775e033fe88ec8d678f11354be936a --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-5/base_mod.js @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Test scenario: modify lexenv, es2abc can specify and throw error + +function a() { + var c = 9; + for (let i = 1; i < 10; i++){ + function b() { + return i + c; + } + } + + for (let j = 1; j < 10; j++){ // add a for loop that use 'j' as lexenv, function a's lexenv changed + function d() { + return j + c; + } + } +} \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-5/expected.txt b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-5/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..9cd1db2abf37c30a5b60b9df88160a34bed47be7 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-5/expected.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Found lexenv size changed, not supported! +Found unsupported change in file, will not generate patch! +Error: [base_mod.js:0:0] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-6/base.js b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-6/base.js new file mode 100644 index 0000000000000000000000000000000000000000..e18a0f0bd0a171965566e0a79977a683b3aca09e --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-6/base.js @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function a() { + var c = 9; + for (let i = 1; i < 10; i++){ + function b() { + return i + c; + } + } +} \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-6/base_mod.js b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-6/base_mod.js new file mode 100644 index 0000000000000000000000000000000000000000..1a82ae38fa5eeb3d261db12ed24af68ec53bddab --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-6/base_mod.js @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Test scenario: modify lexenv, es2abc can specify and throw error + +function a() { + var c = 9; // 'c' is lexenv in base, but not a lexenv in patch + for (let i = 1; i < 10; i++){ + function b() { + return i; + } + } +} \ No newline at end of file diff --git a/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-6/expected.txt b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-6/expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..9cd1db2abf37c30a5b60b9df88160a34bed47be7 --- /dev/null +++ b/es2panda/test/hotfix/hotfix-throwerror/modify-lexenv-6/expected.txt @@ -0,0 +1,17 @@ +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Found lexenv size changed, not supported! +Found unsupported change in file, will not generate patch! +Error: [base_mod.js:0:0] +the size of programs is expected to be 1, but is 0 diff --git a/es2panda/test/runner.py b/es2panda/test/runner.py index 00a84d294f72299f8d80f5fabb30820645f5cf4a..ffab447ee0bc8c14852ca0ef8fec7dd165efa5c4 100644 --- a/es2panda/test/runner.py +++ b/es2panda/test/runner.py @@ -119,6 +119,8 @@ def get_args(): parser.add_argument( '--verbose', '-v', action='store_true', dest='verbose', default=False, help='Enable verbose output') + parser.add_argument('--quick-fix', dest='quick_fix', default=False, + help='run quick fix tests') return parser.parse_args() @@ -714,6 +716,78 @@ class TSCRunner(Runner): return src +class QuickFixTest(Test): + def __init__(self, test_path): + Test.__init__(self, test_path, "") + self.clear_directory() + + + def clear_directory(self): + files_in_dir = os.listdir(self.path) + filtered_files = [file for file in files_in_dir if file.endswith(".map") or file.endswith(".abc")] + for file in filtered_files: + os.remove(os.path.join(self.path, file)) + + + def run(self, runner): + gen_base_cmd = runner.cmd_prefix + [runner.es2panda, '--module'] + gen_base_cmd.extend(['--dump-symbol-table=' + os.path.join(self.path, 'base.map')]) + gen_base_cmd.extend(['--output=' + os.path.join(self.path, 'base.abc')]) + gen_base_cmd.extend([os.path.join(self.path, 'base.js')]) + self.log_cmd(gen_base_cmd) + + gen_patch_cmd = runner.cmd_prefix + [runner.es2panda, '--module', '--generate-patch'] + gen_patch_cmd.extend(['--input-symbol-table=' + os.path.join(self.path, 'base.map')]) + gen_patch_cmd.extend(['--output=' + os.path.join(self.path, 'patch.abc')]) + gen_patch_cmd.extend([os.path.join(self.path, 'base_mod.js')]) + self.log_cmd(gen_patch_cmd) + + process_base = subprocess.Popen(gen_base_cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout_base, stderr_base = process_base.communicate() + if stderr_base: + self.passed = False + self.error = stderr_base.decode("utf-8", errors="ignore") + return self + + process_patch = subprocess.Popen(gen_patch_cmd, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout_patch, stderr_patch = process_patch.communicate() + if stderr_patch: + self.passed = False + self.error = stderr_patch.decode("utf-8", errors="ignore") + + self.output = stdout_patch.decode("utf-8", errors="ignore") + stderr_patch.decode("utf-8", errors="ignore") + expected_path = os.path.join(self.path, 'expected.txt') + try: + with open(expected_path, 'r') as fp: + expected = ''.join(fp.readlines()[13:]) + self.passed = expected == self.output + except Exception: + self.passed = False + + if not self.passed: + self.error = "expected output: " + expected + os.linesep + "actual output: " + self.output + + return self + + +class QuickFixRunner(Runner): + def __init__(self, args): + Runner.__init__(self, args, "QuickFix") + self.add_directory("hotfix", "hotfix-throwerror") + + + def add_directory(self, directory, sub_direcotry): + glob_expression = path.join(self.test_root, directory, sub_direcotry, "*") + tests = glob(glob_expression, recursive=False) + self.tests += list(map(lambda t: QuickFixTest(t), tests)) + + + def test_path(self, src): + return os.path.basename(src) + + def main(): args = get_args() @@ -735,6 +809,9 @@ def main(): if args.tsc: runners.append(TSCRunner(args)) + if args.quick_fix: + runners.append(QuickFixRunner(args)) + failed_tests = 0 for runner in runners: diff --git a/es2panda/util/hotfix.cpp b/es2panda/util/hotfix.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c62bbc1d9fb2e48363269b56354225e6ea8c2c34 --- /dev/null +++ b/es2panda/util/hotfix.cpp @@ -0,0 +1,519 @@ +/** + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "hotfix.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace panda::es2panda::util { + +Hotfix* Hotfix::instance_ = nullptr; + +const std::string ANONYMOUS_DUPLICATE_SPECIFIER = "#"; +const std::string SEPERATOR = "&&"; +const std::string TYPE_SEPERATOR = "|"; +const std::string FUNC_MAIN = "func_main_0"; +const std::string PATCH_MAIN_NEW = "patch_main_0"; +const std::string PATCH_MAIN_MODIFIED = "patch_main_1"; +const size_t MAP_FUNCTION_ITEM_NUMBER = 3; +const size_t MAP_MODULE_ITEM_NUMBER = 1; +const uint32_t PATCH_ENV_VREG = 0; // reuse first param vreg + +bool Hotfix::Initialize(const std::string &mapFile, const std::string &dumpMapFile) +{ + if (!mapFile.empty() && !ReadMapFile(mapFile)) { + std::cerr << "Failed to read map file: " << mapFile << ". Stop generating patch" << std::endl; + return false; + } + mapFile_ = mapFile; + + if (!dumpMapFile.empty()) { + std::fstream fs; + fs.open(dumpMapFile, std::ios_base::out | std::ios_base::trunc); + if (!fs.is_open()) { + std::cerr << "Failed to create output map file: " << dumpMapFile << std::endl; + return false; + } + fs.close(); + } + + dumpMapFile_ = dumpMapFile; + return true; +} + +void Hotfix::Destroy() +{ + if (instance_ != nullptr) { + delete instance_; + instance_ = nullptr; + } +} + +void Hotfix::ProcessFunction(const compiler::PandaGen *pg, panda::pandasm::Function *func, + LiteralBuffers &literalBuffers) +{ + if (!dumpMapFile_.empty()) { + DumpFunctionInfo(pg, func, literalBuffers); + return; + } + + if (!mapFile_.empty()) { + MarkFunctionForPatch(pg, func, literalBuffers); + return; + } +} + +void Hotfix::ProcessModule(const std::string &recordName, + std::vector &moduleBuffer) +{ + if (!dumpMapFile_.empty()) { + DumpModuleInfo(recordName, moduleBuffer); + return; + } + + if (!mapFile_.empty()) { + MarkModuleForPatch(recordName, moduleBuffer); + return; + } +} + +void Hotfix::DumpModuleInfo(const std::string &recordName, + std::vector &moduleBuffer) +{ + std::stringstream ss; + ss << recordName << SEPERATOR; + auto hash = std::hash{}(ConverLiteralToString(moduleBuffer)); + ss << hash << std::endl; + WriteMapFile(ss.str()); +} + +void Hotfix::MarkModuleForPatch(const std::string &recordName, + std::vector &moduleBuffer) +{ + auto it = originModuleInfo_.find(recordName); + if (it == originModuleInfo_.end()) { + std::cerr << "Found new import/export expression, not supported!" << std::endl; + patchError_ = true; + return; + } + + // auto hash = GetHash32String(reinterpret_cast(ConverLiteralToString(moduleBuffer).c_str())); + auto hash = std::hash{}(ConverLiteralToString(moduleBuffer)); + if (std::to_string(hash) != it->second) { + std::cerr << "Found import/export expression changed in " << recordName << ", not supported!" << std::endl; + patchError_ = true; + return; + } +} + +bool Hotfix::IsAnonymousOrDuplicateNameFunction(const std::string &funcName) +{ + return funcName.find(ANONYMOUS_DUPLICATE_SPECIFIER) != std::string::npos; +} + +std::vector Hotfix::GetStringItems(std::string &input, const std::string &delimiter) +{ + std::vector items; + size_t pos = 0; + std::string token; + while ((pos = input.find(delimiter)) != std::string::npos) { + token = input.substr(0, pos); + if (!token.empty()) { + items.push_back(token); + } + input.erase(0, pos + delimiter.length()); + } + if (!input.empty()) { + items.push_back(input); + } + return items; +} + +std::vector> Hotfix::GenerateFunctionAndClassHash(panda::pandasm::Function *func, + LiteralBuffers literalBuffers) +{ + std::stringstream ss; + std::vector> hashList; + + ss << ".function any " << func->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()) << " "; + if (ins.opcode == panda::pandasm::Opcode::ECMA_CREATEARRAYWITHBUFFER || + ins.opcode == panda::pandasm::Opcode::ECMA_CREATEOBJECTWITHBUFFER) { + int64_t bufferIdx = std::get(ins.imms[0]); + ss << ExpandLiteral(bufferIdx, literalBuffers) << " "; + } else if (ins.opcode == panda::pandasm::Opcode::ECMA_DEFINECLASSWITHBUFFER) { + int64_t bufferIdx = std::get(ins.imms[1]); + std::string literalStr = ExpandLiteral(bufferIdx, literalBuffers); + auto classHash = std::hash{}(literalStr); + hashList.push_back(std::pair(ins.ids[0], classHash)); + } + ss << " "; + } + + ss << "}" << 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::string functionStr = ss.str(); + std::string funcName = func->name; + // auto funcHash = GetHash32String(reinterpret_cast(functionStr.c_str())); + auto funcHash = std::hash{}(functionStr); + hashList.push_back(std::pair(funcName, funcHash)); + return hashList; +} + +std::string Hotfix::ConverLiteralToString(std::vector &literalBuffer) +{ + std::stringstream ss; + int count = 0; + for (auto literal : literalBuffer) { + ss << "{" << "index: " << count++ << " "; + ss << "tag: " << static_cast::type>(literal.tag_); + ss << " "; + std::string val; + std::visit([&val](auto&& element) { + val += "val: "; + val += element; + val += " "; + }, literal.value_ ); + ss << val; + ss << "},"; + + } + + return ss.str(); +} + +std::string Hotfix::ExpandLiteral(int64_t bufferIdx, Hotfix::LiteralBuffers &literalBuffers) +{ + for (auto &litPair : literalBuffers) { + if (litPair.first == bufferIdx) { + return ConverLiteralToString(litPair.second); + } + } + + return ""; +} + +bool Hotfix::NeedSetLexicalForPatch(binder::VariableScope *scope) +{ + if (mapFile_.empty()) { + return false; + } + + if (!scope->IsFunctionVariableScope()) { + return false; + } + + auto funcName = scope->AsFunctionVariableScope()->InternalName(); + if (std::string(funcName) != FUNC_MAIN) { + return false; + } + return true; +} + +uint32_t Hotfix::SetLexicalForPatch(binder::VariableScope *scope, const std::string &variableName) +{ + if (originFunctionInfo_.count(FUNC_MAIN) && originFunctionInfo_.at(FUNC_MAIN).lexenv.count(variableName)) { + return originFunctionInfo_.at(FUNC_MAIN).lexenv[variableName].first; + } + + if (!topScopeLexEnvs_.count(variableName)) { + topScopeLexEnvs_[variableName] = topScopeIdx_++; + } + + return UINT32_MAX; +} + +uint32_t Hotfix::GetPatchLexicalIdx(const std::string &variableName) +{ + ASSERT(topScopeLexEnvs_.count(variableName)); + return topScopeLexEnvs_[variableName]; +} + +bool IsFunctionOrClassDefineIns(panda::pandasm::Ins &ins) +{ + if (ins.opcode == panda::pandasm::Opcode::ECMA_DEFINEGENERATORFUNC || + ins.opcode == panda::pandasm::Opcode::ECMA_DEFINEMETHOD || + ins.opcode == panda::pandasm::Opcode::ECMA_DEFINEFUNCDYN || + ins.opcode == panda::pandasm::Opcode::ECMA_DEFINEASYNCFUNC || + ins.opcode == panda::pandasm::Opcode::ECMA_DEFINECLASSWITHBUFFER) { + return true; + } + return false; +} + +void Hotfix::CollectFuncDefineIns(panda::pandasm::Function *func) +{ + for (size_t i = 0; i < func->ins.size(); ++i) { + if (IsFunctionOrClassDefineIns(func->ins[i])) { + funcDefineIns_.push_back(func->ins[i]); // push define ins + funcDefineIns_.push_back(func->ins[i + 1]); // push store ins + } + } +} + +void Hotfix::AddInsForNewFunction(panda::pandasm::Program *prog, std::vector &ins) +{ + panda::pandasm::Ins returnUndefine; + returnUndefine.opcode = pandasm::Opcode::ECMA_RETURNUNDEFINED; + + if (ins.size() == 0) { + ins.push_back(returnUndefine); + return; + } + + panda::pandasm::Ins newLexenv; + newLexenv.opcode = pandasm::Opcode::ECMA_NEWLEXENVDYN; + newLexenv.imms.reserve(1); + auto newFuncNum = long(ins.size() / 2); // each new function has 2 ins: define and store + newLexenv.imms.emplace_back(newFuncNum); + + panda::pandasm::Ins stLexenv; + stLexenv.opcode = pandasm::Opcode::STA_DYN; + stLexenv.regs.reserve(1); + stLexenv.regs.emplace_back(PATCH_ENV_VREG); + + ins.insert(ins.begin(), stLexenv); + ins.insert(ins.begin(), newLexenv); + ins.push_back(returnUndefine); +} + +void Hotfix::Finalize(panda::pandasm::Program **prog) +{ + if (mapFile_.empty()) { + return; + } + + if (patchError_) { + *prog = nullptr; + std::cerr << "Found unsupported change in file, will not generate patch!" << std::endl; + return; + } + + // add patch main for new function and modified function + std::vector newFuncDefineIns_; + std::vector patchFuncDefineIns_; + + for (size_t i = 0; i < funcDefineIns_.size(); ++i) { + if (IsFunctionOrClassDefineIns(funcDefineIns_[i])) { + auto name = funcDefineIns_[i].ids[0]; + if (newFuncNames_.count(name)) { + funcDefineIns_[i].regs[0] = PATCH_ENV_VREG; + newFuncDefineIns_.push_back(funcDefineIns_[i]); + newFuncDefineIns_.push_back(funcDefineIns_[i + 1]); + continue; + } + if (patchFuncNames_.count(name)) { + patchFuncDefineIns_.push_back(funcDefineIns_[i]); + continue; + } + } + } + + AddInsForNewFunction(*prog, newFuncDefineIns_); + + auto srcLang = panda::panda_file::SourceLang::ECMASCRIPT; + panda::pandasm::Function funcNew(PATCH_MAIN_NEW, srcLang); + panda::pandasm::Function funcPatch(PATCH_MAIN_MODIFIED, srcLang); + + size_t defaultParamCount = 3; + funcNew.params.reserve(defaultParamCount); + funcPatch.params.reserve(defaultParamCount); + for (uint32_t i = 0; i < defaultParamCount; ++i) { + funcNew.params.emplace_back(panda::pandasm::Type("any", 0), srcLang); + funcPatch.params.emplace_back(panda::pandasm::Type("any", 0), srcLang); + } + + funcNew.ins = newFuncDefineIns_; + funcPatch.ins = patchFuncDefineIns_; + + (*prog)->function_table.emplace(funcNew.name, std::move(funcNew)); + (*prog)->function_table.emplace(funcPatch.name, std::move(funcPatch)); + + // As this is the last step of patch generating, clear hotfix object + Destroy(); +} + +void Hotfix::MarkFunctionForPatch(const compiler::PandaGen *pg, panda::pandasm::Function *func, + LiteralBuffers &literalBuffers) +{ + std::string funcName = func->name; + if (!originFunctionInfo_.count(funcName)) { + if (IsAnonymousOrDuplicateNameFunction(funcName)) { + std::cerr << "Found new anonymous or duplicate name function " << funcName << " not supported!" << std::endl; + patchError_ = true; + return; + } + newFuncNames_.insert(funcName); + CollectFuncDefineIns(func); + return; + } + + auto bytecodeInfo = originFunctionInfo_.at(funcName); + + // compare lexenv + auto &lexicalVarNames = pg->TopScope()->GetLexicalVarNames(); + auto &lexicalVarTypes = pg->TopScope()->GetLexicalVarTypes(); + auto lexenv = bytecodeInfo.lexenv; + if (funcName != FUNC_MAIN) { + if (lexenv.size() != lexicalVarNames.size()) { + std::cerr << "Found lexenv size changed, not supported!" << std::endl; + patchError_ = true; + return; + } + for (auto &variable: lexicalVarNames) { + auto varName = std::string(variable.second); + if (!lexenv.count(varName)) { + std::cerr << "Found new lex env added, not supported!" << std::endl; + patchError_ = true; + return; + } + auto lexInfo = lexenv[varName]; + if (variable.first != lexInfo.first || lexicalVarTypes[variable.first] != lexInfo.second) { + std::cerr << "Found new lex env changed(slot or type), not supported!" << std::endl; + patchError_ = true; + return; + } + } + } + + // compare function hash + auto hashList = GenerateFunctionAndClassHash(func, literalBuffers); + auto funcHash = std::to_string(hashList.back().second); + if (funcHash == bytecodeInfo.funcHash) { + func->metadata->SetAttribute("external"); + } else { + patchFuncNames_.insert(funcName); + } + + CollectFuncDefineIns(func); + + // compare class + auto classInfo = bytecodeInfo.classHash; + for (size_t i = 0; i < hashList.size() - 1; ++i) { + auto className = hashList[i].first; + if (classInfo.count(className)) { + if (classInfo[className] != std::to_string(hashList[i].second)) { + std::cerr << "Found class " << hashList[i].first << " changed, not supported!" << std::endl; + patchError_ = true; + return; + } + } + } +} + +void Hotfix::DumpFunctionInfo(const compiler::PandaGen *pg, panda::pandasm::Function *func, + Hotfix::LiteralBuffers &literalBuffers) +{ + std::stringstream ss; + + ss << pg->InternalName(); + ss << SEPERATOR << pg->InternalName() << SEPERATOR; + + std::vector> hashList = GenerateFunctionAndClassHash(func, literalBuffers); + ss << hashList.back().second << SEPERATOR; + + ss << TYPE_SEPERATOR; + for (size_t i = 0; i < hashList.size() - 1; ++i) { + ss << hashList[i].first << SEPERATOR << hashList[i].second << SEPERATOR; + } + ss << SEPERATOR << TYPE_SEPERATOR; + + for (auto &variable: pg->TopScope()->GetLexicalVarNames()) { + ss << variable.second << SEPERATOR + << variable.first << SEPERATOR + << pg->TopScope()->GetLexicalVarTypes()[variable.first] << SEPERATOR; + } + ss << SEPERATOR << std::endl; + + WriteMapFile(ss.str()); +} + +bool Hotfix::ReadMapFile(const std::string &mapFile) +{ + std::ifstream ifs; + std::string line; + ifs.open(mapFile.c_str()); + if (!ifs.is_open()) { + std::cerr << "Failed to open map file: " << mapFile << std::endl; + return false; + } + + // read func infos + while (std::getline(ifs, line)) { + std::vector itemList = GetStringItems(line, TYPE_SEPERATOR); + + if (itemList.size() == MAP_FUNCTION_ITEM_NUMBER) { + struct OriginFunctionInfo info(&allocator_); + std::vector funcItems = GetStringItems(itemList[0], SEPERATOR); + std::vector classItems = GetStringItems(itemList[1], SEPERATOR); + std::vector lexItems = GetStringItems(itemList[2], SEPERATOR); + + info.funcName = funcItems[0]; + info.funcInternalName = funcItems[1]; + info.funcHash = funcItems[2]; + for (size_t i = 0; i < classItems.size(); i = i + 2) { + info.classHash.insert(std::pair(classItems[i], classItems[i + 1])); + } + for (size_t i = 0; i < lexItems.size(); i = i + 3) { + std::pair slotAndType(std::atoi(lexItems[i + 1].c_str()), std::atoi(lexItems[i + 2].c_str())); + info.lexenv.insert(std::pair>(lexItems[i], slotAndType)); + } + + originFunctionInfo_.insert(std::pair(info.funcInternalName, info)); + } else if (itemList.size() == MAP_MODULE_ITEM_NUMBER) { + std::vector moduleItems = GetStringItems(itemList[0], SEPERATOR); + originModuleInfo_.insert({moduleItems[0], moduleItems[1]}); + } else { + std::cerr << "Failed to read map file: Error map file format" << std::endl; + } + } + return true; +} + +void Hotfix::WriteMapFile(const std::string &content) +{ + std::lock_guard lock(m_); + std::fstream fs; + fs.open(dumpMapFile_, std::ios_base::app | std::ios_base::in); + if (fs.is_open()) { + fs << content; + fs.close(); + } +} + +} // namespace panda::es2panda::util diff --git a/es2panda/util/hotfix.h b/es2panda/util/hotfix.h new file mode 100644 index 0000000000000000000000000000000000000000..58b576313e8e05a3a9c8baf588e8517803eb703f --- /dev/null +++ b/es2panda/util/hotfix.h @@ -0,0 +1,108 @@ +/** + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#ifndef ES2PANDA_UTIL_HOTFIX_H +#define ES2PANDA_UTIL_HOTFIX_H + +namespace panda::es2panda::compiler { +class PandaGen; +} // namespace panda::es2panda::compiler + +namespace panda::es2panda::util { + +class Hotfix { + struct OriginFunctionInfo { + std::string funcName; + std::string funcInternalName; + std::string funcHash; + ArenaMap> lexenv; + ArenaMap classHash; + + OriginFunctionInfo(ArenaAllocator *allocator) : lexenv(allocator->Adapter()), + classHash(allocator->Adapter()) {} + }; + using FunctionInfos = ArenaUnorderedMap; + using LiteralBuffers = ArenaVector>>; + +public: + static Hotfix* GetInstance() { + if (instance_ == nullptr) { + instance_ = new Hotfix(); + } + return instance_; + } + + bool Initialize(const std::string &mapFile, const std::string &dumpMapFile); + void Finalize(panda::pandasm::Program **prog); + uint32_t SetLexicalForPatch(binder::VariableScope *scope, const std::string &variableName); + bool NeedSetLexicalForPatch(binder::VariableScope *scope); + uint32_t GetPatchLexicalIdx(const std::string &variableName); + void ProcessFunction(const compiler::PandaGen *pg, panda::pandasm::Function *func, LiteralBuffers &literalBuffers); + void ProcessModule(const std::string &recordName, std::vector &moduleBuffer); + +private: + std::vector GetStringItems(std::string &input, const std::string &delimiter); + bool ReadMapFile(const std::string &mapFile); + void WriteMapFile(const std::string &content); + void DumpFunctionInfo(const compiler::PandaGen *pg, panda::pandasm::Function *func, LiteralBuffers &literalBuffers); + void MarkFunctionForPatch(const compiler::PandaGen *pg, panda::pandasm::Function *func, + LiteralBuffers &literalBuffers); + std::vector> GenerateFunctionAndClassHash(panda::pandasm::Function *func, + LiteralBuffers literalBuffers); + void DumpModuleInfo(const std::string &recordName, std::vector &moduleBuffer); + void MarkModuleForPatch(const std::string &recordName, + std::vector &moduleBuffer); + + std::string ExpandLiteral(int64_t bufferIdx, LiteralBuffers &literalBuffers); + std::string ConverLiteralToString(std::vector &literalBuffer); + void CollectFuncDefineIns(panda::pandasm::Function *func); + void AddInsForNewFunction(panda::pandasm::Program *prog, std::vector &ins); + bool IsAnonymousOrDuplicateNameFunction(const std::string &funcName); + void Destroy(); + + Hotfix() : allocator_(SpaceType::SPACE_TYPE_COMPILER, nullptr, true), + originFunctionInfo_(allocator_.Adapter()), + originModuleInfo_(allocator_.Adapter()), + topScopeLexEnvs_(allocator_.Adapter()), + patchFuncNames_(allocator_.Adapter()), + newFuncNames_(allocator_.Adapter()), + funcDefineIns_(allocator_.Adapter()) {} + + static Hotfix *instance_; + ArenaAllocator allocator_; + FunctionInfos originFunctionInfo_; + ArenaUnorderedMap originModuleInfo_; + std::mutex m_; + std::string mapFile_; + std::string dumpMapFile_; + uint32_t topScopeIdx_; + ArenaUnorderedMap topScopeLexEnvs_; + ArenaSet patchFuncNames_; + ArenaSet newFuncNames_; + ArenaVector funcDefineIns_; + bool patchError_ {false}; +}; + +} // namespace panda::es2panda::util +#endif // ES2PANDA_UTIL_HOTFIX_H