From dba662758192b41648e6c1201083d83926f07783 Mon Sep 17 00:00:00 2001 From: Li Jin Date: Wed, 28 Dec 2022 15:55:15 +0800 Subject: add metamethods checking. --- spec/inputs/import.yue | 7 +-- spec/outputs/destructure.lua | 72 +++++++++++++------------- spec/outputs/import.lua | 16 +++--- spec/outputs/metatable.lua | 29 +++++------ src/yuescript/yue_compiler.cpp | 112 +++++++++++++++++++++++++++++++++++++---- 5 files changed, 169 insertions(+), 67 deletions(-) diff --git a/spec/inputs/import.yue b/spec/inputs/import.yue index af5545d..fe5caf5 100644 --- a/spec/inputs/import.yue +++ b/spec/inputs/import.yue @@ -64,8 +64,9 @@ do import "org.package.module" as function:func, if:ifVar do - import "m" as {: b} - import "m" as {e: f, : c} + import "m" as {: b} + import "m" as {:} + import "m" as {e: f, : c} import "m" as {c: d} - import "m" as {g, {: i}} + import "m" as {g, {: i}} diff --git a/spec/outputs/destructure.lua b/spec/outputs/destructure.lua index 8953a2a..cb0eb3f 100644 --- a/spec/outputs/destructure.lua +++ b/spec/outputs/destructure.lua @@ -288,28 +288,31 @@ do end) end end - local _obj_0 = tb - local mtx, y, zItem = getmetatable(_obj_0.x), _obj_0.y, _obj_0.z - if mtx == nil then - mtx = { } - end - local index = getmetatable(_obj_0).__index - if index == nil then - index = function() - return nil + local mtx, y, zItem, index + do + local _obj_0 = tb + mtx, y, zItem = getmetatable(_obj_0.x), _obj_0.y, _obj_0.z + if mtx == nil then + mtx = { } + end + index = getmetatable(_obj_0).__index + if index == nil then + index = function() + return nil + end end end do local _tmp_0 do - local _obj_1 = getmetatable(tb) - _tmp_0 = _obj_1.func + local _obj_0 = getmetatable(tb) + _tmp_0 = _obj_0.func end if _tmp_0 == nil then do - local _obj_1 = item - if _obj_1 ~= nil then - _tmp_0 = _obj_1.defVal + local _obj_0 = item + if _obj_0 ~= nil then + _tmp_0 = _obj_0.defVal end end end @@ -455,9 +458,12 @@ do end end do - local _obj_0 = tb - local value = _obj_0[name] - local value_meta = getmetatable(_obj_0)[name] + local value, value_meta + do + local _obj_0 = tb + value = _obj_0[name] + value_meta = getmetatable(_obj_0)[name] + end end do local tostring, add @@ -492,16 +498,15 @@ do end do local tb = { } - local _obj_0 = tb do local _tmp_0 = c() - local v2 = _obj_0[_tmp_0] + local v2 = tb[_tmp_0] end local v1 do - local _obj_1 = getmetatable(_obj_0) + local _obj_0 = getmetatable(tb) local _tmp_1 = a + b - v1 = _obj_1[_tmp_1] + v1 = _obj_0[_tmp_1] end end do @@ -575,16 +580,15 @@ do local _tab_0 = type(_exp_0) _tab_0 = "table" == _tab_0 or "userdata" == _tab_0 if _tab_0 then - local _obj_0 = _exp_0 do - local _obj_1 = _obj_0.c - local _type_0 = type(_obj_1) + local _obj_0 = _exp_0.c + local _type_0 = type(_obj_0) if "table" == _type_0 or "userdata" == _type_0 then do - local _obj_2 = getmetatable(_obj_1) - local _type_1 = type(_obj_2) + local _obj_1 = getmetatable(_obj_0) + local _type_1 = type(_obj_1) if "table" == _type_1 or "userdata" == _type_1 then - meta_field = _obj_2["abc"] + meta_field = _obj_1["abc"] end end end @@ -593,19 +597,19 @@ do meta_field = "def" end do - local _obj_1 = getmetatable(_obj_0) + local _obj_0 = getmetatable(_exp_0) do - local _obj_2 = _obj_1[ [[any string]]] - local _type_0 = type(_obj_2) + local _obj_1 = _obj_0[ [[any string]]] + local _type_0 = type(_obj_1) if "table" == _type_0 or "userdata" == _type_0 then - abc = _obj_2.d + abc = _obj_1.d end end do - local _obj_2 = _obj_1['str'] - local _type_0 = type(_obj_2) + local _obj_1 = _obj_0['str'] + local _type_0 = type(_obj_1) if "table" == _type_0 or "userdata" == _type_0 then - def = _obj_2.e + def = _obj_1.e end end if abc == nil then diff --git a/spec/outputs/import.lua b/spec/outputs/import.lua index edb7c44..f8d6ef0 100644 --- a/spec/outputs/import.lua +++ b/spec/outputs/import.lua @@ -89,14 +89,18 @@ do end end do - local b = getmetatable(require("m")).__a - local _obj_0 = require("m") - local f = _obj_0.e - local c = getmetatable(_obj_0).__a + local b = getmetatable(require("m")).__gc + local index = getmetatable(require("m")).__index + local f, c + do + local _obj_0 = require("m") + f = _obj_0.e + c = getmetatable(_obj_0).__pairs + end local d = require("m").c local g, i do - local _obj_1 = require("m") - g, i = _obj_1[1], getmetatable(_obj_1[2]).__h + local _obj_0 = require("m") + g, i = _obj_0[1], getmetatable(_obj_0[2]).__close end end diff --git a/spec/outputs/metatable.lua b/spec/outputs/metatable.lua index 937136f..907a584 100644 --- a/spec/outputs/metatable.lua +++ b/spec/outputs/metatable.lua @@ -38,28 +38,29 @@ getmetatable(a).__add = function(x, y) return x + y end do - local _obj_0 = a - local new = _obj_0.new + local new = a.new local close, closeA do - local _obj_1 = getmetatable(_obj_0) - close, closeA = _obj_1.__close, _obj_1.__close + local _obj_0 = getmetatable(a) + close, closeA = _obj_0.__close, _obj_0.__close end print(new, close, closeA) end do local x, new, var, close, closeA, num, add, sub - local _obj_0, _obj_1 - x, _obj_0, _obj_1 = 123, a.b.c, func() - new, var = _obj_0.new, _obj_0.var - do - local _obj_2 = getmetatable(_obj_0) - close, closeA = _obj_2.__close, _obj_2.__close - end - num = _obj_1.num do - local _obj_2 = getmetatable(_obj_1) - add, sub = _obj_2.__add, _obj_2.__sub + local _obj_0, _obj_1 + x, _obj_0, _obj_1 = 123, a.b.c, func() + new, var = _obj_0.new, _obj_0.var + do + local _obj_2 = getmetatable(_obj_0) + close, closeA = _obj_2.__close, _obj_2.__close + end + num = _obj_1.num + do + local _obj_2 = getmetatable(_obj_1) + add, sub = _obj_2.__add, _obj_2.__sub + end end end setmetatable(a.b, { }) diff --git a/src/yuescript/yue_compiler.cpp b/src/yuescript/yue_compiler.cpp index 0d674f8..42310b2 100644 --- a/src/yuescript/yue_compiler.cpp +++ b/src/yuescript/yue_compiler.cpp @@ -60,7 +60,18 @@ namespace yue { typedef std::list str_list; -const std::string_view version = "0.15.18"sv; +static std::unordered_set Metamethods = { + "add"s, "sub"s, "mul"s, "div"s, "mod"s, + "pow"s, "unm"s, "concat"s, "len"s, "eq"s, + "lt"s, "le"s, "index"s, "newindex"s, "call"s, + "gc"s, "mode"s, "tostring"s, "metatable"s, // Lua 5.1 + "pairs"s, "ipairs"s, // Lua 5.2 + "name"s, "idiv"s, "band"s, "bor"s, "bxor"s, + "bnot"s, "shl"s, "shr"s, // Lua 5.3 ipairs deprecated + "close"s // Lua 5.4 +}; + +const std::string_view version = "0.15.19"sv; const std::string_view extension = "yue"sv; class YueCompilerImpl { @@ -1089,6 +1100,46 @@ private: return str; } + void checkMetamethod(const std::string& name, ast_node* x) { + if (Metamethods.find(name) == Metamethods.end()) { + throw std::logic_error(_info.errorMessage("invalid metamethod name"sv, x)); + } + int target = getLuaTarget(x); + switch (target) { + case 501: goto metamethod51; + case 502: goto metamethod52; + case 503: { + if (name == "ipairs"sv) { + throw std::logic_error(_info.errorMessage("metamethod is deprecated since Lua 5.3"sv, x)); + } + goto metamethod53; + } + case 504: { + if (name == "ipairs"sv) { + throw std::logic_error(_info.errorMessage("metamethod is not supported since Lua 5.4"sv, x)); + } + goto metamethod54; + } + } + metamethod51: + if (name == "pairs"sv || name == "ipairs"sv) { + throw std::logic_error(_info.errorMessage("metamethod is not supported until Lua 5.2"sv, x)); + } + metamethod52: + if (name == "name"sv || name == "idiv"sv || + name == "band"sv || name == "bor"sv || + name == "bxor"sv || name == "bnot"sv || + name == "shl"sv || name == "shr"sv) { + throw std::logic_error(_info.errorMessage("metamethod is not supported until Lua 5.3"sv, x)); + } + metamethod53: + if (name == "close"sv) { + throw std::logic_error(_info.errorMessage("metamethod is not supported until Lua 5.4"sv, x)); + } + metamethod54: + return; + } + void transformStatement(Statement_t* statement, str_list& out) { auto x = statement; if (statement->appendix) { @@ -1825,11 +1876,32 @@ private: if (info.destructures.empty()) { transformAssignmentCommon(assignment, out); } else { + auto x = assignment; str_list temp; + if (info.extraScope) { + str_list defs; + for (auto& destruct : info.destructures) { + for (auto& item : destruct.items) { + if (!item.targetVar.empty()) { + if (!isDefined(item.targetVar)) { + defs.push_back(item.targetVar); + } + } + } + } + if (!defs.empty()) { + for (const auto& def : defs) { + checkConst(def, x); + addToScope(def); + } + temp.push_back(indent() + "local "s + join(defs, ", "sv) + nll(x)); + } + temp.push_back(indent() + "do"s + nll(x)); + pushScope(); + } if (info.assignment) { transformAssignmentCommon(info.assignment, temp); } - auto x = assignment; for (auto& destruct : info.destructures) { std::list, ast_ptr>> leftPairs; bool extraScope = false; @@ -1861,7 +1933,7 @@ private: } } if (!isNil) { - auto stmt = toAst(pair.targetVar + "=nil if "s + pair.targetVar + "==nil", pair.defVal); + auto stmt = toAst(pair.targetVar + "=nil if "s + pair.targetVar + "==nil"s, pair.defVal); auto defAssign = stmt->content.as(); auto assign = defAssign->action.as(); assign->values.clear(); @@ -2035,6 +2107,10 @@ private: temp.push_back(clearBuf()); } } + if (info.extraScope) { + popScope(); + temp.push_back(indent() + "end"s + nlr(x)); + } out.push_back(join(temp)); } } @@ -2247,6 +2323,7 @@ private: } auto mp = static_cast(pair); auto name = _parser.toString(mp->name); + checkMetamethod(name, mp->name); _buf << "__"sv << name << ':' << name; auto newPairDef = toAst(clearBuf(), pair); newPairDef->defVal.set(defVal); @@ -2266,6 +2343,7 @@ private: switch (mp->key->getId()) { case id(): { auto key = _parser.toString(mp->key); + checkMetamethod(key, mp->key); _buf << "__"sv << key; auto newKey = toAst(clearBuf(), mp->key); newPair->key.set(newKey); @@ -2309,10 +2387,12 @@ private: struct DestructureInfo { std::list destructures; ast_ptr assignment; + bool extraScope = false; }; DestructureInfo extractDestructureInfo(ExpListAssign_t* assignment, bool varDefOnly, bool optional) { auto x = assignment; + bool extraScope = false; std::list destructs; if (!assignment->action.is()) return {destructs, nullptr}; auto exprs = assignment->expList->exprs.objects(); @@ -2367,6 +2447,7 @@ private: auto mvp = static_cast(item); auto mp = mvp->pair.get(); auto name = _parser.toString(mp->name); + checkMetamethod(name, mp->name); _buf << "__"sv << name << ':' << name; auto newPairDef = toAst(clearBuf(), item); newPairDef->defVal.set(mvp->defVal); @@ -2381,6 +2462,7 @@ private: switch (mp->key->getId()) { case id(): { auto key = _parser.toString(mp->key); + checkMetamethod(key, mp->key); _buf << "__"sv << key; auto newKey = toAst(clearBuf(), mp->key); newPair->key.set(newKey); @@ -2406,6 +2488,7 @@ private: case id(): { auto mp = static_cast(item); auto name = _parser.toString(mp->name); + checkMetamethod(name, mp->name); _buf << "__"sv << name << ':' << name; auto newPairDef = toAst(clearBuf(), item); subMetaDestruct->values.push_back(newPairDef); @@ -2418,6 +2501,7 @@ private: switch (mp->key->getId()) { case id(): { auto key = _parser.toString(mp->key); + checkMetamethod(key, mp->key); _buf << "__"sv << key; auto newKey = toAst(clearBuf(), mp->key); newPair->key.set(newKey); @@ -2462,12 +2546,16 @@ private: } valueItems.push_back(*j); if (!varDefOnly && !subDestruct->values.empty() && !subMetaDestruct->values.empty()) { - auto objVar = getUnusedName("_obj_"sv); - addToScope(objVar); - valueItems.pop_back(); - valueItems.push_back(toAst(objVar, *j)); - exprs.push_back(valueItems.back()); - values.push_back(*j); + auto var = singleVariableFrom(*j, false); + if (var.empty() || !isLocal(var)) { + auto objVar = getUnusedName("_obj_"sv); + addToScope(objVar); + valueItems.pop_back(); + valueItems.push_back(toAst(objVar, *j)); + exprs.push_back(valueItems.back()); + values.push_back(*j); + extraScope = true; + } } TableLit_t* tabs[] = {subDestruct.get(), subMetaDestruct.get()}; for (auto tab : tabs) { @@ -2573,7 +2661,7 @@ private: } } popScope(); - return {std::move(destructs), newAssignment}; + return {std::move(destructs), newAssignment, extraScope}; } void transformAssignmentCommon(ExpListAssign_t* assignment, str_list& out) { @@ -5513,6 +5601,7 @@ private: throw std::logic_error(_info.errorMessage("too many metatable declarations"sv, mp->name)); } auto name = _parser.toString(mp->name); + checkMetamethod(name, mp->name); _buf << "__"sv << name << ':' << name; auto newPair = toAst(clearBuf(), item); metatable->pairs.push_back(newPair); @@ -5529,6 +5618,7 @@ private: switch (mp->key->getId()) { case id(): { auto key = _parser.toString(mp->key); + checkMetamethod(key, mp->key); _buf << "__"sv << key; auto newKey = toAst(clearBuf(), mp->key); newPair->key.set(newKey); @@ -6739,6 +6829,7 @@ private: case id(): { auto mtPair = static_cast(keyValue); auto nameStr = _parser.toString(mtPair->name); + checkMetamethod(nameStr, mtPair->name); ref.set(toAst("__"s + nameStr + ':' + nameStr, keyValue)); keyValue = ref.get(); break; @@ -6748,6 +6839,7 @@ private: auto normal_pair = keyValue->new_ptr(); if (auto name = mtPair->key.as()) { auto nameStr = _parser.toString(name); + checkMetamethod(nameStr, name); normal_pair->key.set(toAst("__"s + nameStr, keyValue)); } else if (auto str = mtPair->key.as()) { normal_pair->key.set(newExp(str, str)); -- cgit v1.2.3-55-g6feb