diff --git a/src/presets.lua b/src/presets.lua index 4d169fe..00ed469 100644 --- a/src/presets.lua +++ b/src/presets.lua @@ -37,7 +37,7 @@ return { { Name = "Vmify"; Settings = { - + }; }, { @@ -55,6 +55,27 @@ return { }, } }; + ["Vmify"] = { + -- The default LuaVersion is Lua51 + LuaVersion = "Lua51"; + -- For minifying no VarNamePrefix is applied + VarNamePrefix = ""; + -- Name Generator for Variables that look like this: IlI1lI1l + NameGenerator = "MangledShuffled"; + -- No pretty printing + PrettyPrint = false; + -- Seed is generated based on current time + Seed = 0; + -- Obfuscation steps + Steps = { + { + Name = "Vmify"; + Settings = { + + }; + }, + } + }; ["Medium"] = { -- The default LuaVersion is Lua51 LuaVersion = "Lua51"; @@ -83,7 +104,7 @@ return { { Name = "Vmify"; Settings = { - + }; }, { @@ -126,7 +147,7 @@ return { { Name = "Vmify"; Settings = { - + }; }, { @@ -144,7 +165,7 @@ return { { Name = "Vmify"; Settings = { - + }; }, { diff --git a/src/prometheus/compiler/compiler.lua b/src/prometheus/compiler/compiler.lua index a3c714e..7857fd6 100644 --- a/src/prometheus/compiler/compiler.lua +++ b/src/prometheus/compiler/compiler.lua @@ -33,7 +33,7 @@ function Compiler:new() registerVars = {}; VAR_REGISTER = newproxy(false); - RETURN_ALL = newproxy(false); + RETURN_ALL = newproxy(false); POS_REGISTER = newproxy(false); RETURN_REGISTER = newproxy(false); UPVALUE = newproxy(false); @@ -86,7 +86,7 @@ function Compiler:setActiveBlock(block) end function Compiler:addStatement(statement, writes, reads, usesUpvals) - if(self.activeBlock.advanceToNextBlock) then + if(self.activeBlock.advanceToNextBlock) then table.insert(self.activeBlock.statements, { statement = statement, writes = lookupify(writes), @@ -123,7 +123,7 @@ function Compiler:compile(ast) local _, setmetatableVar = newGlobalScope:resolve("setmetatable"); local _, getmetatableVar = newGlobalScope:resolve("getmetatable"); local _, selectVar = newGlobalScope:resolve("select"); - + psc:addReferenceToHigherScope(newGlobalScope, getfenvVar, 2); psc:addReferenceToHigherScope(newGlobalScope, tableVar); psc:addReferenceToHigherScope(newGlobalScope, unpackVar); @@ -331,7 +331,7 @@ function Compiler:getCreateClosureVar(argCount) local var = Ast.AssignmentVariable(self.scope, self.scope:addVariable()); local createClosureScope = Scope:new(self.scope); local createClosureSubScope = Scope:new(createClosureScope); - + local createClosurePosArg = createClosureScope:addVariable(); local createClosureUpvalsArg = createClosureScope:addVariable(); local createClosureProxyObject = createClosureScope:addVariable(); @@ -384,7 +384,7 @@ function Compiler:getCreateClosureVar(argCount) } end - + local var = self.createClosureVars[argCount].var; return var.scope, var.id; end @@ -419,7 +419,7 @@ function Compiler:createUpvaluesGcFunc() local ifScope = Scope:new(whileScope); ifScope:addReferenceToHigherScope(self.scope, self.upvaluesReferenceCountsTable, 1); ifScope:addReferenceToHigherScope(self.scope, self.upvaluesTable, 1); - + return Ast.FunctionLiteralExpression({Ast.VariableExpression(scope, selfVar)}, Ast.Block({ Ast.LocalVariableDeclaration(scope, {iteratorVar, valueVar}, {Ast.NumberExpression(1), Ast.IndexExpression(Ast.VariableExpression(scope, selfVar), Ast.NumberExpression(1))}), @@ -674,7 +674,7 @@ function Compiler:emitContainerFuncBody() self.whileScope:addReferenceToHigherScope(self.containerFuncScope, self.returnVar, 1); self.whileScope:addReferenceToHigherScope(self.containerFuncScope, self.posVar); - + self.containerFuncScope:addReferenceToHigherScope(self.scope, self.unpackVar); local declarations = { @@ -736,7 +736,7 @@ function Compiler:allocRegister(isVar) return self.RETURN_REGISTER; end end - + local id = 0; if self.usedRegisters < MAX_REGS * MAX_REGS_MUL then @@ -921,7 +921,7 @@ end function Compiler:setPos(scope, val) if not val then - + local v = Ast.IndexExpression(self:env(scope), randomStrings.randomStringNode(math.random(12, 14))); --Ast.NilExpression(); scope:addReferenceToHigherScope(self.containerFuncScope, self.posVar); return Ast.AssignmentStatement({Ast.AssignmentVariable(self.containerFuncScope, self.posVar)}, {v}); @@ -979,7 +979,7 @@ function Compiler:compileTopNode(node) AstKind.TopNode, } -- Collect Upvalues - visitast(node, function(node, data) + visitast(node, function(node, data) if node.kind == AstKind.Block then node.scope.__depth = data.functionData.depth; end @@ -1100,7 +1100,7 @@ function Compiler:compileFunction(node, funcDepth) self:setActiveBlock(oldActiveBlock); local scope = self.activeBlock.scope; - + local retReg = self:allocRegister(false); local isVarargFunction = #node.args > 0 and node.args[#node.args].kind == AstKind.VarargExpression; @@ -1250,7 +1250,7 @@ function Compiler:compileStatement(statement, funcDepth) for i, reg in ipairs(regs) do self:freeRegister(reg, false); end - + return; end @@ -1283,13 +1283,13 @@ function Compiler:compileStatement(statement, funcDepth) for i, reg in ipairs(regs) do self:freeRegister(reg, false); end - + return; end -- Local Function Declaration if(statement.kind == AstKind.LocalFunctionDeclaration) then - + if(self:isUpvalue(statement.scope, statement.id)) then local varReg = self:getVarRegister(statement.scope, statement.id, funcDepth, nil); scope:addReferenceToHigherScope(self.scope, self.allocUpvalFunction); @@ -1478,7 +1478,7 @@ function Compiler:compileStatement(statement, funcDepth) local innerBlock = self:createBlock(); self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(Ast.AndExpression(self:register(scope, conditionReg), Ast.NumberExpression(innerBlock.id)), Ast.NumberExpression(nextBlock.id))), {self.POS_REGISTER}, {conditionReg}, false); - + self:freeRegister(conditionReg, false); self:setActiveBlock(innerBlock); @@ -1497,7 +1497,7 @@ function Compiler:compileStatement(statement, funcDepth) end local scope = self.activeBlock.scope; self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(Ast.AndExpression(self:register(scope, conditionReg), Ast.NumberExpression(innerBlock.id)), Ast.NumberExpression(nextBlock.id))), {self.POS_REGISTER}, {conditionReg}, false); - + self:freeRegister(conditionReg, false); self:setActiveBlock(innerBlock); @@ -1552,28 +1552,39 @@ function Compiler:compileStatement(statement, funcDepth) if(statement.kind == AstKind.RepeatStatement) then local innerBlock = self:createBlock(); local finalBlock = self:createBlock(); - local checkBlock = self:createBlock(); - statement.__start_block = checkBlock; + statement.__start_block = innerBlock; statement.__final_block = finalBlock; - local conditionReg = self:compileExpression(statement.condition, funcDepth, 1)[1]; self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.NumberExpression(innerBlock.id)), {self.POS_REGISTER}, {}, false); - self:freeRegister(conditionReg, false); - self:setActiveBlock(innerBlock); - self:compileBlock(statement.body, funcDepth); - local scope = self.activeBlock.scope - self:addStatement(self:setPos(scope, checkBlock.id), {self.POS_REGISTER}, {}, false); - self:setActiveBlock(checkBlock); + + -- Compile body statements without automatic variable cleanup + -- self:compileBlock(statement.body, funcDepth); + for i, stat in ipairs(statement.body.statements) do + self:compileStatement(stat, funcDepth); + end; + local scope = self.activeBlock.scope; - local conditionReg = self:compileExpression(statement.condition, funcDepth, 1)[1]; - self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(Ast.AndExpression(self:register(scope, conditionReg), Ast.NumberExpression(finalBlock.id)), Ast.NumberExpression(innerBlock.id))), {self.POS_REGISTER}, {conditionReg}, false); + -- Evaluate condition (can access body's local variables) + local conditionReg = (self:compileExpression(statement.condition, funcDepth, 1))[1]; + self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.OrExpression(Ast.AndExpression(self:register(scope, conditionReg), Ast.NumberExpression(finalBlock.id)), Ast.NumberExpression(innerBlock.id))), { self.POS_REGISTER }, { conditionReg }, false); self:freeRegister(conditionReg, false); - self:setActiveBlock(finalBlock); + -- Clean up body's local variables + for id, name in ipairs(statement.body.scope.variables) do + local varReg = self:getVarRegister(statement.body.scope, id, funcDepth, nil); + if self:isUpvalue(statement.body.scope, id) then + scope:addReferenceToHigherScope(self.scope, self.freeUpvalueFunc); + self:addStatement(self:setRegister(scope, varReg, Ast.FunctionCallExpression(Ast.VariableExpression(self.scope, self.freeUpvalueFunc), { self:register(scope, varReg) })), { varReg }, { varReg }, false); + else + self:addStatement(self:setRegister(scope, varReg, Ast.NilExpression()), { varReg }, {}, false); + end; + self:freeRegister(varReg, true); + end; + self:setActiveBlock(finalBlock); return; - end + end; -- For Statement if(statement.kind == AstKind.ForStatement) then @@ -1602,7 +1613,7 @@ function Compiler:compileStatement(statement, funcDepth) local tmpReg = self:allocRegister(false); self:addStatement(self:setRegister(scope, tmpReg, Ast.NumberExpression(0)), {tmpReg}, {}, false); local incrementIsNegReg = self:allocRegister(false); - self:addStatement(self:setRegister(scope, incrementIsNegReg, Ast.LessThanExpression(self:register(scope, incrementReg), self:register(scope, tmpReg))), {incrementIsNegReg}, {incrementReg, tmpReg}, false); + self:addStatement(self:setRegister(scope, incrementIsNegReg, Ast.LessThanExpression(self:register(scope, incrementReg), self:register(scope, tmpReg))), {incrementIsNegReg}, {incrementReg, tmpReg}, false); self:freeRegister(tmpReg); local currentReg = self:allocRegister(true); @@ -1646,10 +1657,10 @@ function Compiler:compileStatement(statement, funcDepth) self:addStatement(self:setRegister(scope, varReg, self:register(scope, currentReg)), {varReg}, {currentReg}, false); end - + self:compileBlock(statement.body, funcDepth); self:addStatement(self:setRegister(scope, self.POS_REGISTER, Ast.NumberExpression(checkBlock.id)), {self.POS_REGISTER}, {}, false); - + self.registers[self.POS_REGISTER] = self.VAR_REGISTER; self:freeRegister(finalReg); self:freeRegister(incrementIsNegReg); @@ -1983,7 +1994,7 @@ function Compiler:compileExpression(expression, funcDepth, numReturns) retRegs[i] = self:allocRegister(false); end end - + local regs = {}; local args = {}; for i, expr in ipairs(expression.args) do @@ -2005,13 +2016,13 @@ function Compiler:compileExpression(expression, funcDepth, numReturns) else if(numReturns > 1) then local tmpReg = self:allocRegister(false); - + self:addStatement(self:setRegister(scope, tmpReg, Ast.TableConstructorExpression{Ast.TableEntry(Ast.FunctionCallExpression(self:register(scope, baseReg), args))}), {tmpReg}, {baseReg, unpack(regs)}, true); - + for i, reg in ipairs(retRegs) do self:addStatement(self:setRegister(scope, reg, Ast.IndexExpression(self:register(scope, tmpReg), Ast.NumberExpression(i))), {reg}, {tmpReg}, false); end - + self:freeRegister(tmpReg, false); else self:addStatement(self:setRegister(scope, retRegs[1], Ast.FunctionCallExpression(self:register(scope, baseReg), args)), {retRegs[1]}, {baseReg, unpack(regs)}, true); @@ -2022,7 +2033,7 @@ function Compiler:compileExpression(expression, funcDepth, numReturns) for i, reg in ipairs(regs) do self:freeRegister(reg, false); end - + return retRegs; end @@ -2085,7 +2096,7 @@ function Compiler:compileExpression(expression, funcDepth, numReturns) for i, reg in ipairs(regs) do self:freeRegister(reg, false); end - + return retRegs; end @@ -2175,7 +2186,7 @@ function Compiler:compileExpression(expression, funcDepth, numReturns) return regs; end - if(expression.kind == AstKind.OrExpression) then + if(expression.kind == AstKind.OrExpression) then local posState = self.registers[self.POS_REGISTER]; self.registers[self.POS_REGISTER] = self.VAR_REGISTER; @@ -2234,7 +2245,7 @@ function Compiler:compileExpression(expression, funcDepth, numReturns) return regs; end - if(expression.kind == AstKind.AndExpression) then + if(expression.kind == AstKind.AndExpression) then local posState = self.registers[self.POS_REGISTER]; self.registers[self.POS_REGISTER] = self.VAR_REGISTER; @@ -2254,7 +2265,7 @@ function Compiler:compileExpression(expression, funcDepth, numReturns) self:addStatement(self:copyRegisters(scope, {tmpReg}, {self.POS_REGISTER}), {tmpReg}, {self.POS_REGISTER}, false); end - + local lhsReg = self:compileExpression(expression.lhs, funcDepth, 1)[1]; if(expression.rhs.isConstant) then local rhsReg = self:compileExpression(expression.rhs, funcDepth, 1)[1]; diff --git a/tests/repeat-test.lua b/tests/repeat-test.lua new file mode 100644 index 0000000..8111f09 --- /dev/null +++ b/tests/repeat-test.lua @@ -0,0 +1,210 @@ +--============================================================ +-- Repeat–Until Semantics Test Suite +-- Target: Vmify +-- Author: Zaenalos +-- Purpose: Validate correct scope, control flow, and condition +--============================================================ + +local TEST_ID = 0 + +local function test(name, fn) + TEST_ID = TEST_ID + 1 + local ok, err = pcall(fn) + if not ok then + error(string.format( + "[FAIL] #%d %s\n → %s", + TEST_ID, name, err + ), 2) + end + print(string.format("[PASS] #%d %s", TEST_ID, name)) +end + +--============================================================ +-- Test 1: Basic repeat-until with local in condition scope +--============================================================ +test("Basic local visibility in until condition", function() + local count = 0 + repeat + local x = count + count = count + 1 + until x == 5 + + assert(count == 6, "count should be 6") +end) + +--============================================================ +-- Test 2: Locals do not leak outside repeat scope +--============================================================ +test("Repeat locals do not escape scope", function() + repeat + local hidden = 123 + until true + + assert(_G.hidden == nil, "local leaked into global scope") +end) + +--============================================================ +-- Test 3: Immediate exit still executes body once +--============================================================ +test("Immediate termination executes once", function() + local iters = 0 + repeat + iters = iters + 1 + until true + + assert(iters == 1, "repeat body must run exactly once") +end) + +--============================================================ +-- Test 4: Multiple locals and arithmetic correctness +--============================================================ +test("Multiple locals and arithmetic", function() + local i = 0 + local c + repeat + local a = i + local b = a * 2 + c = a + b + i = i + 1 + until c >= 15 + + assert(i == 6, "i should be 6 when c reaches 15") +end) + +--============================================================ +-- Test 5: Nested repeat-until with independent scopes +--============================================================ +test("Nested repeat loops", function() + local outer = 0 + local total_inner = 0 + + repeat + local inner = 0 + repeat + total_inner = total_inner + 1 + inner = inner + 1 + until inner == 3 + outer = outer + 1 + until outer == 3 + + assert(outer == 3, "outer loop count mismatch") + assert(total_inner == 9, "inner loop count mismatch") +end) + +--============================================================ +-- Test 6: Function call inside until condition +--============================================================ +test("Function call in until condition", function() + local function check(x) + return x >= 3 + end + + local k = 0 + local current + repeat + current = k + k = k + 1 + until check(current) + + assert(k == 4, "termination point incorrect") +end) + +--============================================================ +-- Test 7: Upvalue capture inside repeat +--============================================================ +test("Upvalue capture from repeat body", function() + local f + repeat + local x = 42 + f = function() + return x + end + until true + + assert(f() == 42, "upvalue incorrectly captured") +end) + +--============================================================ +-- Test 8: Side effects inside until condition (corrected) +--============================================================ +test("Side effects in until condition", function() + local log = {} + local i = 0 + + repeat + i = i + 1 + until (function() + log[#log + 1] = i -- explicit side effect + return i >= 3 + end)() + + assert(#log == 3, "side effects count mismatch") + assert(log[3] == 3, "final side effect value incorrect") +end) + +--============================================================ +-- Test 9: Break skips until condition +--============================================================ +test("Break bypasses until evaluation", function() + local evaluated = false + + repeat + break + until (function() + evaluated = true + return true + end)() + + assert(evaluated == false, "`until` condition evaluated after break") +end) + +--============================================================ +-- Test 10: Slot reuse and shadowing correctness +--============================================================ +test("Local shadowing and slot reuse", function() + local sum = 0 + local i = 0 + + repeat + local v = i + sum = sum + v + do + local v = v * 2 + sum = sum + v + end + i = i + 1 + until i == 3 + + assert(sum == (0+0) + (1+2) + (2+4), "slot corruption detected") +end) + +--============================================================ +-- Test 11: Table mutation inside repeat +--============================================================ +test("Table writes inside repeat", function() + local t = {} + local i = 1 + + repeat + t[i] = i * i + i = i + 1 + until i > 5 + + assert(#t == 5, "table size incorrect") + assert(t[5] == 25, "table content incorrect") +end) + +--============================================================ +-- Test 12: Deterministic non-linear termination +--============================================================ +test("Non-linear termination logic", function() + local x = 1 + repeat + x = (x * 3 + 1) % 17 + until x == 0 + + assert(x == 0, "non-linear termination failed") +end) + +--============================================================ +print("\nAll repeat-until tests passed successfully.")