const wu=require("./wuLib.js"); const {getZ}=require("./wuRestoreZ.js"); const {wxsBeautify}=require("./wuJs.js"); const fs=require('fs'); const path=require("path"); const esprima=require('esprima'); const {VM}=require('vm2'); const escodegen=require('escodegen'); function analyze(core,z,namePool,xPool,fakePool={},zMulName="0"){ function anaRecursion(core,fakePool={}){ return analyze(core,z,namePool,xPool,fakePool,zMulName); } function push(name,elem){ namePool[name]=elem; } function pushSon(pname,son){ if(fakePool[pname])fakePool[pname].son.push(son); else namePool[pname].son.push(son); } for(let ei=0;ei0) throw Error("Noticable generics content: "+dec.init.arguments[2].toString()); let mv={}; let name=null,base=0; for(let x of dec.init.arguments[1].elements){ let v=x.value; if(!v&&typeof v!="number"){ if(x.type=="UnaryExpression"&&x.operator=="-")v=-x.argument.value; else throw Error("Unknown type of object in _m attrs array: "+x.type); } if(name===null){ name=v; }else{ if(base+v<0)mv[name]=null;else{ mv[name]=z[base+v]; if(base==0)base=v; } name=null; } } push(dec.id.name,{tag:dec.init.arguments[0].value,son:[],v:mv}); } break; case "_mz": { if(dec.init.arguments[3].elements.length>0) throw Error("Noticable generics content: "+dec.init.arguments[3].toString()); let mv={}; let name=null,base=0; for(let x of dec.init.arguments[2].elements){ let v=x.value; if(!v&&typeof v!="number"){ if(x.type=="UnaryExpression"&&x.operator=="-")v=-x.argument.value; else throw Error("Unknown type of object in _mz attrs array: "+x.type); } if(name===null){ name=v; }else{ if(base+v<0)mv[name]=null;else{ mv[name]=z.mul[zMulName][base+v]; if(base==0)base=v; } name=null; } } push(dec.id.name,{tag:dec.init.arguments[1].value,son:[],v:mv}); } break; case "_gd"://template use/is { let is=namePool[dec.init.arguments[1].name].content; let data=null,obj=null; ei++; for(let e of core[ei].consequent.body){ if(e.type=="VariableDeclaration"){ for(let f of e.declarations){ if(f.init.type=="LogicalExpression"&&f.init.left.type=="CallExpression"){ if(f.init.left.callee.name=="_1")data=z[f.init.left.arguments[0].value]; else if(f.init.left.callee.name=="_1z")data=z.mul[zMulName][f.init.left.arguments[1].value]; } } }else if(e.type=="ExpressionStatement"){ let f=e.expression; if(f.type=="AssignmentExpression"&&f.operator=="="&&f.left.property&&f.left.property.name=="wxXCkey"){ obj=f.left.object.name; } } } namePool[obj].tag="template"; Object.assign(namePool[obj].v,{is:is,data:data}); } break; default: { let funName=dec.init.callee.name; if(funName.startsWith("gz$gwx")){ zMulName=funName.slice(6); }else throw Error("Unknown init callee "+funName); } } }else if(dec.init.type=="FunctionExpression"){ push(dec.id.name,{tag:"gen",func:dec.init}); }else if(dec.init.type=="MemberExpression"){ if(dec.init.object.type=="MemberExpression"&&dec.init.object.object.name=="e_"&&dec.init.object.property.type=="MemberExpression"&&dec.init.object.property.object.name=="x"){ if(dec.init.property.name=="j"){//include //do nothing }else if(dec.init.property.name=="i"){//import //do nothing }else throw Error("Unknown member expression declaration."); }else throw Error("Unknown member expression declaration."); }else throw Error("Unknown declaration init type " + dec.init.type); } break; case "IfStatement": if(e.test.callee.name.startsWith("_o")){ function parse_OFun(e){ if(e.test.callee.name=="_o")return z[e.test.arguments[0].value]; else if(e.test.callee.name=="_oz")return z.mul[zMulName][e.test.arguments[1].value]; else throw Error("Unknown if statement test callee name:"+e.test.callee.name); } let vname=e.consequent.body[0].expression.left.object.name; let nif={tag:"block",v:{"wx:if":parse_OFun(e)},son:[]}; anaRecursion(e.consequent.body,{[vname]:nif}); pushSon(vname,nif); if(e.alternate){ while(e.alternate&&e.alternate.type=="IfStatement"){ e=e.alternate; nif={tag:"block",v:{"wx:elif":parse_OFun(e)},son:[]}; anaRecursion(e.consequent.body,{[vname]:nif}); pushSon(vname,nif); } if(e.alternate&&e.alternate.type=="BlockStatement"){ e=e.alternate; nif={tag:"block",v:{"wx:else":null},son:[]}; anaRecursion(e.body,{[vname]:nif}); pushSon(vname,nif); } } }else throw Error("Unknown if statement."); break; default: throw Error("Unknown type "+e.type); } } } function wxmlify(str,isText){ if(typeof str=="undefined"||str===null)return "Empty";//throw Error("Empty str in "+(isText?"text":"prop")); if(isText)return str;//may have some bugs in some specific case(undocumented by tx) else return str.replace(/"/g, '\\"'); } function elemToString(elem,dep,moreInfo=false){ const longerList=[];//put tag name which can't be style. const indent=' '.repeat(4); function isTextTag(elem){ return elem.tag=="__textNode__"&&elem.textNode; } function elemRecursion(elem,dep){ return elemToString(elem,dep,moreInfo); } function trimMerge(rets){ let needTrimLeft=false,ans=""; for(let ret of rets){ if(ret.textNode==1){ if(!needTrimLeft){ needTrimLeft=true; ans=ans.trimRight(); } }else if(needTrimLeft){ needTrimLeft=false; ret=ret.trimLeft(); } ans+=ret; } return ans; } if(isTextTag(elem)){ //In comment, you can use typify text node, which beautify its code, but may destroy ui. //So, we use a "hack" way to solve this problem by letting typify program stop when face textNode let str=new String(wxmlify(elem.content,true)); str.textNode=1; return wxmlify(str,true);//indent.repeat(dep)+wxmlify(elem.content.trim(),true)+"\n"; } if(elem.tag=="block"&&!moreInfo){ if(elem.son.length==1&&!isTextTag(elem.son[0])){ let ok=true,s=elem.son[0]; for(let x in elem.v)if(x in s.v){ ok=false; break; } if(ok&&!(("wx:for" in s.v||"wx:if" in s.v)&&("wx:if" in elem.v||"wx:else" in elem.v||"wx:elif" in elem.v))){//if for and if in one tag, the default result is an if in for. And we should block if nested in elif/else been combined. Object.assign(s.v,elem.v); return elemRecursion(s,dep); } }else if(Object.keys(elem.v).length==0){ let ret=[]; for(let s of elem.son)ret.push(elemRecursion(s,dep)); return trimMerge(ret); } } let ret=indent.repeat(dep)+"<"+elem.tag; for(let v in elem.v)ret+=" "+v+(elem.v[v]!==null?"=\""+wxmlify(elem.v[v])+"\"":""); if(elem.son.length==0){ if(longerList.includes(elem.tag))return ret+" />\n"; else return ret+">\n"; } ret+=">\n"; let rets=[ret]; for(let s of elem.son)rets.push(elemRecursion(s,dep+1)); rets.push(indent.repeat(dep)+"\n"); return trimMerge(rets); } function doWxml(state,dir,name,code,z,xPool,rDs,wxsList,moreInfo){ let rname=code.slice(code.lastIndexOf("return")+6).replace(/[\;\}]/g,"").trim(); code=code.slice(code.indexOf("\n"),code.lastIndexOf("return")).trim(); let r={son:[]}; analyze(esprima.parseScript(code).body,z,{[rname]:r},xPool,{[rname]:r}); let ans=[]; for(let elem of r.son)ans.push(elemToString(elem,0,moreInfo)); let result=[ans.join("")]; for(let v in rDs){ state[0]=v; let oriCode=rDs[v].toString(); let rname=oriCode.slice(oriCode.lastIndexOf("return")+6).replace(/[\;\}]/g,"").trim(); let tryPtr=oriCode.indexOf("\ntry{"); let zPtr=oriCode.indexOf("var z=gz$gwx"); let code=oriCode.slice(tryPtr+5,oriCode.lastIndexOf("\n}catch(")).trim(); if(zPtr!=-1&&tryPtr>zPtr){ let attach=oriCode.slice(zPtr); attach=attach.slice(0,attach.indexOf("()"))+"()\n"; code=attach+code; } let r={tag:"template",v:{name:v},son:[]}; analyze(esprima.parseScript(code).body,z,{[rname]:r},xPool,{[rname]:r}); result.unshift(elemToString(r,0,moreInfo)); } name=path.resolve(dir,name); if(wxsList[name])result.push(wxsList[name]); wu.save(name,result.join("")); } function tryWxml(dir,name,code,z,xPool,rDs,...args){ console.log("Decompile "+name+"..."); let state=[null]; try{ doWxml(state,dir,name,code,z,xPool,rDs,...args); console.log("Decompile success!"); }catch(e){ console.log("error on "+name+"("+(state[0]===null?"Main":"Template-"+state[0])+")\nerr: ",e); if(state[0]===null)wu.save(path.resolve(dir,name+".ori.js"),code); else wu.save(path.resolve(dir,name+".tem-"+state[0]+".ori.js"),rDs[state[0]].toString()); } } function doWxs(code){ const before='nv_module={nv_exports:{}};'; return wxsBeautify(code.slice(code.indexOf(before)+before.length,code.lastIndexOf('return nv_module.nv_exports;}')).replace(/nv\_/g,'')); } function doFrame(name,cb,order){ let moreInfo=order.includes("m"); wxsList={}; wu.get(name,code=>{ getZ(code,z=>{ const before="\nvar nv_require=function(){var nnm="; code=code.slice(code.indexOf(before)+before.length,code.lastIndexOf("if(path&&e_[path]){")); json=code.slice(0,code.indexOf("};")+1); let endOfRequire=code.indexOf("()\r\n")+4; if(endOfRequire==4-1)endOfRequire=code.indexOf("()\n")+3; code=code.slice(endOfRequire); let rD={},rE={},rF={},requireInfo,x,vm=new VM({sandbox:{d_:rD,e_:rE,f_:rF,_vmRev_(data){ [x,requireInfo]=data; },nv_require(path){ return ()=>path; }}}); vm.run(code+"\n_vmRev_([x,"+json+"])"); let dir=path.dirname(name),pF=[]; for(let info in rF)if(typeof rF[info]=="function"){ let name=path.resolve(dir,(info[0]=='/'?'.':'')+info),ref=rF[info](); pF[ref]=info; wu.save(name,doWxs(requireInfo[ref].toString())); } for(let info in rF)if(typeof rF[info]=="object"){ let name=path.resolve(dir,(info[0]=='/'?'.':'')+info); let res=[],now=rF[info]; for(let deps in now){ let ref=now[deps](); if(ref.includes(":"))res.push("\n"+doWxs(requireInfo[ref].toString())+"\n"); else if(pF[ref])res.push(""); else res.push(""); wxsList[name]=res.join("\n"); } } for(let name in rE)tryWxml(dir,name,rE[name].f.toString(),z,x,rD[name],wxsList,moreInfo); cb({[name]:4}); }); }); } module.exports={doFrame:doFrame}; if(require.main===module){ wu.commandExecute(doFrame,"Restore wxml files.\n\n\n\n restore wxml file from page-frame.html or app-wxss.js."); }