@@ -4,6 +4,7 @@ const traverse = require('@babel/traverse').default
4
4
const t = require ( '@babel/types' )
5
5
const ivm = require ( 'isolated-vm' )
6
6
const calculateConstantExp = require ( '../visitor/calculate-constant-exp' )
7
+ const pruneIfBranch = require ( '../visitor/prune-if-branch' )
7
8
8
9
const isolate = new ivm . Isolate ( )
9
10
@@ -844,6 +845,9 @@ function insertDepItemVar(deps, name, path) {
844
845
*/
845
846
function findGlobalFn ( path ) {
846
847
const glo_fn_name = path . node . id ?. name
848
+ if ( path . parentPath . getFunctionParent ( ) ) {
849
+ return null
850
+ }
847
851
if ( ! glo_fn_name ) {
848
852
return null
849
853
}
@@ -963,6 +967,9 @@ function findBufferToString(obj) {
963
967
insertDepItemVar ( obj . deps , obj . a2s_name , obj . a2s_path )
964
968
break
965
969
}
970
+ if ( ! obj . a2s_name ) {
971
+ return false
972
+ }
966
973
binding = obj . a2s_path . scope . getBinding ( obj . a2s_name )
967
974
const b2s_path = binding . referencePaths [ 0 ] . getFunctionParent ( )
968
975
obj . b2s_name = safeGetName ( b2s_path . get ( 'id' ) )
@@ -986,14 +993,15 @@ function findBufferToString(obj) {
986
993
} ,
987
994
} )
988
995
if ( ! valid ) {
989
- return
996
+ return false
990
997
}
991
998
child . push ( {
992
999
name : decode_fn . node . id . name ,
993
1000
decoder : decode_fn ,
994
1001
} )
995
1002
}
996
1003
obj . child = child
1004
+ return true
997
1005
}
998
1006
999
1007
function generatorStringConcealingDepCode ( obj ) {
@@ -1192,7 +1200,9 @@ const deStringConcealing = {
1192
1200
return
1193
1201
}
1194
1202
findGlobalFnRef ( obj )
1195
- findBufferToString ( obj )
1203
+ if ( ! findBufferToString ( obj ) ) {
1204
+ return
1205
+ }
1196
1206
generatorStringConcealingDepCode ( obj )
1197
1207
for ( const item of obj . child ) {
1198
1208
processSingleGetter ( obj , item . name , item . decoder )
@@ -1204,10 +1214,147 @@ const deStringConcealing = {
1204
1214
} ,
1205
1215
}
1206
1216
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
+
1207
1354
/**
1208
1355
* Template:
1209
1356
* ```javascript
1210
- * // This is defined in the glocal space
1357
+ * // This is defined in the global space
1211
1358
* var predicateName = (function () {
1212
1359
* var tempName = {
1213
1360
* prop_array_1: [],
@@ -1230,7 +1377,41 @@ const deStringConcealing = {
1230
1377
* ```
1231
1378
*/
1232
1379
const 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
+ } ,
1234
1415
}
1235
1416
1236
1417
module . exports = function ( code ) {
@@ -1254,10 +1435,13 @@ module.exports = function (code) {
1254
1435
traverse ( ast , deStringCompression )
1255
1436
// StringConcealing
1256
1437
traverse ( ast , deStringConcealing )
1438
+ traverse ( ast , deStringConcealingPlace )
1257
1439
// StringSplitting
1258
1440
traverse ( ast , calculateConstantExp )
1259
1441
// OpaquePredicates
1260
1442
traverse ( ast , deOpaquePredicates )
1443
+ traverse ( ast , calculateConstantExp )
1444
+ traverse ( ast , pruneIfBranch )
1261
1445
code = generator ( ast , {
1262
1446
comments : false ,
1263
1447
jsescOption : { minimal : true } ,
0 commit comments