From ca84ee45c7ee29d5d2985526e71b2618ba2db7d2 Mon Sep 17 00:00:00 2001 From: gavin1012_hw Date: Mon, 18 Jul 2022 20:45:42 +0800 Subject: [PATCH] Support es2panda watch expression for IDE Issue: I5KY6X Signed-off-by: gavin1012_hw Change-Id: I4021dd3237d9e93b8cde8cf99ee19eebc3e1954e --- es2panda/BUILD.gn | 1 + es2panda/aot/main.cpp | 55 ++++++--- es2panda/aot/options.cpp | 62 +++++++--- es2panda/aot/options.h | 9 ++ es2panda/compiler/core/compilerContext.cpp | 5 +- es2panda/compiler/core/compilerContext.h | 8 +- es2panda/compiler/core/compilerImpl.cpp | 2 +- es2panda/compiler/core/pandagen.cpp | 60 ++++++++- es2panda/compiler/core/pandagen.h | 3 + .../compiler/function/functionBuilder.cpp | 4 + es2panda/es2panda.h | 1 + es2panda/ir/expressions/thisExpression.cpp | 6 +- es2panda/ir/expressions/unaryExpression.cpp | 5 +- es2panda/util/base64.cpp | 114 ++++++++++++++++++ es2panda/util/base64.h | 32 +++++ ts2panda/src/cmdOptions.ts | 13 +- ts2panda/src/index.ts | 30 ++++- 17 files changed, 369 insertions(+), 41 deletions(-) create mode 100644 es2panda/util/base64.cpp create mode 100644 es2panda/util/base64.h diff --git a/es2panda/BUILD.gn b/es2panda/BUILD.gn index ea69f56032..0762c34ffb 100644 --- a/es2panda/BUILD.gn +++ b/es2panda/BUILD.gn @@ -234,6 +234,7 @@ es2panda_src = [ "typescript/types/unionType.cpp", "typescript/types/unknownType.cpp", "typescript/types/voidType.cpp", + "util/base64.cpp", "util/bitset.cpp", "util/dumper.cpp", "util/helpers.cpp", diff --git a/es2panda/aot/main.cpp b/es2panda/aot/main.cpp index 467bff4775..8a18c0a436 100644 --- a/es2panda/aot/main.cpp +++ b/es2panda/aot/main.cpp @@ -55,6 +55,38 @@ public: } }; +static void DebuggerEvaluateExpression(panda::pandasm::Program *prog, + panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps *mapsp) +{ + auto pandaFile = panda::pandasm::AsmEmitter::Emit(*prog, mapsp); + const uint8_t *buffer = pandaFile->GetBase(); + size_t size = pandaFile->GetPtr().GetSize(); + std::string content(reinterpret_cast(buffer), size); + std::string base64Output = util::Base64Encode(content); + std::cout << base64Output << std::endl; +} + +static void DumpPandaFileSizeStatistic(std::map &stat) +{ + 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; +} + static int GenerateProgram(panda::pandasm::Program *prog, std::unique_ptr &options) { const std::string output = options->CompilerOutput(); @@ -86,6 +118,11 @@ static int GenerateProgram(panda::pandasm::Program *prog, std::unique_ptrisDebuggerEvaluateExpressionMode()) { + DebuggerEvaluateExpression(prog, mapsp); + return 0; + } + if (!panda::pandasm::AsmEmitter::Emit(output, *prog, statp, mapsp, true)) { return 1; } @@ -95,23 +132,7 @@ static int GenerateProgram(panda::pandasm::Program *prog, std::unique_ptr 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; + DumpPandaFileSizeStatistic(stat); } return 0; diff --git a/es2panda/aot/options.cpp b/es2panda/aot/options.cpp index ba5c41e505..19ba2b689c 100644 --- a/es2panda/aot/options.cpp +++ b/es2panda/aot/options.cpp @@ -64,6 +64,8 @@ bool Options::Parse(int argc, const char **argv) panda::PandArg opSizeStat("dump-size-stat", false, "Dump size statistics"); panda::PandArg opDumpLiteralBuffer("dump-literal-buffer", false, "Dump literal buffer"); panda::PandArg outputFile("output", "", "Compiler binary output (.abc)"); + panda::PandArg debuggerEvaluateExpression("debugger-evaluate-expression", "", + "input base64 data of an expression to be evaluated in debugger mode"); // tail arguments panda::PandArg inputFile("input", "", "input file"); @@ -75,6 +77,7 @@ bool Options::Parse(int argc, const char **argv) argparser_->Add(&opDumpAssembly); argparser_->Add(&opDebugInfo); argparser_->Add(&opDumpDebugInfo); + argparser_->Add(&debuggerEvaluateExpression); argparser_->Add(&opOptLevel); argparser_->Add(&opThreadCount); @@ -88,7 +91,7 @@ bool Options::Parse(int argc, const char **argv) argparser_->EnableTail(); argparser_->EnableRemainder(); - if (!argparser_->Parse(argc, argv) || inputFile.GetValue().empty() || opHelp.GetValue()) { + if (!argparser_->Parse(argc, argv) || opHelp.GetValue()) { std::stringstream ss; ss << argparser_->GetErrorString() << std::endl; @@ -103,25 +106,43 @@ bool Options::Parse(int argc, const char **argv) return false; } - sourceFile_ = inputFile.GetValue(); - std::ifstream inputStream(sourceFile_.c_str()); - - if (inputStream.fail()) { - errorMsg_ = "Failed to open file: "; - errorMsg_.append(sourceFile_); + bool inputIsEmpty = inputFile.GetValue().empty(); + bool base64InputIsEmpty = debuggerEvaluateExpression.GetValue().empty(); + if (inputIsEmpty == base64InputIsEmpty) { + errorMsg_ = "--input and --debugger-evaluate-expression can not be used or unused simultaneously"; return false; } - std::stringstream ss; - ss << inputStream.rdbuf(); - parserInput_ = ss.str(); + if (!inputIsEmpty) { + // in common mode: passed argument is js file path + sourceFile_ = inputFile.GetValue(); + std::ifstream inputStream(sourceFile_.c_str()); - sourceFile_ = BaseName(sourceFile_); + if (inputStream.fail()) { + errorMsg_ = "Failed to open file: "; + errorMsg_.append(sourceFile_); + return false; + } - if (!outputFile.GetValue().empty()) { - compilerOutput_ = outputFile.GetValue(); + 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"); + } } else { - compilerOutput_ = RemoveExtension(sourceFile_).append(".abc"); + // in watch expression mode: base64 input to be evaluated + parserInput_ = ExtractExpressionFromBase64(debuggerEvaluateExpression.GetValue()); + if (parserInput_.empty()) { + errorMsg_ = "The input is not a valid base64 data"; + return false; + } + options_ |= OptionFlags::DEBUGGER_EVALUATE_EXPRESSION; + compilerOptions_.isDebuggerEvaluateExpressionMode = true; } std::string extension = inputExtension.GetValue(); @@ -164,4 +185,17 @@ bool Options::Parse(int argc, const char **argv) return true; } +std::string Options::ExtractExpressionFromBase64(const std::string &watchedExpressionBase64String) +{ + std::string watchedExpression = util::Base64Decode(watchedExpressionBase64String); + if (watchedExpression == "") { + return ""; + } + bool validBase64Input = util::Base64Encode(watchedExpression) == watchedExpressionBase64String; + if (!validBase64Input) { + return ""; + } + return watchedExpression; +} + } // namespace panda::es2panda::aot diff --git a/es2panda/aot/options.h b/es2panda/aot/options.h index 9b681e7f11..1dfecfe39b 100644 --- a/es2panda/aot/options.h +++ b/es2panda/aot/options.h @@ -22,6 +22,7 @@ #include #include #include +#include namespace panda { class PandArgParser; @@ -35,6 +36,7 @@ enum class OptionFlags { PARSE_ONLY = 1 << 1, PARSE_MODULE = 1 << 2, SIZE_STAT = 1 << 3, + DEBUGGER_EVALUATE_EXPRESSION = 1 << 4, }; inline std::underlying_type_t operator&(OptionFlags a, OptionFlags b) @@ -115,6 +117,13 @@ public: return (options_ & OptionFlags::SIZE_STAT) != 0; } + bool isDebuggerEvaluateExpressionMode() const + { + return (options_ & OptionFlags::DEBUGGER_EVALUATE_EXPRESSION) != 0; + } + + std::string ExtractExpressionFromBase64(const std::string &watchedExpression); + private: es2panda::ScriptExtension extension_ {es2panda::ScriptExtension::JS}; es2panda::CompilerOptions compilerOptions_ {}; diff --git a/es2panda/compiler/core/compilerContext.cpp b/es2panda/compiler/core/compilerContext.cpp index 0cb6270324..d254283410 100644 --- a/es2panda/compiler/core/compilerContext.cpp +++ b/es2panda/compiler/core/compilerContext.cpp @@ -19,8 +19,9 @@ namespace panda::es2panda::compiler { -CompilerContext::CompilerContext(binder::Binder *binder, bool isDebug) - : binder_(binder), emitter_(std::make_unique(this)), isDebug_(isDebug) +CompilerContext::CompilerContext(binder::Binder *binder, bool isDebug, bool isDebuggerEvaluateExpressionMode) + : binder_(binder), emitter_(std::make_unique(this)), isDebug_(isDebug), + isDebuggerEvaluateExpressionMode_(isDebuggerEvaluateExpressionMode) { } diff --git a/es2panda/compiler/core/compilerContext.h b/es2panda/compiler/core/compilerContext.h index 517163ae62..f05848f27a 100644 --- a/es2panda/compiler/core/compilerContext.h +++ b/es2panda/compiler/core/compilerContext.h @@ -33,7 +33,7 @@ class Emitter; class CompilerContext { public: - CompilerContext(binder::Binder *binder, bool isDebug); + CompilerContext(binder::Binder *binder, bool isDebug, bool isDebuggerEvaluateExpressionMode); NO_COPY_SEMANTIC(CompilerContext); NO_MOVE_SEMANTIC(CompilerContext); ~CompilerContext() = default; @@ -69,12 +69,18 @@ public: return isDebug_; } + bool isDebuggerEvaluateExpressionMode() const + { + return isDebuggerEvaluateExpressionMode_; + } + private: binder::Binder *binder_; std::unique_ptr emitter_; int32_t literalBufferIdx_ {0}; std::mutex m_; bool isDebug_; + bool isDebuggerEvaluateExpressionMode_; }; } // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/core/compilerImpl.cpp b/es2panda/compiler/core/compilerImpl.cpp index d459904a4e..4501d4005f 100644 --- a/es2panda/compiler/core/compilerImpl.cpp +++ b/es2panda/compiler/core/compilerImpl.cpp @@ -36,7 +36,7 @@ CompilerImpl::~CompilerImpl() panda::pandasm::Program *CompilerImpl::Compile(parser::Program *program, const es2panda::CompilerOptions &options) { - CompilerContext context(program->Binder(), options.isDebug); + CompilerContext context(program->Binder(), options.isDebug, options.isDebuggerEvaluateExpressionMode); if (program->Extension() == ScriptExtension::TS) { ArenaAllocator localAllocator(SpaceType::SPACE_TYPE_COMPILER, nullptr, true); diff --git a/es2panda/compiler/core/pandagen.cpp b/es2panda/compiler/core/pandagen.cpp index 89b3f7e07e..f6fd291cf1 100644 --- a/es2panda/compiler/core/pandagen.cpp +++ b/es2panda/compiler/core/pandagen.cpp @@ -57,6 +57,11 @@ bool PandaGen::IsDebug() const return context_->IsDebug(); } +bool PandaGen::isDebuggerEvaluateExpressionMode() const +{ + return context_->isDebuggerEvaluateExpressionMode(); +} + uint32_t PandaGen::ParamCount() const { if (rootNode_->IsProgram()) { @@ -368,15 +373,66 @@ void PandaGen::StoreOwnProperty(const ir::AstNode *node, VReg obj, const Operand StOwnByName(node, obj, std::get(prop), nameSetting); } +constexpr size_t DEBUGGER_GET_SET_ARGS_NUM = 2; + +void PandaGen::LoadObjByNameViaDebugger(const ir::AstNode *node, const util::StringView &name, + bool throwUndefinedIfHole) +{ + RegScope rs(this); + VReg global = AllocReg(); + LoadConst(node, compiler::Constant::JS_GLOBAL); + StoreAccumulator(node, global); + LoadObjByName(node, global, "debuggerGetValue"); + VReg debuggerGetValueReg = AllocReg(); + StoreAccumulator(node, debuggerGetValueReg); + VReg variableReg = AllocReg(); + LoadAccumulatorString(node, name); + StoreAccumulator(node, variableReg); + VReg boolFlag = AllocReg(); + if (throwUndefinedIfHole) { + LoadConst(node, compiler::Constant::JS_TRUE); + } else { + LoadConst(node, compiler::Constant::JS_FALSE); + } + StoreAccumulator(node, boolFlag); + Call(node, debuggerGetValueReg, DEBUGGER_GET_SET_ARGS_NUM); +} + void PandaGen::TryLoadGlobalByName(const ir::AstNode *node, const util::StringView &name) { - sa_.Emit(node, name); + if (isDebuggerEvaluateExpressionMode()) { + LoadObjByNameViaDebugger(node, name, true); + } else { + sa_.Emit(node, name); + } strings_.insert(name); } +void PandaGen::StoreObjByNameViaDebugger(const ir::AstNode *node, const util::StringView &name) +{ + RegScope rs(this); + VReg valueReg = AllocReg(); + StoreAccumulator(node, valueReg); + VReg global = AllocReg(); + LoadConst(node, compiler::Constant::JS_GLOBAL); + StoreAccumulator(node, global); + LoadObjByName(node, global, "debuggerSetValue"); + VReg debuggerSetValueReg = AllocReg(); + StoreAccumulator(node, debuggerSetValueReg); + VReg variableReg = AllocReg(); + LoadAccumulatorString(node, name); + StoreAccumulator(node, variableReg); + MoveVreg(node, AllocReg(), valueReg); + Call(node, debuggerSetValueReg, DEBUGGER_GET_SET_ARGS_NUM); +} + void PandaGen::TryStoreGlobalByName(const ir::AstNode *node, const util::StringView &name) { - sa_.Emit(node, name); + if (isDebuggerEvaluateExpressionMode()) { + StoreObjByNameViaDebugger(node, name); + } else { + sa_.Emit(node, name); + } strings_.insert(name); } diff --git a/es2panda/compiler/core/pandagen.h b/es2panda/compiler/core/pandagen.h index 10b0dafc5c..fc421d63f6 100644 --- a/es2panda/compiler/core/pandagen.h +++ b/es2panda/compiler/core/pandagen.h @@ -187,6 +187,7 @@ public: } bool IsDebug() const; + bool isDebuggerEvaluateExpressionMode() const; uint32_t ParamCount() const; uint32_t FormalParametersCount() const; uint32_t InternalParamCount() const; @@ -236,7 +237,9 @@ public: void TryLoadGlobalByValue(const ir::AstNode *node, VReg key); void TryStoreGlobalByValue(const ir::AstNode *node, VReg key); + void LoadObjByNameViaDebugger(const ir::AstNode *node, const util::StringView &name, bool throwUndefinedIfHole); void TryLoadGlobalByName(const ir::AstNode *node, const util::StringView &name); + void StoreObjByNameViaDebugger(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); diff --git a/es2panda/compiler/function/functionBuilder.cpp b/es2panda/compiler/function/functionBuilder.cpp index d2e791308b..e6ba14286f 100644 --- a/es2panda/compiler/function/functionBuilder.cpp +++ b/es2panda/compiler/function/functionBuilder.cpp @@ -44,6 +44,10 @@ void FunctionBuilder::ImplicitReturn(const ir::AstNode *node) const const auto *rootNode = pg_->RootNode(); if (!rootNode->IsScriptFunction() || !rootNode->AsScriptFunction()->IsConstructor()) { + if (pg_->isDebuggerEvaluateExpressionMode()) { + pg_->EmitReturn(node); + return; + } pg_->EmitReturnUndefined(node); return; } diff --git a/es2panda/es2panda.h b/es2panda/es2panda.h index 488d2fb422..675a51d58f 100644 --- a/es2panda/es2panda.h +++ b/es2panda/es2panda.h @@ -55,6 +55,7 @@ struct CompilerOptions { bool dumpDebugInfo {false}; bool parseOnly {false}; bool dumpLiteralBuffer {false}; + bool isDebuggerEvaluateExpressionMode {false}; }; enum class ErrorType { diff --git a/es2panda/ir/expressions/thisExpression.cpp b/es2panda/ir/expressions/thisExpression.cpp index 920d897cc9..397b0bab53 100644 --- a/es2panda/ir/expressions/thisExpression.cpp +++ b/es2panda/ir/expressions/thisExpression.cpp @@ -35,7 +35,11 @@ void ThisExpression::Compile(compiler::PandaGen *pg) const binder::ScopeFindResult res = pg->Scope()->Find(binder::Binder::MANDATORY_PARAM_THIS); ASSERT(res.variable && res.variable->IsLocalVariable()); - pg->LoadAccFromLexEnv(this, res); + if (pg->isDebuggerEvaluateExpressionMode()) { + pg->LoadObjByNameViaDebugger(this, "this", true); + } else { + pg->LoadAccFromLexEnv(this, res); + } const ir::ScriptFunction *func = util::Helpers::GetContainingConstructor(this); diff --git a/es2panda/ir/expressions/unaryExpression.cpp b/es2panda/ir/expressions/unaryExpression.cpp index 6e6aaf5b0a..14ea7f45d3 100644 --- a/es2panda/ir/expressions/unaryExpression.cpp +++ b/es2panda/ir/expressions/unaryExpression.cpp @@ -80,13 +80,16 @@ void UnaryExpression::Compile(compiler::PandaGen *pg) const const ir::Identifier *ident = argument_->AsIdentifier(); binder::ScopeFindResult res = pg->Scope()->Find(ident->Name()); - if (!res.variable) { + if (!res.variable && !pg->isDebuggerEvaluateExpressionMode()) { compiler::RegScope rs(pg); compiler::VReg global = pg->AllocReg(); pg->LoadConst(this, compiler::Constant::JS_GLOBAL); pg->StoreAccumulator(this, global); pg->LoadObjByName(this, global, ident->Name()); + } else if (!res.variable && pg->isDebuggerEvaluateExpressionMode()) { + // false: typeof an undeclared variable will return undefined + pg->LoadObjByNameViaDebugger(this, ident->Name(), false); } else { pg->LoadVar(ident, res); } diff --git a/es2panda/util/base64.cpp b/es2panda/util/base64.cpp new file mode 100644 index 0000000000..0a4102e81c --- /dev/null +++ b/es2panda/util/base64.cpp @@ -0,0 +1,114 @@ +/* + * 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 "base64.h" + +namespace panda::es2panda::util { + +std::string Base64Encode(const std::string &inputString) +{ + size_t strLen = inputString.length(); + size_t encodedStrLen = strLen / TO_TRANSFORM_CHAR_NUM * TRANSFORMED_CHAR_NUM; + if (strLen % TO_TRANSFORM_CHAR_NUM != 0) { + encodedStrLen += TRANSFORMED_CHAR_NUM; + } + std::string encodedRes = std::string(encodedStrLen, '\0'); + const char* base64CharSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + for (size_t i = 0, j = 0; i < encodedRes.length() - 2; i += TRANSFORMED_CHAR_NUM, j += TO_TRANSFORM_CHAR_NUM) { + // convert three 8bit into four 6bit; then add two 0 bit in each 6 bit + // former 00 + first 6 bits of the first char + encodedRes[i] = base64CharSet[(inputString[j] & 0xff) >> 2]; + // 00 + the last 2 bits of the first char + the first 4 bits of the second char + encodedRes[i + 1] = base64CharSet[(inputString[j] & 0x03) << 4 | (inputString[j + 1] & 0xf0) >> 4]; + // 00 + last 4 bits of the second char + the first 2 bits of the third char + encodedRes[i + 2] = base64CharSet[(inputString[j + 1] & 0x0f) << 2 | (inputString[j + 2] & 0xc0) >> 6]; + // 00 + the last 6 bits of the third char + encodedRes[i + 3] = base64CharSet[inputString[j + 2] & 0x3f]; + } + switch (strLen % TO_TRANSFORM_CHAR_NUM) + { + case 1: + encodedRes[encodedRes.length() - 2] = '='; + encodedRes[encodedRes.length() - 1] = '='; + break; + case 2: + encodedRes[encodedRes.length() - 1] = '='; + break; + default: + break; + } + return encodedRes; +} + +std::string Base64Decode(const std::string &base64String) +{ + const int decodeTable[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, + 62, // '+' + -1, -1, -1, + 63, // '/' + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // '0'~'9' + -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 'A'~'Z' + -1, -1, -1, -1, -1, -1, + 26, 27, 28, 29, 3-1, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 // 'a'~'z' + }; + + size_t strLen = base64String.length(); + size_t decodedStrLen = strLen / TRANSFORMED_CHAR_NUM * TO_TRANSFORM_CHAR_NUM; + if (base64String.find("==") != std::string::npos) { + decodedStrLen -= std::string("==").length(); + } else if (base64String.find("=") != std::string::npos) { + decodedStrLen -= std::string("=").length(); + } + std::string decodedRes = std::string(decodedStrLen, '\0'); + int firstChar, secondChar, thirdChar, fourthChar = 0; + for (size_t i = 0, j = 0; i < strLen - 2; i += TRANSFORMED_CHAR_NUM, j += TO_TRANSFORM_CHAR_NUM) { + firstChar = decodeTable[static_cast(base64String[i])]; + secondChar = decodeTable[static_cast(base64String[i + 1])]; + thirdChar = decodeTable[static_cast(base64String[i + 2])]; + fourthChar = decodeTable[static_cast(base64String[i + 3])]; + + if (firstChar == -1 || secondChar == -1) { + return ""; + } + // the last 6 bit of the first char + the 2~3 bit of the second char(first 4 bit - 00) + decodedRes[j] = (firstChar << 2) | (secondChar >> 4); + if (j == decodedStrLen-1) { + break; + } + if (thirdChar == -1) { + return ""; + } + // the last 4 bit of the second char + the 2~5 bit of the third char(first 6 bit - 00) + decodedRes[j + 1] = (secondChar << 4) | (thirdChar >> 2); + if (j + 1 == decodedStrLen-1) { + break; + } + if (fourthChar == -1) { + return ""; + } + // the last 2 bit of the third char + the last 6 bit of the fourth char + decodedRes[j + 2] = (thirdChar << 6) | fourthChar; + } + return decodedRes; +} + +} // namespace panda::es2panda::util diff --git a/es2panda/util/base64.h b/es2panda/util/base64.h new file mode 100644 index 0000000000..239a6dff9e --- /dev/null +++ b/es2panda/util/base64.h @@ -0,0 +1,32 @@ +/** + * 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. + */ + +#ifndef ES2PANDA_UTIL_BASE64_H +#define ES2PANDA_UTIL_BASE64_H + +#include +#include + +namespace panda::es2panda::util { + +constexpr size_t TO_TRANSFORM_CHAR_NUM = 3; +constexpr size_t TRANSFORMED_CHAR_NUM = 4; + +std::string Base64Encode(const std::string &inputString); +std::string Base64Decode(const std::string &base64String); + +} // namespace panda::es2panda::util + +#endif diff --git a/ts2panda/src/cmdOptions.ts b/ts2panda/src/cmdOptions.ts index fe304a5613..521bed07aa 100644 --- a/ts2panda/src/cmdOptions.ts +++ b/ts2panda/src/cmdOptions.ts @@ -46,7 +46,8 @@ const ts2pandaOptions = [ { name: 'debug-type', alias: 'g', type: Boolean, defaultValue: false, description: "Print type-related log. Default: false" }, { name: 'output-type', type: Boolean, defaultValue: false, description: "set output type."}, { name: 'display-typeinfo', type: Boolean, defaultValue: false, description: "Display typeinfo of pairs of instruction orders and types when enable-typeinfo is true" }, - { name: 'function-sourcecode', type: Boolean, defaultValue: false, description: "Record functions' sourceCode to support the feature of [function].toString()" } + { name: 'function-sourcecode', type: Boolean, defaultValue: false, description: "Record functions' sourceCode to support the feature of [function].toString()" }, + { name: 'expression-watch-toolchain', type: String, defaultValue: "es2panda", description: "Specify the tool chain used to transform the expression" } ] @@ -128,6 +129,16 @@ export class CmdOptions { return this.options["debug-add-watch"][2]; } + static watchViaEs2pandaToolchain(): boolean { + if (!this.options) { + return false; + } + if (this.options["expression-watch-toolchain"] && this.options["expression-watch-toolchain"] != "es2panda") { + return false; + } + return true; + } + static isCommonJs(): boolean { if (!this.options) { return false; diff --git a/ts2panda/src/index.ts b/ts2panda/src/index.ts index e4eaa800c2..972d123a06 100644 --- a/ts2panda/src/index.ts +++ b/ts2panda/src/index.ts @@ -170,9 +170,29 @@ function getDtsFiles(libDir: string): string[] { const stopWatchingStr = "####"; const watchAbcFileDefaultTimeOut = 10; const watchFileName = "watch_expressions"; +// this path is only available in sdk +const es2abcBinaryPath = path["join"](__dirname, "..", "bin", path.sep); +const es2abcBinaryName = /^win/.test(require('os').platform()) ? "es2abc.exe" : "es2abc"; +const es2abcCommandLineArgs = "--debugger-evaluate-expression"; + +function callEs2pandaToolChain(ideIputStr: string) { + let commandLine = es2abcBinaryPath + es2abcBinaryName + " " + es2abcCommandLineArgs + " \"" + ideIputStr + "\""; + var exec = require('child_process').exec; + exec(`${commandLine}`, function(error, stdout) { + if (error) { + console.log("generate abc file failed, please check the input string and syntax of the expression"); + return; + } + process.stdout.write(stdout); + }); +} function updateWatchJsFile() { let ideIputStr = CmdOptions.getEvaluateExpression(); + if (CmdOptions.watchViaEs2pandaToolchain()) { + callEs2pandaToolChain(ideIputStr); + return; + } if (!isBase64Str(ideIputStr)) { throw new Error("Passed expression string for evaluating is not base64 style."); } @@ -287,6 +307,10 @@ function compileWatchExpression(jsFileName: string, errorMsgFileName: string, op } function launchWatchEvaluateDeamon(parsed: ts.ParsedCommandLine | undefined) { + if (CmdOptions.watchViaEs2pandaToolchain()) { + console.log("startWatchingSuccess supportTimeout"); + return; + } let deamonFilePrefix = CmdOptions.getEvaluateDeamonPath() + path.sep + watchFileName; let jsFileName = deamonFilePrefix + ".js"; let abcFileName = deamonFilePrefix + ".abc"; @@ -374,7 +398,11 @@ function run(args: string[], options?: ts.CompilerOptions): void { return; } if (CmdOptions.isStopEvaluateDeamonMode()) { - fs.writeFileSync(CmdOptions.getEvaluateDeamonPath() + path.sep + watchFileName + ".js", stopWatchingStr); + if (CmdOptions.watchViaEs2pandaToolchain()) { + console.log("stopWatchingSuccess"); + } else { + fs.writeFileSync(CmdOptions.getEvaluateDeamonPath() + path.sep + watchFileName + ".js", stopWatchingStr); + } return; } if (CmdOptions.isWatchEvaluateExpressionMode()) { -- Gitee