Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog/133604.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 133604
summary: Update `DefBootstrap` to handle Error from `ClassValue`
area: Infra/Scripting
type: bug
issues: []
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,10 @@ protected MethodHandle computeValue(Class<?> receiverType) {
try {
return lookup(flavor, name, receiverType).asType(type);
} catch (Throwable t) {
Def.rethrow(t);
// ClassValue.getFromHashMap wraps checked exceptions as Error, so we
// use a sentinel class [PainlessWrappedException] here to work around
// this issue and later unwrap the original exception
Def.rethrow(t instanceof Exception ? new PainlessWrappedException((Exception) t) : t);
throw new AssertionError();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public interface PainlessScript {
* @return The generated ScriptException.
*/
default ScriptException convertToScriptException(Throwable t, Map<String, List<String>> extraMetadata) {
if (t instanceof PainlessWrappedException) {
t = t.getCause();
}
// create a script stack: this is just the script portion
List<String> scriptStack = new ArrayList<>();
ScriptException.Position pos = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.painless;

/**
* Checked exceptions are wrapped in {@link ClassValue}#getFromHashMap in Error
* which leads to unexpected behavior in Painless. This class is used as a
* workaround for that exception wrapping.
*/
public class PainlessWrappedException extends Error {

/**
* Constructor.
* @param cause The {@link Exception} cause.
*/
public PainlessWrappedException(final Exception cause) {
super(cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.elasticsearch.painless.Location;
import org.elasticsearch.painless.PainlessError;
import org.elasticsearch.painless.PainlessExplainError;
import org.elasticsearch.painless.PainlessWrappedException;
import org.elasticsearch.painless.ScriptClassInfo;
import org.elasticsearch.painless.ScriptClassInfo.MethodArgument;
import org.elasticsearch.painless.ir.BinaryImplNode;
Expand Down Expand Up @@ -415,6 +416,7 @@ protected static void injectSandboxExceptions(FunctionNode irFunctionNode) {

for (Class<? extends Throwable> throwable : List.of(
PainlessError.class,
PainlessWrappedException.class,
LinkageError.class,
OutOfMemoryError.class,
StackOverflowError.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,11 @@ public void testMegamorphic() throws Throwable {
map.put("a", "b");
assertEquals(2, (int) handle.invokeExact((Object) map));

final IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> {
final PainlessWrappedException pwe = expectThrows(PainlessWrappedException.class, () -> {
Integer.toString((int) handle.invokeExact(new Object()));
});
assertTrue(pwe.getCause() instanceof IllegalArgumentException);
IllegalArgumentException iae = (IllegalArgumentException) pwe.getCause();
assertEquals("dynamic method [java.lang.Object, size/0] not found", iae.getMessage());
assertTrue("Does not fail inside ClassValue.computeValue()", Arrays.stream(iae.getStackTrace()).anyMatch(e -> {
return e.getMethodName().equals("computeValue") && e.getClassName().startsWith("org.elasticsearch.painless.DefBootstrap$PIC$");
Expand Down