diff --git a/src/MapleFE/Makefile b/src/MapleFE/Makefile index a0d1730fdc576a8dff4cb459cd068a3ad16241a1..f1dbcac61dd5bed54e947fda4a6c6be58a2144c1 100644 --- a/src/MapleFE/Makefile +++ b/src/MapleFE/Makefile @@ -14,7 +14,7 @@ include Makefile.in -TARGS = autogen shared recdetect ladetect astopt java2mpl ast2mpl ts2ast ast2cpp c2ast +TARGS = autogen shared recdetect ladetect astopt java2mpl ast2mpl ts2ast ast2cpp c2ast obfuscate # create BUILDDIR first $(shell $(MKDIR_P) $(BUILDDIR)) @@ -22,7 +22,7 @@ $(shell $(MKDIR_P) $(BUILDDIR)) ifeq ($(SRCLANG),java) TARGET := java2ast ast2mpl else ifeq ($(SRCLANG),typescript) - TARGET := ts2ast ast2cpp + TARGET := ts2ast ast2cpp obfuscate else ifeq ($(SRCLANG),c) TARGET := c2ast endif @@ -53,6 +53,9 @@ astopt: shared recdetect ladetect ast2cpp: astopt ts2ast $(MAKE) -C ast2cpp +obfuscate: astopt ts2ast ast2cpp + $(MAKE) -C tools/obfuscate + shared: autogen $(MAKE) -C shared diff --git a/src/MapleFE/ast2cpp/include/cpp_definition.h b/src/MapleFE/ast2cpp/include/cpp_definition.h index ff6b0c1b0c21f50d75a83bf6da8f3761a1cd6312..ab4c1b077356d1a8b6d1ac7f80797a6e58a25583 100644 --- a/src/MapleFE/ast2cpp/include/cpp_definition.h +++ b/src/MapleFE/ast2cpp/include/cpp_definition.h @@ -26,8 +26,9 @@ class CppDef : public CppEmitter { public: CppDecl &mCppDecl; bool mIsInit; + bool mIsGenerator; - CppDef(Module_Handler *h, CppDecl &d) : CppEmitter(h), mCppDecl(d), mIsInit(false) {} + CppDef(Module_Handler *h, CppDecl &d) : CppEmitter(h), mCppDecl(d), mIsInit(false), mIsGenerator(false) {} std::string Emit() { return EmitTreeNode(GetASTModule()); @@ -64,12 +65,14 @@ public: std::string EmitAsTypeNode(AsTypeNode *node) override; std::string EmitNamespaceNode(NamespaceNode *node) override; std::string EmitRegExprNode(RegExprNode *node); + std::string EmitStructNode(StructNode *node) override; + std::string EmitStructLiteralNode(StructLiteralNode* node) override; + std::string EmitWhileLoopNode(WhileLoopNode *node) override; + std::string EmitYieldNode(YieldNode *node) override; std::string& HandleTreeNode(std::string &str, TreeNode *node) override; std::string EmitClassProps(TreeNode *node); std::string EmitFuncScopeVarDecls(FunctionNode *node); - std::string EmitStructNode(StructNode *node); - std::string EmitStructLiteralNode(StructLiteralNode* node); std::string EmitCppCtor(ClassNode* node); std::string EmitCtorInstance(ClassNode *c); std::string EmitDefaultCtor(ClassNode *c); diff --git a/src/MapleFE/ast2cpp/include/cpp_emitter.h b/src/MapleFE/ast2cpp/include/cpp_emitter.h index a68d25a726857bf06ddc956611121f528e3e9bfd..ec2f269dba259ae718c2db3d18ca98b74b05c500 100644 --- a/src/MapleFE/ast2cpp/include/cpp_emitter.h +++ b/src/MapleFE/ast2cpp/include/cpp_emitter.h @@ -37,6 +37,8 @@ public: bool IsGenerator(TreeNode *node); FunctionNode* GetGeneratorFunc(TreeNode *node); void GetArrayTypeInfo(ArrayLiteralNode* node, int& numDim, std::string& type); + std::string FunctionHeader(FunctionNode* node, std::string retType); + std::string GetClassName(TreeNode* node); }; } // namespace maplefe diff --git a/src/MapleFE/ast2cpp/include/emitter.h b/src/MapleFE/ast2cpp/include/emitter.h index 59178b28813af0c000055392021b181de013f287..f8447db75616fe3886cdee9d83e863082a074955 100644 --- a/src/MapleFE/ast2cpp/include/emitter.h +++ b/src/MapleFE/ast2cpp/include/emitter.h @@ -124,6 +124,7 @@ public: virtual std::string EmitModuleNode(ModuleNode *node); virtual std::string EmitAttrNode(AttrNode *node); virtual std::string EmitArrayTypeNode(ArrayTypeNode *node); + virtual std::string EmitFunctionTypeNode(FunctionTypeNode *node); virtual std::string EmitPrimTypeNode(PrimTypeNode *node); virtual std::string EmitPrimArrayTypeNode(PrimArrayTypeNode *node); diff --git a/src/MapleFE/ast2cpp/include/helper.h b/src/MapleFE/ast2cpp/include/helper.h index 6901cda2070f7d4f5bdf1d0b648e3403075f25b8..0eb8e89843ecb23e65507e68955a116bbc319deb 100644 --- a/src/MapleFE/ast2cpp/include/helper.h +++ b/src/MapleFE/ast2cpp/include/helper.h @@ -29,6 +29,8 @@ using namespace std::string_literals; namespace maplefe { +extern std::string GeneratorFn_start; +extern std::string GeneratorFn_return; extern std::unordered_mapTypeIdToJSType; extern std::unordered_mapTypeIdToJSTypeCXX; @@ -44,11 +46,31 @@ extern std::string GenAnonFuncName(TreeNode* node); inline std::string ClsName(std::string func) { return "Cls_"s + func; } inline std::string GeneratorName(std::string func) { return "Generator_"s + func; } inline std::string GeneratorFuncName(std::string func) { return "GeneratorFunc_"s + func; } -extern void HandleThisParam(unsigned nParams, TreeNode* node, std::string& params, std::string&args); extern std::string hlpGetJSValTypeStr(TypeId typeId); extern std::string ArrayCtorName(int dim, std::string type); extern bool IsBuiltinObj(std::string name); extern std::string ObjectTypeStr(std::string name); +extern std::string GeneratorFuncHeader(std::string cls, unsigned nodeId); +extern std::string FunctionParams(unsigned nodeId, bool handleThis, bool argsOnly = false, bool byRef = false, bool fdInit = false, bool capture = false); + +class GeneratorLabels { +private: + unsigned GenLoopId = 0; + unsigned GenYieldId= 0; +public: + std::string NextLoopLabel(void) { + std::string label = "_loop_" + std::to_string(++GenLoopId); + return label; + } + std::string NextYieldLabel(void) { + std::string label = "_yield_" + std::to_string(++GenYieldId); + return label; + } + void ResetLabels(void) { + GenLoopId = 0; + GenYieldId = 0; + } +}; class FuncTable { private: @@ -114,6 +136,7 @@ public: }; extern FuncTable hFuncTable; +extern GeneratorLabels GenFnLabels; } #endif // __HELPER_H__ diff --git a/src/MapleFE/ast2cpp/runtime/include/builtins.h b/src/MapleFE/ast2cpp/runtime/include/builtins.h index 8acac0e030cdd30ce4605930dec3629cac8ddc83..af0fe8f50dd34f39cf5912d7ed54593be4ce5748 100644 --- a/src/MapleFE/ast2cpp/runtime/include/builtins.h +++ b/src/MapleFE/ast2cpp/runtime/include/builtins.h @@ -133,12 +133,15 @@ class Error : public Object { // ecma-262 section references are based on ecma-262 edition 12.0 // ecma262 27.1.1.5 IteratorResult interface: -struct IteratorResult { - bool _done; // status of iterator next() call - JS_Val _value; // done=false: current iteration element value - // done=true: return value of the iterator, undefined if none returned - IteratorResult() : _done(true), _value(undefined) { } - IteratorResult(bool done, JS_Val val) : _done(done), _value(val) { } +struct IteratorResult : public Object { + bool done; // status of iterator next() call + JS_Val value; // done=false: current iteration element value + // done=true: return value of the iterator, undefined if none returned + IteratorResult() : done(true), value(undefined) { + this->AddProp("done", t2crt::ClassFld(&IteratorResult::done).NewProp(this, t2crt::TY_CXX_Bool)); + this->AddProp("value", t2crt::ClassFld(&IteratorResult::value).NewProp(this, t2crt::TY_CXX_Any)); + } + IteratorResult(bool done, JS_Val val) : done(done), value(val) { } ~IteratorResult() { } }; @@ -159,12 +162,13 @@ struct IteratorResult { // 3) %IteratorPrototype%[Symbol.iterator]() = this (current iterator instance) - used in for loops class IteratorProto : public Object { public: + IteratorResult _res; IteratorProto(Function* ctor, Object* proto) : Object(ctor, proto) { } ~IteratorProto() { } // note: the arg on an iterator's 1st next() call is ignored per spec 27.5.1.2 - virtual IteratorResult _next (JS_Val* arg = nullptr) { return IteratorResult(); } - virtual IteratorResult _return(JS_Val* val = nullptr) { return IteratorResult(); } - virtual IteratorResult _throw(Error exception) { return IteratorResult(); } + virtual IteratorResult* next (JS_Val* arg = nullptr) { return &_res; } + virtual IteratorResult* _return(JS_Val* val = nullptr) { return &_res; } + virtual IteratorResult* _throw(Error exception) { return &_res; } // TODO: %IteratorPrototype%[Symbol.iterator]() = this (current iterator instance) }; @@ -182,13 +186,12 @@ public: bool _finished = false; // flag if generator is in finished state bool _firstNext = true; // flag if first next has been called on iterator (27.5.1.2) - IteratorResult _return(JS_Val* arg = nullptr) override { - IteratorResult res; + IteratorResult* _return(JS_Val* arg = nullptr) override { _finished = true; if (arg != nullptr) { - res._value = *arg; + _res.value = *arg; } - return res; + return &_res; } }; diff --git a/src/MapleFE/ast2cpp/runtime/include/ts2cpp.h b/src/MapleFE/ast2cpp/runtime/include/ts2cpp.h index c94ea6fd2d282526fd2e10f8fea0dde29a300334..cdf48d2d55cc3ac65f17f6585192b2efbcd8fb25 100644 --- a/src/MapleFE/ast2cpp/runtime/include/ts2cpp.h +++ b/src/MapleFE/ast2cpp/runtime/include/ts2cpp.h @@ -52,6 +52,7 @@ typedef enum JS_Type : uint8_t { TY_Function, // "function" TY_Object, // "object" TY_Array, + TY_Any, // JS_Val::x.field points to a JS_Val TY_LAST, TY_CXX_Undef = TY_Undef | TY_CXX, TY_CXX_Null, @@ -64,7 +65,7 @@ typedef enum JS_Type : uint8_t { TY_CXX_Function, TY_CXX_Object, TY_CXX_Array, - TY_CXX_Any, //indicate JS_Val::x.field pointing to a JS_Val + TY_CXX_Any, // JS_Val::x.field points to a JS_Val TY_CXX_LAST, } JS_Type; diff --git a/src/MapleFE/ast2cpp/runtime/src/ts2cpp.cpp b/src/MapleFE/ast2cpp/runtime/src/ts2cpp.cpp index dc0177419dadae333d0bdc07177ff59f42d093d4..50fecb38bc70c6b4c71e517d5bf98f7392e4178e 100644 --- a/src/MapleFE/ast2cpp/runtime/src/ts2cpp.cpp +++ b/src/MapleFE/ast2cpp/runtime/src/ts2cpp.cpp @@ -16,6 +16,7 @@ std::ostream& operator<< (std::ostream& out, const t2crt::JS_Val& v) { case t2crt::TY_Symbol: out << "symbol"; break; case t2crt::TY_Function: out << "function"; break; case t2crt::TY_Object: out << v.x.val_obj; break; + case t2crt::TY_Any: out << *(t2crt::JS_Val*)v.x.field; break; case t2crt::TY_CXX_Undef: out << "undefined"; break; case t2crt::TY_CXX_Null: out << "null"; break; @@ -27,11 +28,14 @@ std::ostream& operator<< (std::ostream& out, const t2crt::JS_Val& v) { case t2crt::TY_CXX_Symbol: out << "symbol"; break; case t2crt::TY_CXX_Function: out << "function"; break; case t2crt::TY_CXX_Object: out << *(Object**)v.x.field; break; + case t2crt::TY_CXX_Any: out << *(t2crt::JS_Val*)v.x.field; break; } return out; } std::ostream& operator<< (std::ostream& out, t2crt::Object *obj) { + if (obj == nullptr) + return out; out << obj->Dump(); return out; } diff --git a/src/MapleFE/ast2cpp/src/Makefile b/src/MapleFE/ast2cpp/src/Makefile index 32bb62fbc396ac746d0bc80f8b53c2182d27d328..084bcad114a3f5728966433d2aaf28c3569731ba 100644 --- a/src/MapleFE/ast2cpp/src/Makefile +++ b/src/MapleFE/ast2cpp/src/Makefile @@ -27,10 +27,12 @@ SRCG := $(wildcard $(BUILDGEN)/gen*.cpp) OBJG := $(patsubst %.cpp, %.o, $(SRCG)) DEPG := $(patsubst %.cpp, %.d, $(SRCG)) -OBJS :=$(foreach obj,$(OBJ), $(BUILD)/$(obj)) $(OBJG) -DEPS :=$(foreach dep,$(DEP), $(BUILD)/$(dep)) $(DEPG) +LOCALOBJS :=$(foreach obj,$(OBJ), $(BUILD)/$(obj)) +LOCALDEPS :=$(foreach dep,$(DEP), $(BUILD)/$(dep)) +OBJS :=$(LOCALOBJS) $(OBJG) +DEPS :=$(LOCALDEPS) $(DEPG) -LIBOBJS :=$(patsubst $(BUILD)/main.o,,$(OBJS)) +LIBOBJS :=$(patsubst $(BUILD)/main.o,,$(LOCALOBJS)) GENDIR:=${BUILDDIR}/ast_gen/shared @@ -45,6 +47,8 @@ INCLUDES := -I $(MAPLEFE_ROOT)/shared/include \ INCLUDEGEN := -I $(MAPLEFE_ROOT)/shared/include -I $(BUILDDIR)/gen -I $(BUILDASTGEN) TARGET=ast2cpp +TARGET_A=ast2cpp.a + SHAREDLIB = $(BUILDDIR)/astopt/astopt.a $(BUILDDIR)/shared/shared.a $(BUILDASTGEN)/genast.a LANGSPEC=$(BUILDDIR)/typescript/lang_spec.o @@ -78,9 +82,12 @@ $(BUILDGEN)/%.d : $(BUILDGEN)/%.cpp # TARGET depends on OBJS and shared OBJS from shared directory # as well as mapleall libraries -$(BUILDBIN)/$(TARGET): $(OBJS) $(SHAREDLIB) +$(BUILD)/$(TARGET_A): $(LIBOBJS) + /usr/bin/ar rcs $(BUILD)/$(TARGET_A) $(LIBOBJS) + +$(BUILDBIN)/$(TARGET): $(BUILD)/$(TARGET_A) $(OBJS) $(SHAREDLIB) @mkdir -p $(BUILDBIN) - $(LD) -o $(BUILDBIN)/$(TARGET) $(OBJS) $(LANGSPEC) $(SHAREDLIB) + $(LD) -o $(BUILDBIN)/$(TARGET) $(BUILD)/main.o $(BUILD)/$(TARGET_A) $(OBJG) $(LANGSPEC) $(SHAREDLIB) clean: rm -rf $(BUILD) diff --git a/src/MapleFE/ast2cpp/src/cpp_declaration.cpp b/src/MapleFE/ast2cpp/src/cpp_declaration.cpp index dd59280855a63b9f32db81a548775f713f86762d..315c8624b6a528f0959b5770b4a3d2642a03470b 100644 --- a/src/MapleFE/ast2cpp/src/cpp_declaration.cpp +++ b/src/MapleFE/ast2cpp/src/cpp_declaration.cpp @@ -383,11 +383,11 @@ namespace )""" + module + R"""( { TreeNode *node = func->GetFuncNode(); std::string funcName = GetIdentifierName(node); + CollectFuncArgInfo(node); if (!IsClassMethod(node)) { std::string ns = GetNamespace(node); if (!ns.empty()) str += "namespace "s + ns + " {\n"s; - CollectFuncArgInfo(node); bool isGenerator = static_cast(node)->IsGenerator(); std::string generatorClassDef; if (isGenerator) { @@ -397,7 +397,7 @@ namespace )""" + module + R"""( { } else { // gen function class for each top level function - str += FunctionClassDecl(GetTypeString(static_cast(node)->GetType(), nullptr), GetIdentifierName(node), node->GetNodeId()); + str += FunctionClassDecl(GetTypeString(static_cast(node)->GetRetType(), nullptr), GetIdentifierName(node), node->GetNodeId()); } if (!mHandler->IsFromLambda(node)) { // top level funcs instantiated here as function objects from their func class @@ -441,7 +441,7 @@ namespace )""" + module + R"""( { std::string CppDecl::EmitFunctionNode(FunctionNode *node) { if (node == nullptr) return std::string(); - std::string str(GetTypeString(node->GetType(), node->GetType())); + std::string str(GetTypeString(node->GetRetType(), node->GetRetType())); if(node->GetStrIdx()) str += " "s + node->GetName(); str += "("s; @@ -1117,17 +1117,7 @@ std::string CppDecl::EmitLiteralNode(LiteralNode *node) { mPrecedence = '\030'; str = HandleTreeNode(str, node); if (auto n = node->GetType()) { - if (str.compare("this") == 0) { - // handle special literal "this" - std::string type = EmitTreeNode(n); - if (type.compare("t2crt::JS_Val ") == 0) - // map type ANY for "this" to generic object type - str = "t2crt::Object* "s + "_this"s; - else - str = type + " _this"; - } - else - str += ": "s + EmitTreeNode(n); + str += ": "s + EmitTreeNode(n); } if (auto n = node->GetInit()) { str += " = "s + EmitTreeNode(n); diff --git a/src/MapleFE/ast2cpp/src/cpp_definition.cpp b/src/MapleFE/ast2cpp/src/cpp_definition.cpp index cd1028195a0134383352a7c693f09ab3455ea839..d6b1935446c577ae09fecea01366394615c1d2df 100644 --- a/src/MapleFE/ast2cpp/src/cpp_definition.cpp +++ b/src/MapleFE/ast2cpp/src/cpp_definition.cpp @@ -188,14 +188,6 @@ std::string CppDef::EmitXXportAsPairNode(XXportAsPairNode *node) { return std::string(); } -// Return class name from class method or class field -inline std::string GetClassName(TreeNode* node) { - TreeNode* n = node->GetParent(); - if (n && n->IsClass()) - return n->GetName(); - return ""s; -} - inline bool IsClassMethod(FunctionNode* f) { return (f && f->GetParent() && f->GetParent()->IsClass()); } @@ -265,54 +257,82 @@ std::string CppDef::EmitFuncScopeVarDecls(FunctionNode *node) { return str; } -std::string CppDef::EmitFunctionNode(FunctionNode *node) { - bool isTopLevel = hFuncTable.IsTopLevelFunc(node); - if (mIsInit || node == nullptr) +std::string CppDef::EmitYieldNode(YieldNode *node) { + if (node == nullptr) return std::string(); + //std::string str(node->IsTransfer() ? "yield* " : "yield "); + std::string str, res; + if (auto n = node->GetResult()) + res = EmitTreeNode(n); + else + res = "undefined"; + + std::string yieldLabel = GenFnLabels.NextYieldLabel(); + str += " yield = &&" + yieldLabel + ";\n"; // save yp + str += " res.value = t2crt::JS_Val(" +res+ ");\n"; // init value and return + str += " res.done = false;\n"; + str += " return;\n"; + str += yieldLabel + ":\n"; // label for this yp + + mPrecedence = '\024'; + return str; +} + +std::string CppDef::EmitWhileLoopNode(WhileLoopNode *node) { +// return(Emitter::EmitWhileLoopNode(node)); - std::string str, className, ns = GetNamespace(node); - if (!ns.empty()) - ns += "::"s; + if (node == nullptr) + return std::string(); + std::string str; + std::string loopLabel; - if (node->IsGenerator()) { - // TODO + if(auto n = node->GetLabel()) { + str = EmitTreeNode(n) + ":\n"s; } - if (node->IsConstructor()) { - className = ns + GetClassName(node); - str = "\n"s; - str += className + "* "s + className + "::Ctor::operator()("s + className + "* obj"s; - } else { - str = mCppDecl.GetTypeString(node->GetType(), node->GetType()); - std::string funcName = GetIdentifierName(node); - str += " "s; - - if (IsClassMethod(node)) - str += ns + GetClassName(node) + "::"s + funcName; - else if (isTopLevel) - str += ns + "Cls_"s + funcName + "::_body"s; // emit body of top level function - else - str += ns + funcName; - str += "("s; - } - - std::string params, unused; - for (unsigned i = 0; i < node->GetParamsNum(); ++i) { - if (i || node->IsConstructor()) - params += ", "s; - if (auto n = node->GetParam(i)) { - params += mCppDecl.EmitTreeNode(n); - if (isTopLevel && i == 0) { - HandleThisParam(node->GetParamsNum(), n, params, unused); - } + + if (mIsGenerator) { // insert label and loop cond check + loopLabel = GenFnLabels.NextLoopLabel(); + str += loopLabel + ":\n"; + if (auto n = node->GetCond()) { + std::string cond = EmitTreeNode(n); + str += " if (!(" +cond+ "))\n"; + str += " goto " +loopLabel+ "_exit;\n"; } + } else { // normal while loop + str += "while("s; + if (auto n = node->GetCond()) { + str += EmitTreeNode(n); + } + str += ')'; } - if (isTopLevel && !IsClassMethod(node)) { - if (node->GetParamsNum() == 0) { - HandleThisParam(0, nullptr, params, unused); + if (auto n = node->GetBody()) { + str += EmitTreeNode(n) + GetEnding(n); + if (mIsGenerator) { + str.insert(str.find_first_of("{"), " "); + str.insert(str.find_last_of("}"), " "); } } - str += params + ") "s; + + if (mIsGenerator) { // insert loop back and label at loop exit + str += " goto " +loopLabel+ ";\n"; + str += loopLabel + "_exit:"; + } + + return HandleTreeNode(str, node); +} + + +std::string CppDef::EmitFunctionNode(FunctionNode *node) { + if (mIsInit || node == nullptr) + return std::string(); + + bool isTopLevel = hFuncTable.IsTopLevelFunc(node); + std::string str; + str += "\n"; + str += FunctionHeader(node, mCppDecl.GetTypeString(node->GetRetType(), node->GetRetType())); + mIsGenerator = node->IsGenerator(); + int bodyPos = str.size(); if (auto n = node->GetBody()) { auto varDecls = EmitFuncScopeVarDecls(node); @@ -327,6 +347,11 @@ std::string CppDef::EmitFunctionNode(FunctionNode *node) { } else str += "{}\n"s; + if (mIsGenerator) { + str.insert(str.find_first_of("{")+1, GeneratorFn_start); + str.insert(str.find_last_of("}"), GeneratorFn_return); + } + if (node->IsConstructor()) { Emitter::Replace(str, "this->", "obj->", 0); std::string ctorBody; @@ -335,6 +360,10 @@ std::string CppDef::EmitFunctionNode(FunctionNode *node) { str += EmitCtorInstance(static_cast(node->GetParent())); } + if (mIsGenerator) { + mIsGenerator = false; + GenFnLabels.ResetLabels(); + } return str; } @@ -730,9 +759,11 @@ std::string CppDef::EmitFieldNode(FieldNode *node) { std::string upper, field, propType; bool isRhs = false; // indicate if field is rhs (val) or lhs (ref) auto upnode = node->GetUpper(); + bool upperIsGenerator = false; if (upnode) { upper = EmitTreeNode(upnode); isRhs = !mHandler->IsDef(upnode); + upperIsGenerator = IsGenerator(upnode); // TODO: await TI fix for generator3.ts } if (auto n = node->GetField()) { if (isRhs) { @@ -792,6 +823,10 @@ std::string CppDef::EmitBlockNode(BlockNode *node) { for (unsigned i = 0; i < node->GetChildrenNum(); ++i) { if (auto n = node->GetChildAtIndex(i)) { std::string s = EmitTreeNode(n); + if (n->IsYield()) { + str += s; + continue; + } if (!s.empty()) str += " "s + s + GetEnding(n); } @@ -1009,6 +1044,9 @@ std::string CppDef::EmitBracketNotationProp(ArrayElementNode* ae, OprId binOpId, case TY_Object: str = objName + "->GetPropObj("s + propKey + ")"s; break; + case TY_Any: + str = objName + "->GetProp("s + propKey + ")"s; + break; default: str = "(*"s + objName + ")["s + propKey + ']'; } @@ -1239,7 +1277,7 @@ std::string CppDef::GetThisParamObjType(TreeNode *node) { std::string str = "t2crt::Object"; if (static_cast(node)->GetParamsNum()) { auto n = static_cast(node)->GetParam(0); - if (n->IsIdentifier() && n->IsThis()) { + if (n->IsThis()) { TreeNode* tn = static_cast(n)->GetType(); str = mCppDecl.GetTypeString(tn, nullptr); if (str.back() == '*') @@ -1283,6 +1321,7 @@ std::string CppDef::EmitNewNode(NewNode *node) { str = fnName + "->ctor("s + newObj + ", "s; // call ctor function with new obj as this arg } } else { + // for builtins str = "new "s + EmitTreeNode(node->GetId()); str += "("s; } diff --git a/src/MapleFE/ast2cpp/src/cpp_emitter.cpp b/src/MapleFE/ast2cpp/src/cpp_emitter.cpp index e04128c96e0c937e82b34af3b383de3af7a54b93..c5c92c0b0409646a598f2cc081f2b3d50197f638 100644 --- a/src/MapleFE/ast2cpp/src/cpp_emitter.cpp +++ b/src/MapleFE/ast2cpp/src/cpp_emitter.cpp @@ -205,4 +205,41 @@ void CppEmitter::GetArrayTypeInfo(ArrayLiteralNode* node, int& numDim, std::stri #endif } +// C++ function header for different TS function types: +// Generator: t2crt::IteratorResult [::]GeneratorFunc_::_body(t2crt::Object* _this, void*& yield[, &]...) +// Class ctor: [::]* ::Ctor::operator()(* obj[, ]...) +// Class method: [::]::([params]...) +// Function: [::]Cls_::_body(t2crt::Object|* _this[, params]...) +std::string CppEmitter::FunctionHeader(FunctionNode* node, std::string retType) { + std::string str; + std::string ns = GetNamespace(node).empty() ? ""s : GetNamespace(node)+"::"; + std::string funcName = GetIdentifierName(node); + std::string className= ns + GetClassName(node); + bool isTopLevel = hFuncTable.IsTopLevelFunc(node); + retType = retType + " "s + ns; + + if (node->IsGenerator()) // generator + str += GeneratorFuncHeader(ns+GeneratorFuncName(funcName)+"::", node->GetNodeId()); + else if (node->IsConstructor()) { // class constructor + std::string param = FunctionParams(node->GetNodeId(), false); + param = param.empty() ? ""s : (", "s+param); + str += className + "* "s + className + "::Ctor::operator()" + "(" +className+ "* obj" +param+ ") "; + } + else if (IsClassMethod(node)) // class method + str += retType + GetClassName(node) + "::" + funcName + "(" + FunctionParams(node->GetNodeId(), false) + ") "; + else if (isTopLevel) // top level function + str += retType + "Cls_" + funcName + "::_body" + "(" + FunctionParams(node->GetNodeId(), true) + ") "; + else + str += retType + funcName + "(" + FunctionParams(node->GetNodeId(), false) + ") "; + return str; +} + +// Return class name from class method or class field +std::string CppEmitter::GetClassName(TreeNode* node) { + TreeNode* n = node->GetParent(); + if (n && n->IsClass()) + return n->GetName(); + return ""s; +} + } // namespace maplefe diff --git a/src/MapleFE/ast2cpp/src/emitter.cpp b/src/MapleFE/ast2cpp/src/emitter.cpp index 3ae234f9dbc026e5d0ec17eb4ca0fd89924a221f..fbf695857eaf577226ec8854914a7f84f48dba2c 100644 --- a/src/MapleFE/ast2cpp/src/emitter.cpp +++ b/src/MapleFE/ast2cpp/src/emitter.cpp @@ -285,7 +285,7 @@ std::string Emitter::EmitFunctionNode(FunctionNode *node) { str += " : asserts "s + EmitTreeNode(n); auto body = node->GetBody(); - if (auto n = node->GetType()) { + if (auto n = node->GetRetType()) { std::string s = EmitTreeNode(n); if(!s.empty()) { str += (body || has_name || inside ? " : "s : " => "s) + s; @@ -704,6 +704,7 @@ std::string Emitter::EmitConditionalTypeNode(ConditionalTypeNode *node) { precd = mPrecedence; } if (auto n = node->GetTypeB()) { + str = Clean(str); if (precd < '\024') str = '(' + str + ')'; str += " extends "s + EmitTreeNode(n); @@ -1537,7 +1538,7 @@ std::string Emitter::EmitCallNode(CallNode *node) { bool optional = n->IsOptional(); if (optional && !s.empty() && s.back() == '?') s.pop_back(); - if(n->IsFunction() || n->IsLambda()) + if(n->IsFunction() || n->IsLambda() || n->IsTerOperator()) str += '(' + s + ')'; else str += s; @@ -1765,7 +1766,7 @@ std::string Emitter::EmitLambdaNode(LambdaNode *node) { str += ')'; if (auto n = node->GetBody()) { - if (auto t = node->GetType()) { + if (auto t = node->GetRetType()) { str += ": "s + EmitTreeNode(t); } std::string s = EmitTreeNode(n); @@ -1775,7 +1776,7 @@ std::string Emitter::EmitLambdaNode(LambdaNode *node) { str += " => "s + s; } else { - if (auto t = node->GetType()) { + if (auto t = node->GetRetType()) { str += " => "s + EmitTreeNode(t); } } @@ -1973,6 +1974,12 @@ std::string Emitter::EmitArrayTypeNode(ArrayTypeNode *node) { return str; } +std::string Emitter::EmitFunctionTypeNode(FunctionTypeNode *node) { + // TODO + std::string str = ""; + return str; +} + std::string Emitter::EmitPrimTypeNode(PrimTypeNode *node) { if (node == nullptr) return std::string(); @@ -2055,6 +2062,9 @@ std::string Emitter::EmitTreeNode(TreeNode *node) { case NK_ArrayType: return EmitArrayTypeNode(static_cast(node)); break; + case NK_FunctionType: + return EmitFunctionTypeNode(static_cast(node)); + break; case NK_UserType: return EmitUserTypeNode(static_cast(node)); break; diff --git a/src/MapleFE/ast2cpp/src/helper.cpp b/src/MapleFE/ast2cpp/src/helper.cpp index 432ed630020e44b89acbec32a9a90728e05a650f..1222f2341802e47bf06284101f033cd65dd21376 100644 --- a/src/MapleFE/ast2cpp/src/helper.cpp +++ b/src/MapleFE/ast2cpp/src/helper.cpp @@ -16,6 +16,9 @@ namespace maplefe { +FuncTable hFuncTable; +GeneratorLabels GenFnLabels; + std::unordered_mapTypeIdToJSTypeCXX = { // AST TypeId to t2crt JS_Type mapping for JS_Val type of obj props that pts to CXX class fields {TY_Object, "t2crt::TY_CXX_Object"}, @@ -30,15 +33,25 @@ std::unordered_mapTypeIdToJSTypeCXX = { {TY_Any, "t2crt::TY_CXX_Any"}, }; -FuncTable hFuncTable; +std::string GeneratorFn_start = R"""( + if (yield != nullptr) + goto *yield; +)"""; + +std::string GeneratorFn_return = R"""( + res.value = undefined; + res.done = true; + return; +)"""; // Used to build GetProp for calls to get Object (class Object in ts2cpp.h) property std::string hlpGetJSValTypeStr(TypeId typeId) { switch(typeId) { case TY_Object: case TY_Class: - case TY_Any: return "Obj"; + case TY_Any: + return ""; case TY_Function: return "Func"; case TY_Boolean: @@ -95,26 +108,20 @@ std::string GenClassFldAddProp(std::string objName, return str; } -// Each first level function is instantiated from a corresponding class generated with interfaces below: -// Body - user defined function code -// () - functor for OrdinaryCallEvaluteBody [9.2.1.3] -// Ctor - Call user defined code as constructor (with object from new() op) -// Call - Call user defined code with designated 'this' -// Apply - Call user defined code with designated 'this' and array of argument -// Bind - Create and return function object binded to designated this and optional args -// note: the parameter args is expected to be a string that start with "_this" -// TODO: apply and bind may be moved to ts2cpp.h Fuction class as virtual -// note: TSC prohibits calling non-void constructor func with new(), so in the code generated below -// for ctor(), it calls _body() but ignores return val from _body(), and instead returns _this -// per TS/JS spec. - -std::string FunctionClassDecl(std::string retType, std::string funcName, unsigned nodeId) { +// From TS func param info, generate param and arg list for corresponding mapped C++ func. +// +// Different formats of arg list as needed by C++ mapping of function/class/generators +// - args for function class functor and generation class constructor +// () - generator class constructor field init list +// & - args passed by reference to generation function _body method +// ; - generator class fields for capturing closure +// +std::string FunctionParams(unsigned nodeId, bool handleThis, bool argsOnly, bool byRef, bool fdInit, bool capture) { std::vector> funcParams = hFuncTable.GetArgInfo(nodeId); - std::string args, params; - std::string t2cObjType = "t2crt::Object*"; - std::string thisType = t2cObjType; + std::string ObjT = "t2crt::Object*"; + std::string str; - // Map TS function paramters to C++ interface args and params: + // "this" in TS function paramter mapping to C++: // // TS2cpp's C++ mapping for TS func has a "this" obj in the c++ func param list // which will be generated from AST if "this" is declared as a TS func parameter @@ -122,46 +129,68 @@ std::string FunctionClassDecl(std::string retType, std::string funcName, unsigne // are not required to declare it, in which case emitter has to insert one. // // Cases: - // o if TS func has no param + // if TS func has no param // - insert param "ts2crt::Object* _this" - // o if 1st TS func param is not "this" + // if 1st TS func param is not "this" // - insert param "ts2crt::Object* _this" - // o if 1st TS func param is "this" + // if 1st TS func param is "this" // - rename to "_this" // - if type is Any (JS_Val), change to "ts2crt::Object*" - - if (funcParams.size() == 0) { - // TS func has no param. Insert _this for ts2cpp mapping. - params += t2cObjType + " "s + "_this"s; - args += "_this"s; + // + if (handleThis) { + if (funcParams.size() == 0) // func has no param + return argsOnly ? "_this"s : (ObjT + " _this"); } - for (bool firstParam=true; auto elem : funcParams) { + + for (bool first=true; auto elem : funcParams) { std::string type = elem.first, name = elem.second; - if (!firstParam) { // not 1st param - params+= ", "s; - args += ", "s; - } else { // 1st param of TS func - firstParam = false; - if (name.compare("this") != 0) { - // 1st TS param not "this" - insert _this parameter - params += t2cObjType + " "s + "_this"s + ", "s; - args += "_this"s + ", "s; - thisType = t2cObjType; - } else { - // 1st TS param is "this" - change to "_this" - name = "_this"; - if (type.compare("t2crt::JS_Val") == 0) - type = t2cObjType; // change type Any to Object* - thisType = type; + if (!first) + str += ", "s; + else { // 1st param of TS func + if (handleThis) { + if (name.compare("this") != 0) // if not "this", insert _this + str += argsOnly? ("_this, "s): (ObjT + " _this, "s); + else { // if "this" + name = "_this"; // rename to "_this" + if (type.compare("t2crt::JS_Val") == 0) + type = ObjT; // change type Any to Object* + } } + first = false; } - params += type + " "s + name; - args += name; + if (fdInit) + str += name + "(" + name + ")"; + else if (capture) + str += type + " " + name + ";\n"; + else + str += argsOnly? name: (type + (byRef?"\&":"") + " "s + name); } - std::string str; + return str; +} + +// Each first level function is instantiated from a corresponding class generated with interfaces below: +// Body - user defined function code +// () - functor for OrdinaryCallEvaluteBody [9.2.1.3] +// Ctor - Call user defined code as constructor (with object from new() op) +// Call - Call user defined code with designated 'this' +// Apply - Call user defined code with designated 'this' and array of argument +// Bind - Create and return function object binded to designated this and optional args +// note: the parameter args is expected to be a string that start with "_this" +// TODO: apply and bind may be moved to ts2cpp.h Fuction class as virtual +// note: TSC prohibits calling non-void constructor func with new(), so in the code generated below +// for ctor(), it calls _body() but ignores return val from _body(), and instead returns _this +// per TS/JS spec. + +std::string FunctionClassDecl(std::string retType, std::string funcName, unsigned nodeId) { + std::string str, args, params, thisType; + std::string clsName = ClsName(funcName); - std::string functorArgs = args; + params = FunctionParams(nodeId, true, false); + args = FunctionParams(nodeId, true, true); + thisType = params.substr(0, params.find(" ")); // extract return type of "this" parameter + std::string functorParams = params; + std::string functorArgs = args; functorArgs.replace(0, 5, "_thisArg"); // replace _this with _thisArg size_t pos; if ((pos = functorParams.find("_this, ")) != std::string::npos) @@ -193,110 +222,86 @@ class )""" + clsName + R"""( : public t2crt::Function { return str; } -// Template for generating Generators and Generator Functions: +// build generator function header for _body +std::string GeneratorFuncHeader(std::string cls, unsigned nodeId) { + std::string params = FunctionParams(nodeId, false, false, true); // pass params by ref into _body() + if (!params.empty()) + params = ", " + params; + return "void " + cls + "_body(t2crt::Object* _this, void*& yield, t2crt::IteratorResult& res" + params + ")"; +} + +// Generating Generators and Generator Functions: // For each TS generator function, 2 C++ classes: generator and generator function are emitted. // The generator function has only a single instance. It is called to create generator instances. std::string GeneratorClassDecl(std::string funcName, unsigned nodeId) { std::string str; std::string generatorName = GeneratorName(funcName); std::string generatorFuncName = GeneratorFuncName(funcName); - std::vector> args = hFuncTable.GetArgInfo(nodeId); - - // Different formats of arg list as needed by generator and generator function interfaces: - // - args for function class functor and generation class constructor - // () - generator class constructor field init list - // & - args passed by reference to generation function _body method - // ; - generator class fields for capturing closure - std::string functorArgs, ctorArgs, refArgs, initList, captureFields; - - for (bool hasArg=false; auto elem : args) { - if (!hasArg) - hasArg = true; - else { - functorArgs += ", "s; - refArgs += ", "s; - initList += ", "s; - } - std::string type = elem.first, name = elem.second; - functorArgs += type + " " + name; - refArgs += type + "& "+ name; - initList += name + "("s+ name + ")"s; - captureFields += tab(1) + type + " " + name + ";\n"s; - } - if (!refArgs.empty()) - refArgs = ", " + refArgs; - if (!initList.empty()) - initList = ", " + initList; - ctorArgs = functorArgs.empty()? std::string(): (", "s + functorArgs); - - str = R"""( -// )""" + funcName + R"""( generators -class )""" + generatorName + R"""( : public t2crt::GeneratorProto { -public: - )""" + generatorName + R"""((t2crt::Function* ctor, t2crt::Object* proto)""" + ctorArgs + R"""() : t2crt::GeneratorProto(ctor, proto))""" + initList + R"""( {} - ~)""" + generatorName + R"""(() {} - - // closure capture fields -)""" + captureFields + R"""( - // iterator interface (override _return and _throw when needed) - t2crt::IteratorResult _next(t2crt::JS_Val* arg = nullptr) override; -}; - -// )""" + funcName + R"""( generator function -class )""" + generatorFuncName + R"""( : public t2crt::GeneratorFuncPrototype { -public: - )""" + generatorFuncName + R"""(() : t2crt::GeneratorFuncPrototype(&t2crt::GeneratorFunction, &t2crt::Generator, t2crt::GeneratorPrototype) {} - ~)""" + generatorFuncName + R"""(() {} - - // call operator returns generator instances - )""" + generatorName + R"""(* operator()()""" + functorArgs + R"""(); - // generator function body - t2crt::IteratorResult _body(t2crt::Object* _this, void*& yield)""" + refArgs + R"""(); -}; -)"""; + std::string functorArgs = FunctionParams(nodeId, false, false); + std::string initList = FunctionParams(nodeId, false, false, false, true) ; + std::string captureFields = FunctionParams(nodeId, false, false, false, false, true); + std::string ctorArgs = functorArgs.empty()? std::string(): (", "s + functorArgs); + initList = initList.empty()? "": (", "s + initList); + + std::string genClsDecl[] = { +"// " +funcName+ " generators", +"class " +generatorName+ " : public t2crt::GeneratorProto {", +"public:", +" " +generatorName+ "(t2crt::Function* ctor, t2crt::Object* proto" +ctorArgs+ ") : t2crt::GeneratorProto(ctor, proto)" +initList+ " {}", +" ~" +generatorName+ "() {}", +" // closure capture fields", +" " +captureFields, +" // iterator interface (override _return and _throw when needed)", +" t2crt::IteratorResult* next(t2crt::JS_Val* arg = nullptr) override;", +"};", +"// " +funcName+ " generator function", +"class " +generatorFuncName+ " : public t2crt::GeneratorFuncPrototype {", +"public:", +" " +generatorFuncName+ "() : t2crt::GeneratorFuncPrototype(&t2crt::GeneratorFunction, &t2crt::Generator, t2crt::GeneratorPrototype) {}", +" ~" +generatorFuncName+ "() {}", +" // call operator returns generator instances", +" " +generatorName+ "* operator()(" +functorArgs+ ");", +" // generator function body", +" " +GeneratorFuncHeader("", nodeId)+ ";", +"};", +"" + }; + + str += "\n"; + for (auto elem : genClsDecl) + str += elem + "\n"; return str; } std::string GeneratorClassDef(std::string ns, std::string funcName, unsigned nodeId) { - std::string str, params, ctorArgs, functorArgs; + std::string str; std::string generatorName = ns + GeneratorName(funcName); std::string generatorFuncName = ns + GeneratorFuncName(funcName); - std::vector> args = hFuncTable.GetArgInfo(nodeId); if (!ns.empty()) funcName = ns + "::" + funcName; - for (bool hasArg=false; auto elem : args) { - if (!hasArg) - hasArg = true; - else { - functorArgs += ", "s; - params += ", "s; - } - functorArgs += elem.first + " " + elem.second; //1st=type 2nd=name - params += " " + elem.second; - } - ctorArgs = functorArgs.empty()? std::string(): (", "s + functorArgs); - params = params.empty()? std::string(): (", "s + params); - str = R"""( -t2crt::IteratorResult )""" + generatorName + R"""(::_next(t2crt::JS_Val* arg) { - t2crt::IteratorResult res; + std::string params = FunctionParams(nodeId, false, false); + std::string args = FunctionParams(nodeId, false, true); + if (!args.empty()) + args = ", " + args; + str = R"""( +t2crt::IteratorResult* )""" + generatorName + R"""(::next(t2crt::JS_Val* arg) { if (_finished) { - res._done = true; - return res; + _res.done = true; + return &_res; } - // iterate by calling generation function with captures in generator - res = foo->_body(this, _yield)""" + params + R"""(); - if (res._done == true) + )""" + funcName + R"""(->_body(this, _yield, _res)""" + args + R"""(); + if (_res.done == true) _finished = true; - return res; + return &_res; } -)""" + generatorName + "* "s + generatorFuncName + R"""(::operator()()""" + functorArgs + R"""() { - return new )""" + generatorName + R"""((&t2crt::Generator, foo->prototype)""" + params + R"""(); +)""" + generatorName + "* "s + generatorFuncName + R"""(::operator()()""" + params + R"""() { + return new )""" + generatorName + R"""((&t2crt::Generator, foo->prototype)""" + args + R"""(); } )"""; @@ -315,29 +320,6 @@ std::string GenAnonFuncName(TreeNode* node) { return "_anon_func_"s + std::to_string(node->GetNodeId()); } -// Check 1st param of top level function for "this" and do substitution. -void HandleThisParam(unsigned nParams, TreeNode* node, std::string& params, std::string&args) { - if (nParams == 0) { - // ts2cpp's C++ mapping for TS func has a "this" obj in the c++ func param list - // which will be generated from AST if "this" is declared as a TS func parameter - // as required by TS strict mode. However TS funcs that do not reference 'this' - // are not required to declare it, so emitter has to check and insert one. - params = "t2crt::Object* _this"s; - args = "_this"s; - return; - } - - if (node->IsThis()) { - args = "_this"; - Emitter::Replace(params, "this", "_this"); // change this to _this to avoid c++ keyword - Emitter::Replace(params, "t2crt::JS_Val", "t2crt::Object*"); // change type any (JS_Val) to Object* per ts2cpp func mapping to C++ interface - } else { - // if 1st func param is not "this", insert one to work with c++ mapping for TS func - args = "_this, "s + args; - params = "t2crt::Object* _this, "s + params; - } -} - // return array constructor name of given type // format: // 1D array: t2crt::Array::ctor diff --git a/src/MapleFE/ast2mpl/src/mpl_processor.cpp b/src/MapleFE/ast2mpl/src/mpl_processor.cpp index a943ae29b8ffbfb96745bb411e02c4aff25c05d0..378c18860ff09647cbe0d32e5dd21e526a94b0f3 100644 --- a/src/MapleFE/ast2mpl/src/mpl_processor.cpp +++ b/src/MapleFE/ast2mpl/src/mpl_processor.cpp @@ -613,6 +613,10 @@ maple::BaseNode *Ast2MplBuilder::ProcessTripleSlash(StmtExprKind skind, TreeNode return nullptr; } +maple::BaseNode *Ast2MplBuilder::ProcessFunctionType(StmtExprKind skind, TreeNode *tnode, BlockNode *block) { + return nullptr; +} + maple::BaseNode *Ast2MplBuilder::ProcessBlockDecl(StmtExprKind skind, TreeNode *tnode, BlockNode *block) { BlockNode *ast_block = static_cast(tnode); for (int i = 0; i < ast_block->GetChildrenNum(); i++) { @@ -654,7 +658,7 @@ maple::BaseNode *Ast2MplBuilder::ProcessFuncDecl(StmtExprKind skind, TreeNode *t // SmallVector mAttrs; // SmallVector mAnnotations; //annotation or pragma // SmallVector mThrows; // exceptions it can throw - TreeNode *ast_rettype = ast_func->GetType(); // return type + TreeNode *ast_rettype = ast_func->GetRetType(); // return type // SmallVector mParams; // BlockNode *ast_body = ast_func->GetBody(); // DimensionNode *mDims; diff --git a/src/MapleFE/astopt/include/ast_info.h b/src/MapleFE/astopt/include/ast_info.h index de56fcf645574eaef13096dd707166bc0c01008e..83a0fc37122e10231fdde2f6d7050bd0eaf908ff 100644 --- a/src/MapleFE/astopt/include/ast_info.h +++ b/src/MapleFE/astopt/include/ast_info.h @@ -154,6 +154,21 @@ class ClassStructVisitor : public AstVisitor { FunctionNode *VisitFunctionNode(FunctionNode *node); }; +class FunctionVisitor : public AstVisitor { + private: + Module_Handler *mHandler; + AST_INFO *mInfo; + + public: + explicit FunctionVisitor(Module_Handler *h, unsigned f, bool base = false) + : AstVisitor((f & FLG_trace_1) && base), mHandler(h) { + mInfo= mHandler->GetINFO(); + } + ~FunctionVisitor() = default; + + FunctionNode *VisitFunctionNode(FunctionNode *node); +}; + class FindStrIdxVisitor : public AstVisitor { private: Module_Handler *mHandler; diff --git a/src/MapleFE/astopt/include/ast_scp.h b/src/MapleFE/astopt/include/ast_scp.h index fd2dcb485df20f92f96bb929b1b4fc31c5662012..1a9a7124a2adbd4d89b4da617fa21fbb57763f6a 100644 --- a/src/MapleFE/astopt/include/ast_scp.h +++ b/src/MapleFE/astopt/include/ast_scp.h @@ -93,7 +93,7 @@ class BuildScopeVisitor : public BuildScopeBaseVisitor { void SetRunIt(bool b) { mRunIt = b; } void InitInternalTypes(); - ClassNode *AddClass(std::string name, unsigned tyidx = 0); + ClassNode *AddClass(unsigned stridx, unsigned tyidx = 0); FunctionNode *AddFunction(std::string name); void AddType(ASTScope *scope, TreeNode *node); diff --git a/src/MapleFE/astopt/src/ast_adj.cpp b/src/MapleFE/astopt/src/ast_adj.cpp index 12de84bcec91e2ccefe2bd83343c3a7062849631..64b8e82bf1f0de9ea9ecc9c1e7f3ef79609bd954 100644 --- a/src/MapleFE/astopt/src/ast_adj.cpp +++ b/src/MapleFE/astopt/src/ast_adj.cpp @@ -53,6 +53,18 @@ ClassNode *AdjustASTVisitor::VisitClassNode(ClassNode *node) { (void) AstVisitor::VisitClassNode(node); CheckAndRenameCppKeywords(node); AssignPseudoName(node); + + // record names + gStringPool.AddAltStrIdx(node->GetStrIdx()); + for (unsigned i = 0; i < node->GetFieldsNum(); i++) { + TreeNode *n = node->GetField(i); + gStringPool.AddAltStrIdx(n->GetStrIdx()); + } + for (unsigned i = 0; i < node->GetMethodsNum(); i++) { + TreeNode *n = node->GetMethod(i); + gStringPool.AddAltStrIdx(n->GetStrIdx()); + } + // skip getting canonical type if not only fields if (node->GetMethodsNum() || node->GetSuperClassesNum() || node->GetSuperInterfacesNum() || node->GetSuperClassesNum() || node->GetTypeParamsNum()) { @@ -76,6 +88,18 @@ ClassNode *AdjustASTVisitor::VisitClassNode(ClassNode *node) { InterfaceNode *AdjustASTVisitor::VisitInterfaceNode(InterfaceNode *node) { (void) AstVisitor::VisitInterfaceNode(node); CheckAndRenameCppKeywords(node); + + // record names + gStringPool.AddAltStrIdx(node->GetStrIdx()); + for (unsigned i = 0; i < node->GetFieldsNum(); i++) { + TreeNode *n = node->GetField(i); + gStringPool.AddAltStrIdx(n->GetStrIdx()); + } + for (unsigned i = 0; i < node->GetMethodsNum(); i++) { + TreeNode *n = node->GetMethod(i); + gStringPool.AddAltStrIdx(n->GetStrIdx()); + } + // skip getting canonical type if not only fields if (node->GetMethodsNum() || node->GetSuperInterfacesNum()) { return node; @@ -108,6 +132,7 @@ StructLiteralNode *AdjustASTVisitor::VisitStructLiteralNode(StructLiteralNode *n } TreeNode *newnode = mInfo->GetCanonicStructNode(node); + gStringPool.AddAltStrIdx(newnode->GetStrIdx()); if (newnode != node) { node->SetTypeIdx(newnode->GetTypeIdx()); } @@ -118,6 +143,18 @@ StructLiteralNode *AdjustASTVisitor::VisitStructLiteralNode(StructLiteralNode *n StructNode *AdjustASTVisitor::VisitStructNode(StructNode *node) { (void) AstVisitor::VisitStructNode(node); CheckAndRenameCppKeywords(node); + + // record names + gStringPool.AddAltStrIdx(node->GetStrIdx()); + for (unsigned i = 0; i < node->GetFieldsNum(); i++) { + TreeNode *n = node->GetField(i); + gStringPool.AddAltStrIdx(n->GetStrIdx()); + } + for (unsigned i = 0; i < node->GetMethodsNum(); i++) { + TreeNode *n = node->GetMethod(i); + gStringPool.AddAltStrIdx(n->GetStrIdx()); + } + // skip getting canonical type for TypeAlias TreeNode *parent_orig = node->GetParent(); TreeNode *p = parent_orig; @@ -284,7 +321,14 @@ FunctionNode *AdjustASTVisitor::VisitFunctionNode(FunctionNode *node) { (void) AstVisitor::VisitFunctionNode(node); CheckAndRenameCppKeywords(node); - TreeNode *type = node->GetType(); + gStringPool.AddAltStrIdx(node->GetStrIdx()); + + for(unsigned i = 0; i < node->GetParamsNum(); i++) { + TreeNode *it = node->GetParam(i); + gStringPool.AddAltStrIdx(it->GetStrIdx()); + } + + TreeNode *type = node->GetRetType(); if (type && type->IsUserType()) { type->SetParent(node); } @@ -314,6 +358,7 @@ DeclNode *AdjustASTVisitor::VisitDeclNode(DeclNode *node) { unsigned stridx = inode->GetStrIdx(); if (stridx) { node->SetStrIdx(stridx); + gStringPool.AddAltStrIdx(stridx); mUpdated = true; } @@ -509,7 +554,7 @@ LambdaNode *AdjustASTVisitor::VisitLambdaNode(LambdaNode *node) { if (tn) { if (tn->IsBlock()) { func->SetBody(static_cast(tn)); - func->SetType(node->GetType()); + func->SetRetType(node->GetRetType()); } else { BlockNode *blk = mHandler->NewTreeNode(); ReturnNode *ret = mHandler->NewTreeNode(); @@ -521,8 +566,8 @@ LambdaNode *AdjustASTVisitor::VisitLambdaNode(LambdaNode *node) { } // func return type - if (node->GetType()) { - func->SetType(node->GetType()); + if (node->GetRetType()) { + func->SetRetType(node->GetRetType()); } mUpdated = true; diff --git a/src/MapleFE/astopt/src/ast_info.cpp b/src/MapleFE/astopt/src/ast_info.cpp index 650fe12dfdc4290c2e33914a4940653d7b1b43a2..5c8ef881ec5d39d5ac2f54f6a6cae737fbb8cb82 100644 --- a/src/MapleFE/astopt/src/ast_info.cpp +++ b/src/MapleFE/astopt/src/ast_info.cpp @@ -67,6 +67,10 @@ void AST_INFO::CollectInfo() { mPass = 2; MSGNOLOC0("============== merge class/interface/struct =============="); visitor.Visit(module); + + // collect function types + FunctionVisitor func_visitor(mHandler, mFlags, true); + func_visitor.Visit(module); } void AST_INFO::AddBuiltInTypes() { @@ -503,7 +507,7 @@ StructNode *AST_INFO::CreateStructFromStructLiteral(StructLiteralNode *node) { } unsigned AST_INFO::GetAnonymousName() { - std::string str("AnonymousStruct_"); + std::string str("AnonymousStruct__"); str += std::to_string(mNum++); unsigned stridx = gStringPool.GetStrIdx(str); return stridx; @@ -682,7 +686,7 @@ IdentifierNode *FillNodeInfoVisitor::VisitIdentifierNode(IdentifierNode *node) { FunctionNode *FillNodeInfoVisitor::VisitFunctionNode(FunctionNode *node) { (void) AstVisitor::VisitFunctionNode(node); - TreeNode *type = node->GetType(); + TreeNode *type = node->GetRetType(); if (type) { mInfo->SetTypeId(node, type->GetTypeId()); mInfo->SetTypeIdx(node, type->GetTypeIdx()); @@ -690,7 +694,7 @@ FunctionNode *FillNodeInfoVisitor::VisitFunctionNode(FunctionNode *node) { unsigned stridx = gStringPool.GetStrIdx("Generator"); unsigned tidx = mInfo->GetBuiltInTypeIdx(stridx); UserTypeNode *ut = mInfo->CreateUserTypeNode(stridx); - node->SetType(ut); + node->SetRetType(ut); } return node; } @@ -936,6 +940,24 @@ FunctionNode *ClassStructVisitor::VisitFunctionNode(FunctionNode *node) { return node; } +FunctionNode *FunctionVisitor::VisitFunctionNode(FunctionNode *node) { + FunctionTypeNode *functype = mHandler->NewTreeNode(); + TreeNode *n = NULL; + for (unsigned i = 0; i < node->GetParamsNum(); i++) { + n = node->GetParam(i); + functype->AddParam(n ? n->GetTypeIdx() : 0); + } + + // add return + n = node->GetRetType(); + functype->AddParam(n ? n->GetTypeIdx() : 0); + + unsigned tidx = gTypeTable.GetOrCreateFunctionTypeIdx(functype); + node->SetTypeIdx(tidx); + + return node; +} + IdentifierNode *FindStrIdxVisitor::VisitIdentifierNode(IdentifierNode *node) { (void) AstVisitor::VisitIdentifierNode(node); if (node->GetStrIdx() == mStrIdx) { diff --git a/src/MapleFE/astopt/src/ast_scp.cpp b/src/MapleFE/astopt/src/ast_scp.cpp index 7a87e6f2566455db9171a1c58a626b1e5cdb6ce1..fe472ccd5efd3289ee290da38b51789e456a4f01 100644 --- a/src/MapleFE/astopt/src/ast_scp.cpp +++ b/src/MapleFE/astopt/src/ast_scp.cpp @@ -140,20 +140,24 @@ void BuildScopeVisitor::InitInternalTypes() { } // add dummpy console.log() - ClassNode *console = AddClass("console"); - ASTScope *scp = NewScope(scope, console); - mStrIdx2ScopeMap[console->GetStrIdx()] = scp; - FunctionNode *log = AddFunction("log"); - log->SetTypeIdx(TY_Void); - console->AddMethod(log); - log->SetScope(scp); - AddDecl(scp, log); -} - -ClassNode *BuildScopeVisitor::AddClass(std::string name, unsigned tyidx) { + unsigned size = gStringPool.GetSize(); + unsigned stridx = gStringPool.GetStrIdx("console"); + TreeNode *type = gTypeTable.GetTypeFromStrIdx(stridx); + if (!type) { + ClassNode *console = AddClass(stridx); + ASTScope *scp = NewScope(scope, console); + mStrIdx2ScopeMap[console->GetStrIdx()] = scp; + FunctionNode *log = AddFunction("log"); + log->SetTypeIdx(TY_Void); + console->AddMethod(log); + log->SetScope(scp); + AddDecl(scp, log); + } +} + +ClassNode *BuildScopeVisitor::AddClass(unsigned stridx, unsigned tyidx) { ClassNode *node = mHandler->NewTreeNode(); - unsigned idx = gStringPool.GetStrIdx(name); - node->SetStrIdx(idx); + node->SetStrIdx(stridx); node->SetTypeIdx(tyidx); ModuleNode *module = mHandler->GetASTModule(); @@ -488,7 +492,7 @@ UserTypeNode *BuildScopeVisitor::VisitUserTypeNode(UserTypeNode *node) { if (p->IsFunction()) { // exclude function return type FunctionNode *f = static_cast(p); - if (f->GetType() == node) { + if (f->GetRetType() == node) { return node; } } else if (p->IsTypeAlias()) { @@ -761,6 +765,7 @@ void AST_SCP::RenameVar() { str += std::to_string(size); visitor.mOldStrIdx = stridx; visitor.mNewStrIdx = gStringPool.GetStrIdx(str); + gStringPool.AddAltStrIdx(visitor.mNewStrIdx); TreeNode *tn = mHandler->GetAstOpt()->GetNodeFromNodeId(nid); ASTScope *scope = tn->GetScope(); tn = scope->GetTree(); diff --git a/src/MapleFE/astopt/src/ast_ti.cpp b/src/MapleFE/astopt/src/ast_ti.cpp index 3d75298fa66fb555aa1c5e65c3d4b0d31546547a..99439b59de694b757e30393cd8db4d3a3456d115 100644 --- a/src/MapleFE/astopt/src/ast_ti.cpp +++ b/src/MapleFE/astopt/src/ast_ti.cpp @@ -320,11 +320,11 @@ TypeId TypeInferVisitor::MergeTypeId(TypeId tia, TypeId tib) { } unsigned TypeInferVisitor::MergeTypeIdx(unsigned tia, unsigned tib) { - if (tia == tib || tib == 0) { + if (tia == tib || tib <= 1) { return tia; } - if (tia == 0) { + if (tia <= 1) { return tib; } @@ -433,7 +433,11 @@ PrimTypeNode *TypeInferVisitor::GetOrClonePrimTypeNode(PrimTypeNode *pt, TypeId new_pt->SetPrimType(pt->GetPrimType()); } SetTypeId(new_pt, tid); - SetTypeIdx(new_pt, tid); + if (IsPrimTypeId(tid)) { + SetTypeIdx(new_pt, tid); + } else { + SetTypeIdx(new_pt, gTypeTable.GetTypeFromTypeId(tid)->GetTypeIdx()); + } SetUpdated(); } return new_pt; @@ -501,7 +505,7 @@ void TypeInferVisitor::UpdateTypeUseNode(TreeNode *target, TreeNode *input) { TypeId new_elemTypeId = GetArrayElemTypeId(target); TreeNode *type = static_cast(target)->GetType(); MASSERT(target->IsIdentifier() && "target node not identifier"); - if (type->IsPrimArrayType()) { + if (type && type->IsPrimArrayType()) { unsigned nid = target->GetNodeId(); mParam2ArgArrayDeclMap[nid].insert(decl); if (old_elemTypeId != new_elemTypeId) { @@ -570,13 +574,13 @@ void TypeInferVisitor::UpdateFuncRetTypeId(FunctionNode *node, TypeId tid, unsig if (!node || (node->GetTypeId() == tid && node->GetTypeIdx() == tidx)) { return; } - TreeNode *type = node->GetType(); + TreeNode *type = node->GetRetType(); // create new return type node if it was shared if (type) { if (type->IsPrimType() && type->IsTypeIdNone()) { type = GetOrClonePrimTypeNode((PrimTypeNode *)type, tid); - node->SetType(type); + node->SetRetType(type); } tid = MergeTypeId(type->GetTypeId(), tid); SetTypeId(type, tid); @@ -1089,8 +1093,8 @@ CallNode *TypeInferVisitor::VisitCallNode(CallNode *node) { mHandler->AddGeneratorUsed(node->GetNodeId(), func); } // update call's return type - if (func->GetType()) { - UpdateTypeId(node, func->GetType()->GetTypeId()); + if (func->GetRetType()) { + UpdateTypeId(node, func->GetRetType()->GetTypeId()); } // skip imported and exported functions as they are generic // so should not restrict their types @@ -1152,6 +1156,13 @@ CallNode *TypeInferVisitor::VisitCallNode(CallNode *node) { } } else if (decl->IsLiteral()) { NOTYETIMPL("VisitCallNode literal node"); + } else if (decl->IsTypeIdClass()) { + // object + if (node->GetArgsNum()) { + TreeNode *arg = node->GetArg(0); + SetTypeId(arg, TY_Object); + SetTypeIdx(arg, decl->GetTypeIdx()); + } } else { NOTYETIMPL("VisitCallNode not function node"); } @@ -1185,13 +1196,17 @@ CastNode *TypeInferVisitor::VisitCastNode(CastNode *node) { AsTypeNode *TypeInferVisitor::VisitAsTypeNode(AsTypeNode *node) { (void) AstVisitor::VisitAsTypeNode(node); TreeNode *dest = node->GetType(); - SetTypeId(node, dest); + if (node->GetTypeIdx() == 0) { + SetTypeId(node, dest); + } TreeNode *parent = node->GetParent(); if (parent) { // pass to parent, need refine if multiple AsTypeNode if (parent->GetAsTypesNum() == 1 && parent->GetAsTypeAtIndex(0) == node) { - SetTypeId(parent, dest); + if (parent->GetTypeIdx() == 0) { + SetTypeId(parent, dest); + } } } return node; @@ -1532,9 +1547,6 @@ FunctionNode *TypeInferVisitor::VisitFunctionNode(FunctionNode *node) { if (node->GetFuncName()) { SetTypeId(node->GetFuncName(), node->GetTypeId()); } - if (node->GetType()) { - SetTypeIdx(node, node->GetType()->GetTypeIdx()); - } return node; } @@ -1626,6 +1638,8 @@ IdentifierNode *TypeInferVisitor::VisitIdentifierNode(IdentifierNode *node) { UpdateTypeId(node, decl); UpdateTypeIdx(node, decl); } + // pass IsGeneratorUsed + mHandler->UpdateGeneratorUsed(node->GetNodeId(), decl->GetNodeId()); } else { NOTYETIMPL("node not declared"); MSGNOLOC0(node->GetName()); @@ -1650,7 +1664,7 @@ IsNode *TypeInferVisitor::VisitIsNode(IsNode *node) { TreeNode *parent = node->GetParent(); if (parent->IsFunction()) { FunctionNode *func = static_cast(parent); - if (func->GetType() == node) { + if (func->GetRetType() == node) { TreeNode *right = node->GetRight(); if (right->IsUserType()) { TreeNode *id = static_cast(right)->GetId(); @@ -1767,16 +1781,16 @@ ReturnNode *TypeInferVisitor::VisitReturnNode(ReturnNode *node) { if (tn) { FunctionNode *func = static_cast(tn); // use dummy PrimTypeNode as return type of function if not set to carry return TypeId - if (!func->GetType()) { + if (!func->GetRetType()) { PrimTypeNode *type = mHandler->NewTreeNode(); type->SetPrimType(TY_None); - func->SetType(type); + func->SetRetType(type); } if (!func->IsGenerator() && !func->IsIterator()) { UpdateFuncRetTypeId(func, node->GetTypeId(), node->GetTypeIdx()); if (res) { // use res to update function's return type - UpdateTypeUseNode(func->GetType(), res); + UpdateTypeUseNode(func->GetRetType(), res); } } } diff --git a/src/MapleFE/autogen/reserved.spec b/src/MapleFE/autogen/reserved.spec index 1832b7c5dcb0cdf016191982576d931dd51444a7..cd258684dc2ad9b79fe06683a1d021930fe3fa98 100644 --- a/src/MapleFE/autogen/reserved.spec +++ b/src/MapleFE/autogen/reserved.spec @@ -39,3 +39,4 @@ rule IRREGULAR_CHAR : "this_is_for_fake_rule" rule UTF8 : "this_is_for_fake_rule" rule TemplateLiteral : "this_is_for_fake_rule" rule RegularExpression : "this_is_for_fake_rule" +rule NoLineTerminator : "this_is_for_fake_rule" diff --git a/src/MapleFE/shared/include/ast.h b/src/MapleFE/shared/include/ast.h index 4ef9361c73e6c6afca0cb01d4e51b632b6374970..347f8d443493ec5bb3bf048c33a8f7d29e0328a6 100644 --- a/src/MapleFE/shared/include/ast.h +++ b/src/MapleFE/shared/include/ast.h @@ -1830,7 +1830,7 @@ private: SmallVector mThrows; // exceptions it can throw SmallVector mTypeParams; TreeNode *mFuncName; // function name, usually an identifier - TreeNode *mType; // return type + TreeNode *mRetType; // return type SmallVector mParams; // BlockNode *mBody; DimensionNode *mDims; @@ -1909,8 +1909,8 @@ public: void SetTypeParamAtIndex(unsigned i, TreeNode* n) {*(mTypeParams.RefAtIndex(i)) = n; SETPARENT(n);} void AddTypeParam(TreeNode *); - void SetType(TreeNode *t) {mType = t; SETPARENT(t);} - TreeNode* GetType(){return mType;} + void SetRetType(TreeNode *t) {mRetType = t; SETPARENT(t);} + TreeNode* GetRetType(){return mRetType;} DimensionNode* GetDims() {return mDims;} void SetDims(DimensionNode *t) {mDims = t;} @@ -2120,7 +2120,7 @@ enum LambdaProperty { class LambdaNode : public TreeNode { private: LambdaProperty mProperty; - TreeNode *mType; // The return type. nullptr as Java Lambda. + TreeNode *mRetType; // The return type. nullptr as Java Lambda. SmallVector mParams; // A param could be an IdentifierNode or DeclNode. TreeNode *mBody; // the body could be an expression, or block. // nullptr as TS FunctionType and ConstructorType @@ -2128,7 +2128,7 @@ private: SmallVector mAttrs; public: LambdaNode() : TreeNode(NK_Lambda), - mBody(nullptr), mProperty(LP_JSArrowFunction), mType(nullptr) {} + mBody(nullptr), mProperty(LP_JSArrowFunction), mRetType(nullptr) {} ~LambdaNode(){Release();} @@ -2138,8 +2138,8 @@ public: LambdaProperty GetProperty() {return mProperty;} void SetProperty(LambdaProperty p) {mProperty = p;} - TreeNode* GetType() {return mType;} - void SetType(TreeNode* t) {mType = t; SETPARENT(t);} + TreeNode* GetRetType() {return mRetType;} + void SetRetType(TreeNode* t) {mRetType = t; SETPARENT(t);} unsigned GetParamsNum() {return mParams.GetNum();} TreeNode* GetParam(unsigned i) {return mParams.ValueAtIndex(i);} diff --git a/src/MapleFE/shared/include/ast_type.h b/src/MapleFE/shared/include/ast_type.h index 0936dfeff560a8baf7d6007498e9ef21a1d9a5f9..8186eabd80377cdb7754c0dc88c6bde3e496ecb4 100644 --- a/src/MapleFE/shared/include/ast_type.h +++ b/src/MapleFE/shared/include/ast_type.h @@ -167,23 +167,21 @@ public: /////////////////////////////////////////////////////////////////////////////// // FunctionTypeNode -// It is used to specify function types with its signature and return type +// It is used to specify function types with its parameters and return type /////////////////////////////////////////////////////////////////////////////// class FunctionTypeNode : public TreeNode { private: SmallVector mParams; // type index of formal parameters - unsigned mRetType; // type index of return type + // and return which is the last one public: - FunctionTypeNode() : TreeNode(NK_FunctionType), mRetType(0) {} + FunctionTypeNode() : TreeNode(NK_FunctionType) {} ~FunctionTypeNode(){} unsigned GetParamsNum() {return mParams.GetNum();} unsigned GetParam(unsigned i) {return mParams.ValueAtIndex(i);} void SetParam(unsigned i, unsigned n) {*(mParams.RefAtIndex(i)) = n;} void AddParam(unsigned i) {mParams.PushBack(i);} - void SetRetType(unsigned i) {mRetType = i;} - unsigned GetRetType() {return mRetType;} void ClearParam() {mParams.Clear();} bool IsEqual(FunctionTypeNode *f); diff --git a/src/MapleFE/shared/include/rule_summary.h b/src/MapleFE/shared/include/rule_summary.h index 5487d3d070eaa4751915bc26ecf5e23303fddbcc..014cc3ba470deedb3984ee98e199b4cf3bf6ff77 100644 --- a/src/MapleFE/shared/include/rule_summary.h +++ b/src/MapleFE/shared/include/rule_summary.h @@ -28,6 +28,7 @@ extern RuleTable TblHEXDIGIT; extern RuleTable TblUTF8; extern RuleTable TblIRREGULAR_CHAR; +extern RuleTable TblNoLineTerminator; extern RuleTable TblTemplateLiteral; extern RuleTable TblRegularExpression; extern RuleTable TblExpression; diff --git a/src/MapleFE/shared/include/stringpool.h b/src/MapleFE/shared/include/stringpool.h index 259e33e6209f4e48f58465f7c7ba4ef7b7cc5cb7..fd8dfee8ab8012e29a2bf5586db57f588e7d1721 100644 --- a/src/MapleFE/shared/include/stringpool.h +++ b/src/MapleFE/shared/include/stringpool.h @@ -23,6 +23,8 @@ #include #include +#include +#include #include #include "massert.h" @@ -45,18 +47,32 @@ private: StringMap *mMap; std::vector mBlocks; int mFirstAvail; // -1 means no available. + bool mUseAltStr; // use alter string std::vector mLongStrings; // for strings longer than block size, // we allocate them by malloc. std::vector mStringTable; + // alternate string which can be used for obfuscation + std::unordered_set mAltStrIdxSet; + std::unordered_map mAltStrIdxMap; + friend class StringMap; public: StringPool(); ~StringPool(); + void SetUseAltStr(bool b) { mUseAltStr = b; } + void AddAltStrIdx(unsigned idx) { mAltStrIdxSet.insert(idx); } + unsigned GetAltStrSize() { return mAltStrIdxSet.size(); } + bool IsAltStrIdx(unsigned idx) { + return mAltStrIdxSet.find(idx) != mAltStrIdxSet.end(); + } + void AddAltStrIdxMap(unsigned orig, unsigned alt) { mAltStrIdxMap[orig] = alt; } + void SetAltStrIdxMap(); + char* AllocBlock(); char* Alloc(const size_t); char* Alloc(const std::string&); @@ -71,12 +87,12 @@ public: unsigned GetStrIdx(const char*); unsigned GetStrIdx(const char*, size_t); - const char *GetStringFromStrIdx(unsigned idx) { - MASSERT(idx < mStringTable.size() && "string index out of range"); - return mStringTable[idx]; - } + unsigned GetSize() {return mStringTable.size();} + + const char *GetStringFromStrIdx(unsigned idx); void Dump(); + void DumpAlt(); }; // Lexing, Parsing, AST Building and IR Building all share one global diff --git a/src/MapleFE/shared/include/typetable.h b/src/MapleFE/shared/include/typetable.h index c6bbcd2ca049a8b3b6b166d31cd2888bee595adb..d53b17719156f086fefca6fbc7d2f9ec02844e92 100644 --- a/src/MapleFE/shared/include/typetable.h +++ b/src/MapleFE/shared/include/typetable.h @@ -27,6 +27,7 @@ #include #include "massert.h" #include "ast.h" +#include "ast_type.h" namespace maplefe { @@ -53,6 +54,7 @@ private: std::unordered_map mNodeId2TypeIdxMap; std::unordered_map mTypeId2TypeMap; std::unordered_set mPrimTypeId; + std::unordered_set mFuncTypeIdx; unsigned mPrimSize; unsigned mPreBuildSize; @@ -62,16 +64,23 @@ public: unsigned size() { return mTypeTable.size(); } unsigned GetPreBuildSize() { return mPreBuildSize; } + + bool IsPrimTypeId(TypeId tid) { return mPrimTypeId.find(tid) != mPrimTypeId.end(); } unsigned GetPrimSize() { return mPrimSize; } TreeNode *CreatePrimType(std::string name, TypeId tid); TreeNode *CreateBuiltinType(std::string name, TypeId tid); + void AddPrimTypeId(TypeId tid); void AddPrimAndBuiltinTypes(); bool AddType(TreeNode *node); + TypeEntry *GetTypeEntryFromTypeIdx(unsigned tidx); TreeNode *GetTypeFromTypeIdx(unsigned tidx); TreeNode *GetTypeFromTypeId(TypeId tid) { return mTypeId2TypeMap[tid]; } - bool IsPrimTypeId(TypeId tid) { return mPrimTypeId.find(tid) != mPrimTypeId.end(); } + TreeNode *GetTypeFromStrIdx(unsigned strid); + + unsigned GetOrCreateFunctionTypeIdx(FunctionTypeNode *type); + void Dump(); }; diff --git a/src/MapleFE/shared/src/ast.cpp b/src/MapleFE/shared/src/ast.cpp index 937d6431c5b012e9e83f255cde59fd2f8dd0b623..f42e21d7ff0442a6825f5ca3dcb6e92968630f91 100644 --- a/src/MapleFE/shared/src/ast.cpp +++ b/src/MapleFE/shared/src/ast.cpp @@ -1775,7 +1775,7 @@ void ClassNode::Dump(unsigned indent) { ////////////////////////////////////////////////////////////////////////////////////// FunctionNode::FunctionNode() : TreeNode(NK_Function), - mFuncName(NULL), mType(NULL), mBody(NULL), mDims(NULL), + mFuncName(NULL), mRetType(NULL), mBody(NULL), mDims(NULL), mIsConstructor(false), mIsGenerator(false), mIsIterator(false), mIsGetAccessor(false), mIsSetAccessor(false), mIsCallSignature(false), mIsConstructSignature(false), mAssert(NULL) {} @@ -1797,7 +1797,7 @@ void FunctionNode::AddTypeParam(TreeNode *param) { // and parameter types. So languages require Type Erasure at first, like Java. // Type erasure should be done earlier in language specific process. bool FunctionNode::OverrideEquivalent(FunctionNode *fun) { - if (!mType->TypeEquivalent(fun->GetType())) + if (!mRetType->TypeEquivalent(fun->GetRetType())) return false; if (GetStrIdx() != fun->GetStrIdx()) return false; diff --git a/src/MapleFE/shared/src/ast_builder.cpp b/src/MapleFE/shared/src/ast_builder.cpp index e715baa5a137440ac60f7652e975f3f61c698a8e..707f0e13e694266eb809fe5e0c69ad4f93ec9e8c 100644 --- a/src/MapleFE/shared/src/ast_builder.cpp +++ b/src/MapleFE/shared/src/ast_builder.cpp @@ -176,14 +176,14 @@ static void add_type_to(TreeNode *tree, TreeNode *type) { lit->SetType(type); } else if (tree->IsLambda()) { LambdaNode *lam = (LambdaNode*)tree; - lam->SetType(type); + lam->SetRetType(type); } else if (tree->IsVarList()) { VarListNode *vl = (VarListNode*)tree; for (unsigned i = 0; i < vl->GetVarsNum(); i++) vl->GetVarAtIndex(i)->SetType(type); } else if (tree->IsFunction()) { FunctionNode *func = (FunctionNode*)tree; - func->SetType(type); + func->SetRetType(type); } else if (tree->IsBindingPattern()) { BindingPatternNode *bp = (BindingPatternNode*)tree; bp->SetType(type); diff --git a/src/MapleFE/shared/src/ast_type.cpp b/src/MapleFE/shared/src/ast_type.cpp index f2c58b4ea231c0bc834da0322a5b47788ee82a61..1ac736af9365f1263a8f29738d538c35fe56491d 100644 --- a/src/MapleFE/shared/src/ast_type.cpp +++ b/src/MapleFE/shared/src/ast_type.cpp @@ -42,6 +42,7 @@ void UserTypeNode::AddUnionInterType(TreeNode *args) { args->IsKeyOf() || args->IsImport() || args->IsField() || + args->IsTemplateLiteral() || args->IsStruct()) { mUnionInterTypes.PushBack(args); SETPARENT(args); @@ -181,9 +182,7 @@ static TypeId FindPrimTypeId(const char *keyword) { bool FunctionTypeNode::IsEqual(FunctionTypeNode *node) { bool result = true; - if (node->GetRetType() != mRetType) { - result = false; - } else if (node->GetParamsNum() != GetParamsNum()) { + if (node->GetParamsNum() != GetParamsNum()) { result = false; } else { for (unsigned i = 0; i < GetParamsNum(); i++) { diff --git a/src/MapleFE/shared/src/parser.cpp b/src/MapleFE/shared/src/parser.cpp index ea109fc5f0ec11dd79ebe3b3e199f0b68e1bd6b0..a3f8139a95310f5d692004b8e687b0ca5c6bbca6 100644 --- a/src/MapleFE/shared/src/parser.cpp +++ b/src/MapleFE/shared/src/parser.cpp @@ -870,6 +870,10 @@ bool Parser::LookAheadFail(RuleTable *rule_table, unsigned token) { if (curr_token->IsTempLit() || curr_token->IsRegExpr()) found = true; } + if (rule_table == &TblNoLineTerminator) { + if (!curr_token->mLineBegin) + found = true; + } } break; case LA_Identifier: @@ -1204,6 +1208,14 @@ bool Parser::TraverseRuleTableRegular(RuleTable *rule_table, AppealNode *appeal) if ((rule_table == &TblRegularExpression)) return TraverseRegularExpression(rule_table, appeal); + if (rule_table == &TblNoLineTerminator) { + Token *token = mActiveTokens.ValueAtIndex(mCurToken); + if (token->mLineBegin) + return false; + else + return true; + } + EntryType type = rule_table->mType; switch(type) { case ET_Oneof: @@ -1697,17 +1709,21 @@ bool Parser::TraverseConcatenate(RuleTable *rule_table, AppealNode *appeal) { bool turned_on_AltToken = false; for (unsigned i = 0; i < rule_table->mNum; i++) { - bool is_zeroxxx = false; + bool is_zeroxxx = false; // If the table is Zeroorxxx(), or NoLineTerminator. + bool no_line_term = false; // If the table is NoLineTerminator + bool no_line_term_met = false; // If the table is NoLineTerminator and token is no line term. bool is_asi = false; bool is_token = false; bool old_mInAltTokensMatching = mInAltTokensMatching; TableData *data = rule_table->mData + i; if (data->mType == DT_Subtable) { - RuleTable *zero_rt = data->mData.mEntry; - if (zero_rt->mType == ET_Zeroormore || zero_rt->mType == ET_Zeroorone) + RuleTable *curr_rt = data->mData.mEntry; + if (curr_rt == &TblNoLineTerminator) + no_line_term = true; + if (curr_rt->mType == ET_Zeroormore || curr_rt->mType == ET_Zeroorone) is_zeroxxx = true; - if (zero_rt->mType == ET_ASI) + if (curr_rt->mType == ET_ASI) is_asi = true; } else if (data->mType == DT_Token) { is_token = true; @@ -1749,11 +1765,18 @@ bool Parser::TraverseConcatenate(RuleTable *rule_table, AppealNode *appeal) { } } + if ((prev_succ_tokens.GetNum() == 1) && no_line_term) { + unsigned prev = prev_succ_tokens.ValueAtIndex(0); + Token *t = GetActiveToken(prev + 1); + if (!t->mLineBegin) + no_line_term_met = true; + } + // for Zeroorone/Zeroormore node it always returns true. NO matter how // many tokens it really matches, 'zero' is also a correct match. we // need take it into account so that the next rule table can try // on it. - if (!is_zeroxxx) + if (!is_zeroxxx && !no_line_term_met) prev_succ_tokens.Clear(); // is_zeroxxx seems redundant because the traversal should always be true. @@ -1887,8 +1910,10 @@ void Parser::SetIsDone(RuleTable *rt, unsigned start_token) { SuccMatch *succ = &gSucc[rt->mIndex]; bool found = succ->GetStartToken(start_token); - MASSERT(found); - succ->SetIsDone(); + if (rt != &TblNoLineTerminator) { + MASSERT(found); + succ->SetIsDone(); + } } ///////////////////////////////////////////////////////////////////////////// @@ -2276,7 +2301,7 @@ void Parser::SortOutConcatenate(AppealNode *parent) { if (!child) { if (data->mType == DT_Subtable) { RuleTable *table = data->mData.mEntry; - if (table->mType == ET_Zeroorone || table->mType == ET_Zeroormore) + if (table->mType == ET_Zeroorone || table->mType == ET_Zeroormore || table == &TblNoLineTerminator) good_child = true; if (table->mType == ET_ASI) good_child = true; diff --git a/src/MapleFE/shared/src/stringpool.cpp b/src/MapleFE/shared/src/stringpool.cpp index 1c96f4553e687943d4a866b4be67693bcb9dc7ab..046555147f14d2f83a2868c5c97209f68230d63b 100644 --- a/src/MapleFE/shared/src/stringpool.cpp +++ b/src/MapleFE/shared/src/stringpool.cpp @@ -20,6 +20,7 @@ #include #include +#include #include "stringpool.h" #include "stringmap.h" @@ -31,6 +32,7 @@ namespace maplefe { StringPool gStringPool; StringPool::StringPool() { + mUseAltStr = false; mMap = new StringMap(); mMap->SetPool(this); mFirstAvail = -1; @@ -173,11 +175,76 @@ unsigned StringPool::GetStrIdx(const char *str, size_t len) { return mMap->LookupEntryFor(s)->GetStrIdx(); } +const char *StringPool::GetStringFromStrIdx(unsigned idx) { + MASSERT(idx < mStringTable.size() && "string index out of range"); + if (mUseAltStr) { + if (mAltStrIdxMap.find(idx) != mAltStrIdxMap.end()) { + idx = mAltStrIdxMap[idx]; + } + } + return mStringTable[idx]; +} + +// This is the public interface to setup AltStrIdxMap used for obfuscation +// a name is mapped to a fixed length random unused name. +// two letters, [a-zA-Z] [a-zA-Z], which will cover over 2K names +// AA Aa AB Ab, ...., zz +#define LEN 2 +// +#define KIND 52 +#define SIZE KIND*KIND +void StringPool::SetAltStrIdxMap() { + unsigned size = mAltStrIdxSet.size(); + bool done = false; + char *A = (char*)malloc(LEN+1); + *(A+LEN) = 0; + for (auto stridx : mAltStrIdxSet) { + done = false; + while (!done) { + int r = rand() % (SIZE); + int t = r/KIND; + int s = r%KIND; + + // first char, use upper case for odd number + bool odd = t%2; + *A = (odd ? 'A' : 'a') + t/2; + + // second char, use upper case for odd number + odd = s%2; + *(A+1) = (odd ? 'A' : 'a') + s/2; + + unsigned size = GetSize(); + unsigned alt = GetStrIdx(A); + // make sure alt is a new string + if (alt == size) { + mAltStrIdxMap[stridx] = alt; + done = true; + } + } + } + + free(A); +} + void StringPool::Dump() { std::cout << "===================== StringTable =====================" << std::endl; for (unsigned idx = 1; idx < mStringTable.size(); idx++) { std::cout << " " << idx << " : " << mStringTable[idx] << std::endl; } } + +void StringPool::DumpAlt() { + std::cout << "================= Alt String Map ======================" << std::endl; + unsigned count = 0; + for (auto stridx : mAltStrIdxSet) { + unsigned alt = mAltStrIdxMap[stridx]; + std::cout << "count #" << stridx + << " str " << GetStringFromStrIdx(stridx) + << " --> " + << " alt " << GetStringFromStrIdx(alt) + << std::endl; + } +} + } diff --git a/src/MapleFE/shared/src/typetable.cpp b/src/MapleFE/shared/src/typetable.cpp index 6c28e9243a8ab59d94d9f0f8efc43a481087968b..8a1d8a93e96c53f2c8636eafbdebf5d22e352a57 100644 --- a/src/MapleFE/shared/src/typetable.cpp +++ b/src/MapleFE/shared/src/typetable.cpp @@ -83,15 +83,15 @@ TreeNode *TypeTable::CreateBuiltinType(std::string name, TypeId tid) { } bool TypeTable::AddType(TreeNode *node) { - unsigned id = node->GetNodeId(); - if (mNodeId2TypeIdxMap.find(id) != mNodeId2TypeIdxMap.end()) { + unsigned nid = node->GetNodeId(); + if (mNodeId2TypeIdxMap.find(nid) != mNodeId2TypeIdxMap.end()) { return false; } - unsigned tid = mTypeTable.size(); - mNodeId2TypeIdxMap[id] = tid; - node->SetTypeIdx(tid); + unsigned tidx = mTypeTable.size(); + mNodeId2TypeIdxMap[nid] = tidx; + node->SetTypeIdx(tidx); if (node->IsUserType()) { - static_cast(node)->GetId()->SetTypeIdx(tid); + static_cast(node)->GetId()->SetTypeIdx(tidx); } TypeEntry *entry = new TypeEntry(node); mTypeTable.push_back(entry); @@ -135,14 +135,46 @@ void TypeTable::AddPrimAndBuiltinTypes() { return; } -TypeEntry *TypeTable::GetTypeEntryFromTypeIdx(unsigned idx) { - MASSERT(idx < mTypeTable.size() && "type index out of range"); - return mTypeTable[idx]; +TypeEntry *TypeTable::GetTypeEntryFromTypeIdx(unsigned tidx) { + MASSERT(tidx < mTypeTable.size() && "type index out of range"); + return mTypeTable[tidx]; } -TreeNode *TypeTable::GetTypeFromTypeIdx(unsigned idx) { - MASSERT(idx < mTypeTable.size() && "type index out of range"); - return mTypeTable[idx]->GetType(); +TreeNode *TypeTable::GetTypeFromTypeIdx(unsigned tidx) { + MASSERT(tidx < mTypeTable.size() && "type index out of range"); + return mTypeTable[tidx]->GetType(); +} + +TreeNode *TypeTable::GetTypeFromStrIdx(unsigned stridx) { + for (auto entry : mTypeTable) { + TreeNode *node = entry->GetType(); + if (node && node->GetStrIdx() == stridx) { + return node; + } + } + return NULL; +} + +unsigned TypeTable::GetOrCreateFunctionTypeIdx(FunctionTypeNode *node) { + for (auto tidx: mFuncTypeIdx) { + TreeNode *type = GetTypeFromTypeIdx(tidx); + FunctionTypeNode *functype = static_cast(type); + bool found = functype->IsEqual(node); + if (found) { + return tidx; + } + } + bool status = AddType(node); + MASSERT(status && "failed to add a functiontype"); + unsigned tidx = node->GetTypeIdx(); + mFuncTypeIdx.insert(tidx); + + std::string str("FuncType__"); + str += std::to_string(tidx); + unsigned stridx = gStringPool.GetStrIdx(str); + node->SetStrIdx(stridx); + + return tidx; } void TypeTable::Dump() { diff --git a/src/MapleFE/shared/src/vfy.cpp b/src/MapleFE/shared/src/vfy.cpp index 02a87ff57b13acdecd16e77d55cd209417e93aca..07809d0f89ed58d2191117ee254c9fe0ca949449 100644 --- a/src/MapleFE/shared/src/vfy.cpp +++ b/src/MapleFE/shared/src/vfy.cpp @@ -572,4 +572,8 @@ void Verifier::VerifyTripleSlash(TripleSlashNode *tree){ return; } +void Verifier::VerifyFunctionType(FunctionTypeNode *tree){ + return; +} + } diff --git a/src/MapleFE/test/astdump.sh b/src/MapleFE/test/astdump.sh index db62279ca83707ad71e16a7c3827851c59a1cebb..2260c58f95c703efdf91b6bb871d883f67219a3f 100755 --- a/src/MapleFE/test/astdump.sh +++ b/src/MapleFE/test/astdump.sh @@ -110,8 +110,6 @@ for ts in $LIST; do T=$(sed -e "s/\(.*\)\(\.d\)\(\.ts-$PROCID.out\)/\1\2\3\2/" <<< "$ts-$PROCID.out.ts") eval $cmd <<< "$out" > "$T" [ -z "$NAME" ] || sed -i 's/__v[0-9][0-9]*//g' "$T" - clang-format-10 -i --style="{ColumnLimit: 120, JavaScriptWrapImports: false, AlignOperands: false}" "$T" - sed -i -e 's/?? =/??=/g' -e 's/ int\[/ number[/g' "$T" echo -e "\n====== TS Reformatted ======\n" $HIGHLIGHT "$T" echo TREEDIFF=$TREEDIFF @@ -132,8 +130,6 @@ for ts in $LIST; do [ -n "$KEEP" ] || rm -f "$T" else cp $ts $ts.tmp.ts - clang-format-10 -i --style="{ColumnLimit: 120, JavaScriptWrapImports: false, AlignOperands: false, JavaScriptQuotes: Double}" $ts.tmp.ts - sed -i 's/?? =/??=/g' $ts.tmp.ts $TS2AST $ts.tmp.ts if [ $? -eq 0 ]; then $AST2CPP $ts.tmp.ts.ast $TREEDIFF | sed -n '/^AstDump:/,/^}/p' | sed 's/\(mStrIdx: unsigned int, \)[0-9]* =>/\1=>/' diff --git a/src/MapleFE/test/typescript/unit_tests/class6.ts b/src/MapleFE/test/typescript/unit_tests/class6.ts new file mode 100644 index 0000000000000000000000000000000000000000..ad924fc06795ae44aa9fc447ddf7a8480482c9f2 --- /dev/null +++ b/src/MapleFE/test/typescript/unit_tests/class6.ts @@ -0,0 +1,20 @@ +class Klass { + public num: number = 1; + + if(n: number): boolean { + return this.num == n; + } + + try(n: number): void { + if(n == this.num) + console.log("EQ"); + else + console.log("NE"); + } +} + +var obj: Klass = new Klass(); +console.log(obj.if(0)); +console.log(obj.if(1)); +obj.try(0); +obj.try(1); diff --git a/src/MapleFE/test/typescript/unit_tests/class6.ts.result b/src/MapleFE/test/typescript/unit_tests/class6.ts.result new file mode 100644 index 0000000000000000000000000000000000000000..08a20f91d9cb7bde8eba40afc7635cd73adeb7ca --- /dev/null +++ b/src/MapleFE/test/typescript/unit_tests/class6.ts.result @@ -0,0 +1,34 @@ +Matched 61 tokens. +Matched 71 tokens. +Matched 83 tokens. +Matched 95 tokens. +Matched 102 tokens. +Matched 109 tokens. +============= Module =========== +== Sub Tree == +class Klass + Fields: + num=1 + Instance Initializer: + Constructors: + Methods: + func if(n) throws: + return this.num EQ n + func try(n) throws: + cond-branch cond:n EQ this.num + true branch : + console.log("EQ") false branch : + console.log("NE") + LocalClasses: + LocalInterfaces: + +== Sub Tree == +js_var Decl: obj=new Klass() +== Sub Tree == +console.log(obj.if(0)) +== Sub Tree == +console.log(obj.if(1)) +== Sub Tree == +obj.try(0) +== Sub Tree == +obj.try(1) diff --git a/src/MapleFE/test/typescript/unit_tests/else-as-prop-name.ts b/src/MapleFE/test/typescript/unit_tests/else-as-prop-name.ts new file mode 100644 index 0000000000000000000000000000000000000000..dd9965a57cfc8382a32c2ae6a50b371fa371ef6d --- /dev/null +++ b/src/MapleFE/test/typescript/unit_tests/else-as-prop-name.ts @@ -0,0 +1,6 @@ +class Klass { + else: number = 0; +} + +var obj: Klass = new Klass(); +console.log(obj, obj.else); diff --git a/src/MapleFE/test/typescript/unit_tests/else-as-prop-name.ts.result b/src/MapleFE/test/typescript/unit_tests/else-as-prop-name.ts.result new file mode 100644 index 0000000000000000000000000000000000000000..b14f7e7d22946f638f60fdd316acf89110a295ee --- /dev/null +++ b/src/MapleFE/test/typescript/unit_tests/else-as-prop-name.ts.result @@ -0,0 +1,18 @@ +Matched 10 tokens. +Matched 20 tokens. +Matched 31 tokens. +============= Module =========== +== Sub Tree == +class Klass + Fields: + else=0 + Instance Initializer: + Constructors: + Methods: + LocalClasses: + LocalInterfaces: + +== Sub Tree == +js_var Decl: obj=new Klass() +== Sub Tree == +console.log(obj,obj.else) diff --git a/src/MapleFE/test/typescript/unit_tests/semicolon-missing15.ts b/src/MapleFE/test/typescript/unit_tests/semicolon-missing15.ts new file mode 100644 index 0000000000000000000000000000000000000000..a21a6ce8a1190aa1bc6a8f85e5d85179ee1dbbb8 --- /dev/null +++ b/src/MapleFE/test/typescript/unit_tests/semicolon-missing15.ts @@ -0,0 +1,16 @@ +var n: number = 1; +switch (true) { + case n < 5: + console.log(n, " is less than 5"); + case n > 2 && n < 5: + console.log(n, " + 1 is equal to", n + 1); + break; + case n == 6: + console.log(n, " is equal to 6"); + break; + case n < 8: + console.log(n, " is greater than 4 and less than 8"); + break + default: + console.log(n, " is greater than 7"); +} diff --git a/src/MapleFE/test/typescript/unit_tests/semicolon-missing15.ts.result b/src/MapleFE/test/typescript/unit_tests/semicolon-missing15.ts.result new file mode 100644 index 0000000000000000000000000000000000000000..78c460f980018fd17fd60b48153b7b3b98ca4a9e --- /dev/null +++ b/src/MapleFE/test/typescript/unit_tests/semicolon-missing15.ts.result @@ -0,0 +1,8 @@ +Matched 7 tokens. +Matched 93 tokens. +============= Module =========== +== Sub Tree == +js_var Decl: n=1 +== Sub Tree == +A switch + diff --git a/src/MapleFE/test/typescript/unit_tests/semicolon-missing16.ts b/src/MapleFE/test/typescript/unit_tests/semicolon-missing16.ts new file mode 100644 index 0000000000000000000000000000000000000000..3caad616a659fe6951afcd48192e7063c6a0f87c --- /dev/null +++ b/src/MapleFE/test/typescript/unit_tests/semicolon-missing16.ts @@ -0,0 +1,8 @@ +function func(arg: number): number | undefined { + if(arg < 1) return + for(let i = 0; i < arg; i++) + console.log(i); + return arg * 10; +} +console.log(func(3)); + diff --git a/src/MapleFE/test/typescript/unit_tests/semicolon-missing17.ts b/src/MapleFE/test/typescript/unit_tests/semicolon-missing17.ts new file mode 100644 index 0000000000000000000000000000000000000000..f5c4aff91fda35a3bfecf34ccd70dfe72cdf7624 --- /dev/null +++ b/src/MapleFE/test/typescript/unit_tests/semicolon-missing17.ts @@ -0,0 +1,9 @@ +function func(arg: number): number | undefined { + for(let i = 0; i < arg; i++) { + if(i % 2 > 0) continue + console.log(i); + } + return arg * 10; +} +console.log(func(5)); + diff --git a/src/MapleFE/test/typescript/unit_tests/semicolon-missing17.ts.result b/src/MapleFE/test/typescript/unit_tests/semicolon-missing17.ts.result new file mode 100644 index 0000000000000000000000000000000000000000..6de52c4b72ce0abb85198d4eeeaf62bc051bb110 --- /dev/null +++ b/src/MapleFE/test/typescript/unit_tests/semicolon-missing17.ts.result @@ -0,0 +1,17 @@ +Matched 50 tokens. +Matched 60 tokens. +============= Module =========== +== Sub Tree == +func func(arg) throws: + for ( ) + cond-branch cond:i Mod 2 GT 0 + true branch : + continue: + false branch : + + console.log(i) + + return arg Mul 10 + +== Sub Tree == +console.log(func(5)) diff --git a/src/MapleFE/test/typescript/unit_tests/template-literal-type.ts b/src/MapleFE/test/typescript/unit_tests/template-literal-type.ts new file mode 100644 index 0000000000000000000000000000000000000000..073bae7598a49870fb29a75da43f6c479ed99402 --- /dev/null +++ b/src/MapleFE/test/typescript/unit_tests/template-literal-type.ts @@ -0,0 +1,12 @@ +class Num { + neg: boolean = false; + val: number = 0; +} + +function func(v: Num): `${string}n` | `-${string}n` { + return `${v.neg ? '-' : ''}${v.val}n`; +} + +var obj : Num = {neg: true, val: 123}; +console.log(func(obj)); +console.log(typeof func(obj)); diff --git a/src/MapleFE/test/typescript/unit_tests/template-literal-type.ts.result b/src/MapleFE/test/typescript/unit_tests/template-literal-type.ts.result new file mode 100644 index 0000000000000000000000000000000000000000..70ca48c63fb79ef537785175df99a5d33a80e5f2 --- /dev/null +++ b/src/MapleFE/test/typescript/unit_tests/template-literal-type.ts.result @@ -0,0 +1,30 @@ +Matched 16 tokens. +Matched 32 tokens. +Matched 47 tokens. +Matched 57 tokens. +Matched 68 tokens. +Matched 69 tokens. +Matched 70 tokens. +Matched 77 tokens. +Matched 80 tokens. +============= Module =========== +== Sub Tree == +class Num + Fields: + neg=false val=0 + Instance Initializer: + Constructors: + Methods: + LocalClasses: + LocalInterfaces: + +== Sub Tree == +func func(v) throws: + return template-literal: NULL,v.neg ? "-" : "",NULL,v.val,"n",NULL + +== Sub Tree == +js_var Decl: obj= {neg:true, val:123} +== Sub Tree == +console.log(func(obj)) +== Sub Tree == +console.log( typeof func(obj)) diff --git a/src/MapleFE/test/typescript/unit_tests/triple-slash-comment.ts.result b/src/MapleFE/test/typescript/unit_tests/triple-slash-comment.ts.result new file mode 100644 index 0000000000000000000000000000000000000000..e4afff30c02415e10dfeab2420a63675b1286d5d --- /dev/null +++ b/src/MapleFE/test/typescript/unit_tests/triple-slash-comment.ts.result @@ -0,0 +1,10 @@ +Matched 12 tokens. +Matched 19 tokens. +Matched 26 tokens. +============= Module =========== +== Sub Tree == +ts_enum: ET {TOP="top";BOTTOM="bottom" } +== Sub Tree == +js_let Decl: et=ET.TOP +== Sub Tree == +console.log(et) diff --git a/src/MapleFE/test/typescript/unit_tests/var-as-prop-name.ts b/src/MapleFE/test/typescript/unit_tests/var-as-prop-name.ts new file mode 100644 index 0000000000000000000000000000000000000000..bd97f5cab3b2af51e3f45bca1c7320496a53b5ff --- /dev/null +++ b/src/MapleFE/test/typescript/unit_tests/var-as-prop-name.ts @@ -0,0 +1,10 @@ +const obj = { + else() { + return this; + }, + var() { + console.log("var"); + } +} + +obj.else().var(); diff --git a/src/MapleFE/test/typescript/unit_tests/var-as-prop-name.ts.result b/src/MapleFE/test/typescript/unit_tests/var-as-prop-name.ts.result new file mode 100644 index 0000000000000000000000000000000000000000..7fcef6b23a7beaf3d03a4115b40cfebcf5da9d1f --- /dev/null +++ b/src/MapleFE/test/typescript/unit_tests/var-as-prop-name.ts.result @@ -0,0 +1,11 @@ +Matched 26 tokens. +Matched 36 tokens. +============= Module =========== +== Sub Tree == +js_const Decl: obj= {else:func else() throws: + return this +, var:func var() throws: + console.log("var") +} +== Sub Tree == +obj.else().var() diff --git a/src/MapleFE/tools/obfuscate/Makefile b/src/MapleFE/tools/obfuscate/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..41d43c8f5c02a856a80d0a4013c14f127c08e548 --- /dev/null +++ b/src/MapleFE/tools/obfuscate/Makefile @@ -0,0 +1,27 @@ +# Copyright (C) [2022] Futurewei Technologies, Inc. All rights reverved. +# +# OpenArkFE is licensed under the Mulan PSL v2. +# You can use this software according to the terms and conditions of the Mulan PSL v2. +# You may obtain a copy of Mulan PSL v2 at: +# +# http://license.coscl.org.cn/MulanPSL2 +# +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +# FIT FOR A PARTICULAR PURPOSE. +# See the Mulan PSL v2 for more details. +# + +include ../../Makefile.in + +all: + $(MAKE) -C src + +clean: + rm -rf $(BUILDDIR)/tools/obfuscate + +test: + $(MAKE) -C ../test p + +.PHONY: $(TARGS) + diff --git a/src/MapleFE/tools/obfuscate/include/obfuscate.h b/src/MapleFE/tools/obfuscate/include/obfuscate.h new file mode 100644 index 0000000000000000000000000000000000000000..63574ba6d02657eb5293605a36236e4a14374fe3 --- /dev/null +++ b/src/MapleFE/tools/obfuscate/include/obfuscate.h @@ -0,0 +1,52 @@ +/* +* Copyright (C) [2022] Futurewei Technologies, Inc. All rights reverved. +* +* OpenArkFE is licensed under the Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* +* http://license.coscl.org.cn/MulanPSL2 +* +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +* FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +////////////////////////////////////////////////////////////////////////////////////////////// +// This is the interface to translate AST to C++ +////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef __OBFUSCATE_HEADER__ +#define __OBFUSCATE_HEADER__ + +#include "astopt.h" +#include "ast_handler.h" +#include "ast_module.h" + +namespace maplefe { + +class Obfuscate : public AstOpt { +private: + AST_Handler *mASTHandler; + unsigned mFlags; + unsigned mIndexImported; + +public: + explicit Obfuscate(AST_Handler *h, unsigned flags) : + AstOpt(h, flags), + mASTHandler(h), + mFlags(flags), + mIndexImported(0) {} + ~Obfuscate() = default; + + void EmitTS(); + bool LoadImportedModules(); + + // return 0 if successful + // return non-zero if failed + int ProcessAST(); +}; + +} +#endif diff --git a/src/MapleFE/tools/obfuscate/src/Makefile b/src/MapleFE/tools/obfuscate/src/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..f8453d6a3e8b2523054b131d662a8e9a0e953310 --- /dev/null +++ b/src/MapleFE/tools/obfuscate/src/Makefile @@ -0,0 +1,90 @@ +# Copyright (C) [2022] Futurewei Technologies, Inc. All rights reverved. +# +# OpenArkFE is licensed under the Mulan PSL v2. +# You can use this software according to the terms and conditions of the Mulan PSL v2. +# You may obtain a copy of Mulan PSL v2 at: +# +# http://license.coscl.org.cn/MulanPSL2 +# +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +# FIT FOR A PARTICULAR PURPOSE. +# See the Mulan PSL v2 for more details. +# + +include ../../../Makefile.in +BUILDBIN=$(BUILDDIR)/bin +BUILD=$(BUILDDIR)/tools/obfuscate +BUILDGEN=$(BUILDDIR)/gen +BUILDASTGEN=$(BUILDDIR)/ast_gen/shared +$(shell $(MKDIR_P) $(BUILD)) + +SRC=$(wildcard *.cpp) +OBJ :=$(patsubst %.cpp,%.o,$(SRC)) +DEP :=$(patsubst %.cpp,%.d,$(SRC)) + +SRCG := $(wildcard $(BUILDGEN)/gen*.cpp) +OBJG := $(patsubst %.cpp, %.o, $(SRCG)) +DEPG := $(patsubst %.cpp, %.d, $(SRCG)) + +LOCALOBJS :=$(foreach obj,$(OBJ), $(BUILD)/$(obj)) +LOCALDEPS :=$(foreach dep,$(DEP), $(BUILD)/$(dep)) +OBJS :=$(LOCALOBJS) $(OBJG) +DEPS :=$(LOCALDEPS) $(DEPG) + +LIBOBJS :=$(patsubst $(BUILD)/main.o,,$(LOCALOBJS)) + +GENDIR:=${BUILDDIR}/ast_gen/shared + +INCLUDES := -I $(MAPLEFE_ROOT)/shared/include \ + -I $(MAPLEFE_ROOT)/astopt/include \ + -I $(MAPLEFE_ROOT)/tools/obfuscate/include \ + -I $(MAPLEFE_ROOT)/ast2cpp/include \ + -I $(MAPLEFE_ROOT)/autogen/include \ + -I $(MAPLEFE_ROOT)/shared/include \ + -I $(MAPLEFE_ROOT)/typescript/include \ + $(MAPLEALL_INC) -I ${GENDIR} + +INCLUDEGEN := -I $(MAPLEFE_ROOT)/shared/include -I $(BUILDDIR)/gen -I $(BUILDASTGEN) + +TARGET=obfuscate + +SHAREDLIB = $(BUILDDIR)/ast2cpp/ast2cpp.a $(BUILDDIR)/astopt/astopt.a $(BUILDDIR)/shared/shared.a $(BUILDASTGEN)/genast.a +LANGSPEC=$(BUILDDIR)/typescript/lang_spec.o + +.PHONY: all +all: $(BUILDBIN)/$(TARGET) + +-include $(DEPS) +.PHONY: clean + +vpath %.o $(BUILD) +vpath %.d $(BUILD) + +#Pattern Rules +$(BUILD)/%.o : %.cpp + $(CXX) $(CXXFLAGS) -fpermissive $(INCLUDES) -w -c $< -o $@ + +$(BUILD)/%.d : %.cpp + @$(CXX) $(CXXFLAGS) -MM $(INCLUDES) $< > $@ + @mv -f $(BUILD)/$*.d $(BUILD)/$*.d.tmp + @sed -e 's|.*:|$(BUILD)/$*.o:|' < $(BUILD)/$*.d.tmp > $(BUILD)/$*.d + @rm -f $(BUILD)/$*.d.tmp + +$(BUILDGEN)/%.o : $(BUILDGEN)/%.cpp $(BUILDGEN)/%.d + $(CXX) $(CXXFLAGS) -fpermissive $(INCLUDEGEN) -w -c $< -o $@ + +$(BUILDGEN)/%.d : $(BUILDGEN)/%.cpp + @$(CXX) $(CXXFLAGS) -std=c++11 -MM $(INCLUDEGEN) $< > $@ + @mv -f $(BUILDGEN)/$*.d $(BUILDGEN)/$*.d.tmp + @sed -e 's|.*:|$(BUILDGEN)/$*.o:|' < $(BUILDGEN)/$*.d.tmp > $(BUILDGEN)/$*.d + @rm -f $(BUILDGEN)/$*.d.tmp + +# TARGET depends on OBJS and shared OBJS from shared directory +# as well as mapleall libraries +$(BUILDBIN)/$(TARGET): $(OBJS) $(SHAREDLIB) + @mkdir -p $(BUILDBIN) + $(LD) -o $(BUILDBIN)/$(TARGET) $(LOCALOBJS) $(OBJG) $(LANGSPEC) $(SHAREDLIB) + +clean: + rm -rf $(BUILD) diff --git a/src/MapleFE/tools/obfuscate/src/main.cpp b/src/MapleFE/tools/obfuscate/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7eb882bd046729711e2f879c894b76980a81b954 --- /dev/null +++ b/src/MapleFE/tools/obfuscate/src/main.cpp @@ -0,0 +1,108 @@ +/* +* Copyright (C) [2022] Futurewei Technologies, Inc. All rights reverved. +* +* OpenArkFE is licensed under the Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* +* http://license.coscl.org.cn/MulanPSL2 +* +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +* FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +#include +#include +#include +#include "gen_astload.h" +#include "ast_handler.h" +#include "obfuscate.h" + +static void help() { + std::cout << "ast2cpp a.ast[,b.ast] [options]:" << std::endl; + std::cout << " --out=x.cpp : cpp output file" << std::endl; + std::cout << " --help : print this help" << std::endl; + std::cout << " --trace=n : Emit trace with 4-bit combo levels 1...15" << std::endl; + std::cout << " 1 : Emit ast tree visits" << std::endl; + std::cout << " 2 : Emit graph" << std::endl; + std::cout << " --emit-ts-only : Emit ts code only" << std::endl; + std::cout << " --emit-ts : Emit ts code" << std::endl; + std::cout << " --format-cpp : Format cpp" << std::endl; + std::cout << " --no-imported : Do not process the imported modules" << std::endl; + std::cout << "default out name uses the first input name: a.cpp" << std::endl; +} + +int main (int argc, char *argv[]) { + if (argc == 1 || (!strncmp(argv[1], "--help", 6) && (strlen(argv[1]) == 6))) { + help(); + exit(-1); + } + + unsigned flags; + // one or more input .ast files separated by ',' + const char *inputname = argv[1]; + // output .cpp file + const char *outputname = nullptr; + + // Parse the argument + for (unsigned i = 2; i < argc; i++) { + if (!strncmp(argv[i], "--trace=", 8)) { + int val = atoi(argv[i] + 8); + if (val < 1 || val > 15) { + help(); + exit(-1); + } + flags |= val; + } else if (!strncmp(argv[i], "--emit-ts-only", 14)) { + flags |= maplefe::FLG_emit_ts_only; + } else if (!strncmp(argv[i], "--emit-ts", 9)) { + flags |= maplefe::FLG_emit_ts; + } else if (!strncmp(argv[i], "--format-cpp", 12)) { + flags |= maplefe::FLG_format_cpp; + } else if (!strncmp(argv[i], "--no-imported", 13)) { + flags |= maplefe::FLG_no_imported; + } else if (!strncmp(argv[i], "--in=", 5)) { + inputname = argv[i]+5; + } else if (!strncmp(argv[i], "--out=", 6)) { + outputname = argv[i]+6; + } else { + std::cerr << "unknown option " << argv[i] << std::endl; + exit(-1); + } + } + + // input ast files + std::vector inputfiles; + if (inputname) { + std::stringstream ss; + ss.str(inputname); + std::string item; + while (std::getline(ss, item, ',')) { + // std::cout << "item " << item << " xxx"<< std::endl; + inputfiles.push_back(item); + } + } + + unsigned trace = (flags & maplefe::FLG_trace); + maplefe::AST_Handler handler(trace); + for (auto astfile: inputfiles) { + std::ifstream input(astfile, std::ifstream::binary); + input >> std::noskipws; + std::istream_iterator s(input), e; + maplefe::AstBuffer vec(s, e); + maplefe::AstLoad loadAst; + maplefe::ModuleNode *mod = loadAst.LoadFromAstBuf(vec); + // add mod to the vector + while(mod) { + handler.AddModule(mod); + mod = loadAst.Next(); + } + } + + maplefe::Obfuscate *obfuscate = new maplefe::Obfuscate(&handler, flags); + int res = obfuscate->ProcessAST(); + + return res; +} diff --git a/src/MapleFE/tools/obfuscate/src/obfuscate.cpp b/src/MapleFE/tools/obfuscate/src/obfuscate.cpp new file mode 100644 index 0000000000000000000000000000000000000000..17c750339a9e4969da356b7984cb935e0bb3b559 --- /dev/null +++ b/src/MapleFE/tools/obfuscate/src/obfuscate.cpp @@ -0,0 +1,160 @@ +/* +* Copyright (C) [2022] Futurewei Technologies, Inc. All rights reverved. +* +* OpenArkFE is licensed under the Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* +* http://license.coscl.org.cn/MulanPSL2 +* +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR +* FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +#include +#include +#include +#include +#include + +#include "obfuscate.h" +#include "ast_handler.h" +#include "gen_astdump.h" +#include "gen_astgraph.h" +#include "gen_aststore.h" +#include "gen_astload.h" +#include "cpp_definition.h" +#include "cpp_declaration.h" +#include "a2c_util.h" + +namespace maplefe { + +bool Obfuscate::LoadImportedModules() { + std::queue queue; + for (HandlerIndex i = 0; i < GetModuleNum(); i++) { + Module_Handler *handler = mASTHandler->GetModuleHandler(i); + ModuleNode *module = handler->GetASTModule(); + ImportedFiles imported(module); + imported.VisitTreeNode(module); + for(const auto &e: imported.mFilenames) + queue.push(e); + } + + bool err = false; + while(!queue.empty()) { + std::string filename = queue.front(); + queue.pop(); + if(mASTHandler->GetHandlerIndex(filename.c_str()) == HandlerNotFound) { + std::ifstream input(filename, std::ifstream::binary); + if(input.fail()) { + std::cerr << "Error: File " << filename << " not found for imported module" << std::endl; + err = true; + continue; + } + input >> std::noskipws; + std::istream_iterator s(input), e; + maplefe::AstBuffer vec(s, e); + maplefe::AstLoad loadAst; + maplefe::ModuleNode *mod = loadAst.LoadFromAstBuf(vec); + // add mod to the vector + while(mod) { + mASTHandler->AddModule(mod); + ImportedFiles imported(mod); + imported.VisitTreeNode(mod); + for(const auto &e: imported.mFilenames) + queue.push(e); + mod = loadAst.Next(); + } + } + } + return err; +} + +// starting point of AST +int Obfuscate::ProcessAST() { + mIndexImported = GetModuleNum(); + + // load all imported modules + if (!(mFlags & FLG_no_imported)) { + if (LoadImportedModules()) { + return 1; + } + } + + // loop through module handlers + for (HandlerIndex i = 0; i < GetModuleNum(); i++) { + Module_Handler *handler = mASTHandler->GetModuleHandler(i); + ModuleNode *module = handler->GetASTModule(); + + if (mFlags & FLG_trace_1) { + std::cout << "============= in ProcessAST ===========" << std::endl; + std::cout << "srcLang : " << module->GetSrcLangString() << std::endl; + + for(unsigned k = 0; k < module->GetTreesNum(); k++) { + TreeNode *tnode = module->GetTree(k); + if (mFlags & FLG_trace_1) { + tnode->Dump(0); + std::cout << std::endl; + } + } + } + + if (mFlags & FLG_trace_2) { + std::cout << "============= AstGraph ===========" << std::endl; + AstGraph graph(module); + graph.DumpGraph("After LoadFromAstBuf()", &std::cout); + } + } + + // build dependency of modules + PreprocessModules(); + + // loop through module handlers in import/export dependency order + for (auto handler: mHandlersInOrder) { + ModuleNode *module = handler->GetASTModule(); + + // basic analysis + handler->BasicAnalysis(); + + if (mFlags & FLG_trace_2) { + std::cout << "============= After BasicAnalysis ===========" << std::endl; + for(unsigned k = 0; k < module->GetTreesNum(); k++) { + TreeNode *tnode = module->GetTree(k); + if (mFlags & FLG_trace_1) { + tnode->Dump(0); + std::cout << std::endl; + } + } + AstGraph graph(module); + graph.DumpGraph("After BasicAnalysis()", &std::cout); + } + } + + if (mFlags & FLG_trace_3) { + gStringPool.Dump(); + } + + gStringPool.SetAltStrIdxMap(); + + if (mFlags & FLG_trace_3) { + gStringPool.Dump(); + gStringPool.DumpAlt(); + } + + gStringPool.SetUseAltStr(true); + + for (auto handler: mHandlersInOrder) { + ModuleNode *module = handler->GetASTModule(); + + std::cout << "============= Emitter ===========" << std::endl; + maplefe::Emitter emitter(handler); + std::string code = emitter.Emit("Convert AST to TypeScript code"); + std::cout << code; + } + + return 0; +} + +} // namespace maplefe diff --git a/src/MapleFE/typescript/src/lang_spec.cpp b/src/MapleFE/typescript/src/lang_spec.cpp index 54a95b6c359fbcf625b2aa4318eef9467487130a..d51d1f312d0184badb28eac07e422c6a24a65632 100644 --- a/src/MapleFE/typescript/src/lang_spec.cpp +++ b/src/MapleFE/typescript/src/lang_spec.cpp @@ -404,7 +404,18 @@ bool TypescriptLexer::FindTripleSlash() { if (line[i] == ' ') { i++; } else if (line[i] == '<') { - return true; + // Need make sure the following word is 'reference', + // or 'amd-module', or 'amd-dependency' + i++; + while(line[i] == ' ') { + i++; + } + if (!strncmp(line + i, "reference", 9) || + !strncmp(line + i, "amd-module", 10) || + !strncmp(line + i, "amd-dependency", 14)) { + if (i < current_line_size) + return true; + } } else { return false; } diff --git a/src/MapleFE/typescript/stmt.spec b/src/MapleFE/typescript/stmt.spec index 4754d774aa9473138e7448c3a27ec11650f4dd8c..a37f858835384b055fbb617bb90fcb8d420f9ca4 100644 --- a/src/MapleFE/typescript/stmt.spec +++ b/src/MapleFE/typescript/stmt.spec @@ -359,6 +359,7 @@ rule KeywordPropName : ONEOF("break", "export", "const", "if", + "try", "else", "continue", "implements", @@ -472,7 +473,8 @@ rule CallExpression : ONEOF( "set" + ZEROORONE(TypeArguments) + Arguments + ZEROORMORE(AsType), "get" + ZEROORONE(TypeArguments) + Arguments + ZEROORMORE(AsType), CallExpression + "?." + Arguments + ZEROORMORE(AsType), - ImportFunction) + ImportFunction, + CallExpression + '.' + KeywordPropName + ZEROORMORE(AsType)) attr.action.%1,%3,%10,%11 : BuildCall(%1) attr.action.%1,%10,%11 : AddAsType(%4) attr.action.%1,%10,%11 : AddTypeGenerics(%2) @@ -481,8 +483,8 @@ rule CallExpression : ONEOF( attr.action.%3 : AddAsType(%3) attr.action.%4 : BuildArrayElement(%1, %3) attr.action.%4 : AddAsType(%5) - attr.action.%5 : BuildField(%1, %3) - attr.action.%5 : AddAsType(%4) + attr.action.%5,%14 : BuildField(%1, %3) + attr.action.%5,%14 : AddAsType(%4) attr.action.%7 : SetIsNonNull(%1) attr.action.%7 : AddAsType(%1, %3) attr.action.%8 : SetIsOptional(%1) @@ -1144,9 +1146,9 @@ rule ForBinding : ONEOF(BindingIdentifier, ## continue [no LineTerminator here] LabelIdentifier[?Yield] ; rule ContinueStatement : ONEOF( "continue" + ZEROORONE(';'), - "continue" + LabelIdentifier + ZEROORONE(';')) + "continue" + NoLineTerminator + LabelIdentifier + ZEROORONE(';')) attr.action.%1 : BuildContinue() - attr.action.%2 : BuildContinue(%2) + attr.action.%2 : BuildContinue(%3) ##----------------------------------- ##rule BreakStatement[Yield] : @@ -1154,9 +1156,9 @@ rule ContinueStatement : ONEOF( ## break [no LineTerminator here] LabelIdentifier[?Yield] ; rule BreakStatement : ONEOF( "break" + ZEROORONE(';'), - "break" + LabelIdentifier + ZEROORONE(';')) + "break" + NoLineTerminator + LabelIdentifier + ZEROORONE(';')) attr.action.%1 : BuildBreak() - attr.action.%2 : BuildBreak(%2) + attr.action.%2 : BuildBreak(%3) ##----------------------------------- ##rule ReturnStatement[Yield] : @@ -1854,9 +1856,10 @@ rule TupleElementType: ONEOF(ZEROORONE(JSIdentifier + ':') + Type, rule UnionType : ONEOF(ZEROORONE('|') + UnionOrIntersectionOrPrimaryType + '|' + IntersectionOrPrimaryType, UnionOrIntersectionOrPrimaryType + '|' + KeyOf, KeyOf + '|' + UnionOrIntersectionOrPrimaryType, - TypeQuery + '|' + UnionOrIntersectionOrPrimaryType) + TypeQuery + '|' + UnionOrIntersectionOrPrimaryType, + TemplateLiteral + '|' + TemplateLiteral) attr.action.%1 : BuildUnionUserType(%2, %4) - attr.action.%2,%3,%4 : BuildUnionUserType(%1, %3) + attr.action.%2,%3,%4,%5 : BuildUnionUserType(%1, %3) ## rule IntersectionType: IntersectionOrPrimaryType & PrimaryType rule IntersectionType: ONEOF(IntersectionOrPrimaryType + '&' + PrimaryType, @@ -2222,11 +2225,11 @@ rule PropertyMemberDeclaration: ONEOF(MemberVariableDeclaration, ## MemberVariableDeclaration: AccessibilityModifieropt staticopt PropertyName TypeAnnotationopt Initializeropt ; rule MemberVariableDeclaration: ONEOF( - ZEROORMORE(Annotation) + ZEROORMORE(AccessibilityModifier) + PropertyName + ZEROORONE(TypeAnnotation) + ZEROORONE(Initializer) + ZEROORONE(';'), - ZEROORMORE(Annotation) + ZEROORMORE(AccessibilityModifier) + PropertyName + '?' + ZEROORONE(TypeAnnotation) + ZEROORONE(Initializer) + ZEROORONE(';'), + ZEROORMORE(Annotation) + ZEROORMORE(AccessibilityModifier) + PropertySignatureName + ZEROORONE(TypeAnnotation) + ZEROORONE(Initializer) + ZEROORONE(';'), + ZEROORMORE(Annotation) + ZEROORMORE(AccessibilityModifier) + PropertySignatureName + '?' + ZEROORONE(TypeAnnotation) + ZEROORONE(Initializer) + ZEROORONE(';'), ZEROORMORE(Annotation) + ZEROORMORE(AccessibilityModifier) + "get" + '=' + ArrowFunction + ZEROORONE(';'), ZEROORMORE(Annotation) + ZEROORMORE(AccessibilityModifier) + "set" + '=' + ArrowFunction + ZEROORONE(';'), - '#' + PropertyName + ZEROORONE(TypeAnnotation) + ZEROORONE(Initializer) + ZEROORONE(';'), + '#' + PropertySignatureName + ZEROORONE(TypeAnnotation) + ZEROORONE(Initializer) + ZEROORONE(';'), '#' + "private" + ZEROORONE(TypeAnnotation) + ZEROORONE(Initializer) + ZEROORONE(';'), ZEROORMORE(Annotation) + ZEROORMORE(AccessibilityModifier) + "if" + ZEROORONE(TypeAnnotation) + ZEROORONE(Initializer) + ZEROORONE(';')) attr.action.%1: AddInitTo(%3, %5) @@ -2263,6 +2266,13 @@ rule KeywordMemberFunctionName : ONEOF("return", "set", "continue", "break", + "const", + "let", + "var", + "if", + "else", + "for", + "try", "export") attr.action : BuildIdentifier()