Skip to content

Commit 57ac810

Browse files
authored
Script: track this pointer capture from blocks within lambdas (#82228)
* Script: track this pointer capture from blocks within lambdas If a lambda contains a block that calls into a user function, the painless compiler was not tracking that the lambda needs to capture the `this` pointer. Scripts that attempted to use a lambda that calls a user function from inside a block would trigger an `illegal_state_exception`: > no 'this' pointer within static method `BlockScope` now forwards `setUsesInstanceMethod` calls to it's parent, which may be a `LambdaScope`. `LambdaScope` will also forward `setUsesInstanceMethod` to it's parent after setting it's own `usesInstanceMethod` flag, propagating `this` pointer capture in the case of nested lambdas. Fixes: #82224
1 parent fe7f0c5 commit 57ac810

File tree

3 files changed

+52
-1
lines changed

3 files changed

+52
-1
lines changed

docs/changelog/82228.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 82228
2+
summary: "Script: track this pointer capture from blocks within lambdas"
3+
area: Infra/Scripting
4+
type: bug
5+
issues: []

modules/lang-painless/src/main/java/org/elasticsearch/painless/symbol/SemanticScope.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,9 @@ public void setUsesInstanceMethod() {
198198
return;
199199
}
200200
usesInstanceMethod = true;
201+
if (parent != null) {
202+
parent.setUsesInstanceMethod();
203+
}
201204
}
202205

203206
@Override
@@ -261,6 +264,12 @@ public Class<?> getReturnType() {
261264
public String getReturnCanonicalTypeName() {
262265
return parent.getReturnCanonicalTypeName();
263266
}
267+
268+
@Override
269+
// If the parent scope is a lambda, we want to track this usage, so forward call to parent.
270+
public void setUsesInstanceMethod() {
271+
parent.setUsesInstanceMethod();
272+
}
264273
}
265274

266275
/**
@@ -356,7 +365,8 @@ public Variable defineVariable(Location location, Class<?> type, String name, bo
356365

357366
public abstract Variable getVariable(Location location, String name);
358367

359-
// We only want to track instance method use inside of lambdas for "this" injection. It's a noop for other scopes.
368+
// We only want to track instance method use inside of lambdas (and blocks inside lambdas) for "this" injection.
369+
// It's a noop for other scopes.
360370
public void setUsesInstanceMethod() {}
361371

362372
public boolean usesInstanceMethod() {

modules/lang-painless/src/test/java/org/elasticsearch/painless/UserFunctionTests.java

+36
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,40 @@ public void testLambdaCapture() {
163163
assertEquals(List.of(100, 1, -100), exec(source, Map.of("a", 1), false));
164164
assertBytecodeExists(source, "public static synthetic lambda$synthetic$0(ILjava/lang/Object;Ljava/lang/Object;)I");
165165
}
166+
167+
public void testCallUserMethodFromStatementWithinLambda() {
168+
String source = ""
169+
+ "int test1() { return 1; }"
170+
+ "void test(Map params) { "
171+
+ " int i = 0;"
172+
+ " params.forEach("
173+
+ " (k, v) -> { if (i == 0) { test1() } else { 20 } }"
174+
+ " );"
175+
+ "}"
176+
+ "test(params)";
177+
assertNull(exec(source, Map.of("a", 5), false));
178+
assertBytecodeExists(source, "public synthetic lambda$synthetic$0(ILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
179+
}
180+
181+
public void testCallUserMethodFromStatementWithinNestedLambda() {
182+
String source = ""
183+
+ "int test1() { return 1; }"
184+
+ "void test(Map params) { "
185+
+ " int i = 0;"
186+
+ " int j = 5;"
187+
+ " params.replaceAll( "
188+
+ " (n, m) -> {"
189+
+ " m.forEach("
190+
+ " (k, v) -> { if (i == 0) { test1() } else { 20 } }"
191+
+ " );"
192+
+ " return ['aaa': j];"
193+
+ " }"
194+
+ " );"
195+
+ "}"
196+
+ "Map myParams = new HashMap(params);"
197+
+ "test(myParams);"
198+
+ "myParams['a']['aaa']";
199+
assertEquals(5, exec(source, Map.of("a", Map.of("b", 1)), false));
200+
assertBytecodeExists(source, "public synthetic lambda$synthetic$1(IILjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
201+
}
166202
}

0 commit comments

Comments
 (0)