Skip to content

Commit

Permalink
Avoid generate @DeleGate method body NPE when deletegate target is nu…
Browse files Browse the repository at this point in the history
…ll on javac's HandleDelegate
  • Loading branch information
eeoun committed Feb 11, 2025
1 parent 47cb06e commit 9764189
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 18 deletions.
19 changes: 13 additions & 6 deletions src/core/lombok/experimental/Delegate.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
/*
* Copyright (C) 2010-2017 The Project Lombok Authors.
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Expand All @@ -28,14 +28,14 @@

/**
* Put on any field to make lombok generate delegate methods that forward the call to this field.
*
* <p>
* Example:
* <pre>
* private &#64;Delegate List&lt;String&gt; foo;
* </pre>
*
* <p>
* will generate for example an {@code boolean add(String)} method, which contains: {@code return foo.add(arg);}, as well as all other methods in {@code List}.
*
* <p>
* All public instance methods of the field's type, as well as all public instance methods of all the field's type's superfields are delegated, except for all methods
* that exist in {@link Object}, the {@code canEqual(Object)} method, and any methods that appear in types
* that are listed in the {@code excludes} property.
Expand Down Expand Up @@ -65,4 +65,11 @@
* @return For each method (not already in {@code java.lang.Object}) in these types, skip generating a delegate method (overrides {@code types()}).
*/
Class<?>[] excludes() default {};

/**
* If configuration turn to true. When field is null and target type delegate method return as a references type, just return a null instead of throw a NPE
*
* @return Is it needed to avoid NPE make the utmost effort.
*/
boolean skipAndReturnNullOnNullRef() default false;
}
32 changes: 20 additions & 12 deletions src/core/lombok/javac/handlers/HandleDelegate.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

import static com.sun.tools.javac.code.Flags.*;
import static lombok.core.handlers.HandlerUtil.handleExperimentalFlagUsage;
import static lombok.javac.Javac.CTC_EQUAL;
import static lombok.javac.Javac.CTC_BOT;
import static lombok.javac.handlers.JavacHandlerUtil.*;

import java.lang.annotation.Annotation;
Expand Down Expand Up @@ -72,13 +74,8 @@
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.experimental.Delegate;
import lombok.javac.FindTypeVarScanner;
import lombok.javac.JavacAnnotationHandler;
import lombok.javac.JavacNode;
import lombok.javac.JavacResolution;
import lombok.javac.*;
import lombok.javac.JavacResolution.TypeNotConvertibleException;
import lombok.javac.JavacTreeMaker;
import lombok.javac.ResolutionResetNeeded;
import lombok.permit.Permit;
import lombok.spi.Provides;

Expand Down Expand Up @@ -111,7 +108,7 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> {

@SuppressWarnings("deprecation") Class<? extends Annotation> oldDelegate = lombok.Delegate.class;
deleteAnnotationIfNeccessary(annotationNode, Delegate.class, oldDelegate);

Type delegateType;
Name delegateName = annotationNode.toName(annotationNode.up().getName());
DelegateReceiver delegateReceiver;
Expand Down Expand Up @@ -211,16 +208,16 @@ public class HandleDelegate extends JavacAnnotationHandler<Delegate> {
}
}

for (MethodSig sig : signaturesToDelegate) generateAndAdd(sig, annotationNode, delegateName, delegateReceiver);
for (MethodSig sig : signaturesToDelegate) generateAndAdd(sig, annotationNode, delegateName, delegateReceiver, annotation.getInstance().skipAndReturnNullOnNullRef());
} catch (DelegateRecursion e) {
annotationNode.addError(String.format(RECURSION_NOT_ALLOWED, e.member, e.type));
}
}

public void generateAndAdd(MethodSig sig, JavacNode annotation, Name delegateName, DelegateReceiver delegateReceiver) {
public void generateAndAdd(MethodSig sig, JavacNode annotation, Name delegateName, DelegateReceiver delegateReceiver, boolean tryToAvoidNOE) {
List<JCMethodDecl> toAdd = new ArrayList<JCMethodDecl>();
try {
toAdd.add(createDelegateMethod(sig, annotation, delegateName, delegateReceiver));
toAdd.add(createDelegateMethod(sig, annotation, delegateName, delegateReceiver, tryToAvoidNOE));
} catch (TypeNotConvertibleException e) {
annotation.addError("Can't create delegate method for " + sig.name + ": " + e.getMessage());
return;
Expand Down Expand Up @@ -285,7 +282,7 @@ public void checkConflictOfTypeVarNames(MethodSig sig, JavacNode annotation) thr
}
}

public JCMethodDecl createDelegateMethod(MethodSig sig, JavacNode annotation, Name delegateName, DelegateReceiver delegateReceiver) throws TypeNotConvertibleException, CantMakeDelegates {
public JCMethodDecl createDelegateMethod(MethodSig sig, JavacNode annotation, Name delegateName, DelegateReceiver delegateReceiver, boolean tryToAvoidNOE) throws TypeNotConvertibleException, CantMakeDelegates {
/* public <T, U, ...> ReturnType methodName(ParamType1 name1, ParamType2 name2, ...) throws T1, T2, ... {
* (return) delegate.<T, U>methodName(name1, name2);
* }
Expand Down Expand Up @@ -346,7 +343,18 @@ public JCMethodDecl createDelegateMethod(MethodSig sig, JavacNode annotation, Na

JCExpression delegateCall = maker.Apply(toList(typeArgs), maker.Select(delegateReceiver.get(annotation, delegateName), sig.name), toList(args));
JCStatement body = useReturn ? maker.Return(delegateCall) : maker.Exec(delegateCall);
JCBlock bodyBlock = maker.Block(0, com.sun.tools.javac.util.List.of(body));
JCBlock bodyBlock = null;
// only worked when avoid NPE been told and target method not return primitive type
if (tryToAvoidNOE && !Javac.isPrimitive(returnType)){
bodyBlock = maker.Block(0, com.sun.tools.javac.util.List.of(
maker.If(maker.Binary(CTC_EQUAL, delegateReceiver.get(annotation, delegateName), maker.Literal(CTC_BOT, null))
, maker.Return(useReturn ? maker.Literal(CTC_BOT, null) : null)
, null
)
, body));
}else{
bodyBlock = maker.Block(0, com.sun.tools.javac.util.List.of(body));
}

return recursiveSetGeneratedBy(maker.MethodDef(mods, sig.name, returnType, toList(typeParams), toList(params), toList(thrown), bodyBlock, null), annotation);
}
Expand Down

0 comments on commit 9764189

Please sign in to comment.