2020-06-10 11:46:00 +08:00
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 ; ei < core . length ; ei ++ ) {
let e = core [ ei ] ;
switch ( e . type ) {
case "ExpressionStatement" : {
let f = e . expression ;
if ( f . callee ) {
if ( f . callee . type == "Identifier" ) {
switch ( f . callee . name ) {
case "_r" :
namePool [ f . arguments [ 0 ] . name ] . v [ f . arguments [ 1 ] . value ] = z [ f . arguments [ 2 ] . value ] ;
break ;
case "_rz" :
namePool [ f . arguments [ 1 ] . name ] . v [ f . arguments [ 2 ] . value ] = z . mul [ zMulName ] [ f . arguments [ 3 ] . value ] ;
break ;
case "_" :
pushSon ( f . arguments [ 0 ] . name , namePool [ f . arguments [ 1 ] . name ] ) ;
break ;
case "_2" : {
let item = f . arguments [ 6 ] . value ; //def:item
let index = f . arguments [ 7 ] . value ; //def:index
let data = z [ f . arguments [ 0 ] . value ] ;
let key = escodegen . generate ( f . arguments [ 8 ] ) . slice ( 1 , - 1 ) ; //f.arguments[8].value;//def:""
let obj = namePool [ f . arguments [ 5 ] . name ] ;
let gen = namePool [ f . arguments [ 1 ] . name ] ;
if ( gen . tag == "gen" ) {
let ret = gen . func . body . body . pop ( ) . argument . name ;
anaRecursion ( gen . func . body . body , { [ ret ] : obj } ) ;
}
obj . v [ "wx:for" ] = data ;
if ( index != "index" ) obj . v [ "wx:for-index" ] = index ;
if ( item != "item" ) obj . v [ "wx:for-item" ] = item ;
if ( key != "" ) obj . v [ "wx:key" ] = key ;
}
break ;
case "_2z" : {
let item = f . arguments [ 7 ] . value ; //def:item
let index = f . arguments [ 8 ] . value ; //def:index
let data = z . mul [ zMulName ] [ f . arguments [ 1 ] . value ] ;
let key = escodegen . generate ( f . arguments [ 9 ] ) . slice ( 1 , - 1 ) ; //f.arguments[9].value;//def:""
let obj = namePool [ f . arguments [ 6 ] . name ] ;
let gen = namePool [ f . arguments [ 2 ] . name ] ;
if ( gen . tag == "gen" ) {
let ret = gen . func . body . body . pop ( ) . argument . name ;
anaRecursion ( gen . func . body . body , { [ ret ] : obj } ) ;
}
obj . v [ "wx:for" ] = data ;
if ( index != "index" ) obj . v [ "wx:for-index" ] = index ;
if ( item != "item" ) obj . v [ "wx:for-item" ] = item ;
if ( key != "" ) obj . v [ "wx:key" ] = key ;
}
break ;
case "_ic" :
pushSon ( f . arguments [ 5 ] . name , {
tag : "include" ,
son : [ ] ,
v : { src : xPool [ f . arguments [ 0 ] . property . value ] }
} ) ;
break ;
case "_ai" : { //template import
let to = Object . keys ( fakePool ) [ 0 ] ;
if ( to ) pushSon ( to , {
tag : "import" ,
son : [ ] ,
v : { src : xPool [ f . arguments [ 1 ] . property . value ] }
} ) ;
else throw Error ( "Unexpected fake pool" ) ;
}
break ;
case "_af" :
//ignore _af
break ;
default :
throw Error ( "Unknown expression callee name " + f . callee . name ) ;
}
} else if ( f . callee . type == "MemberExpression" ) {
if ( f . callee . object . name == "cs" || f . callee . property . name == "pop" ) break ;
throw Error ( "Unknown member expression" ) ;
} else throw Error ( "Unknown callee type " + f . callee . type ) ;
} else if ( f . type == "AssignmentExpression" && f . operator == "=" ) {
//no special use
} else throw Error ( "Unknown expression statement." ) ;
break ;
}
case "VariableDeclaration" :
for ( let dec of e . declarations ) {
if ( dec . init . type == "CallExpression" ) {
switch ( dec . init . callee . name ) {
case "_n" :
push ( dec . id . name , { tag : dec . init . arguments [ 0 ] . value , son : [ ] , v : { } } ) ;
break ;
case "_v" :
push ( dec . id . name , { tag : "block" , son : [ ] , v : { } } ) ;
break ;
case "_o" :
push ( dec . id . name , {
tag : "__textNode__" ,
textNode : true ,
content : z [ dec . init . arguments [ 0 ] . value ]
} ) ;
break ;
case "_oz" :
push ( dec . id . name , {
tag : "__textNode__" ,
textNode : true ,
content : z . mul [ zMulName ] [ dec . init . arguments [ 1 ] . value ]
} ) ;
break ;
case "_m" : {
if ( dec . init . arguments [ 2 ] . elements . length > 0 )
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 ) ;
}
}
2019-12-28 14:39:42 +08:00
}
2020-06-10 11:46:00 +08:00
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 , '\\"' ) ;
2019-12-28 14:39:42 +08:00
}
2020-06-10 11:46:00 +08:00
function elemToString ( elem , dep , moreInfo = false ) {
const longerList = [ ] ; //put tag name which can't be <x /> 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 + "></" + elem . tag + ">\n" ;
}
ret += ">\n" ;
let rets = [ ret ] ;
for ( let s of elem . son ) rets . push ( elemRecursion ( s , dep + 1 ) ) ;
rets . push ( indent . repeat ( dep ) + "</" + elem . tag + ">\n" ) ;
return trimMerge ( rets ) ;
2019-12-28 14:39:42 +08:00
}
2020-06-10 11:46:00 +08:00
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 ( "" ) ) ;
2019-12-28 14:39:42 +08:00
}
2020-06-10 11:46:00 +08:00
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 ( ) ) ;
}
2019-12-28 14:39:42 +08:00
}
2020-06-10 11:46:00 +08:00
function doWxs ( code , name ) {
name = name || '' ;
name = name . substring ( 0 , name . lastIndexOf ( '/' ) + 1 ) ;
const before = 'nv_module={nv_exports:{}};' ;
return wxsBeautify ( code . slice ( code . indexOf ( before ) + before . length , code . lastIndexOf ( 'return nv_module.nv_exports;}' ) ) . replace ( eval ( '/' + ( 'p_' + name ) . replace ( /\//g , '\\/' ) + '/g' ) , '' ) . replace ( /nv\_/g , '' ) . replace ( /(require\(.*?\))\(\)/g , '$1' ) ) ;
2019-12-28 14:39:42 +08:00
}
2020-06-10 11:46:00 +08:00
function doFrame ( name , cb , order , mainDir ) {
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 . lastIndexOf ( 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 ;
}
}
} ) ;
let vmCode = code + "\n_vmRev_([x," + json + "])" ;
vm . run ( vmCode ) ;
let dir = mainDir || 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 ( ) , info ) ) ;
}
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 ( "<wxs module=\"" + deps + "\">\n" + doWxs ( requireInfo [ ref ] . toString ( ) ) + "\n</wxs>" ) ;
else if ( pF [ ref ] ) res . push ( "<wxs module=\"" + deps + "\" src=\"" + wu . toDir ( pF [ ref ] , info ) + "\" />" ) ;
else res . push ( "<wxs module=\"" + deps + "\" src=\"" + wu . toDir ( ref . slice ( 2 ) , info ) + "\" />" ) ;
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 } ) ;
} ) ;
} ) ;
2019-12-28 14:39:42 +08:00
}
2020-06-10 11:46:00 +08:00
module . exports = { doFrame : doFrame } ;
if ( require . main === module ) {
wu . commandExecute ( doFrame , "Restore wxml files.\n\n<files...>\n\n<files...> restore wxml file from page-frame.html or app-wxss.js." ) ;
2019-12-28 14:39:42 +08:00
}