eclipse-jdt / eclipse-jdt/eclipse.jdt.core
Request for a new method in IVariableBinding to retrieve the original binding of a variable captured by a Lambda
- Dominant language
- Java
- Stars
- 237
- Forks
- 195
- Avg merge
- 1d 10h
- Merged PRs (30d)
- 49
Description
Hello,
I'm currently working on a project that involves analyzing Java code, and I frequently encounter Lambda functions that capture variables from their enclosing scopes. In these scenarios, I need to retrieve the original `IVariableBinding` of the captured variable. However, the existing JDT API does not provide a straightforward way to accomplish this.
To illustrate, consider the following Java code:
```java
void f() {
int i = 0;
List.of(1, 2).foreach((j) -> { System.out.println(j + i); })
}
```
In this case, JDT generates an `IVariableBinding` for `i` within the Lambda body. However, this `IVariableBinding` does not point to the declaration `int i = 0;`. Instead, it may point to something like `final int val$i ...`, encapsulating the `org.eclipse.jdt.internal.compiler.lookup.SyntheticArgumentBinding` object in the `binding` field.
My goal is to retrieve the `IVariableBinding` that points to the original `i` in `int i = 0;`.
As a workaround, I am currently using the following method:
```java
/**
* Use a non-ideal way to obtain the original variable, which might not work in future versions.
* @param var the variable binding returned by JDT
* @return the original variable
*/
private IVariableBinding obtainOriginalVar(IVariableBinding var) {
// Use reflection to get:
// 1. the resolver
// 2. the binding (from jdt.internal.compiler.lookup)
try {
Class variableBinding = var.getClass();
Field binding = variableBinding.getDeclaredField("binding");
binding.setAccessible(true);
Object bindingObj = binding.get(var);
if (!(bindingObj instanceof SyntheticArgumentBinding sab)) {
throw new UnsupportedOperationException();
}
Binding originalBinding = sab.actualOuterLocalVariable;
Field resolver = variableBinding.getDeclaredField("resolver");
resolver.setAccessible(true);
Object resolverObj = resolver.get(var);
Class resolverKlass = resolverObj.getClass();
Method getBinding = resolverKlass.getDeclaredMethod("getBinding", Binding.class);
getBinding.setAccessible(true);
Object ret = getBinding.invoke(resolverObj, originalBinding);
return (IVariableBinding) ret;
} catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
throw new UnsupportedOperationException(e);
}
}
```
This code uses reflection to access the `SyntheticArgumentBinding` object wrapped by `IVariableBinding`, and then retrieves the original `actualOuterLocalVariable` of the captured variable. While this workaround serves the purpose, it is not ideal as it relies on the internal details of JDT and could potentially break with future internal implementation changes.
Therefore, I propose the addition of a new method in `IVariableBinding`, perhaps named `getOriginalBinding()`, which would return the original `IVariableBinding` of the captured variable when applicable. This would make the API more robust and user-friendly.
I believe this enhancement would benefit many users who need to analyze Java code, especially those dealing with Lambda functions and variable capture. I eagerly await your feedback on this proposal.
Contributor guide
Assessment
This issue has not been assessed yet.