From 9764189144e5d450b22949a0488811cfbf5df148 Mon Sep 17 00:00:00 2001 From: liyang Date: Sat, 18 Jan 2025 00:59:58 +0800 Subject: [PATCH] Avoid generate @Delegate method body NPE when deletegate target is null on javac's HandleDelegate --- src/core/lombok/experimental/Delegate.java | 19 +++++++---- .../lombok/javac/handlers/HandleDelegate.java | 32 ++++++++++++------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/core/lombok/experimental/Delegate.java b/src/core/lombok/experimental/Delegate.java index cc84452642..e68bf5422f 100644 --- a/src/core/lombok/experimental/Delegate.java +++ b/src/core/lombok/experimental/Delegate.java @@ -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 @@ -28,14 +28,14 @@ /** * Put on any field to make lombok generate delegate methods that forward the call to this field. - * + *

* Example: *

  *     private @Delegate List<String> foo;
  * 
- * + *

* 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}. - * + *

* 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. @@ -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; } diff --git a/src/core/lombok/javac/handlers/HandleDelegate.java b/src/core/lombok/javac/handlers/HandleDelegate.java index 4d72eea32c..a88d7206eb 100644 --- a/src/core/lombok/javac/handlers/HandleDelegate.java +++ b/src/core/lombok/javac/handlers/HandleDelegate.java @@ -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; @@ -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; @@ -111,7 +108,7 @@ public class HandleDelegate extends JavacAnnotationHandler { @SuppressWarnings("deprecation") Class oldDelegate = lombok.Delegate.class; deleteAnnotationIfNeccessary(annotationNode, Delegate.class, oldDelegate); - + Type delegateType; Name delegateName = annotationNode.toName(annotationNode.up().getName()); DelegateReceiver delegateReceiver; @@ -211,16 +208,16 @@ public class HandleDelegate extends JavacAnnotationHandler { } } - 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 toAdd = new ArrayList(); 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; @@ -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 ReturnType methodName(ParamType1 name1, ParamType2 name2, ...) throws T1, T2, ... { * (return) delegate.methodName(name1, name2); * } @@ -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); }