@@ -4,6 +4,7 @@ const traverse = require('@babel/traverse').default
44const t = require ( '@babel/types' )
55const ivm = require ( 'isolated-vm' )
66const calculateConstantExp = require ( '../visitor/calculate-constant-exp' )
7+ const pruneIfBranch = require ( '../visitor/prune-if-branch' )
78
89const isolate = new ivm . Isolate ( )
910
@@ -844,6 +845,9 @@ function insertDepItemVar(deps, name, path) {
844845 */
845846function findGlobalFn ( path ) {
846847 const glo_fn_name = path . node . id ?. name
848+ if ( path . parentPath . getFunctionParent ( ) ) {
849+ return null
850+ }
847851 if ( ! glo_fn_name ) {
848852 return null
849853 }
@@ -963,6 +967,9 @@ function findBufferToString(obj) {
963967 insertDepItemVar ( obj . deps , obj . a2s_name , obj . a2s_path )
964968 break
965969 }
970+ if ( ! obj . a2s_name ) {
971+ return false
972+ }
966973 binding = obj . a2s_path . scope . getBinding ( obj . a2s_name )
967974 const b2s_path = binding . referencePaths [ 0 ] . getFunctionParent ( )
968975 obj . b2s_name = safeGetName ( b2s_path . get ( 'id' ) )
@@ -986,14 +993,15 @@ function findBufferToString(obj) {
986993 } ,
987994 } )
988995 if ( ! valid ) {
989- return
996+ return false
990997 }
991998 child . push ( {
992999 name : decode_fn . node . id . name ,
9931000 decoder : decode_fn ,
9941001 } )
9951002 }
9961003 obj . child = child
1004+ return true
9971005}
9981006
9991007function generatorStringConcealingDepCode ( obj ) {
@@ -1192,7 +1200,9 @@ const deStringConcealing = {
11921200 return
11931201 }
11941202 findGlobalFnRef ( obj )
1195- findBufferToString ( obj )
1203+ if ( ! findBufferToString ( obj ) ) {
1204+ return
1205+ }
11961206 generatorStringConcealingDepCode ( obj )
11971207 for ( const item of obj . child ) {
11981208 processSingleGetter ( obj , item . name , item . decoder )
@@ -1204,10 +1214,147 @@ const deStringConcealing = {
12041214 } ,
12051215}
12061216
1217+ function tryStringConcealingPlace ( path ) {
1218+ const parent = path . parentPath
1219+ if ( ! parent . isAssignmentExpression ( ) ) {
1220+ return
1221+ }
1222+ const name = safeGetName ( parent . get ( 'left' ) )
1223+ let binding = parent . scope . getBinding ( name )
1224+ if ( binding ?. constantViolations ?. length !== 1 ) {
1225+ return
1226+ }
1227+ const code = generator ( parent . node ) . code
1228+ const vm = isolate . createContextSync ( )
1229+ vm . evalSync ( 'var ' + code )
1230+ for ( const ref of binding . referencePaths ) {
1231+ if ( ref . key !== 'object' ) {
1232+ continue
1233+ }
1234+ const test = generator ( ref . parent ) . code
1235+ const res = vm . evalSync ( test )
1236+ safeReplace ( ref . parentPath , res )
1237+ }
1238+ safeDeleteNode ( name , parent )
1239+ }
1240+
1241+ const deStringConcealingPlace = {
1242+ ArrayExpression ( path ) {
1243+ let valid = true
1244+ if ( path . node . elements . length === 0 ) {
1245+ return
1246+ }
1247+ for ( const ele of path . node . elements ) {
1248+ if ( ! t . isStringLiteral ( ele ) ) {
1249+ valid = false
1250+ break
1251+ }
1252+ }
1253+ if ( ! valid ) {
1254+ return
1255+ }
1256+ tryStringConcealingPlace ( path )
1257+ } ,
1258+ ObjectExpression ( path ) {
1259+ let valid = true
1260+ if ( path . node . properties . length === 0 ) {
1261+ return
1262+ }
1263+ for ( const ele of path . node . properties ) {
1264+ if ( ! t . isStringLiteral ( ele . value ) ) {
1265+ valid = false
1266+ break
1267+ }
1268+ }
1269+ if ( ! valid ) {
1270+ return
1271+ }
1272+ tryStringConcealingPlace ( path )
1273+ } ,
1274+ }
1275+
1276+ function checkOpaqueObject ( path ) {
1277+ const parent = path . parentPath
1278+ if ( ! parent . isAssignmentExpression ( ) ) {
1279+ return null
1280+ }
1281+ const tmp_name = safeGetName ( parent . get ( 'left' ) )
1282+ const func_path = parent . getFunctionParent ( )
1283+ if (
1284+ ! func_path ||
1285+ func_path . key !== 'callee' ||
1286+ ! func_path . parentPath . isCallExpression ( )
1287+ ) {
1288+ return null
1289+ }
1290+ const func_body = func_path . node . body ?. body
1291+ if ( ! func_body || func_body . length < 2 ) {
1292+ return null
1293+ }
1294+ const last_node = func_body [ func_body . length - 1 ]
1295+ if (
1296+ ! t . isReturnStatement ( last_node ) ||
1297+ last_node . argument ?. name !== tmp_name
1298+ ) {
1299+ return null
1300+ }
1301+ const root_path = func_path . parentPath . parentPath
1302+ if ( ! root_path . isAssignmentExpression ( ) ) {
1303+ return null
1304+ }
1305+ const pred_name = safeGetName ( root_path . get ( 'left' ) )
1306+ const obj = {
1307+ pred_name : pred_name ,
1308+ pred_path : root_path ,
1309+ props : { } ,
1310+ }
1311+ for ( const prop of path . node . properties ) {
1312+ const key = prop . key . name
1313+ const value = prop . value
1314+ if ( t . isNumericLiteral ( value ) ) {
1315+ obj . props [ key ] = {
1316+ type : 'number' ,
1317+ }
1318+ continue
1319+ }
1320+ if ( t . isStringLiteral ( value ) ) {
1321+ obj . props [ key ] = {
1322+ type : 'string' ,
1323+ }
1324+ continue
1325+ }
1326+ if ( t . isArrayExpression ( value ) ) {
1327+ if ( value . elements . length === 0 ) {
1328+ obj . props [ key ] = {
1329+ type : 'array_dep' ,
1330+ }
1331+ }
1332+ continue
1333+ }
1334+ if ( t . isArrowFunctionExpression ( value ) || t . isFunctionExpression ( value ) ) {
1335+ const param = value . params ?. [ 0 ] ?. left ?. name
1336+ if ( ! param ) {
1337+ continue
1338+ }
1339+ const code = generator ( value ) . code
1340+ const template =
1341+ `(${ param } =){if(${ pred_name } [0])${ pred_name } push()` +
1342+ `return${ pred_name } ${ param } }`
1343+ if ( checkPattern ( code , template ) ) {
1344+ obj . props [ key ] = {
1345+ type : 'array' ,
1346+ }
1347+ }
1348+ continue
1349+ }
1350+ }
1351+ return obj
1352+ }
1353+
12071354/**
12081355 * Template:
12091356 * ```javascript
1210- * // This is defined in the glocal space
1357+ * // This is defined in the global space
12111358 * var predicateName = (function () {
12121359 * var tempName = {
12131360 * prop_array_1: [],
@@ -1230,7 +1377,41 @@ const deStringConcealing = {
12301377 * ```
12311378 */
12321379const deOpaquePredicates = {
1233- MemberExpression ( path ) { } ,
1380+ ObjectExpression ( path ) {
1381+ const obj = checkOpaqueObject ( path )
1382+ if ( ! obj ) {
1383+ return
1384+ }
1385+ console . log ( `[OpaquePredicates] predicateName : ${ obj . pred_name } ` )
1386+ const vm = isolate . createContextSync ( )
1387+ const code = generator ( obj . pred_path . node ) . code
1388+ vm . evalSync ( 'var ' + code )
1389+ obj . pred_path . get ( 'right' ) . replaceWith ( t . numericLiteral ( 0 ) )
1390+ let binding = obj . pred_path . scope . getBinding ( obj . pred_name )
1391+ binding . scope . crawl ( )
1392+ binding = binding . scope . getBinding ( obj . pred_name )
1393+ for ( const ref of binding . referencePaths ) {
1394+ if ( ref . key !== 'object' ) {
1395+ continue
1396+ }
1397+ const member = ref . parentPath
1398+ const prop = member . get ( 'property' )
1399+ if ( ! prop || ! Object . prototype . hasOwnProperty . call ( obj . props , prop ) ) {
1400+ continue
1401+ }
1402+ let expr = member
1403+ while (
1404+ expr . parentPath . isCallExpression ( ) ||
1405+ expr . parentPath . isMemberExpression ( )
1406+ ) {
1407+ expr = expr . parentPath
1408+ }
1409+ const test = generator ( expr . node ) . code
1410+ const res = vm . evalSync ( test )
1411+ safeReplace ( expr , res )
1412+ }
1413+ safeDeleteNode ( obj . pred_name , obj . pred_path )
1414+ } ,
12341415}
12351416
12361417module . exports = function ( code ) {
@@ -1254,10 +1435,13 @@ module.exports = function (code) {
12541435 traverse ( ast , deStringCompression )
12551436 // StringConcealing
12561437 traverse ( ast , deStringConcealing )
1438+ traverse ( ast , deStringConcealingPlace )
12571439 // StringSplitting
12581440 traverse ( ast , calculateConstantExp )
12591441 // OpaquePredicates
12601442 traverse ( ast , deOpaquePredicates )
1443+ traverse ( ast , calculateConstantExp )
1444+ traverse ( ast , pruneIfBranch )
12611445 code = generator ( ast , {
12621446 comments : false ,
12631447 jsescOption : { minimal : true } ,
0 commit comments