1111import io .codemodder .Either ;
1212import io .codemodder .ast .ASTTransforms ;
1313import io .codemodder .ast .ASTs ;
14+ import io .codemodder .ast .ExpressionStmtVariableDeclaration ;
15+ import io .codemodder .ast .LocalScope ;
1416import io .codemodder .ast .LocalVariableDeclaration ;
1517import io .codemodder .ast .TryResourceDeclaration ;
1618import java .util .*;
@@ -174,44 +176,85 @@ private Optional<MethodCallExpr> validateExecuteCall(final MethodCallExpr execut
174176 * the call itself, if immediate, or the {@link LocalVariableDeclaration} that has it as an
175177 * initializer.
176178 */
177- private Optional <Either <MethodCallExpr , LocalVariableDeclaration >> findStatementCreationExpr (
178- final MethodCallExpr executeCall ) {
179- // Is itself a MethodCallExpr like <conn>.createStatement
179+ private Optional <Either <MethodCallExpr , Either < AssignExpr , LocalVariableDeclaration >>>
180+ findStatementCreationExpr ( final MethodCallExpr executeCall ) {
181+ // Has the form: <conn>.createStatement().executeQuery(...)
180182 // We require <conn> to be a nameExpr in this case.
181- final Optional <Either <MethodCallExpr , LocalVariableDeclaration >> maybeImmediate =
182- executeCall
183- .getScope ()
184- .flatMap (this ::isConnectionCreateStatement )
185- // .filter(scope -> scope.getScope().filter(Expression::isNameExpr).isPresent())
186- .map (Either ::left );
183+ final Optional <Either <MethodCallExpr , Either <AssignExpr , LocalVariableDeclaration >>>
184+ maybeImmediate =
185+ executeCall
186+ .getScope ()
187+ .flatMap (this ::isConnectionCreateStatement )
188+ // .filter(scope -> scope.getScope().filter(Expression::isNameExpr).isPresent())
189+ .map (Either ::left );
190+
191+ if (maybeImmediate .isPresent ()) {
192+ return maybeImmediate ;
193+ }
187194
188- // Is a NameExpr that is initialized with <conn>.createStatement
189- final Optional <Either <MethodCallExpr , LocalVariableDeclaration >> maybeLVD =
195+ // Has the form: <stmt>.executeQuery()
196+ // Find the <stmt> declaration
197+ final Optional <LocalVariableDeclaration > maybeLVD =
190198 executeCall
191199 .getScope ()
192200 .map (expr -> expr instanceof NameExpr ? expr .asNameExpr () : null )
193- .flatMap (ne -> ASTs .findEarliestLocalVariableDeclarationOf (ne , ne .getNameAsString ()))
194- .filter (
195- lvd ->
196- lvd .getVariableDeclarator ()
197- .getInitializer ()
198- .map (this ::isConnectionCreateStatement )
199- .isPresent ())
200- .map (Either ::right );
201-
202- return maybeImmediate .or (() -> maybeLVD );
201+ .flatMap (ne -> ASTs .findEarliestLocalVariableDeclarationOf (ne , ne .getNameAsString ()));
202+
203+ // Has a single assignment
204+ // We erroniously assume that it always shadows the init expression
205+ // Needs some flow analysis to correctly address this case
206+ final Optional <AssignExpr > maybeSingleAssigned =
207+ maybeLVD
208+ .map (lvd -> ASTs .findAllAssignments (lvd ).limit (2 ).toList ())
209+ .filter (allAssignments -> allAssignments .size () == 1 )
210+ .map (allAssignments -> allAssignments .get (0 ))
211+ .filter (assign -> assign .getTarget ().isNameExpr ())
212+ .filter (assign -> isConnectionCreateStatement (assign .getValue ()).isPresent ());
213+
214+ if (maybeSingleAssigned .isPresent ()) {
215+ return maybeSingleAssigned .map (a -> Either .right (Either .left (a )));
216+ }
217+
218+ // Is <stmt> initialized with <conn>.createStatement()?
219+ final Optional <LocalVariableDeclaration > maybeInitExpr =
220+ maybeLVD .filter (
221+ lvd ->
222+ lvd .getVariableDeclarator ()
223+ .getInitializer ()
224+ .map (this ::isConnectionCreateStatement )
225+ .isPresent ());
226+
227+ return maybeInitExpr .map (init -> Either .right (Either .right (init )));
203228 }
204229
205- private Optional <Either <MethodCallExpr , LocalVariableDeclaration >> validateStatementCreationExpr (
206- final Either <MethodCallExpr , LocalVariableDeclaration > stmtObject ) {
207- if (stmtObject .isRight () && !canChangeTypes (stmtObject .getRight ())) {
208- return Optional .empty ();
209- }
230+ private Optional <Either <MethodCallExpr , Either <AssignExpr , LocalVariableDeclaration >>>
231+ validateStatementCreationExpr (
232+ final Either <MethodCallExpr , Either <AssignExpr , LocalVariableDeclaration >> stmtObject ) {
210233 if (stmtObject .isRight ()
211- && stmtObject .getRight () instanceof TryResourceDeclaration
212- && !validateTryResource (( TryResourceDeclaration ) stmtObject .getRight (), executeCall )) {
234+ && stmtObject .getRight (). isRight ()
235+ && !canChangeTypes ( stmtObject . getRight () .getRight ())) {
213236 return Optional .empty ();
214237 }
238+ if (stmtObject .isRight ()) {
239+ // For the assignment case, the declaration must be from an ExpressionStmt
240+ if (stmtObject .getRight ().isLeft ()) {
241+ final var maybelvd =
242+ ASTs .findEarliestLocalVariableDeclarationOf (
243+ stmtObject .getRight ().getLeft (),
244+ stmtObject .getRight ().getLeft ().getTarget ().asNameExpr ().getNameAsString ())
245+ .filter (lvd -> lvd instanceof ExpressionStmtVariableDeclaration );
246+ if (maybelvd .isEmpty ()) {
247+ return Optional .empty ();
248+ }
249+
250+ } else {
251+ if (stmtObject .getRight ().getRight () instanceof TryResourceDeclaration
252+ && !validateTryResource (
253+ (TryResourceDeclaration ) stmtObject .getRight ().getRight (), executeCall )) {
254+ return Optional .empty ();
255+ }
256+ }
257+ }
215258 return Optional .of (stmtObject );
216259 }
217260
@@ -388,31 +431,37 @@ private Expression buildParameter(
388431 * <p>(3) Change <stmtCreation>.execute*() to pstmt.execute().
389432 */
390433 private MethodCallExpr fix (
391- final Either <MethodCallExpr , LocalVariableDeclaration > stmtCreation ,
434+ final Either <MethodCallExpr , Either < AssignExpr , LocalVariableDeclaration > > stmtCreation ,
392435 final QueryParameterizer queryParameterizer ,
393436 final MethodCallExpr executeCall ) {
394437
395438 var executeStmt = ASTs .findParentStatementFrom (executeCall ).get ();
396439 // (0)
397- if (stmtCreation .isRight () && executeStmt == stmtCreation .getRight ().getStatement ()) {
440+ if (stmtCreation .isRight ()
441+ && stmtCreation .getRight ().isRight ()
442+ && executeStmt == stmtCreation .getRight ().getRight ().getStatement ()) {
398443 final int stmtObjectIndex =
399444 stmtCreation
445+ .getRight ()
400446 .getRight ()
401447 .getStatement ()
402448 .asTryStmt ()
403449 .getResources ()
404- .indexOf (stmtCreation .getRight ().getVariableDeclarationExpr ());
450+ .indexOf (stmtCreation .getRight ().getRight (). getVariableDeclarationExpr ());
405451
406452 executeStmt =
407453 ASTTransforms .splitResources (
408- stmtCreation .getRight ().getStatement ().asTryStmt (), stmtObjectIndex )
454+ stmtCreation .getRight ().getRight (). getStatement ().asTryStmt (), stmtObjectIndex )
409455 .getTryBlock ()
410456 .getStatement (0 );
411457 }
412458
413459 final String stmtName =
414460 stmtCreation .ifLeftOrElseGet (
415- mce -> generateNameWithSuffix (preparedStatementNamePrefix , mce ), lvd -> lvd .getName ());
461+ mce -> generateNameWithSuffix (preparedStatementNamePrefix , mce ),
462+ assignOrLVD ->
463+ assignOrLVD .ifLeftOrElseGet (
464+ a -> a .getTarget ().asNameExpr ().getNameAsString (), lvd -> lvd .getName ()));
416465
417466 // (1)
418467 final var combinedExpressions =
@@ -423,7 +472,8 @@ private MethodCallExpr fix(
423472 var topStatement = executeStmt ;
424473 for (int i = combinedExpressions .size () - 1 ; i >= 0 ; i --) {
425474 final var expr = combinedExpressions .get (i );
426- final var setStmt =
475+ ExpressionStmt setStmt = null ;
476+ setStmt =
427477 new ExpressionStmt (
428478 new MethodCallExpr (
429479 new NameExpr (stmtName ),
@@ -441,12 +491,15 @@ private MethodCallExpr fix(
441491 args .addAll (
442492 stmtCreation .ifLeftOrElseGet (
443493 mce -> mce .getArguments (),
444- lvd ->
445- lvd .getVariableDeclarator ()
446- .getInitializer ()
447- .get ()
448- .asMethodCallExpr ()
449- .getArguments ()));
494+ assignOrLVD ->
495+ assignOrLVD .ifLeftOrElseGet (
496+ a -> a .getValue ().asMethodCallExpr ().getArguments (),
497+ lvd ->
498+ lvd .getVariableDeclarator ()
499+ .getInitializer ()
500+ .get ()
501+ .asMethodCallExpr ()
502+ .getArguments ())));
450503
451504 // (3)
452505 executeCall .setName ("execute" );
@@ -467,29 +520,82 @@ private MethodCallExpr fix(
467520
468521 // (2.b)
469522 } else {
470- stmtCreation
471- .getRight ()
472- .getVariableDeclarator ()
473- .setType (StaticJavaParser .parseType ("PreparedStatement" ));
474- stmtCreation
475- .getRight ()
476- .getVariableDeclarator ()
477- .getInitializer ()
478- .ifPresent (expr -> expr .asMethodCallExpr ().setName ("prepareStatement" ));
479- stmtCreation
480- .getRight ()
481- .getVariableDeclarator ()
482- .getInitializer ()
483- .ifPresent (expr -> expr .asMethodCallExpr ().setArguments (args ));
484- pstmtCreation =
485- stmtCreation .getRight ().getVariableDeclarator ().getInitializer ().get ().asMethodCallExpr ();
523+ final var assignOrLVD = stmtCreation .getRight ();
524+ if (assignOrLVD .isLeft ()) {
525+ pstmtCreation = assignOrLVD .getLeft ().getValue ().asMethodCallExpr ();
526+ pstmtCreation .setArguments (args );
527+ pstmtCreation .setName ("prepareStatement" );
528+
529+ // change the assignment
530+ assignOrLVD .getLeft ().setValue (StaticJavaParser .parseExpression ("a" ));
531+ assignOrLVD .getLeft ().setValue (pstmtCreation );
532+
533+ // change the initialization to be null and its type to PreparedStatement
534+ // This will only work assuming a single shadowing assignment, may require changes here in
535+ // the future
536+ var maybeLVD =
537+ ASTs .findEarliestLocalVariableDeclarationOf (
538+ assignOrLVD .getLeft ().getTarget (),
539+ assignOrLVD .getLeft ().getTarget ().asNameExpr ().getNameAsString ());
540+ if (maybeLVD .isPresent ()) {
541+ var vd = maybeLVD .get ().getVariableDeclarator ();
542+ vd .setInitializer (new NullLiteralExpr ());
543+ vd .setType (StaticJavaParser .parseType ("PreparedStatement" ));
544+ }
545+
546+ } else {
547+ assignOrLVD
548+ .getRight ()
549+ .getVariableDeclarator ()
550+ .setType (StaticJavaParser .parseType ("PreparedStatement" ));
551+ assignOrLVD
552+ .getRight ()
553+ .getVariableDeclarator ()
554+ .getInitializer ()
555+ .ifPresent (expr -> expr .asMethodCallExpr ().setName ("prepareStatement" ));
556+ assignOrLVD
557+ .getRight ()
558+ .getVariableDeclarator ()
559+ .getInitializer ()
560+ .ifPresent (expr -> expr .asMethodCallExpr ().setArguments (args ));
561+ pstmtCreation =
562+ assignOrLVD
563+ .getRight ()
564+ .getVariableDeclarator ()
565+ .getInitializer ()
566+ .get ()
567+ .asMethodCallExpr ();
568+ }
486569 }
487570 return pstmtCreation ;
488571 }
489572
490- private boolean assignedOrDefinedInScope (NameExpr name , LocalVariableDeclaration lvd ) {
573+ private boolean resolvedInScope (
574+ final Either <AssignExpr , LocalVariableDeclaration > assignOrLVD , Expression expr ) {
575+ if (assignOrLVD .isLeft ()) {
576+ final var scope =
577+ LocalScope .fromAssignExpression (
578+ assignOrLVD
579+ .getLeft ()); // Unsupported case for scope calculation, fail here until we add
580+ // some
581+ if (scope .stream ().findAny ().isEmpty ()) {
582+ return true ;
583+ }
584+ return scope .inScope (expr );
585+ }
586+ return assignOrLVD .getRight ().getScope ().inScope (expr );
587+ }
588+
589+ private boolean assignedOrDefinedInScope (
590+ final NameExpr name , final Either <AssignExpr , LocalVariableDeclaration > assignOrLVD ) {
591+ final var scope =
592+ assignOrLVD .ifLeftOrElseGet (a -> LocalScope .fromAssignExpression (a ), lvd -> lvd .getScope ());
593+ // Unsupported case for scope calculation, fail here until we add some
594+ if (scope .stream ().findAny ().isEmpty ()) {
595+ return true ;
596+ }
491597 final Stream <AssignExpr > assignmentsInScope =
492- lvd . getScope () .stream ()
598+ scope .stream ()
493599 .flatMap (
494600 node -> node instanceof AssignExpr ? Stream .of ((AssignExpr ) node ) : Stream .empty ());
495601
@@ -500,7 +606,7 @@ private boolean assignedOrDefinedInScope(NameExpr name, LocalVariableDeclaration
500606
501607 final boolean definedInScope =
502608 ASTs .findNonCallableSimpleNameSource (name .getName ())
503- .filter (source -> lvd . getScope () .inScope (source ))
609+ .filter (source -> scope .inScope (source ))
504610 .isPresent ();
505611
506612 return assignedInScope || definedInScope ;
@@ -521,6 +627,7 @@ public Optional<MethodCallExpr> checkAndFix() {
521627 // Now find the stmt creation expression, if any and validate it
522628 final var stmtObject =
523629 findStatementCreationExpr (executeCall ).flatMap (this ::validateStatementCreationExpr );
630+
524631 if (stmtObject .isPresent ()) {
525632 // Now look for injections
526633 final QueryParameterizer queryp ;
@@ -536,22 +643,22 @@ public Optional<MethodCallExpr> checkAndFix() {
536643 .get ()
537644 .ifLeftOrElseGet (
538645 mcd -> false ,
539- stmtLVD ->
646+ assignOrLVD ->
540647 queryp .getLinearizedQuery ().getResolvedExpressionsMap ().keySet ().stream ()
541- .anyMatch (expr -> stmtLVD . getScope (). inScope ( expr )));
648+ .anyMatch (expr -> resolvedInScope ( assignOrLVD , expr )));
542649
543- // Is any name in the linearized expression defined/assigned inside the scope of the
650+ //// Is any name in the linearized expression defined/assigned inside the scope of the
544651 // Statement Object?
545652 final boolean nameInScope =
546653 stmtObject
547654 .get ()
548655 .ifLeftOrElseGet (
549656 mcd -> false ,
550- stmtLVD ->
657+ assignOrLVD ->
551658 queryp .getLinearizedQuery ().getLinearized ().stream ()
552659 .filter (expr -> expr .isNameExpr ())
553660 .map (expr -> expr .asNameExpr ())
554- .anyMatch (name -> assignedOrDefinedInScope (name , stmtLVD )));
661+ .anyMatch (name -> assignedOrDefinedInScope (name , assignOrLVD )));
555662
556663 if (queryp .getInjections ().isEmpty () || resolvedInScope || nameInScope ) {
557664 return Optional .empty ();
0 commit comments