lowleveljs
Version:
1. 使用`@babel/parser`解析出js代码的语法树 2. 把所有的功能语句编译成最基础的操作指令列表 3. 编写解释器来运行这些基础操作指令
389 lines (373 loc) • 12.2 kB
JavaScript
const {CMDLS, NotSupport, mask, CMDLS_BIN_ext, CMDLS_FLOW_ext, CMDLS_SIG_ext, ARR_typ}=require('./common')
const [VAR, FUNC, FVAR, MOV, BIN, IF, ARR, CAL, TRY, FOR, FLOW, ATTR, OBJ, SWITCH, SIG, COPY, FARGU, LOG]=CMDLS
const [LGR, LSR, EQU, ADD, MULTI, SUB, DIV, AND, SEQU, SNEQU, OR, INSTANCEOF, IN, MOD, LSRE, LGRE, NEQU, BIT_MOV_LEFT, BIT_MOV_RIGHT, XOR, BIT_AND, BIT_OR, NO_SYMBOL_MOV_RIGHT]=CMDLS_BIN_ext
const [RET, THROW, BREAK, CONTINUE]=CMDLS_FLOW_ext
const [UNARY, TYPEOF, VOID, DELETE, INIT_THIS, BIT_NEGATE]=CMDLS_SIG_ext
const [ARR_ARRAY, ARR_MEMBER]=ARR_typ
let typ_i=1
const typeVar=typ_i++
const typeFunc=typ_i++
const typeArr=typ_i++ // 普通数组
const typeArrMember=typ_i++ // 取值时需要先reduce
const typeObj=typ_i++
const typeGlobalObject=typ_i++
function unpack(OUT6, outerVars, top, maskarr) {
outerVars=outerVars || {}
// 先还原为原始序列
OUT6=mask(maskarr, OUT6, 1)
let _out1=[]
for(let i=0; i<OUT6.length; i++) {
if(OUT6[i]===0) {
i+=2
if(OUT6[i]===0) {
_out1.push(0)
}else{
let n=0
for(let j=OUT6[i]; j-->0; ) {
n+=OUT6[++i]<<(8*j)
}
_out1.push(n)
}
}else{
_out1.push(OUT6[i])
}
}
// 取出常量和全局变量
let OUT=[], c=1
for(let i=0; i<_out1[0]; i++) {
let n=_out1[c]
OUT.push(_out1.slice(c+1, c+n+1))
c+=1+n
}
let [MEM, gv]=JSON.parse(String.fromCharCode(..._out1.slice(c+1, c+1+_out1[c])))
let globalContextVars={}
for(let i=0; i<gv.length; i+=2) {
globalContextVars[gv[i]]=gv[i+1]
}
c+=1+_out1[c]
const gobj={}
for(let idx in globalContextVars) {
let kk=globalContextVars[idx]
try{
if(top.hasOwnProperty(kk)===false && outerVars.hasOwnProperty(kk)===false) continue
}catch(e) {}
gobj[idx]=outerVars[kk] || top[kk]
}
return [OUT, MEM, gobj]
}
let OUT3 // 此变量仅用于调试,因此不考虑窜线场景
function find_val(contexts, val, useValue, mustDefined) {
for(let j=contexts.length; j-->0; ) {
const o=contexts[j][val]
if(!o) continue
return useValue? o.value: o
}
if(mustDefined) {
throw new Error(`${val} is not defined`)
}
const o={value: val, type: typeVar}
contexts[0][val]=o
return useValue? o.value: o
}
function parse_val(SEQ, MEM, contexts, x, fullNode, notMustDefined) {
let _ret=node=>fullNode? node: node.value
if(x<MEM.length) return _ret({value: MEM[x], type: typeGlobalObject})
return _ret(find_val(contexts, x, 0, notMustDefined? 0: 1))
}
function set_val(contexts, key, val, type) {
const found=find_val(contexts, key)
found.value=val
found.type=type || typeVar
}
function parse_member_array(arr) {
return arr.reduce((a, b)=>a[b])
}
// params: [入参数组,是否已return,return的结果, this]
function runner(SEQ, MEM, contexts, begin=0, end=-1, params=null) {
if(end===-1) {
const globalContext=contexts[0]
let ctx2={
_lastContexts: [0, {}],
}
for(let a in globalContext) {
ctx2[a]={value: globalContext[a], type: typeGlobalObject}
}
contexts=[ctx2]
}
contexts=contexts.concat([])
for(let i=begin, ctx=contexts[contexts.length-1], I=(end<0? SEQ.length: end); i<I; i++) {
// console.log("###", [i, end], OUT3[i], SEQ[i])
const [CMD, ...argv]=SEQ[i]
if(CMD===FVAR) {
for(let i=0; i<argv.length; i++) {
set_val(contexts, argv[i], params[0][i])
}
}else if(CMD===OBJ) {
const [ret, ...kv]=argv
const obj=find_val(contexts, ret)
if(obj.type!==typeObj) {
obj.value={}
obj.type=typeObj
}
for(let i=0; i<kv.length; i+=2) {
let key=parse_val(SEQ, MEM, contexts, kv[i]),
value=parse_val(SEQ, MEM, contexts, kv[i+1])
obj.value[key]=value
}
}else if(CMD===FLOW) {
const [op, val]=argv
let rval
if(val===undefined) {
rval=undefined
}else{
rval=parse_val(SEQ, MEM, contexts, val)
}
if(op===RET) {
params[1]=RET
params[2]=rval
}else if(op===BREAK) {
params[1]=BREAK
}else if(op===CONTINUE) {
params[1]=CONTINUE
}else if(op===THROW) {
throw rval
}else{
NotSupport(op)
}
break
}else if(CMD===ATTR) {
const [retVal]=argv
const fd=parse_val(SEQ, MEM, contexts, retVal)
let fe
if(fd.constructor===Array) {
fe=parse_member_array(fd)
}else if(fd.constructor===String) {
fe=parse_val(SEQ, MEM, contexts, fd, 0, 1)
}else{
fe=fd
}
set_val(contexts, retVal, fe)
}else if(CMD===COPY) {
const [retVal, copy_val]=argv
const fd1=find_val(contexts, retVal)
const fd2=parse_val(SEQ, MEM, contexts, copy_val)
fd1.value.reduce((a, b, i)=>{
if(i<fd1.value.length-1) return a[b]
else a[b]=fd2
})
}else if(CMD===BIN) {
const [ret, op, v1, v2]=argv
let p1=parse_val(SEQ, MEM, contexts, v1, 1), p2=parse_val(SEQ, MEM, contexts, v2, 1)
; [p1, p2]=[p1, p2].map(p=>{
if(p.type===typeArrMember) {
return parse_member_array(p.value)
}else return p.value
})
let rr
if(op===LGR) rr=p1>p2
else if(op===LSR) rr=p1<p2
else if(op===ADD) rr=p1+p2
else if(op===SUB) rr=p1-p2
else if(op===MULTI) rr=p1*p2
else if(op===DIV) rr=p1/p2
else if(op===EQU) rr=p1==p2
else if(op===AND) rr=p1 && p2
else if(op===SEQU) rr=p1===p2
else if(op===SNEQU) rr=p1!==p2
else if(op===OR) rr=p1 || p2
else if(op===INSTANCEOF) rr=p1 instanceof p2
else if(op===IN) rr=p1 in p2
else if(op===MOD) rr=p1 % p2
else if(op===LSRE) rr=p1 <= p2
else if(op===LGRE) rr=p1 >= p2
else if(op===NEQU) rr=p1 != p2
else if(op===BIT_MOV_LEFT) rr=p1<<p2
else if(op===BIT_MOV_RIGHT) rr=p1>>p2
else if(op===XOR) rr=p1 ^ p2
else if(op===BIT_AND) rr=p1 & p2
else if(op===BIT_OR) rr=p1 | p2
else if(op===NO_SYMBOL_MOV_RIGHT) rr=p1 >>> p2
else NotSupport(op)
set_val(contexts, ret, rr)
}else if(CMD===IF) {
const [judge, else_l, endif_l]=argv
if(find_val(contexts, judge, 1)) {
runner(SEQ, MEM, contexts, i+1, else_l, params)
}else{
runner(SEQ, MEM, contexts, else_l, endif_l, params)
}
i=endif_l-1
}else if(CMD===VAR) {
ctx={}
contexts=contexts.concat([ctx])
argv.map(v=>ctx[v]={value: undefined, type: typeVar})
}else if(CMD===FUNC) {
const [func_fn, func_line_end]=argv
const [begin_l, end_l]=[i+1, func_line_end]
set_val(contexts, func_fn, function(...argu) {
let params=[argu, 0, undefined, this]
runner(SEQ, MEM, contexts.concat([{}]), begin_l, end_l, params)
return params[2]
}, typeFunc)
i=func_line_end-1
}else if(CMD===MOV) {
for(let i=0, n=argv.length/2; i<n; i++) {
let {type, value}=parse_val(SEQ, MEM, contexts, argv[n+i], 1)
set_val(contexts, argv[i], type===typeArrMember? parse_member_array(value): value)
}
}else if(CMD===ARR) {
let [val, ...x]=argv
const found=find_val(contexts, val)
if(!found.value) {
found.value=[]
let p=x.shift()
if(p===ARR_ARRAY) {
found.type=typeArr
}else if(p===ARR_MEMBER) {
found.type=typeArrMember
}else{
NotSupport(p)
}
}
for(let i=0; i<x.length; i++) {
let v=parse_val(SEQ, MEM, contexts, x[i])
found.value.push(v)
}
}else if(CMD===CAL) {
const [isNew, ret, fn, arg]=argv
let argu=parse_val(SEQ, MEM, contexts, arg) || []
let func=parse_val(SEQ, MEM, contexts, fn)
let realFunc, realThis, _hasSetThis=0
if(typeof func==='function') {
realFunc=func
}else if(func.constructor===Array) {
let fn, _this
for(let i=0; i<func.length; i++) {
if(!i) {
// if(typeof func[i]==='string') fn=find_val(contexts, func[i], 1)
// else
fn=func[i]
}else{
fn=fn[func[i]]
}
if(i<func.length-1) _this=fn
}
realFunc=fn
realThis=_this
_hasSetThis=1
}else if(func.constructor===String) {
realFunc=find_val(contexts, func, 1)
}else{
NotSupport(func)
}
let returnVal
if(isNew) {
returnVal=new realFunc(...argu)
}else{
returnVal=realFunc.apply(_hasSetThis? realThis: (params ? params[3]: this), argu)
}
set_val(contexts, ret, returnVal)
}else if(CMD===TRY) {
const [e, catch_l, finally_l, end_l]=argv
try{
runner(SEQ, MEM, contexts, i+1, catch_l, params)
}catch(err) {
if(e===0) throw err
runner(SEQ, MEM, contexts.concat([{
[e]: {type: typeVar, value: err}
}]), catch_l, finally_l, params)
}finally{
runner(SEQ, MEM, contexts, finally_l, end_l, params)
}
i=end_l-1
}else if(CMD===FOR) {
const [test_val, endtest_l, endupdate_l, end_l]=argv
// params的第1个参数表示流程控制,取 CMDLS_BIN_ext[] 常量
// break:params[1]=BREAK
// continue:params[1]=CONTINUE
let for_params=params || [[], 0, undefined, this]
for(;;) {
runner(SEQ, MEM, contexts, i+1, endtest_l)
const tested=parse_val(SEQ, MEM, contexts, test_val)
if(!tested) break
runner(SEQ, MEM, contexts, endupdate_l, end_l, for_params)
const v=for_params[1]
if(v===CONTINUE) for_params[1]=0 // continue
if(v===BREAK || v===RET) {
if(v===BREAK) for_params[1]=0
break
}
runner(SEQ, MEM, contexts, endtest_l, endupdate_l)
}
i=end_l-1
}else if(CMD===SWITCH) {
const [judgeVal, ...caseValLines]=argv
const realVal=parse_val(SEQ, MEM, contexts, judgeVal)
for(let si=0, lstLine=i+1, matched=false; si<caseValLines.length; si+=3) {
const caseExpLineEnd=caseValLines[si]
const caseLineEnd=caseValLines[si+2]
let exp_params=params || [[], 0, undefined, this]
runner(SEQ, MEM, contexts, lstLine, caseExpLineEnd, exp_params)
const ve=exp_params[1]
if(ve===THROW) break
const caseVal=parse_val(SEQ, MEM, contexts, caseValLines[si+1])
if(caseVal===realVal) matched=true
if(matched) {
let switch_params=params || [[], 0, undefined, this]
runner(SEQ, MEM, contexts, caseExpLineEnd, caseLineEnd, switch_params)
const v=switch_params[1]
if(v===BREAK || v===RET) {
if(v===BREAK) switch_params[1]=0
break
}
}
lstLine=caseLineEnd
}
i=caseValLines[caseValLines.length-1]-1
}else if(CMD===SIG) {
const [ret, op, v1]=argv
let p1, rr
if(op===TYPEOF) {
for(let j=contexts.length; j-->0; ) {
if(!contexts[j][v1]) continue
rr=typeof contexts[j][v1].value
}
if(!rr) rr='undefined'
}else{
p1=parse_val(SEQ, MEM, contexts, v1)
}
if(rr);
else if(op===UNARY) rr=!p1
else if(op===VOID) rr=void p1
else if(op===DELETE) {
rr=p1.reduce((a, b, i)=>{
if(i<p1.length-1) return a[b]
else return delete a[b]
})
}else if(op===INIT_THIS) {
rr=contexts[0]
}else if(op===BIT_NEGATE) {
rr=~p1
}
else NotSupport(op)
set_val(contexts, ret, rr)
}else if(CMD===FARGU) {
const [_arguments, _this]=argv
set_val(contexts, _arguments, params[0])
set_val(contexts, _this, params[3])
}else if(CMD===LOG) {
const [...v]=argv
for(let i=0; i<v.length; i++) {
console.log({key: v[i], value: parse_val(SEQ, MEM, contexts, v[i], 1)})
}
}else{
NotSupport(OUT3[i] || (i+1+'. '+SEQ[i]))
}
if(params && params[1]) break
}
}
module.exports=([BIN, TXT], VARS, top, maskarr)=>{
const [SEQ, MEM, globalContext]=unpack(BIN, VARS, top, maskarr)
if(TXT) OUT3=TXT.split('\n')
runner(SEQ, MEM, [globalContext])
}