aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/lombok/EqualsAndHashCode.java23
-rwxr-xr-xsrc/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java105
-rw-r--r--src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java68
-rwxr-xr-xsrc/delombok/lombok/delombok/Delombok.java8
-rw-r--r--src/stubs/com/sun/tools/javac/main/Arguments.java3
5 files changed, 190 insertions, 17 deletions
diff --git a/src/core/lombok/EqualsAndHashCode.java b/src/core/lombok/EqualsAndHashCode.java
index 6805d214..279e6e3d 100644
--- a/src/core/lombok/EqualsAndHashCode.java
+++ b/src/core/lombok/EqualsAndHashCode.java
@@ -71,6 +71,14 @@ public @interface EqualsAndHashCode {
* @return If {@code true}, always use direct field access instead of calling the getter method.
*/
boolean doNotUseGetters() default false;
+
+ /**
+ * Determines how the result of the {@code hashCode} method will be cached.
+ * <strong>default: {@link CacheStrategy#NEVER}</strong>
+ *
+ * @return The {@code hashCode} cache strategy to be used.
+ */
+ CacheStrategy cacheStrategy() default CacheStrategy.NEVER;
/**
* Any annotations listed here are put on the generated parameter of {@code equals} and {@code canEqual}.
@@ -132,4 +140,19 @@ public @interface EqualsAndHashCode {
*/
int rank() default 0;
}
+
+ public enum CacheStrategy {
+ /**
+ * Never cache. Perform the calculation every time the method is called.
+ */
+ NEVER,
+ /**
+ * Cache the result of the first invocation of {@code hashCode} and use it for subsequent invocations.
+ * This can improve performance if all fields used for calculating the {@code hashCode} are immutable
+ * and thus every invocation of {@code hashCode} will always return the same value.
+ * <strong>Do not use this if there's <em>any</em> chance that different invocations of {@code hashCode}
+ * might return different values.</strong>
+ */
+ LAZY
+ }
}
diff --git a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
index 7147343e..deb19c00 100755
--- a/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/eclipse/handlers/HandleEqualsAndHashCode.java
@@ -36,6 +36,7 @@ import java.util.Set;
import lombok.AccessLevel;
import lombok.ConfigurationKeys;
import lombok.EqualsAndHashCode;
+import lombok.EqualsAndHashCode.CacheStrategy;
import lombok.core.AST.Kind;
import lombok.core.handlers.HandlerUtil;
import lombok.core.handlers.InclusionExclusionUtils;
@@ -59,6 +60,8 @@ import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
import org.eclipse.jdt.internal.compiler.ast.EqualExpression;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FalseLiteral;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.IfStatement;
import org.eclipse.jdt.internal.compiler.ast.InstanceOfExpression;
import org.eclipse.jdt.internal.compiler.ast.IntLiteral;
@@ -94,6 +97,9 @@ import org.mangosdk.spi.ProviderFor;
@ProviderFor(EclipseAnnotationHandler.class)
public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndHashCode> {
+ private static final String HASH_CODE_CACHE_NAME = "$hashCodeCache";
+
+ private final char[] HASH_CODE_CACHE_NAME_ARR = HASH_CODE_CACHE_NAME.toCharArray();
private final char[] PRIME = "PRIME".toCharArray();
private final char[] RESULT = "result".toCharArray();
@@ -116,7 +122,9 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
- generateMethods(annotationNode.up(), annotationNode, members, callSuper, true, fieldAccess, onParam);
+ boolean cacheHashCode = ann.cacheStrategy() == CacheStrategy.LAZY;
+
+ generateMethods(annotationNode.up(), annotationNode, members, callSuper, true, cacheHashCode, fieldAccess, onParam);
}
public void generateEqualsAndHashCodeForType(EclipseNode typeNode, EclipseNode errorNode) {
@@ -130,11 +138,11 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
Boolean doNotUseGettersConfiguration = typeNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS);
FieldAccess access = doNotUseGettersConfiguration == null || !doNotUseGettersConfiguration ? FieldAccess.GETTER : FieldAccess.PREFER_FIELD;
- generateMethods(typeNode, errorNode, members, null, false, access, new ArrayList<Annotation>());
+ generateMethods(typeNode, errorNode, members, null, false, false, access, new ArrayList<Annotation>());
}
public void generateMethods(EclipseNode typeNode, EclipseNode errorNode, List<Included<EclipseNode, EqualsAndHashCode.Include>> members,
- Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<Annotation> onParam) {
+ Boolean callSuper, boolean whineIfExists, boolean cacheHashCode, FieldAccess fieldAccess, List<Annotation> onParam) {
TypeDeclaration typeDecl = null;
@@ -222,12 +230,33 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
injectMethod(typeNode, canEqualMethod);
}
- MethodDeclaration hashCodeMethod = createHashCode(typeNode, members, callSuper, errorNode.get(), fieldAccess);
+ if (cacheHashCode){
+ if (fieldExists(HASH_CODE_CACHE_NAME, typeNode) != MemberExistsResult.NOT_EXISTS) {
+ String msg = String.format("Not caching the result of hashCode: A field named %s already exists.", HASH_CODE_CACHE_NAME);
+ errorNode.addWarning(msg);
+ cacheHashCode = false;
+ } else {
+ createHashCodeCacheField(typeNode, errorNode.get());
+ }
+ }
+
+ MethodDeclaration hashCodeMethod = createHashCode(typeNode, members, callSuper, cacheHashCode, errorNode.get(), fieldAccess);
hashCodeMethod.traverse(new SetGeneratedByVisitor(errorNode.get()), ((TypeDeclaration)typeNode.get()).scope);
injectMethod(typeNode, hashCodeMethod);
}
+
+ private void createHashCodeCacheField(EclipseNode typeNode, ASTNode source) {
+ FieldDeclaration hashCodeCacheDecl = new FieldDeclaration(HASH_CODE_CACHE_NAME_ARR, 0, 0);
+ hashCodeCacheDecl.modifiers = ClassFileConstants.AccPrivate | ClassFileConstants.AccTransient;
+ hashCodeCacheDecl.bits |= Eclipse.ECLIPSE_DO_NOT_TOUCH_FLAG;
+ hashCodeCacheDecl.type = TypeReference.baseTypeReference(TypeIds.T_int, 0);
+ hashCodeCacheDecl.declarationSourceEnd = -1;
+ injectFieldAndMarkGenerated(typeNode, hashCodeCacheDecl);
+ setGeneratedBy(hashCodeCacheDecl, source);
+ setGeneratedBy(hashCodeCacheDecl.type, source);
+ }
- public MethodDeclaration createHashCode(EclipseNode type, Collection<Included<EclipseNode, EqualsAndHashCode.Include>> members, boolean callSuper, ASTNode source, FieldAccess fieldAccess) {
+ public MethodDeclaration createHashCode(EclipseNode type, Collection<Included<EclipseNode, EqualsAndHashCode.Include>> members, boolean callSuper, boolean cacheHashCode, ASTNode source, FieldAccess fieldAccess) {
int pS = source.sourceStart, pE = source.sourceEnd;
long p = (long) pS << 32 | pE;
@@ -238,7 +267,10 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
method.returnType = TypeReference.baseTypeReference(TypeIds.T_int, 0);
setGeneratedBy(method.returnType, source);
Annotation overrideAnnotation = makeMarkerAnnotation(TypeConstants.JAVA_LANG_OVERRIDE, source);
- if (getCheckerFrameworkVersion(type).generateSideEffectFree()) {
+ CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(type);
+ if (cacheHashCode && checkerFramework.generatePure()) {
+ method.annotations = new Annotation[] { overrideAnnotation, generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__PURE) };
+ } else if (checkerFramework.generateSideEffectFree()) {
method.annotations = new Annotation[] { overrideAnnotation, generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE) };
} else {
method.annotations = new Annotation[] { overrideAnnotation };
@@ -262,6 +294,22 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
}
}
+ /* if (this.$hashCodeCache != 0) return this.$hashCodeCache; */ {
+ if (cacheHashCode) {
+ FieldReference hashCodeCacheRef = new FieldReference(HASH_CODE_CACHE_NAME_ARR, p);
+ hashCodeCacheRef.receiver = new ThisReference(pS, pE);
+ setGeneratedBy(hashCodeCacheRef, source);
+ setGeneratedBy(hashCodeCacheRef.receiver, source);
+ EqualExpression cacheNotZero = new EqualExpression(hashCodeCacheRef, makeIntLiteral("0".toCharArray(), source), OperatorIds.NOT_EQUAL);
+ setGeneratedBy(cacheNotZero, source);
+ ReturnStatement returnCache = new ReturnStatement(hashCodeCacheRef, pS, pE);
+ setGeneratedBy(returnCache, source);
+ IfStatement ifStatement = new IfStatement(cacheNotZero, returnCache, pS, pE);
+ setGeneratedBy(ifStatement, source);
+ statements.add(ifStatement);
+ }
+ }
+
/* final int PRIME = X; */ {
/* Without members, PRIME isn't used, as that would trigger a 'local variable not used' warning. */
if (!isEmpty) {
@@ -296,7 +344,7 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
resultDecl.initialization = init;
resultDecl.type = TypeReference.baseTypeReference(TypeIds.T_int, 0);
resultDecl.type.sourceStart = pS; resultDecl.type.sourceEnd = pE;
- if (isEmpty) resultDecl.modifiers |= Modifier.FINAL;
+ if (isEmpty && !cacheHashCode) resultDecl.modifiers |= Modifier.FINAL;
setGeneratedBy(resultDecl.type, source);
statements.add(resultDecl);
}
@@ -391,6 +439,49 @@ public class HandleEqualsAndHashCode extends EclipseAnnotationHandler<EqualsAndH
}
}
+ /*
+ * if (result == 0) result = Integer.MIN_VALUE;
+ * this.$hashCodeCache = result;
+ *
+ */ {
+ if (cacheHashCode) {
+ SingleNameReference resultRef = new SingleNameReference(RESULT, p);
+ setGeneratedBy(resultRef, source);
+
+ EqualExpression resultIsZero = new EqualExpression(resultRef, makeIntLiteral("0".toCharArray(), source), OperatorIds.EQUAL_EQUAL);
+ setGeneratedBy(resultIsZero, source);
+
+ resultRef = new SingleNameReference(RESULT, p);
+ setGeneratedBy(resultRef, source);
+
+ FieldReference integerMinValue = new FieldReference("MIN_VALUE".toCharArray(), p);
+ integerMinValue.receiver = generateQualifiedNameRef(source, TypeConstants.JAVA_LANG_INTEGER);
+ setGeneratedBy(integerMinValue, source);
+
+ Assignment newResult = new Assignment(resultRef, integerMinValue, pE);
+ newResult.sourceStart = pS; newResult.statementEnd = newResult.sourceEnd = pE;
+ setGeneratedBy(newResult, source);
+
+ IfStatement ifStatement = new IfStatement(resultIsZero, newResult, pS, pE);
+ setGeneratedBy(ifStatement, source);
+ statements.add(ifStatement);
+
+
+ FieldReference hashCodeCacheRef = new FieldReference(HASH_CODE_CACHE_NAME_ARR, p);
+ hashCodeCacheRef.receiver = new ThisReference(pS, pE);
+ setGeneratedBy(hashCodeCacheRef, source);
+ setGeneratedBy(hashCodeCacheRef.receiver, source);
+
+ resultRef = new SingleNameReference(RESULT, p);
+ setGeneratedBy(resultRef, source);
+
+ Assignment cacheResult = new Assignment(hashCodeCacheRef, resultRef, pE);
+ cacheResult.sourceStart = pS; cacheResult.statementEnd = cacheResult.sourceEnd = pE;
+ setGeneratedBy(cacheResult, source);
+ statements.add(cacheResult);
+ }
+ }
+
/* return result; */ {
SingleNameReference resultRef = new SingleNameReference(RESULT, p);
setGeneratedBy(resultRef, source);
diff --git a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
index 3935ab4e..c65fa491 100644
--- a/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
+++ b/src/core/lombok/javac/handlers/HandleEqualsAndHashCode.java
@@ -42,6 +42,8 @@ import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
+import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCMethodInvocation;
import com.sun.tools.javac.tree.JCTree.JCModifiers;
@@ -56,6 +58,7 @@ import com.sun.tools.javac.util.Name;
import lombok.ConfigurationKeys;
import lombok.EqualsAndHashCode;
+import lombok.EqualsAndHashCode.CacheStrategy;
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.configuration.CallSuperType;
@@ -76,11 +79,13 @@ import lombok.javac.handlers.JavacHandlerUtil.MemberExistsResult;
public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHashCode> {
private static final String RESULT_NAME = "result";
private static final String PRIME_NAME = "PRIME";
+ private static final String HASH_CODE_CACHE_NAME = "$hashCodeCache";
@Override public void handle(AnnotationValues<EqualsAndHashCode> annotation, JCAnnotation ast, JavacNode annotationNode) {
handleFlagUsage(annotationNode, ConfigurationKeys.EQUALS_AND_HASH_CODE_FLAG_USAGE, "@EqualsAndHashCode");
deleteAnnotationIfNeccessary(annotationNode, EqualsAndHashCode.class);
+ deleteImportFromCompilationUnit(annotationNode, CacheStrategy.class.getName());
EqualsAndHashCode ann = annotation.getInstance();
java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(annotationNode.up(), annotation, annotationNode);
JavacNode typeNode = annotationNode.up();
@@ -92,8 +97,10 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
Boolean doNotUseGettersConfiguration = annotationNode.getAst().readConfiguration(ConfigurationKeys.EQUALS_AND_HASH_CODE_DO_NOT_USE_GETTERS);
boolean doNotUseGetters = annotation.isExplicit("doNotUseGetters") || doNotUseGettersConfiguration == null ? ann.doNotUseGetters() : doNotUseGettersConfiguration;
FieldAccess fieldAccess = doNotUseGetters ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER;
-
- generateMethods(typeNode, annotationNode, members, callSuper, true, fieldAccess, onParam);
+
+ boolean cacheHashCode = ann.cacheStrategy() == CacheStrategy.LAZY;
+
+ generateMethods(typeNode, annotationNode, members, callSuper, true, cacheHashCode, fieldAccess, onParam);
}
public void generateEqualsAndHashCodeForType(JavacNode typeNode, JavacNode source) {
@@ -107,11 +114,11 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members = InclusionExclusionUtils.handleEqualsAndHashCodeMarking(typeNode, null, null);
- generateMethods(typeNode, source, members, null, false, access, List.<JCAnnotation>nil());
+ generateMethods(typeNode, source, members, null, false, false, access, List.<JCAnnotation>nil());
}
public void generateMethods(JavacNode typeNode, JavacNode source, java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members,
- Boolean callSuper, boolean whineIfExists, FieldAccess fieldAccess, List<JCAnnotation> onParam) {
+ Boolean callSuper, boolean whineIfExists, boolean cacheHashCode, FieldAccess fieldAccess, List<JCAnnotation> onParam) {
boolean notAClass = true;
if (typeNode.get() instanceof JCClassDecl) {
@@ -196,17 +203,39 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
injectMethod(typeNode, canEqualMethod);
}
- JCMethodDecl hashCodeMethod = createHashCode(typeNode, members, callSuper, fieldAccess, source.get());
+ if (cacheHashCode){
+ if (fieldExists(HASH_CODE_CACHE_NAME, typeNode) != MemberExistsResult.NOT_EXISTS) {
+ String msg = String.format("Not caching the result of hashCode: A field named %s already exists.", HASH_CODE_CACHE_NAME);
+ source.addWarning(msg);
+ cacheHashCode = false;
+ } else {
+ createHashCodeCacheField(typeNode, source.get());
+ }
+ }
+
+ JCMethodDecl hashCodeMethod = createHashCode(typeNode, members, callSuper, cacheHashCode, fieldAccess, source.get());
injectMethod(typeNode, hashCodeMethod);
}
+
+ private void createHashCodeCacheField(JavacNode typeNode, JCTree source) {
+ JavacTreeMaker maker = typeNode.getTreeMaker();
+ JCModifiers mods = maker.Modifiers(Flags.PRIVATE | Flags.TRANSIENT);
+ JCVariableDecl hashCodeCacheField = maker.VarDef(mods, typeNode.toName(HASH_CODE_CACHE_NAME), maker.TypeIdent(CTC_INT), null);
+ injectFieldAndMarkGenerated(typeNode, hashCodeCacheField);
+ recursiveSetGeneratedBy(hashCodeCacheField, source, typeNode.getContext());
+ }
- public JCMethodDecl createHashCode(JavacNode typeNode, java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members, boolean callSuper, FieldAccess fieldAccess, JCTree source) {
+ public JCMethodDecl createHashCode(JavacNode typeNode, java.util.List<Included<JavacNode, EqualsAndHashCode.Include>> members, boolean callSuper, boolean cacheHashCode, FieldAccess fieldAccess, JCTree source) {
JavacTreeMaker maker = typeNode.getTreeMaker();
JCAnnotation overrideAnnotation = maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.<JCExpression>nil());
List<JCAnnotation> annsOnMethod = List.of(overrideAnnotation);
CheckerFrameworkVersion checkerFramework = getCheckerFrameworkVersion(typeNode);
- if (checkerFramework.generateSideEffectFree()) annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil()));
+ if (cacheHashCode && checkerFramework.generatePure()) {
+ annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__PURE), List.<JCExpression>nil()));
+ } else if (checkerFramework.generateSideEffectFree()) {
+ annsOnMethod = annsOnMethod.prepend(maker.Annotation(genTypeRef(typeNode, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE), List.<JCExpression>nil()));
+ }
JCModifiers mods = maker.Modifiers(Flags.PUBLIC, annsOnMethod);
JCExpression returnType = maker.TypeIdent(CTC_INT);
ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
@@ -217,6 +246,15 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
boolean isEmpty = members.isEmpty();
+ /* if (this.$hashCodeCache != 0) return this.$hashCodeCache; */ {
+ if (cacheHashCode) {
+ JCIdent receiver = maker.Ident(typeNode.toName("this"));
+ JCFieldAccess cacheHashCodeFieldAccess = maker.Select(receiver, typeNode.toName(HASH_CODE_CACHE_NAME));
+ JCExpression cacheNotZero = maker.Binary(CTC_NOT_EQUAL, cacheHashCodeFieldAccess, maker.Literal(CTC_INT, 0));
+ statements.append(maker.If(cacheNotZero, maker.Return(cacheHashCodeFieldAccess), null));
+ }
+ }
+
/* final int PRIME = X; */ {
if (!isEmpty) {
statements.append(maker.VarDef(maker.Modifiers(finalFlag), primeName, maker.TypeIdent(CTC_INT), maker.Literal(HandlerUtil.primeForHashcode())));
@@ -234,7 +272,7 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
/* ... 1; */
init = maker.Literal(1);
}
- statements.append(maker.VarDef(maker.Modifiers(isEmpty ? finalFlag : 0L), resultName, maker.TypeIdent(CTC_INT), init));
+ statements.append(maker.VarDef(maker.Modifiers(isEmpty && !cacheHashCode ? finalFlag : 0L), resultName, maker.TypeIdent(CTC_INT), init));
}
for (Included<JavacNode, EqualsAndHashCode.Include> member : members) {
@@ -306,6 +344,20 @@ public class HandleEqualsAndHashCode extends JavacAnnotationHandler<EqualsAndHas
}
}
+ /*
+ * if (result == 0) result = Integer.MIN_VALUE;
+ * this.$hashCodeCache = result;
+ *
+ */ {
+ if (cacheHashCode) {
+ statements.append(maker.If(maker.Binary(CTC_EQUAL, maker.Ident(resultName), maker.Literal(CTC_INT, 0)),
+ maker.Exec(maker.Assign(maker.Ident(resultName), genJavaLangTypeRef(typeNode, "Integer", "MIN_VALUE"))), null));
+
+ JCFieldAccess cacheHashCodeFieldAccess = maker.Select(maker.Ident(typeNode.toName("this")), typeNode.toName(HASH_CODE_CACHE_NAME));
+ statements.append(maker.Exec(maker.Assign(cacheHashCodeFieldAccess, maker.Ident(resultName))));
+ }
+ }
+
/* return result; */ {
statements.append(maker.Return(maker.Ident(resultName)));
}
diff --git a/src/delombok/lombok/delombok/Delombok.java b/src/delombok/lombok/delombok/Delombok.java
index a1fd0e56..6c15068a 100755
--- a/src/delombok/lombok/delombok/Delombok.java
+++ b/src/delombok/lombok/delombok/Delombok.java
@@ -701,8 +701,12 @@ public class Delombok {
if (!disablePreview && Javac.getJavaCompilerVersion() >= 11) argsList.add("--enable-preview");
- String[] argv = argsList.toArray(new String[0]);
- args.init("javac", argv);
+ if (Javac.getJavaCompilerVersion() < 15) {
+ String[] argv = argsList.toArray(new String[0]);
+ args.init("javac", argv);
+ } else {
+ args.init("javac", argsList);
+ }
options.put("diags.legacy", "TRUE");
options.put("allowStringFolding", "FALSE");
} else {
diff --git a/src/stubs/com/sun/tools/javac/main/Arguments.java b/src/stubs/com/sun/tools/javac/main/Arguments.java
index ea866b6e..3d91734c 100644
--- a/src/stubs/com/sun/tools/javac/main/Arguments.java
+++ b/src/stubs/com/sun/tools/javac/main/Arguments.java
@@ -10,4 +10,7 @@ public class Arguments {
public void init(String ownName, String... argv) {}
public Map<Option, String> getDeferredFileManagerOptions() { return null; }
public boolean validate() { return false; }
+
+ // JDK15
+ public void init(String ownName, Iterable<String> args) {}
}