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

Open
#2,184 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.