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
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@
public final class CN1LambdaSupport {
private static final ThreadLocal<Interpreter> CURRENT_INTERPRETER = new ThreadLocal<Interpreter>();
private static final ThreadLocal<NameSpace> CURRENT_NAMESPACE = new ThreadLocal<NameSpace>();
private static final ThreadLocal<LambdaErrorHandler> CURRENT_ERROR_HANDLER = new ThreadLocal<LambdaErrorHandler>();

/** Callback invoked when a lambda body raises an exception during
* {@link LambdaValue#invoke(Object[])}. Lets a host (e.g. the
* playground UI) surface runtime failures that would otherwise be
* lost to the EDT's silent exception handling. */
public interface LambdaErrorHandler {
void onLambdaError(Throwable error, String bodySource);
}

private CN1LambdaSupport() {
}
Expand All @@ -33,14 +42,28 @@ public static void clearCurrentNameSpace() {
CURRENT_NAMESPACE.remove();
}

/** Register a {@link LambdaErrorHandler} that will be captured by any
* {@link LambdaValue} created on this thread while the handler is
* active. The captured handler stays with the lambda for its
* lifetime so errors raised on later EDT firings can still surface
* back to the host. */
public static void pushErrorHandler(LambdaErrorHandler handler) {
CURRENT_ERROR_HANDLER.set(handler);
}

public static void clearErrorHandler() {
CURRENT_ERROR_HANDLER.remove();
}

public static LambdaValue lambda(String[] parameterNames, String bodySource) {
Interpreter interpreter = CURRENT_INTERPRETER.get();
if (interpreter == null) {
throw new IllegalStateException("No active BeanShell interpreter available for lambda capture.");
}
NameSpace activeNs = CURRENT_NAMESPACE.get();
NameSpace parentNs = activeNs != null ? activeNs : interpreter.getNameSpace();
return new LambdaValue(interpreter, parentNs, sanitizeParams(parameterNames), bodySource == null ? "" : bodySource);
return new LambdaValue(interpreter, parentNs, sanitizeParams(parameterNames),
bodySource == null ? "" : bodySource, CURRENT_ERROR_HANDLER.get());
}

private static String[] sanitizeParams(String[] parameterNames) {
Expand Down Expand Up @@ -135,12 +158,15 @@ public static final class LambdaValue implements
private final NameSpace parentNameSpace;
private final String[] parameterNames;
private final String bodySource;
private final LambdaErrorHandler errorHandler;

LambdaValue(Interpreter interpreter, NameSpace parentNameSpace, String[] parameterNames, String bodySource) {
LambdaValue(Interpreter interpreter, NameSpace parentNameSpace, String[] parameterNames,
String bodySource, LambdaErrorHandler errorHandler) {
this.interpreter = interpreter;
this.parentNameSpace = parentNameSpace;
this.parameterNames = parameterNames;
this.bodySource = bodySource;
this.errorHandler = errorHandler;
}

/** Runnable adapter — zero-arg lambda. */
Expand Down Expand Up @@ -206,7 +232,9 @@ public Object invoke(Object[] args) throws EvalError {
lambdaNs.setVariable(parameterNames[i], safeArgs[i], false);
}
} catch (UtilEvalError ex) {
throw ex.toEvalError(null, null);
EvalError converted = ex.toEvalError(null, null);
reportError(converted);
throw converted;
}
Interpreter prevInterpreter = CURRENT_INTERPRETER.get();
NameSpace prevNamespace = CURRENT_NAMESPACE.get();
Expand All @@ -216,6 +244,12 @@ public Object invoke(Object[] args) throws EvalError {
synchronized (interpreter) {
return Primitive.unwrap(interpreter.eval(bodySource, lambdaNs));
}
} catch (EvalError ex) {
reportError(ex);
throw ex;
} catch (RuntimeException ex) {
reportError(ex);
throw ex;
} finally {
if (prevInterpreter != null) {
CURRENT_INTERPRETER.set(prevInterpreter);
Expand All @@ -229,6 +263,17 @@ public Object invoke(Object[] args) throws EvalError {
}
}
}

private void reportError(Throwable error) {
if (errorHandler == null) {
return;
}
try {
errorHandler.onLambdaError(error, bodySource);
} catch (Throwable ignored) {
// Reporter failures must not displace the real lambda error.
}
}
}

/** Adapt a {@link LambdaValue} to a {@link java.util.function.BinaryOperator}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,7 @@ public final class GeneratedCN1Access implements CN1Access {
"com.codenameone.playground.CN1Playground",
"com.codenameone.playground.PlaygroundContext",
"com.codenameone.playground.PlaygroundContext.Logger",
"com.codenameone.playground.PlaygroundContext.RuntimeErrorReporter",
"com.codenameone.playground.PlaygroundLambdaBridge",
"com.codenameone.playground.PlaygroundListenerBridge",
"com.codenameone.playground.WebsiteThemeNative",
Expand Down Expand Up @@ -1599,19 +1600,20 @@ private static void fillMethodIndex9(Map<String, String[]> index) {
index.put("com.codename1.xml.XMLParser", splitMembers(""));
index.put("com.codename1.xml.XMLWriter", splitMembers(""));
index.put("com.codenameone.playground.CN1Playground", splitMembers("destroy()getTheme()init(Object)runApp()start()stop()"));
index.put("com.codenameone.playground.PlaygroundContext", splitMembers("captureShownForm(Form)clearCreatedComponents()clearPreview()clearShownForm()getCreatedComponents()getFirstCreatedComponent()getFirstCreatedForm()getHostForm()getPreviewRoot()getShownForm()getTheme()log(String)recordCreatedComponent(Component)refreshPreview()setTitle(String)debug(String)getCurrent()interceptMethodInvocation(Object, String, Object[])notifyConstructed(Object)"));
index.put("com.codenameone.playground.PlaygroundContext", splitMembers("captureShownForm(Form)clearCreatedComponents()clearPreview()clearShownForm()getCreatedComponents()getFirstCreatedComponent()getFirstCreatedForm()getHostForm()getPreviewRoot()getShownForm()getTheme()log(String)recordCreatedComponent(Component)refreshPreview()reportRuntimeError(String, Throwable)setTitle(String)debug(String)getCurrent()interceptMethodInvocation(Object, String, Object[])notifyConstructed(Object)"));
index.put("com.codenameone.playground.PlaygroundContext.Logger", splitMembers("log(String)"));
index.put("com.codenameone.playground.PlaygroundContext.RuntimeErrorReporter", splitMembers("reportRuntimeError(String, Throwable)"));
index.put("com.codenameone.playground.PlaygroundLambdaBridge", splitMembers("lambda(Object[], String)lambda(String[], String)"));
index.put("com.codenameone.playground.PlaygroundListenerBridge", splitMembers("actionListener(Object)networkListener(Object)onComplete(Object)runnable(Object)"));
index.put("com.codenameone.playground.WebsiteThemeNative", splitMembers("isDarkMode()isSupported()notifyUiReady()"));
index.put("java.io.ByteArrayInputStream", splitMembers(""));
index.put("java.io.ByteArrayOutputStream", splitMembers(""));
index.put("java.io.DataInput", splitMembers(""));
index.put("java.io.DataInputStream", splitMembers(""));
index.put("java.io.DataOutput", splitMembers(""));
}

private static void fillMethodIndex10(Map<String, String[]> index) {
index.put("java.io.DataOutput", splitMembers(""));
index.put("java.io.DataOutputStream", splitMembers(""));
index.put("java.io.EOFException", splitMembers(""));
index.put("java.io.Flushable", splitMembers(""));
Expand Down Expand Up @@ -1675,10 +1677,10 @@ private static void fillMethodIndex10(Map<String, String[]> index) {
index.put("java.lang.OutOfMemoryError", splitMembers(""));
index.put("java.lang.Override", splitMembers(""));
index.put("java.lang.Runnable", splitMembers(""));
index.put("java.lang.Runtime", splitMembers(""));
}

private static void fillMethodIndex11(Map<String, String[]> index) {
index.put("java.lang.Runtime", splitMembers(""));
index.put("java.lang.RuntimeException", splitMembers(""));
index.put("java.lang.SafeVarargs", splitMembers(""));
index.put("java.lang.SecurityException", splitMembers(""));
Expand Down Expand Up @@ -1742,10 +1744,10 @@ private static void fillMethodIndex11(Map<String, String[]> index) {
index.put("java.util.Comparator", splitMembers(""));
index.put("java.util.ConcurrentModificationException", splitMembers(""));
index.put("java.util.Date", splitMembers(""));
index.put("java.util.Deque", splitMembers(""));
}

private static void fillMethodIndex12(Map<String, String[]> index) {
index.put("java.util.Deque", splitMembers(""));
index.put("java.util.Dictionary", splitMembers(""));
index.put("java.util.EmptyStackException", splitMembers(""));
index.put("java.util.Enumeration", splitMembers(""));
Expand Down Expand Up @@ -2476,17 +2478,18 @@ private static void fillFieldIndex9(Map<String, String[]> index) {
index.put("com.codenameone.playground.CN1Playground", splitMembers(""));
index.put("com.codenameone.playground.PlaygroundContext", splitMembers(""));
index.put("com.codenameone.playground.PlaygroundContext.Logger", splitMembers(""));
index.put("com.codenameone.playground.PlaygroundContext.RuntimeErrorReporter", splitMembers(""));
index.put("com.codenameone.playground.PlaygroundLambdaBridge", splitMembers(""));
index.put("com.codenameone.playground.PlaygroundListenerBridge", splitMembers(""));
index.put("com.codenameone.playground.WebsiteThemeNative", splitMembers(""));
index.put("java.io.ByteArrayInputStream", splitMembers(""));
index.put("java.io.ByteArrayOutputStream", splitMembers(""));
index.put("java.io.DataInput", splitMembers(""));
index.put("java.io.DataInputStream", splitMembers(""));
index.put("java.io.DataOutput", splitMembers(""));
}

private static void fillFieldIndex10(Map<String, String[]> index) {
index.put("java.io.DataOutput", splitMembers(""));
index.put("java.io.DataOutputStream", splitMembers(""));
index.put("java.io.EOFException", splitMembers(""));
index.put("java.io.Flushable", splitMembers(""));
Expand Down Expand Up @@ -2550,10 +2553,10 @@ private static void fillFieldIndex10(Map<String, String[]> index) {
index.put("java.lang.OutOfMemoryError", splitMembers(""));
index.put("java.lang.Override", splitMembers(""));
index.put("java.lang.Runnable", splitMembers(""));
index.put("java.lang.Runtime", splitMembers(""));
}

private static void fillFieldIndex11(Map<String, String[]> index) {
index.put("java.lang.Runtime", splitMembers(""));
index.put("java.lang.RuntimeException", splitMembers(""));
index.put("java.lang.SafeVarargs", splitMembers(""));
index.put("java.lang.SecurityException", splitMembers(""));
Expand Down Expand Up @@ -2617,10 +2620,10 @@ private static void fillFieldIndex11(Map<String, String[]> index) {
index.put("java.util.Comparator", splitMembers(""));
index.put("java.util.ConcurrentModificationException", splitMembers(""));
index.put("java.util.Date", splitMembers(""));
index.put("java.util.Deque", splitMembers(""));
}

private static void fillFieldIndex12(Map<String, String[]> index) {
index.put("java.util.Deque", splitMembers(""));
index.put("java.util.Dictionary", splitMembers(""));
index.put("java.util.EmptyStackException", splitMembers(""));
index.put("java.util.Enumeration", splitMembers(""));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ private static Class<?> findClassChunk0(String simpleName) {
if ("Logger".equals(simpleName)) {
return com.codenameone.playground.PlaygroundContext.Logger.class;
}
if ("RuntimeErrorReporter".equals(simpleName)) {
return com.codenameone.playground.PlaygroundContext.RuntimeErrorReporter.class;
}
if ("PlaygroundLambdaBridge".equals(simpleName)) {
return com.codenameone.playground.PlaygroundLambdaBridge.class;
}
Expand All @@ -51,6 +54,10 @@ public static Object construct(Class<?> type, Object[] args) throws Exception {
Object[] adaptedArgs = adaptArgs(safeArgs, new Class<?>[]{com.codename1.ui.Form.class, com.codename1.ui.Container.class, com.codename1.ui.util.Resources.class, com.codenameone.playground.PlaygroundContext.Logger.class}, false);
return new com.codenameone.playground.PlaygroundContext((com.codename1.ui.Form) adaptedArgs[0], (com.codename1.ui.Container) adaptedArgs[1], (com.codename1.ui.util.Resources) adaptedArgs[2], (com.codenameone.playground.PlaygroundContext.Logger) adaptedArgs[3]);
}
if (matches(safeArgs, new Class<?>[]{com.codename1.ui.Form.class, com.codename1.ui.Container.class, com.codename1.ui.util.Resources.class, com.codenameone.playground.PlaygroundContext.Logger.class, com.codenameone.playground.PlaygroundContext.RuntimeErrorReporter.class}, false)) {
Object[] adaptedArgs = adaptArgs(safeArgs, new Class<?>[]{com.codename1.ui.Form.class, com.codename1.ui.Container.class, com.codename1.ui.util.Resources.class, com.codenameone.playground.PlaygroundContext.Logger.class, com.codenameone.playground.PlaygroundContext.RuntimeErrorReporter.class}, false);
return new com.codenameone.playground.PlaygroundContext((com.codename1.ui.Form) adaptedArgs[0], (com.codename1.ui.Container) adaptedArgs[1], (com.codename1.ui.util.Resources) adaptedArgs[2], (com.codenameone.playground.PlaygroundContext.Logger) adaptedArgs[3], (com.codenameone.playground.PlaygroundContext.RuntimeErrorReporter) adaptedArgs[4]);
}
}
throw unsupportedConstruct(type, safeArgs);
}
Expand Down Expand Up @@ -126,9 +133,16 @@ public static Object invoke(Object target, String name, Object[] args) throws Ex
unsupported = ex;
}
}
if (target instanceof com.codenameone.playground.PlaygroundContext.RuntimeErrorReporter) {
try {
return invoke5((com.codenameone.playground.PlaygroundContext.RuntimeErrorReporter) target, name, safeArgs);
} catch (CN1AccessException ex) {
unsupported = ex;
}
}
if (target instanceof com.codenameone.playground.WebsiteThemeNative) {
try {
return invoke5((com.codenameone.playground.WebsiteThemeNative) target, name, safeArgs);
return invoke6((com.codenameone.playground.WebsiteThemeNative) target, name, safeArgs);
} catch (CN1AccessException ex) {
unsupported = ex;
}
Expand Down Expand Up @@ -248,6 +262,12 @@ private static Object invoke1(com.codenameone.playground.PlaygroundContext typed
typedTarget.refreshPreview(); return null;
}
}
if ("reportRuntimeError".equals(name)) {
if (matches(safeArgs, new Class<?>[]{java.lang.String.class, java.lang.Throwable.class}, false)) {
Object[] adaptedArgs = adaptArgs(safeArgs, new Class<?>[]{java.lang.String.class, java.lang.Throwable.class}, false);
typedTarget.reportRuntimeError((java.lang.String) adaptedArgs[0], (java.lang.Throwable) adaptedArgs[1]); return null;
}
}
if ("setTitle".equals(name)) {
if (matches(safeArgs, new Class<?>[]{java.lang.String.class}, false)) {
Object[] adaptedArgs = adaptArgs(safeArgs, new Class<?>[]{java.lang.String.class}, false);
Expand Down Expand Up @@ -309,7 +329,17 @@ private static Object invoke4(com.codenameone.playground.PlaygroundContext.Logge
throw unsupportedInstance(typedTarget, name, safeArgs);
}

private static Object invoke5(com.codenameone.playground.WebsiteThemeNative typedTarget, String name, Object[] safeArgs) throws Exception {
private static Object invoke5(com.codenameone.playground.PlaygroundContext.RuntimeErrorReporter typedTarget, String name, Object[] safeArgs) throws Exception {
if ("reportRuntimeError".equals(name)) {
if (matches(safeArgs, new Class<?>[]{java.lang.String.class, java.lang.Throwable.class}, false)) {
Object[] adaptedArgs = adaptArgs(safeArgs, new Class<?>[]{java.lang.String.class, java.lang.Throwable.class}, false);
typedTarget.reportRuntimeError((java.lang.String) adaptedArgs[0], (java.lang.Throwable) adaptedArgs[1]); return null;
}
}
throw unsupportedInstance(typedTarget, name, safeArgs);
}

private static Object invoke6(com.codenameone.playground.WebsiteThemeNative typedTarget, String name, Object[] safeArgs) throws Exception {
if ("isDarkMode".equals(name)) {
if (safeArgs.length == 0) {
return typedTarget.isDarkMode();
Expand Down
Loading
Loading