@@ -3,6 +3,7 @@ const generator = require('@babel/generator').default
33const traverse = require ( '@babel/traverse' ) . default
44const t = require ( '@babel/types' )
55const ivm = require ( 'isolated-vm' )
6+ const calculateConstantExp = require ( '../visitor/calculate-constant-exp' )
67
78const isolate = new ivm . Isolate ( )
89
@@ -22,6 +23,16 @@ function safeReplace(path, value) {
2223 path . replaceWithSourceString ( value )
2324}
2425
26+ function safeGetName ( path ) {
27+ if ( path . isIdentifier ( ) ) {
28+ return path . node . name
29+ }
30+ if ( path . isLiteral ( ) ) {
31+ return path . node . value
32+ }
33+ return null
34+ }
35+
2536function deAntiToolingCheckFunc ( path ) {
2637 if ( path . node . params . length ) {
2738 return false
@@ -299,6 +310,185 @@ function checkFuncLen(path) {
299310 }
300311}
301312
313+ /**
314+ * type: param, value, ref, invalid
315+ */
316+ function initStackCache ( len ) {
317+ const cache = { }
318+ for ( let i = 0 ; i < len ; ++ i ) {
319+ cache [ i ] = {
320+ type : 'param' ,
321+ }
322+ }
323+ return cache
324+ }
325+
326+ function processAssignLeft ( vm , cache , path , prop_name , stk_name ) {
327+ const father = path . parentPath
328+ const right = father . get ( 'right' )
329+ if ( right . isBinaryExpression ( ) ) {
330+ return
331+ }
332+ if ( right . isLiteral ( ) ) {
333+ vm . evalSync ( generator ( father . node ) . code )
334+ cache [ prop_name ] = {
335+ type : 'value' ,
336+ value : right . node . value ,
337+ }
338+ return
339+ }
340+ if ( right . isUnaryExpression ( ) && right . node . operator === '-' ) {
341+ const value = vm . evalSync ( generator ( right . node ) . code )
342+ vm . evalSync ( generator ( father . node ) . code )
343+ cache [ prop_name ] = {
344+ type : 'value' ,
345+ value : value ,
346+ }
347+ return
348+ }
349+ if ( right . isMemberExpression ( ) && right . node . object ?. name === stk_name ) {
350+ const right_prop = right . get ( 'property' )
351+ if ( right_prop . isBinaryExpression ( ) ) {
352+ return
353+ }
354+ let ref = safeGetName ( right_prop )
355+ if ( ! Object . prototype . hasOwnProperty . call ( cache , ref ) ) {
356+ vm . evalSync ( generator ( father . node ) . code )
357+ cache [ prop_name ] = {
358+ type : 'value' ,
359+ value : undefined ,
360+ }
361+ return
362+ }
363+ while ( cache [ ref ] . type === 'ref' ) {
364+ ref = cache [ ref ] . value
365+ }
366+ if ( cache [ ref ] . type === 'value' ) {
367+ safeReplace ( right , cache [ ref ] . value )
368+ vm . evalSync ( generator ( father . node ) . code )
369+ cache [ prop_name ] = {
370+ type : 'value' ,
371+ value : cache [ ref ] . value ,
372+ }
373+ } else {
374+ cache [ prop_name ] = {
375+ type : 'ref' ,
376+ value : ref ,
377+ }
378+ }
379+ return
380+ }
381+ cache [ prop_name ] = {
382+ type : 'invalid' ,
383+ }
384+ }
385+
386+ function processAssignInvalid ( cache , path , prop_name ) {
387+ cache [ prop_name ] = {
388+ type : 'invalid' ,
389+ }
390+ }
391+
392+ function processReplace ( cache , path , prop_name ) {
393+ const value = cache [ prop_name ] . value
394+ const type = cache [ prop_name ] . type
395+ if ( type === 'ref' ) {
396+ path . node . computed = true
397+ safeReplace ( path . get ( 'property' ) , value )
398+ return true
399+ }
400+ if ( type === 'value' ) {
401+ safeReplace ( path , value )
402+ return true
403+ }
404+ return false
405+ }
406+
407+ function checkStackInvalid ( path ) {
408+ const stk_name = path . node . params [ 0 ] . argument . name
409+ const body_path = path . get ( 'body' )
410+ const obj = { }
411+ body_path . traverse ( {
412+ MemberExpression : {
413+ exit ( path ) {
414+ if ( path . node . object . name !== stk_name ) {
415+ return
416+ }
417+ const father = path . parentPath
418+ if ( body_path . scope == father . scope ) {
419+ return
420+ }
421+ if ( ! father . isAssignmentExpression ( ) || path . key !== 'left' ) {
422+ return
423+ }
424+ const prop = path . get ( 'property' )
425+ const prop_name = safeGetName ( prop )
426+ obj [ prop_name ] = 1
427+ } ,
428+ } ,
429+ } )
430+ return obj
431+ }
432+
433+ function tryStackReplace ( path , len , invalid ) {
434+ const stk_name = path . node . params [ 0 ] . argument . name
435+ const body_path = path . get ( 'body' )
436+ const cache = initStackCache ( len )
437+ const vm = isolate . createContextSync ( )
438+ vm . evalSync ( `var ${ stk_name } = []` )
439+ let changed = false
440+ body_path . traverse ( {
441+ MemberExpression : {
442+ exit ( path ) {
443+ if ( path . node . object . name !== stk_name ) {
444+ return
445+ }
446+ const prop = path . get ( 'property' )
447+ if ( prop . isBinaryExpression ( ) ) {
448+ return
449+ }
450+ const prop_name = safeGetName ( prop )
451+ if ( ! prop_name ) {
452+ return
453+ }
454+ if ( Object . prototype . hasOwnProperty . call ( invalid , prop_name ) ) {
455+ processAssignInvalid ( cache , path , prop_name )
456+ return
457+ }
458+ const exist = Object . prototype . hasOwnProperty . call ( cache , prop_name )
459+ if ( exist && cache [ prop_name ] . type === 'param' ) {
460+ return
461+ }
462+ const father = path . parentPath
463+ if ( father . isAssignmentExpression ( ) && path . key === 'left' ) {
464+ processAssignLeft ( vm , cache , path , prop_name , stk_name )
465+ } else if ( exist ) {
466+ changed |= processReplace ( cache , path , prop_name )
467+ }
468+ } ,
469+ } ,
470+ } )
471+ const binding = body_path . scope . getBinding ( stk_name )
472+ binding . scope . crawl ( )
473+ return changed
474+ }
475+
476+ function processStackParam ( path , len ) {
477+ if ( path . isArrowFunctionExpression ( ) ) {
478+ console . log ( `Process arrowFunctionExpression, len: ${ len } ` )
479+ } else if ( path . isFunctionExpression ( ) ) {
480+ console . log ( `Process functionExpression, len: ${ len } ` )
481+ } else {
482+ console . log ( `Process Function ${ path . node . id . name } , len: ${ len } ` )
483+ }
484+ let changed = true
485+ const invalid = checkStackInvalid ( path )
486+ while ( changed ) {
487+ changed = tryStackReplace ( path , len , invalid )
488+ path . traverse ( calculateConstantExp )
489+ }
490+ }
491+
302492const deStackFuncLen = {
303493 Identifier ( path ) {
304494 let obj = checkFuncLen ( path )
@@ -314,10 +504,15 @@ const deStackFuncLen = {
314504 }
315505 const repl_path = ref . parentPath
316506 const arg = repl_path . node . arguments [ 0 ]
507+ const len = repl_path . node . arguments [ 1 ] . value
317508 if ( t . isIdentifier ( arg ) ) {
509+ const func_name = arg . name
510+ const func_decl = repl_path . scope . getBinding ( func_name ) . path
511+ processStackParam ( func_decl , len )
318512 repl_path . remove ( )
319513 } else {
320514 repl_path . replaceWith ( arg )
515+ processStackParam ( repl_path , len )
321516 }
322517 }
323518 binding . scope . crawl ( )
0 commit comments