Created
August 17, 2014 02:21
-
-
Save MDFL64/228474c4ec949390cddf to your computer and use it in GitHub Desktop.
o god why
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if CLIENT then return end | |
/* jit.util library functions | |
funcbc = function: builtin#142 | |
funck = function: builtin#143 | |
funcinfo = function: builtin#141 | |
traceinfo = function: builtin#145 | |
tracek = function: builtin#147 | |
tracesnap = function: builtin#148 | |
traceir = function: builtin#146 | |
tracemc = function: builtin#149 | |
ircalladdr = function: builtin#151 | |
traceexitstub = function: builtin#150 | |
funcuvname = function: builtin#144 | |
*/ | |
local MAX_EXECUTION_TIME = .1 | |
local OPCODE_NAMES = { | |
"ISGE","ISLE","ISGT", | |
"ISEQV","ISNEV","ISEQS","ISNES","ISEQN","ISNEN","ISEQP","ISNEP", | |
"ISTC","ISFC","IST","ISF", | |
"MOV","NOT","UNM","LEN", | |
"ADDVN","SUBVN","MULVN","DIVVN","MODVN", | |
"ADDNV","SUBNV","MULNV","DIVNV","MODNV", | |
"ADDVV","SUBVV","MULVV","DIVVV","MODVV", | |
"POW","CAT", | |
"KSTR","KCDATA","KSHORT","KNUM","KPRI","KNIL", | |
"UGET","USETV","USETS","USETN","USETP","UCLO","FNEW", | |
"TNEW","TDUP","GGET","GSET","TGETV","TGETS","TGETB","TSETV","TSETS","TSETB","TSETM", | |
"CALLM","CALL","CALLMT","CALLT","ITERC","ITERN","VARG","ISNEXT", | |
"RETM","RET","RET0","RET1", | |
"FORI","JFORI", | |
"FORL","IFORL","JFORL", | |
"ITERL","IITERL","JITERL", | |
"LOOP","ILOOP","JLOOP", | |
"JMP", | |
"FUNCF","IFUNCF","JFUNCF","FUNCV","IFUNCV","JFUNCV","FUNCC","FUNCCW" | |
} | |
OPCODE_NAMES[0]="ISLT" | |
local function INS_GET_A(ins) | |
return bit.band(bit.rshift(ins,8),255) | |
end | |
local function INS_GET_B(ins) | |
return bit.rshift(ins,24) | |
end | |
local function INS_GET_C(ins) | |
return bit.band(bit.rshift(ins,16),255) | |
end | |
local function INS_GET_D(ins) | |
return bit.rshift(ins,16) | |
end | |
local PRIMTYPES = {} | |
PRIMTYPES[0]=nil | |
PRIMTYPES[1]=false | |
PRIMTYPES[2]=true | |
function func2scode(func) | |
//Read code | |
local code = {} | |
local pc=0 | |
while true do | |
local ins,m = jit.util.funcbc(func,pc) | |
if (ins==nil) then break end | |
code[pc]= ins | |
pc=pc+1 | |
end | |
//Read constants | |
local consts = {} | |
local cc=0 | |
while true do | |
local const = jit.util.funck(func,cc) | |
if (const==nil) then break end | |
consts[cc]=const | |
cc=cc+1 | |
end | |
cc=-1 | |
while true do | |
local const = jit.util.funck(func,cc) | |
if (const==nil) then break end | |
if type(const)=="proto" then | |
const = func2scode(const) | |
end | |
consts[cc]=const | |
cc=cc-1 | |
end | |
//Get function info | |
local info = jit.util.funcinfo(func) | |
return {___IS_SLAG_CODE___=true,code=code,consts=consts,arg_count=info.params} | |
end | |
function scode2blob(scodeTab) | |
end | |
local function formatError(err) | |
return err:gsub("^[^:]+:%d+: ","",1):gsub("^%l",string.upper):gsub("[^!?.]$",function(s) return s.."." end) | |
end | |
function VM(scode) | |
local vm={} | |
vm.data={} | |
vm.code=scode.code | |
vm.consts=scode.consts | |
vm.arg_count=scode.arg_count | |
vm.pc = 0 | |
vm.multres = 0 | |
vm.vargs={} | |
function vm:step() | |
if self.called_vm then | |
local status, error_or_retval = self.called_vm:step() | |
if status==true then | |
//handle returned stuff | |
elseif status==false then | |
return status, error_or_retval | |
end | |
end | |
local ins = self.code[self.pc] | |
if !ins then return false, "Reached end of code, no return!" end | |
local op = bit.band(ins,255) | |
local a = INS_GET_A(ins) | |
/* | |
* GreaterThan/LessThan tests. Catch errors because they happen a lot when comparing disparate types. | |
*/ | |
if op==0 then //ISLT | |
local d = INS_GET_D(ins) | |
local _,err = pcall(function() if !(self.data[a]<self.data[d]) then self.pc=self.pc+1 end end) | |
if err then return false, formatError(err) end | |
elseif op==1 then //ISGE | |
local d = INS_GET_D(ins) | |
local _,err = pcall(function() if !(self.data[a]>=self.data[d]) then self.pc=self.pc+1 end end) | |
if err then return false, formatError(err) end | |
elseif op==2 then //ISLE | |
local d = INS_GET_D(ins) | |
local _,err = pcall(function() if !(self.data[a]<=self.data[d]) then self.pc=self.pc+1 end end) | |
if err then return false, formatError(err) end | |
elseif op==3 then //ISGT | |
local d = INS_GET_D(ins) | |
local _,err = pcall(function() if !(self.data[a]>self.data[d]) then self.pc=self.pc+1 end end) | |
if err then return false, formatError(err) end | |
/* | |
* Equivalence tests. Don't catch errors, they're probably the result of shitty metamethod implementations. | |
*/ | |
elseif op==4 then //ISEQV | |
local d = INS_GET_D(ins) | |
if !(self.data[a]==self.data[d]) then self.pc=self.pc+1 end | |
elseif op==5 then //ISNEV | |
local d = INS_GET_D(ins) | |
if !(self.data[a]!=self.data[d]) then self.pc=self.pc+1 end | |
elseif op==6 then //ISEQS | |
local str = self:getConst(INS_GET_D(ins)) | |
if !(self.data[a]==str) then self.pc=self.pc+1 end | |
elseif op==7 then //ISNES | |
local str = self:getConst(INS_GET_D(ins)) | |
if !(self.data[a]!=str) then self.pc=self.pc+1 end | |
elseif op==8 then //ISEQN | |
local num = self:getConstNum(INS_GET_D(ins)) | |
if !(self.data[a]==num) then self.pc=self.pc+1 end | |
elseif op==9 then //ISNEN | |
local num = self:getConstNum(INS_GET_D(ins)) | |
if !(self.data[a]!=num) then self.pc=self.pc+1 end | |
elseif op==10 then //ISEQP | |
local pri = PRIMTYPES[INS_GET_D(ins)] | |
if !(self.data[a]==pri) then self.pc=self.pc+1 end | |
elseif op==11 then //ISNEP | |
local pri = PRIMTYPES[INS_GET_D(ins)] | |
if !(self.data[a]!=pri) then self.pc=self.pc+1 end | |
/* | |
* Unary tests. Don't catch errors, these are dead simple and should never generate them. | |
*/ | |
elseif op==12 then //ISTC | |
local d = self.data[INS_GET_D(ins)] | |
if d then | |
self.data[a]=d | |
else | |
self.pc=self.pc+1 | |
end | |
elseif op==13 then //ISFC | |
local d = self.data[INS_GET_D(ins)] | |
if !d then | |
self.data[a]=d | |
else | |
self.pc=self.pc+1 | |
end | |
elseif op==14 then //IST | |
local d = self.data[INS_GET_D(ins)] | |
if !d then | |
self.pc=self.pc+1 | |
end | |
elseif op==15 then //ISF | |
local d = self.data[INS_GET_D(ins)] | |
if d then | |
self.pc=self.pc+1 | |
end | |
/* | |
* Unary Operators. Catch errors for negate and length ops. | |
*/ | |
elseif op==16 then //MOV | |
local src = self.data[INS_GET_D(ins)] | |
self.data[a]= src | |
elseif op==17 then //NOT | |
local src = self.data[INS_GET_D(ins)] | |
self.data[a]= !src | |
elseif op==18 then //UNM | |
local d = INS_GET_D(ins) | |
local _,err = pcall(function() self.data[a]= -self.data[d] end) | |
if err then return false, formatError(err) end | |
elseif op==19 then //LEN | |
local d = INS_GET_D(ins) | |
local _,err = pcall(function() self.data[a]= #self.data[d] end) | |
if err then return false, formatError(err) end | |
/* | |
* Binary Operators. Catch all errors, lots of stuff can go wrong. | |
*/ | |
elseif op==20 then //ADDVN | |
local b = INS_GET_B(ins) | |
local n = self:getConstNum(INS_GET_C(ins)) | |
local _,err = pcall(function() self.data[a]= self.data[b]+n end) | |
if err then return false, formatError(err) end | |
elseif op==21 then //SUBVN | |
local b = INS_GET_B(ins) | |
local n = self:getConstNum(INS_GET_C(ins)) | |
local _,err = pcall(function() self.data[a]= self.data[b]-n end) | |
if err then return false, formatError(err) end | |
elseif op==22 then //MULVN | |
local b = INS_GET_B(ins) | |
local n = self:getConstNum(INS_GET_C(ins)) | |
local _,err = pcall(function() self.data[a]= self.data[b]*n end) | |
if err then return false, formatError(err) end | |
elseif op==23 then //DIVVN | |
local b = INS_GET_B(ins) | |
local n = self:getConstNum(INS_GET_C(ins)) | |
local _,err = pcall(function() self.data[a]= self.data[b]/n end) | |
if err then return false, formatError(err) end | |
elseif op==24 then //MODVN | |
local b = INS_GET_B(ins) | |
local n = self:getConstNum(INS_GET_C(ins)) | |
local _,err = pcall(function() self.data[a]= self.data[b]%n end) | |
if err then return false, formatError(err) end | |
elseif op==25 then //ADDNV | |
local b = INS_GET_B(ins) | |
local n = self:getConstNum(INS_GET_C(ins)) | |
local _,err = pcall(function() self.data[a]= n+self.data[b] end) | |
if err then return false, formatError(err) end | |
elseif op==26 then //SUBNV | |
local b = INS_GET_B(ins) | |
local n = self:getConstNum(INS_GET_C(ins)) | |
local _,err = pcall(function() self.data[a]= n-self.data[b] end) | |
if err then return false, formatError(err) end | |
elseif op==27 then //MULNV | |
local b = INS_GET_B(ins) | |
local n = self:getConstNum(INS_GET_C(ins)) | |
local _,err = pcall(function() self.data[a]= n*self.data[b] end) | |
if err then return false, formatError(err) end | |
elseif op==28 then //DIVNV | |
local b = INS_GET_B(ins) | |
local n = self:getConstNum(INS_GET_C(ins)) | |
local _,err = pcall(function() self.data[a]= n/self.data[b] end) | |
if err then return false, formatError(err) end | |
elseif op==29 then //MODNV | |
local b = INS_GET_B(ins) | |
local n = self:getConstNum(INS_GET_C(ins)) | |
local _,err = pcall(function() self.data[a]= n%self.data[b] end) | |
if err then return false, formatError(err) end | |
elseif op==30 then //ADDVV | |
local b = INS_GET_B(ins) | |
local c = INS_GET_C(ins) | |
local _,err = pcall(function() self.data[a]= self.data[b]+self.data[c] end) | |
if err then return false, formatError(err) end | |
elseif op==31 then //SUBVV | |
local b = INS_GET_B(ins) | |
local c = INS_GET_C(ins) | |
local _,err = pcall(function() self.data[a]= self.data[b]-self.data[c] end) | |
if err then return false, formatError(err) end | |
elseif op==32 then //MULVV | |
local b = INS_GET_B(ins) | |
local c = INS_GET_C(ins) | |
local _,err = pcall(function() self.data[a]= self.data[b]*self.data[c] end) | |
if err then return false, formatError(err) end | |
elseif op==33 then //DIVVV | |
local b = INS_GET_B(ins) | |
local c = INS_GET_C(ins) | |
local _,err = pcall(function() self.data[a]= self.data[b]/self.data[c] end) | |
if err then return false, formatError(err) end | |
elseif op==34 then //MODVV | |
local b = INS_GET_B(ins) | |
local c = INS_GET_C(ins) | |
local _,err = pcall(function() self.data[a]= self.data[b]%self.data[c] end) | |
if err then return false, formatError(err) end | |
elseif op==35 then //POW | |
local b = INS_GET_B(ins) | |
local c = INS_GET_C(ins) | |
local _,err = pcall(function() self.data[a]= self.data[b]^self.data[c] end) | |
if err then return false, formatError(err) end | |
elseif op==36 then //CAT | |
local b = INS_GET_B(ins) | |
local c = INS_GET_C(ins) | |
local _,err = pcall(function() self.data[a]= self.data[b]..self.data[c] end) | |
if err then return false, formatError(err) end | |
/* | |
* Constants. No possibility of errors. | |
*/ | |
elseif op==37 then //KSTR | |
local str = self:getConst(INS_GET_D(ins)) | |
self.data[a]=str | |
elseif op==38 then //KCDATA | |
return false,"KCDATA OP CALLED. PLEASE INFORM PARAKEET." | |
elseif op==39 then //KSHORT | |
local int = INS_GET_D(ins) | |
self.data[a]=int | |
elseif op==40 then //KNUM | |
local num = self:getConstNum(INS_GET_D(ins)) | |
self.data[a]=num | |
elseif op==41 then //KPRI | |
local pri = PRIMTYPES[INS_GET_D(ins)] | |
self.data[a]=pri | |
elseif op==42 then //KNIL | |
local last_slot = INS_GET_D(ins) | |
if last_slot<a then return false,"KNIL END<START. PLEASE INFORM PARAKEET." end | |
while a<=last_slot do | |
self.data[a]=nil | |
a=a+1 | |
end | |
/* | |
* Upvalue and Functions. | |
*/ | |
//SKIP FOR NOW, K? | |
/* | |
* Table Ops. Some need error handling. | |
*/ | |
elseif op==50 then //TNEW | |
self.data[a]={} | |
elseif op==51 then //TDUP | |
local d = INS_GET_D(ins) | |
self.data[a]=table.Copy(self:getConst(d)) | |
elseif op==52 then //GGET | |
local name = self:getConst(INS_GET_D(ins)) | |
self.data[a]=_G[name] | |
elseif op==53 then //GSET | |
local name = self:getConst(INS_GET_D(ins)) | |
_G[name]=self.data[a] | |
elseif op==54 then //TGETV | |
local b = INS_GET_B(ins) | |
local key = self.data[INS_GET_C(ins)] | |
local _,err = pcall(function() self.data[a]=self.data[b][key] end) | |
if err then return false, formatError(err) end | |
elseif op==55 then //TGETS | |
local b = INS_GET_B(ins) | |
local key = self:getConst(INS_GET_C(ins)) | |
local _,err = pcall(function() self.data[a]=self.data[b][key] end) | |
if err then return false, formatError(err) end | |
elseif op==56 then //TGETB | |
local b = INS_GET_B(ins) | |
local key = INS_GET_C(ins) | |
local _,err = pcall(function() self.data[a]=self.data[b][key] end) | |
if err then return false, formatError(err) end | |
elseif op==57 then //TSETV | |
local b = INS_GET_B(ins) | |
local key = self.data[INS_GET_C(ins)] | |
local _,err = pcall(function() self.data[b][key]=self.data[a] end) | |
if err then return false, formatError(err) end | |
elseif op==58 then //TSETS | |
local b = INS_GET_B(ins) | |
local key = self:getConst(INS_GET_C(ins)) | |
local _,err = pcall(function() self.data[b][key]=self.data[a] end) | |
if err then return false, formatError(err) end | |
elseif op==59 then //TSETB | |
local b = INS_GET_B(ins) | |
local key = INS_GET_C(ins) | |
local _,err = pcall(function() self.data[b][key]=self.data[a] end) | |
if err then return false, formatError(err) end | |
elseif op==60 then //TSETM | |
local tbl = self.data[a-1] | |
local items = self:getRange(a,self.multres) | |
//local n = self:getConstNum(INS_GET_D(ins)) | |
//local dingus_offset = bit.band((math.frexp(self:getConstNum(INS_GET_D(ins))) * 2 - 1) * math.ldexp(.5,53),4095) | |
local tbl_offset = bit.band(self:getConstNum(INS_GET_D(ins))*2,4095) | |
for k,v in pairs(items) do | |
tbl[tbl_offset+k-1]=v | |
end | |
//local bullshit_magic_indexer, teemo = math.frexp(1000000) | |
/*print(">>",INS_GET_D(ins),n,frac,dec) | |
print("AA:",3*(2^52)) | |
print("++",bit.tobit(3*(2^52))) | |
print("--",bit.band(3*(2^52),2^32-1)) | |
print("##",string.len(dingus_double(n)))*/ | |
print(tbl_offset) | |
/* | |
* Calls and Varargs. Handle errors of calls. | |
*/ | |
elseif op==61 then //CALLM | |
local func = self.data[a] | |
local b = INS_GET_B(ins) | |
local c = INS_GET_C(ins) | |
local returns | |
local _,err = pcall(function() returns = {func(unpack(self:getRange(a+1,c+self.multres)))} end) | |
if err then return false, formatError(err) end | |
self:handleReturns(returns,a,b) | |
elseif op==62 then //CALL | |
local func = self.data[a] | |
local b = INS_GET_B(ins) | |
local c = INS_GET_C(ins) | |
local returns | |
local _,err = pcall(function() returns = {func(unpack(self:getRange(a+1,c-1)))} end) | |
if err then return false, formatError(err) end | |
self:handleReturns(returns,a,b) | |
elseif op==67 then //VARG | |
local b = INS_GET_B(ins) | |
local c = INS_GET_C(ins) | |
self:handleReturns(self.vargs,a,b) | |
/* | |
* Returns. | |
*/ | |
elseif op==71 then //ret0 | |
return true, {} | |
elseif op==72 then | |
local retval = self.data[a] | |
return true, {retval} | |
/* | |
* Loops, Jumps. | |
*/ | |
elseif op==81 then //LOOP | |
//EFFECTIVE NOP | |
elseif op==82 then //ILOOP | |
//EFFECTIVE NOP | |
elseif op==83 then //JLOOP | |
//EFFECTIVE NOP | |
elseif op==84 then //JMP | |
self.pc=self.pc+INS_GET_D(ins)-32768 | |
/* | |
* Function headers. "FUNCF","IFUNCF","JFUNCF","FUNCV","IFUNCV","JFUNCV","FUNCC","FUNCCW" | |
*/ | |
elseif op==85 then //FUNCF | |
/* | |
This passes informations about how big our stack is. | |
We don't care, since we're a shit VM and just use lau tables for everything. | |
Maybe will need for upvalues? | |
*/ | |
elseif op==86 then //IFUNCF | |
//... | |
elseif op==87 then //JFUNCF | |
//... | |
elseif op==88 then //FUNCV | |
//NUMBER 88! | |
elseif op==89 then //IFUNCV | |
//NOT NUMBER 88! | |
elseif op==90 then //JFUNCV | |
//... | |
elseif op==91 then //FUNCC | |
//C function header... can this even be called? | |
elseif op==92 then //FUNCCW | |
//C wrapped function header... can this even be called? | |
//There may be some more header opcodes after this! | |
else | |
return false, ">> "..OPCODE_NAMES[op].."("..op..")" | |
end | |
self.pc=self.pc+1 | |
end | |
function vm:run(args) | |
if args then | |
for k,v in pairs(args) do | |
if !isnumber(k) then error("SlagVM received malformed arguments table.") end | |
if k>self.arg_count then | |
self.vargs[k-self.arg_count]=v | |
else | |
self.data[k-1]=v | |
end | |
end | |
end | |
local start = SysTime() | |
while true do | |
local status, error_or_retval = self:step() | |
if start+MAX_EXECUTION_TIME<SysTime() then | |
status=false | |
error_or_retval= "Script timed out." | |
end | |
if status==true then | |
return error_or_retval | |
elseif status==false then | |
MsgC(Color(255,100,100),"[SlagVM Error] "..error_or_retval.."\n") | |
error("SlagVM Halted.",0) | |
end | |
end | |
end | |
function vm:getConst(i) | |
return self.consts[-1-i] | |
end | |
function vm:getConstNum(i) | |
return self.consts[i] | |
end | |
function vm:getRange(i,count) | |
local range={} | |
while true do | |
if count<=0 then return range end | |
table.insert(range,self.data[i]) | |
count=count-1 | |
i=i+1 | |
end | |
end | |
function vm:handleReturns(returns,a,b) | |
if b==0 then | |
self.multres = #returns | |
for _,v in pairs(returns) do | |
self.data[a]=v | |
a=a+1 | |
end | |
elseif b>1 then | |
local last_ret_slot = a+b-2 | |
local i=0 | |
while a+i<=last_ret_slot do | |
self.data[a+i]= returns[i+1] | |
i=i+1 | |
end | |
end | |
end | |
return vm | |
end | |
//Our test function. | |
local function asshole(a,b,c,...) | |
print(...) | |
local dingus = {6,6,6,string.find("dingus mah zingus", "zing")} | |
PrintTable(dingus) | |
return 1337 | |
end | |
local function run_slag(func,...) | |
MsgC(Color(100,255,100),"[SlagVM] Running function...\n") | |
local codez = func2scode(asshole) | |
local machine = VM(codez) | |
local returned = machine:run{...} | |
MsgC(Color(100,255,100),"[SlagVM] Returned: ",string.Implode(", ", returned),"\n") | |
end | |
run_slag(asshole,"arg1","arg2","arg3","varg1","varg2","varg3") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment