aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java94
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java268
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java423
-rw-r--r--src/eclipseAgent/lombok/eclipse/agent/PatchVal.java280
4 files changed, 550 insertions, 515 deletions
diff --git a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
index 5d24d81e..c4889bb6 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/EclipsePatcher.java
@@ -26,9 +26,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
-import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
-
import lombok.core.Agent;
import lombok.patcher.Hook;
import lombok.patcher.MethodTarget;
@@ -260,94 +257,7 @@ public class EclipsePatcher extends Agent {
}
private static void patchEcjTransformers(ScriptManager sm, boolean ecj) {
- patchDelegate(sm);
- patchHandleVal(sm, ecj);
- }
-
- private static void patchDelegate(ScriptManager sm) {
- final String TYPEDECLARATION_SIG = "org.eclipse.jdt.internal.compiler.ast.TypeDeclaration";
-
-// sm.addScript(ScriptBuilder.exitEarly()
-// .target(new MethodTarget(TYPEDECLARATION_SIG, "resolve", "void"))
-// .request(StackRequest.THIS)
-// .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "handleDelegateForType", "boolean", TYPEDECLARATION_SIG))
-// .build());
-
- final String CLASSSCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.ClassScope";
-
- sm.addScript(ScriptBuilder.exitEarly()
- .target(new MethodTarget(CLASSSCOPE_SIG, "buildFieldsAndMethods", "void"))
- .request(StackRequest.THIS)
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "handleDelegateForType2", "boolean", CLASSSCOPE_SIG))
- .build());
- }
-
- // Creates a copy of the 'initialization' field on a LocalDeclaration if the type of the LocalDeclaration is 'val', because the completion parser will null this out,
- // which in turn stops us from inferring the intended type for 'val x = 5;'. We look at the copy.
- // Also patches local declaration to not call .resolveType() on the initializer expression if we've already done so (calling it twice causes weird errors),
- // and patches .resolve() on LocalDeclaration itself to just-in-time replace the 'val' vartype with the right one.
-
- private static void patchHandleVal(ScriptManager sm, boolean ecj) {
- final String LOCALDECLARATION_SIG = "org.eclipse.jdt.internal.compiler.ast.LocalDeclaration";
- final String FOREACHSTATEMENT_SIG = "org.eclipse.jdt.internal.compiler.ast.ForeachStatement";
- final String EXPRESSION_SIG = "org.eclipse.jdt.internal.compiler.ast.Expression";
- final String BLOCKSCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.BlockScope";
- final String PARSER_SIG = "org.eclipse.jdt.internal.compiler.parser.Parser";
- final String TYPEBINDING_SIG = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding";
-
- sm.addScript(ScriptBuilder.exitEarly()
- .target(new MethodTarget(LOCALDECLARATION_SIG, "resolve", "void", BLOCKSCOPE_SIG))
- .request(StackRequest.THIS, StackRequest.PARAM1)
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "handleValForLocalDeclaration", "boolean", LOCALDECLARATION_SIG, BLOCKSCOPE_SIG))
- .build());
-
- sm.addScript(ScriptBuilder.replaceMethodCall()
- .target(new MethodTarget(LOCALDECLARATION_SIG, "resolve", "void", BLOCKSCOPE_SIG))
- .methodToReplace(new Hook(EXPRESSION_SIG, "resolveType", TYPEBINDING_SIG, BLOCKSCOPE_SIG))
- .requestExtra(StackRequest.THIS)
- .replacementMethod(new Hook("lombok.eclipse.agent.PatchFixes", "skipResolveInitializerIfAlreadyCalled2", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG, LOCALDECLARATION_SIG))
- .build());
-
- sm.addScript(ScriptBuilder.exitEarly()
- .target(new MethodTarget(FOREACHSTATEMENT_SIG, "resolve", "void", BLOCKSCOPE_SIG))
- .request(StackRequest.THIS, StackRequest.PARAM1)
- .decisionMethod(new Hook("lombok.eclipse.agent.PatchFixes", "handleValForForEach", "boolean", FOREACHSTATEMENT_SIG, BLOCKSCOPE_SIG))
- .build());
-
- sm.addScript(ScriptBuilder.replaceMethodCall()
- .target(new MethodTarget(FOREACHSTATEMENT_SIG, "resolve", "void", BLOCKSCOPE_SIG))
- .methodToReplace(new Hook(EXPRESSION_SIG, "resolveType", TYPEBINDING_SIG, BLOCKSCOPE_SIG))
- .replacementMethod(new Hook("lombok.eclipse.agent.PatchFixes", "skipResolveInitializerIfAlreadyCalled", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG))
- .build());
-
- if (!ecj) {
- sm.addScript(ScriptBuilder.addField()
- .fieldName("$initCopy")
- .fieldType("Lorg/eclipse/jdt/internal/compiler/ast/ASTNode;")
- .setPublic()
- .setTransient()
- .targetClass("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration")
- .build());
-
- sm.addScript(ScriptBuilder.addField()
- .fieldName("$iterableCopy")
- .fieldType("Lorg/eclipse/jdt/internal/compiler/ast/ASTNode;")
- .setPublic()
- .setTransient()
- .targetClass("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration")
- .build());
-
- sm.addScript(ScriptBuilder.wrapReturnValue()
- .target(new MethodTarget(PARSER_SIG, "consumeExitVariableWithInitialization", "void"))
- .request(StackRequest.THIS)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "copyInitializationOfLocalDeclarationForVal", "void", PARSER_SIG))
- .build());
-
- sm.addScript(ScriptBuilder.wrapReturnValue()
- .target(new MethodTarget(PARSER_SIG, "consumeEnhancedForStatementHeader", "void"))
- .request(StackRequest.THIS)
- .wrapMethod(new Hook("lombok.eclipse.agent.PatchFixes", "copyInitializationOfForEachIterable", "void", PARSER_SIG))
- .build());
- }
+ PatchDelegate.addPatches(sm, ecj);
+ PatchVal.addPatches(sm, ecj);
}
}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java
new file mode 100644
index 00000000..fb18958a
--- /dev/null
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchDelegate.java
@@ -0,0 +1,268 @@
+package lombok.eclipse.agent;
+
+import static lombok.eclipse.Eclipse.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jdt.internal.compiler.CompilationResult;
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Annotation;
+import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
+import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
+import org.eclipse.jdt.internal.compiler.ast.Clinit;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldReference;
+import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
+import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
+import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.Statement;
+import org.eclipse.jdt.internal.compiler.ast.ThisReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+
+import lombok.eclipse.Eclipse;
+import lombok.eclipse.handlers.EclipseHandlerUtil;
+import lombok.patcher.Hook;
+import lombok.patcher.MethodTarget;
+import lombok.patcher.ScriptManager;
+import lombok.patcher.StackRequest;
+import lombok.patcher.scripts.ScriptBuilder;
+
+public class PatchDelegate {
+ static void addPatches(ScriptManager sm, boolean ecj) {
+ final String CLASSSCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.ClassScope";
+
+ sm.addScript(ScriptBuilder.exitEarly()
+ .target(new MethodTarget(CLASSSCOPE_SIG, "buildFieldsAndMethods", "void"))
+ .request(StackRequest.THIS)
+ .decisionMethod(new Hook(PatchDelegate.class.getName(), "handleDelegateForType", "boolean", CLASSSCOPE_SIG))
+ .build());
+ }
+
+ public static boolean handleDelegateForType(ClassScope scope) {
+ TypeDeclaration decl = scope.referenceContext;
+ if (decl == null) return false;
+
+ if (decl.fields != null) for (FieldDeclaration field : decl.fields) {
+ if (field.annotations == null) continue;
+ for (Annotation ann : field.annotations) {
+ if (ann.type == null) continue;
+ TypeBinding tb = ann.type.resolveType(decl.initializerScope);
+ if (!charArrayEquals("lombok", tb.qualifiedPackageName())) continue;
+ if (!charArrayEquals("Delegate", tb.qualifiedSourceName())) continue;
+
+ List<ClassLiteralAccess> rawTypes = new ArrayList<ClassLiteralAccess>();
+ for (MemberValuePair pair : ann.memberValuePairs()) {
+ if (pair.name == null || charArrayEquals("value", pair.name)) {
+ if (pair.value instanceof ArrayInitializer) {
+ for (Expression expr : ((ArrayInitializer)pair.value).expressions) {
+ if (expr instanceof ClassLiteralAccess) rawTypes.add((ClassLiteralAccess) expr);
+ }
+ }
+ if (pair.value instanceof ClassLiteralAccess) {
+ rawTypes.add((ClassLiteralAccess) pair.value);
+ }
+ }
+ }
+
+ List<MethodBinding> methodsToDelegate = new ArrayList<MethodBinding>();
+
+ if (rawTypes.isEmpty()) {
+ addAllMethodBindings(methodsToDelegate, field.type.resolveType(decl.initializerScope));
+ } else {
+ for (ClassLiteralAccess cla : rawTypes) {
+ addAllMethodBindings(methodsToDelegate, cla.type.resolveType(decl.initializerScope));
+ }
+ }
+
+ generateDelegateMethods(decl, methodsToDelegate, field.name, ann);
+ }
+ }
+
+ return false;
+ }
+
+ private static void generateDelegateMethods(TypeDeclaration type, List<MethodBinding> methods, char[] delegate, ASTNode source) {
+ for (MethodBinding binding : methods) {
+ MethodDeclaration method = generateDelegateMethod(delegate, binding, type.compilationResult, source);
+ if (type.methods == null) {
+ type.methods = new AbstractMethodDeclaration[1];
+ type.methods[0] = method;
+ } else {
+ int insertionPoint;
+ for (insertionPoint = 0; insertionPoint < type.methods.length; insertionPoint++) {
+ AbstractMethodDeclaration current = type.methods[insertionPoint];
+ if (current instanceof Clinit) continue;
+ if (Eclipse.isGenerated(current)) continue;
+ break;
+ }
+ AbstractMethodDeclaration[] newArray = new AbstractMethodDeclaration[type.methods.length + 1];
+ System.arraycopy(type.methods, 0, newArray, 0, insertionPoint);
+ if (insertionPoint <= type.methods.length) {
+ System.arraycopy(type.methods, insertionPoint, newArray, insertionPoint + 1, type.methods.length - insertionPoint);
+ }
+
+ newArray[insertionPoint] = method;
+ type.methods = newArray;
+ }
+ }
+ }
+
+ private static MethodDeclaration generateDelegateMethod(char[] name, MethodBinding binding, CompilationResult compilationResult, ASTNode source) {
+ MethodDeclaration method = new MethodDeclaration(compilationResult);
+ Eclipse.setGeneratedBy(method, source);
+ method.modifiers = ClassFileConstants.AccPublic;
+ method.returnType = Eclipse.makeType(binding.returnType, source, false);
+ method.annotations = EclipseHandlerUtil.createSuppressWarningsAll(source, null);
+ if (binding.parameters != null && binding.parameters.length > 0) {
+ method.arguments = new Argument[binding.parameters.length];
+ for (int i = 0; i < method.arguments.length; i++) {
+ String argName = "$p" + i;
+ method.arguments[i] = new Argument(
+ argName.toCharArray(), pos(source),
+ Eclipse.makeType(binding.parameters[i], source, false),
+ ClassFileConstants.AccFinal);
+ }
+ }
+ method.selector = binding.selector;
+ if (binding.thrownExceptions != null && binding.thrownExceptions.length > 0) {
+ method.thrownExceptions = new TypeReference[binding.thrownExceptions.length];
+ for (int i = 0; i < method.thrownExceptions.length; i++) {
+ method.thrownExceptions[i] = Eclipse.makeType(binding.thrownExceptions[i], source, false);
+ }
+ }
+
+ method.typeParameters = null; // TODO think about this
+ method.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
+ FieldReference fieldRef = new FieldReference(name, pos(source));
+ fieldRef.receiver = new ThisReference(source.sourceStart, source.sourceEnd);
+ MessageSend call = new MessageSend();
+ call.receiver = fieldRef;
+ call.selector = binding.selector;
+ if (method.arguments != null) {
+ call.arguments = new Expression[method.arguments.length];
+ for (int i = 0; i < method.arguments.length; i++) {
+ call.arguments[i] = new SingleNameReference(("$p" + i).toCharArray(), pos(source));
+ }
+ }
+
+ Statement body;
+ if (method.returnType instanceof SingleTypeReference && ((SingleTypeReference)method.returnType).token == TypeConstants.VOID) {
+ body = call;
+ } else {
+ body = new ReturnStatement(call, source.sourceStart, source.sourceEnd);
+ }
+
+ method.statements = new Statement[] {body};
+ // TODO add Eclipse.setGeneratedBy everywhere.
+ method.bodyStart = method.declarationSourceStart = method.sourceStart = source.sourceStart;
+ method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = source.sourceEnd;
+ return method;
+ }
+
+ private static void addAllMethodBindings(List<MethodBinding> list, TypeBinding binding) {
+ List<String> ban = new ArrayList<String>();
+ ban.addAll(METHODS_IN_OBJECT);
+ addAllMethodBindings(list, binding, ban);
+ }
+
+ private static void addAllMethodBindings(List<MethodBinding> list, TypeBinding binding, List<String> banList) {
+ if (binding == null) return;
+ if (binding instanceof ReferenceBinding) {
+ ReferenceBinding rb = (ReferenceBinding) binding;
+ for (MethodBinding mb : rb.availableMethods()) {
+ if (mb.isStatic()) continue;
+ if (mb.isBridge()) continue;
+ if (mb.isConstructor()) continue;
+ if (mb.isDefaultAbstract()) continue;
+ if (!mb.isPublic()) continue;
+ if (mb.isSynthetic()) continue;
+ if (mb.isFinal()) {
+ banList.add(printSig(mb));
+ continue;
+ }
+ if (banList.contains(printSig(mb))) continue;
+ list.add(mb);
+ }
+ addAllMethodBindings(list, rb.superclass());
+ ReferenceBinding[] interfaces = rb.superInterfaces();
+ if (interfaces != null) {
+ for (ReferenceBinding iface : interfaces) addAllMethodBindings(list, iface);
+ }
+ }
+ }
+
+ private static final List<String> METHODS_IN_OBJECT = Collections.unmodifiableList(Arrays.asList(
+ "hashCode()",
+ "equals(java.lang.Object)",
+ "wait()",
+ "wait(long)",
+ "wait(long, int)",
+ "notify()",
+ "notifyAll()",
+ "toString()",
+ "getClass()",
+ "clone()",
+ "finalize()"));
+
+ private static String printSig(MethodBinding binding) {
+ StringBuilder signature = new StringBuilder();
+
+ signature.append(binding.selector);
+ signature.append("(");
+ boolean first = true;
+ if (binding.parameters != null) for (TypeBinding param : binding.parameters) {
+ if (!first) signature.append(", ");
+ first = false;
+ signature.append(simpleTypeBindingToString(param));
+ }
+ signature.append(")");
+
+ return signature.toString();
+ }
+
+ private static String simpleTypeBindingToString(TypeBinding binding) {
+ binding = binding.erasure();
+ if (binding != null && binding.isBaseType()) {
+ return new String (binding.sourceName());
+ } else if (binding instanceof ReferenceBinding) {
+ String pkg = binding.qualifiedPackageName() == null ? "" : new String(binding.qualifiedPackageName());
+ String qsn = binding.qualifiedSourceName() == null ? "" : new String(binding.qualifiedSourceName());
+ return pkg.isEmpty() ? qsn : (pkg + "." + qsn);
+ } else if (binding instanceof ArrayBinding) {
+ StringBuilder out = new StringBuilder();
+ out.append(binding.leafComponentType());
+ for (int i = 0; i < binding.dimensions(); i++) out.append("[]");
+ return out.toString();
+ }
+
+ return "";
+ }
+
+ private static boolean charArrayEquals(String s, char[] c) {
+ if (s == null) return c == null;
+ if (c == null) return false;
+
+ if (s.length() != c.length) return false;
+ for (int i = 0; i < s.length(); i++) if (s.charAt(i) != c[i]) return false;
+ return true;
+
+
+ }
+}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java b/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
index 341c54b3..efe4e18f 100644
--- a/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchFixes.java
@@ -34,9 +34,7 @@ import java.util.List;
import lombok.core.DiagnosticsReceiver;
import lombok.core.PostCompiler;
-import lombok.core.AST.Kind;
import lombok.eclipse.Eclipse;
-import lombok.eclipse.EclipseNode;
import lombok.eclipse.handlers.EclipseHandlerUtil;
import org.eclipse.jdt.core.IMethod;
@@ -44,22 +42,17 @@ import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
import org.eclipse.jdt.internal.compiler.ast.Clinit;
-import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
-import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
-import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
-import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
@@ -68,20 +61,14 @@ import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
-import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
-import org.eclipse.jdt.internal.compiler.lookup.Binding;
-import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
-import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
-import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
-import org.eclipse.jdt.internal.compiler.parser.Parser;
public class PatchFixes {
public static int fixRetrieveStartingCatchPosition(int in) {
@@ -174,414 +161,4 @@ public class PatchFixes {
String fileName = path + "/" + name;
return new BufferedOutputStream(PostCompiler.wrapOutputStream(out, fileName, DiagnosticsReceiver.CONSOLE));
}
-
- private static Field astStackField, astPtrField;
-
- static {
- try {
- astStackField = Parser.class.getDeclaredField("astStack");
- astStackField.setAccessible(true);
- astPtrField = Parser.class.getDeclaredField("astPtr");
- astPtrField.setAccessible(true);
- } catch (Exception e) {
- // Most likely we're in ecj or some other plugin usage of the eclipse compiler. No need for this.
- }
- }
-
- public static void copyInitializationOfForEachIterable(Parser parser) {
- ASTNode[] astStack;
- int astPtr;
- try {
- astStack = (ASTNode[]) astStackField.get(parser);
- astPtr = (Integer)astPtrField.get(parser);
- } catch (Exception e) {
- // Most likely we're in ecj or some other plugin usage of the eclipse compiler. No need for this.
- return;
- }
-
- ForeachStatement foreachDecl = (ForeachStatement) astStack[astPtr];
- ASTNode init = foreachDecl.collection;
- if (init == null) return;
- if (foreachDecl.elementVariable != null && foreachDecl.elementVariable.type instanceof SingleTypeReference) {
- SingleTypeReference ref = (SingleTypeReference) foreachDecl.elementVariable.type;
- if (ref.token == null || ref.token.length != 3 || ref.token[0] != 'v' || ref.token[1] != 'a' || ref.token[2] != 'l') return;
- } else return;
-
- try {
- if (iterableCopyField != null) iterableCopyField.set(foreachDecl.elementVariable, init);
- } catch (Exception e) {
- // In ecj mode this field isn't there and we don't need the copy anyway, so, we ignore the exception.
- }
- }
-
- public static void copyInitializationOfLocalDeclarationForVal(Parser parser) {
- ASTNode[] astStack;
- int astPtr;
- try {
- astStack = (ASTNode[]) astStackField.get(parser);
- astPtr = (Integer)astPtrField.get(parser);
- } catch (Exception e) {
- // Most likely we're in ecj or some other plugin usage of the eclipse compiler. No need for this.
- return;
- }
- AbstractVariableDeclaration variableDecl = (AbstractVariableDeclaration) astStack[astPtr];
- if (!(variableDecl instanceof LocalDeclaration)) return;
- ASTNode init = variableDecl.initialization;
- if (init == null) return;
- if (variableDecl.type instanceof SingleTypeReference) {
- SingleTypeReference ref = (SingleTypeReference) variableDecl.type;
- if (ref.token == null || ref.token.length != 3 || ref.token[0] != 'v' || ref.token[1] != 'a' || ref.token[2] != 'l') return;
- } else return;
-
- try {
- if (initCopyField != null) initCopyField.set(variableDecl, init);
- } catch (Exception e) {
- // In ecj mode this field isn't there and we don't need the copy anyway, so, we ignore the exception.
- }
- }
-
- private static Field initCopyField, iterableCopyField;
-
- static {
- try {
- initCopyField = LocalDeclaration.class.getDeclaredField("$initCopy");
- iterableCopyField = LocalDeclaration.class.getDeclaredField("$iterableCopy");
- } catch (Throwable t) {
- //ignore - no $initCopy exists when running in ecj.
- }
- }
-
- public static boolean handleValForForEach(ForeachStatement forEach, BlockScope scope) {
- if (forEach.elementVariable != null && forEach.elementVariable.type instanceof SingleTypeReference) {
- char[] token = ((SingleTypeReference)forEach.elementVariable.type).token;
- if (token == null || token.length != 3) return false;
- else if (token[0] != 'v' || token[1] != 'a' || token[2] != 'l') return false;
- } else return false;
-
- TypeBinding component = getForEachComponentType(forEach.collection, scope);
- TypeReference replacement = Eclipse.makeType(component, forEach.elementVariable.type, false);
-
- forEach.elementVariable.modifiers |= ClassFileConstants.AccFinal;
- forEach.elementVariable.type = replacement != null ? replacement :
- new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, Eclipse.poss(forEach.elementVariable.type, 3));
-
- return false;
- }
-
- private static TypeBinding getForEachComponentType(Expression collection, BlockScope scope) {
- if (collection != null) {
- TypeBinding resolved = collection.resolveType(scope);
- if (resolved.isArrayType()) {
- resolved = ((ArrayBinding) resolved).elementsType();
- return resolved;
- } else if (resolved instanceof ReferenceBinding) {
- ReferenceBinding iterableType = ((ReferenceBinding)resolved).findSuperTypeOriginatingFrom(TypeIds.T_JavaLangIterable, false);
-
- TypeBinding[] arguments = null;
- if (iterableType != null) switch (iterableType.kind()) {
- case Binding.GENERIC_TYPE : // for (T t : Iterable<T>) - in case used inside Iterable itself
- arguments = iterableType.typeVariables();
- break;
- case Binding.PARAMETERIZED_TYPE : // for(E e : Iterable<E>)
- arguments = ((ParameterizedTypeBinding)iterableType).arguments;
- break;
- }
-
- if (arguments != null && arguments.length == 1) {
- return arguments[0];
- }
- }
- }
-
- return null;
- }
-
- public static boolean handleValForLocalDeclaration(LocalDeclaration local, BlockScope scope) {
- if (local == null || !LocalDeclaration.class.equals(local.getClass())) return false;
- boolean decomponent = false;
-
- if (local.type instanceof SingleTypeReference) {
- char[] token = ((SingleTypeReference)local.type).token;
- if (token == null || token.length != 3) return false;
- else if (token[0] != 'v' || token[1] != 'a' || token[2] != 'l') return false;
- } else return false;
-
- Expression init = local.initialization;
- if (init == null && initCopyField != null) {
- try {
- init = (Expression) initCopyField.get(local);
- } catch (Exception e) {
- }
- }
-
- if (init == null && iterableCopyField != null) {
- try {
- init = (Expression) iterableCopyField.get(local);
- decomponent = true;
- } catch (Exception e) {
- }
- }
-
- TypeReference replacement = null;
- if (init != null && decomponent) {
- }
-
- if (init != null) {
- TypeBinding resolved = decomponent ? getForEachComponentType(init, scope) : init.resolveType(scope);
- if (resolved != null) {
- replacement = Eclipse.makeType(resolved, local.type, false);
- }
- }
-
- local.modifiers |= ClassFileConstants.AccFinal;
- local.type = replacement != null ? replacement : new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, Eclipse.poss(local.type, 3));
-
- return false;
- }
-
- public static TypeBinding skipResolveInitializerIfAlreadyCalled(Expression expr, BlockScope scope) {
- if (expr.resolvedType != null) return expr.resolvedType;
- return expr.resolveType(scope);
- }
-
- public static TypeBinding skipResolveInitializerIfAlreadyCalled2(Expression expr, BlockScope scope, LocalDeclaration decl) {
- if (decl != null && LocalDeclaration.class.equals(decl.getClass()) && expr.resolvedType != null) return expr.resolvedType;
- return expr.resolveType(scope);
- }
-
- public static boolean handleDelegateForType(TypeDeclaration decl) {
- return false;
- }
-
- public static boolean handleDelegateForType2(ClassScope scope) {
- TypeDeclaration decl = scope.referenceContext;
- if (decl == null) return false;
-
- boolean continueAdding = false;
-
- /* debug */ try {
- MethodBinding[] existingMethods = (MethodBinding[]) sourceTypeBindingMethodsField.get(decl.binding);
- System.out.println("Existing method bindings in type.SourceTypeBinding: " + new String(scope.referenceContext.name));
- for (MethodBinding binding : existingMethods) {
- System.out.println(" " + binding);
- }
- FieldBinding[] existingFields = (FieldBinding[]) sourceTypeBindingFieldsField.get(decl.binding);
- System.out.println("Existing field bindings in type.SourceTypeBinding: ");
- for (FieldBinding binding : existingFields) {
- System.out.println(" " + binding);
- }
-
- if (charArrayEquals("Test", scope.referenceContext.name)) {
- for (AbstractMethodDeclaration m : scope.referenceContext.methods) {
- if (m instanceof MethodDeclaration) {
- if (charArrayEquals("example", m.selector)) {
- System.out.println("Example scope now: " + m.scope);
- System.out.println("Example binding now: " + m.binding);
- if (m.scope == null && m.binding == null) continueAdding = true;
- Thread.dumpStack();
- }
- }
- }
- }
- } catch (Exception e) {
- System.err.println("EXCEPTION DURING DEBUG 1");
- e.printStackTrace();
- }
-
- if (!continueAdding) return false;
-
- if (decl.fields != null) for (FieldDeclaration field : decl.fields) {
- if (field.annotations == null) continue;
- for (Annotation ann : field.annotations) {
- if (ann.type == null) continue;
- TypeBinding tb = ann.type.resolveType(decl.initializerScope);
- if (!charArrayEquals("lombok", tb.qualifiedPackageName())) continue;
- if (!charArrayEquals("Delegate", tb.qualifiedSourceName())) continue;
-
- List<ClassLiteralAccess> rawTypes = new ArrayList<ClassLiteralAccess>();
- for (MemberValuePair pair : ann.memberValuePairs()) {
- if (pair.name == null || charArrayEquals("value", pair.name)) {
- if (pair.value instanceof ArrayInitializer) {
- for (Expression expr : ((ArrayInitializer)pair.value).expressions) {
- if (expr instanceof ClassLiteralAccess) rawTypes.add((ClassLiteralAccess) expr);
- }
- }
- if (pair.value instanceof ClassLiteralAccess) {
- rawTypes.add((ClassLiteralAccess) pair.value);
- }
- }
- }
-
- List<MethodBinding> methodsToDelegate = new ArrayList<MethodBinding>();
-
- if (rawTypes.isEmpty()) {
- addAllMethodBindings(methodsToDelegate, field.type.resolveType(decl.initializerScope));
- } else {
- for (ClassLiteralAccess cla : rawTypes) {
- addAllMethodBindings(methodsToDelegate, cla.type.resolveType(decl.initializerScope));
- }
- }
-
- System.out.println("About to generate the following methods, all delegating to: this." + new String(field.name));
- for (MethodBinding mb : methodsToDelegate) {
- System.out.println(mb);
- }
- System.out.println("-----------");
-
- generateDelegateMethods(decl, methodsToDelegate, field.name, ann);
- }
- }
-
- return false;
- }
-
- private static final Method methodScopeCreateMethodMethod;
- private static final Field sourceTypeBindingMethodsField, sourceTypeBindingFieldsField;
-
- static {
- Method m = null;
- Field f = null, g = null;
- Exception ex = null;
-
- try {
- m = MethodScope.class.getDeclaredMethod("createMethod", AbstractMethodDeclaration.class);
- m.setAccessible(true);
- f = SourceTypeBinding.class.getDeclaredField("methods");
- f.setAccessible(true);
- g = SourceTypeBinding.class.getDeclaredField("fields");
- g.setAccessible(true);
- } catch (Exception e) {
- ex = e;
- }
-
- methodScopeCreateMethodMethod = m;
- sourceTypeBindingMethodsField = f;
- sourceTypeBindingFieldsField = g;
- if (ex != null) throw new RuntimeException(ex);
- }
-
- private static void generateDelegateMethods(TypeDeclaration type, List<MethodBinding> methods, char[] delegate, ASTNode source) {
- for (MethodBinding binding : methods) {
- MethodDeclaration method = generateDelegateMethod(delegate, binding, type.compilationResult, source);
- if (type.methods == null) {
- type.methods = new AbstractMethodDeclaration[1];
- type.methods[0] = method;
- } else {
- int insertionPoint;
- for (insertionPoint = 0; insertionPoint < type.methods.length; insertionPoint++) {
- AbstractMethodDeclaration current = type.methods[insertionPoint];
- if (current instanceof Clinit) continue;
- if (Eclipse.isGenerated(current)) continue;
- break;
- }
- AbstractMethodDeclaration[] newArray = new AbstractMethodDeclaration[type.methods.length + 1];
- System.arraycopy(type.methods, 0, newArray, 0, insertionPoint);
- if (insertionPoint <= type.methods.length) {
- System.arraycopy(type.methods, insertionPoint, newArray, insertionPoint + 1, type.methods.length - insertionPoint);
- }
-
- newArray[insertionPoint] = method;
- type.methods = newArray;
-// MethodScope methodScope = new MethodScope(type.scope, method, false);
-//
-// try {
-// MethodBinding methodBinding = (MethodBinding) methodScopeCreateMethodMethod.invoke(methodScope, method);
-// System.out.println("SCOPE NOW: " + method.scope);
-//
-// method.resolve(type.scope);
-// System.out.println("Bind now: " + methodBinding.returnType);
-//
-// MethodBinding[] existing = (MethodBinding[]) sourceTypeBindingMethodsField.get(type.binding);
-// if (existing == null) existing = new MethodBinding[] {methodBinding};
-// else {
-// MethodBinding[] copy = new MethodBinding[existing.length + 1];
-// System.arraycopy(existing, 0, copy, 0, existing.length);
-// copy[existing.length] = methodBinding;
-// }
-// sourceTypeBindingMethodsField.set(type.binding, existing);
-// System.out.println("Added method binding: " + methodBinding);
-// System.out.println(method);
-// } catch (Exception e) {
-// throw new RuntimeException(e);
-// }
- }
- }
- }
-
- private static MethodDeclaration generateDelegateMethod(char[] name, MethodBinding binding, CompilationResult compilationResult, ASTNode source) {
- MethodDeclaration method = new MethodDeclaration(compilationResult);
- Eclipse.setGeneratedBy(method, source);
- method.modifiers = ClassFileConstants.AccPublic;
- method.returnType = Eclipse.makeType(binding.returnType, source, false);
- method.annotations = EclipseHandlerUtil.createSuppressWarningsAll(source, null);
- if (binding.parameters != null && binding.parameters.length > 0) {
- method.arguments = new Argument[binding.parameters.length];
- for (int i = 0; i < method.arguments.length; i++) {
- String argName = "$p" + i;
- method.arguments[i] = new Argument(
- argName.toCharArray(), pos(source),
- Eclipse.makeType(binding.parameters[i], source, false),
- ClassFileConstants.AccFinal);
- }
- }
- method.selector = binding.selector;
- if (binding.thrownExceptions != null && binding.thrownExceptions.length > 0) {
- method.thrownExceptions = new TypeReference[binding.thrownExceptions.length];
- for (int i = 0; i < method.thrownExceptions.length; i++) {
- method.thrownExceptions[i] = Eclipse.makeType(binding.thrownExceptions[i], source, false);
- }
- }
-
- method.typeParameters = null; // TODO think about this
- method.bits |= ECLIPSE_DO_NOT_TOUCH_FLAG;
- FieldReference fieldRef = new FieldReference(name, pos(source));
- fieldRef.receiver = new ThisReference(source.sourceStart, source.sourceEnd);
- MessageSend call = new MessageSend();
- call.receiver = fieldRef;
- call.selector = binding.selector;
- if (method.arguments != null) {
- call.arguments = new Expression[method.arguments.length];
- for (int i = 0; i < method.arguments.length; i++) {
- call.arguments[i] = new SingleNameReference(("$p" + i).toCharArray(), pos(source));
- }
- }
-
- Statement body;
- if (method.returnType instanceof SingleTypeReference && ((SingleTypeReference)method.returnType).token == TypeConstants.VOID) {
- body = call;
- } else {
- body = new ReturnStatement(call, source.sourceStart, source.sourceEnd);
- }
-
- method.statements = new Statement[] {body};
- // TODO add Eclipse.setGeneratedBy everywhere.
- method.bodyStart = method.declarationSourceStart = method.sourceStart = source.sourceStart;
- method.bodyEnd = method.declarationSourceEnd = method.sourceEnd = source.sourceEnd;
- return method;
- }
-
- private static void addAllMethodBindings(List<MethodBinding> list, TypeBinding binding) {
- if (binding instanceof ReferenceBinding) {
- for (MethodBinding mb : ((ReferenceBinding)binding).availableMethods()) {
- if (mb.isStatic()) continue;
- if (mb.isBridge()) continue;
- if (mb.isConstructor()) continue;
- if (mb.isDefaultAbstract()) continue;
- if (!mb.isPublic()) continue;
- if (mb.isSynthetic()) continue;
- list.add(mb);
- }
- }
- }
-
- private static boolean charArrayEquals(String s, char[] c) {
- if (s == null) return c == null;
- if (c == null) return false;
-
- if (s.length() != c.length) return false;
- for (int i = 0; i < s.length(); i++) if (s.charAt(i) != c[i]) return false;
- return true;
-
-
- }
}
diff --git a/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
new file mode 100644
index 00000000..d1294779
--- /dev/null
+++ b/src/eclipseAgent/lombok/eclipse/agent/PatchVal.java
@@ -0,0 +1,280 @@
+package lombok.eclipse.agent;
+
+import java.lang.reflect.Field;
+
+import lombok.eclipse.Eclipse;
+import lombok.patcher.Hook;
+import lombok.patcher.MethodTarget;
+import lombok.patcher.ScriptManager;
+import lombok.patcher.StackRequest;
+import lombok.patcher.scripts.ScriptBuilder;
+
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Expression;
+import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
+import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Binding;
+import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
+import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+import org.eclipse.jdt.internal.compiler.parser.Parser;
+
+public class PatchVal {
+
+ // Creates a copy of the 'initialization' field on a LocalDeclaration if the type of the LocalDeclaration is 'val', because the completion parser will null this out,
+ // which in turn stops us from inferring the intended type for 'val x = 5;'. We look at the copy.
+ // Also patches local declaration to not call .resolveType() on the initializer expression if we've already done so (calling it twice causes weird errors),
+ // and patches .resolve() on LocalDeclaration itself to just-in-time replace the 'val' vartype with the right one.
+
+ static void addPatches(ScriptManager sm, boolean ecj) {
+ final String LOCALDECLARATION_SIG = "org.eclipse.jdt.internal.compiler.ast.LocalDeclaration";
+ final String FOREACHSTATEMENT_SIG = "org.eclipse.jdt.internal.compiler.ast.ForeachStatement";
+ final String EXPRESSION_SIG = "org.eclipse.jdt.internal.compiler.ast.Expression";
+ final String BLOCKSCOPE_SIG = "org.eclipse.jdt.internal.compiler.lookup.BlockScope";
+ final String PARSER_SIG = "org.eclipse.jdt.internal.compiler.parser.Parser";
+ final String TYPEBINDING_SIG = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding";
+
+ sm.addScript(ScriptBuilder.exitEarly()
+ .target(new MethodTarget(LOCALDECLARATION_SIG, "resolve", "void", BLOCKSCOPE_SIG))
+ .request(StackRequest.THIS, StackRequest.PARAM1)
+ .decisionMethod(new Hook("lombok.eclipse.agent.PatchVal", "handleValForLocalDeclaration", "boolean", LOCALDECLARATION_SIG, BLOCKSCOPE_SIG))
+ .build());
+
+ sm.addScript(ScriptBuilder.replaceMethodCall()
+ .target(new MethodTarget(LOCALDECLARATION_SIG, "resolve", "void", BLOCKSCOPE_SIG))
+ .methodToReplace(new Hook(EXPRESSION_SIG, "resolveType", TYPEBINDING_SIG, BLOCKSCOPE_SIG))
+ .requestExtra(StackRequest.THIS)
+ .replacementMethod(new Hook("lombok.eclipse.agent.PatchVal", "skipResolveInitializerIfAlreadyCalled2", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG, LOCALDECLARATION_SIG))
+ .build());
+
+ sm.addScript(ScriptBuilder.replaceMethodCall()
+ .target(new MethodTarget(FOREACHSTATEMENT_SIG, "resolve", "void", BLOCKSCOPE_SIG))
+ .methodToReplace(new Hook(EXPRESSION_SIG, "resolveType", TYPEBINDING_SIG, BLOCKSCOPE_SIG))
+ .replacementMethod(new Hook("lombok.eclipse.agent.PatchVal", "skipResolveInitializerIfAlreadyCalled", TYPEBINDING_SIG, EXPRESSION_SIG, BLOCKSCOPE_SIG))
+ .build());
+
+ sm.addScript(ScriptBuilder.exitEarly()
+ .target(new MethodTarget(FOREACHSTATEMENT_SIG, "resolve", "void", BLOCKSCOPE_SIG))
+ .request(StackRequest.THIS, StackRequest.PARAM1)
+ .decisionMethod(new Hook("lombok.eclipse.agent.PatchVal", "handleValForForEach", "boolean", FOREACHSTATEMENT_SIG, BLOCKSCOPE_SIG))
+ .build());
+
+ if (!ecj) {
+ sm.addScript(ScriptBuilder.addField()
+ .fieldName("$initCopy")
+ .fieldType("Lorg/eclipse/jdt/internal/compiler/ast/ASTNode;")
+ .setPublic()
+ .setTransient()
+ .targetClass("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration")
+ .build());
+
+ sm.addScript(ScriptBuilder.addField()
+ .fieldName("$iterableCopy")
+ .fieldType("Lorg/eclipse/jdt/internal/compiler/ast/ASTNode;")
+ .setPublic()
+ .setTransient()
+ .targetClass("org.eclipse.jdt.internal.compiler.ast.LocalDeclaration")
+ .build());
+
+ sm.addScript(ScriptBuilder.wrapReturnValue()
+ .target(new MethodTarget(PARSER_SIG, "consumeExitVariableWithInitialization", "void"))
+ .request(StackRequest.THIS)
+ .wrapMethod(new Hook("lombok.eclipse.agent.PatchVal", "copyInitializationOfLocalDeclaration", "void", PARSER_SIG))
+ .build());
+
+ sm.addScript(ScriptBuilder.wrapReturnValue()
+ .target(new MethodTarget(PARSER_SIG, "consumeEnhancedForStatementHeader", "void"))
+ .request(StackRequest.THIS)
+ .wrapMethod(new Hook("lombok.eclipse.agent.PatchVal", "copyInitializationOfForEachIterable", "void", PARSER_SIG))
+ .build());
+ }
+ }
+
+ private static final class Reflection {
+ private static final Field initCopyField, iterableCopyField;
+ private static final Field astStackField, astPtrField;
+
+ static {
+ Field a = null, b = null, c = null, d = null;
+
+ try {
+ a = LocalDeclaration.class.getDeclaredField("$initCopy");
+ b = LocalDeclaration.class.getDeclaredField("$iterableCopy");
+ } catch (Throwable t) {
+ //ignore - no $initCopy exists when running in ecj.
+ }
+
+ try {
+ c = Parser.class.getDeclaredField("astStack");
+ c.setAccessible(true);
+ d = Parser.class.getDeclaredField("astPtr");
+ d.setAccessible(true);
+ } catch (Exception e) {
+ // Most likely we're in ecj or some other plugin usage of the eclipse compiler. No need for this.
+ }
+
+ initCopyField = a;
+ iterableCopyField = b;
+ astStackField = c;
+ astPtrField = d;
+ }
+ }
+
+ public static void copyInitializationOfLocalDeclaration(Parser parser) {
+ ASTNode[] astStack;
+ int astPtr;
+ try {
+ astStack = (ASTNode[]) Reflection.astStackField.get(parser);
+ astPtr = (Integer)Reflection.astPtrField.get(parser);
+ } catch (Exception e) {
+ // Most likely we're in ecj or some other plugin usage of the eclipse compiler. No need for this.
+ return;
+ }
+ AbstractVariableDeclaration variableDecl = (AbstractVariableDeclaration) astStack[astPtr];
+ if (!(variableDecl instanceof LocalDeclaration)) return;
+ ASTNode init = variableDecl.initialization;
+ if (init == null) return;
+ if (variableDecl.type instanceof SingleTypeReference) {
+ SingleTypeReference ref = (SingleTypeReference) variableDecl.type;
+ if (ref.token == null || ref.token.length != 3 || ref.token[0] != 'v' || ref.token[1] != 'a' || ref.token[2] != 'l') return;
+ } else return;
+
+ try {
+ if (Reflection.initCopyField != null) Reflection.initCopyField.set(variableDecl, init);
+ } catch (Exception e) {
+ // In ecj mode this field isn't there and we don't need the copy anyway, so, we ignore the exception.
+ }
+ }
+
+ public static TypeBinding skipResolveInitializerIfAlreadyCalled(Expression expr, BlockScope scope) {
+ if (expr.resolvedType != null) return expr.resolvedType;
+ return expr.resolveType(scope);
+ }
+
+ public static TypeBinding skipResolveInitializerIfAlreadyCalled2(Expression expr, BlockScope scope, LocalDeclaration decl) {
+ if (decl != null && LocalDeclaration.class.equals(decl.getClass()) && expr.resolvedType != null) return expr.resolvedType;
+ return expr.resolveType(scope);
+ }
+
+ public static boolean handleValForLocalDeclaration(LocalDeclaration local, BlockScope scope) {
+ if (local == null || !LocalDeclaration.class.equals(local.getClass())) return false;
+ boolean decomponent = false;
+
+ if (local.type instanceof SingleTypeReference) {
+ char[] token = ((SingleTypeReference)local.type).token;
+ if (token == null || token.length != 3) return false;
+ else if (token[0] != 'v' || token[1] != 'a' || token[2] != 'l') return false;
+ } else return false;
+
+ Expression init = local.initialization;
+ if (init == null && Reflection.initCopyField != null) {
+ try {
+ init = (Expression) Reflection.initCopyField.get(local);
+ } catch (Exception e) {
+ }
+ }
+
+ if (init == null && Reflection.iterableCopyField != null) {
+ try {
+ init = (Expression) Reflection.iterableCopyField.get(local);
+ decomponent = true;
+ } catch (Exception e) {
+ }
+ }
+
+ TypeReference replacement = null;
+ if (init != null && decomponent) {
+ }
+
+ if (init != null) {
+ TypeBinding resolved = decomponent ? getForEachComponentType(init, scope) : init.resolveType(scope);
+ if (resolved != null) {
+ replacement = Eclipse.makeType(resolved, local.type, false);
+ }
+ }
+
+ local.modifiers |= ClassFileConstants.AccFinal;
+ local.type = replacement != null ? replacement : new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, Eclipse.poss(local.type, 3));
+
+ return false;
+ }
+ public static void copyInitializationOfForEachIterable(Parser parser) {
+ ASTNode[] astStack;
+ int astPtr;
+ try {
+ astStack = (ASTNode[]) Reflection.astStackField.get(parser);
+ astPtr = (Integer) Reflection.astPtrField.get(parser);
+ } catch (Exception e) {
+ // Most likely we're in ecj or some other plugin usage of the eclipse compiler. No need for this.
+ return;
+ }
+
+ ForeachStatement foreachDecl = (ForeachStatement) astStack[astPtr];
+ ASTNode init = foreachDecl.collection;
+ if (init == null) return;
+ if (foreachDecl.elementVariable != null && foreachDecl.elementVariable.type instanceof SingleTypeReference) {
+ SingleTypeReference ref = (SingleTypeReference) foreachDecl.elementVariable.type;
+ if (ref.token == null || ref.token.length != 3 || ref.token[0] != 'v' || ref.token[1] != 'a' || ref.token[2] != 'l') return;
+ } else return;
+
+ try {
+ if (Reflection.iterableCopyField != null) Reflection.iterableCopyField.set(foreachDecl.elementVariable, init);
+ } catch (Exception e) {
+ // In ecj mode this field isn't there and we don't need the copy anyway, so, we ignore the exception.
+ }
+ }
+
+ public static boolean handleValForForEach(ForeachStatement forEach, BlockScope scope) {
+ if (forEach.elementVariable != null && forEach.elementVariable.type instanceof SingleTypeReference) {
+ char[] token = ((SingleTypeReference)forEach.elementVariable.type).token;
+ if (token == null || token.length != 3) return false;
+ else if (token[0] != 'v' || token[1] != 'a' || token[2] != 'l') return false;
+ } else return false;
+
+ TypeBinding component = getForEachComponentType(forEach.collection, scope);
+ TypeReference replacement = Eclipse.makeType(component, forEach.elementVariable.type, false);
+
+ forEach.elementVariable.modifiers |= ClassFileConstants.AccFinal;
+ forEach.elementVariable.type = replacement != null ? replacement :
+ new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, Eclipse.poss(forEach.elementVariable.type, 3));
+
+ return false;
+ }
+
+ private static TypeBinding getForEachComponentType(Expression collection, BlockScope scope) {
+ if (collection != null) {
+ TypeBinding resolved = collection.resolveType(scope);
+ if (resolved.isArrayType()) {
+ resolved = ((ArrayBinding) resolved).elementsType();
+ return resolved;
+ } else if (resolved instanceof ReferenceBinding) {
+ ReferenceBinding iterableType = ((ReferenceBinding)resolved).findSuperTypeOriginatingFrom(TypeIds.T_JavaLangIterable, false);
+
+ TypeBinding[] arguments = null;
+ if (iterableType != null) switch (iterableType.kind()) {
+ case Binding.GENERIC_TYPE : // for (T t : Iterable<T>) - in case used inside Iterable itself
+ arguments = iterableType.typeVariables();
+ break;
+ case Binding.PARAMETERIZED_TYPE : // for(E e : Iterable<E>)
+ arguments = ((ParameterizedTypeBinding)iterableType).arguments;
+ break;
+ }
+
+ if (arguments != null && arguments.length == 1) {
+ return arguments[0];
+ }
+ }
+ }
+
+ return null;
+ }
+}