Skip to content

Commit 8ac202d

Browse files
committed
refactor: propagate exceptions properly
1 parent 2e5cd36 commit 8ac202d

1 file changed

Lines changed: 66 additions & 27 deletions

File tree

groovy-android/src/main/java/com/tyron/groovy/GrooidShell.java

Lines changed: 66 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
import groovy.lang.GrooidClassLoader;
2626
import groovy.lang.Script;
2727

28-
// TODO: Handle API 26, rethrow exceptions instead of swallowing them
28+
// TODO: Handle API 26
29+
@RequiresApi(api = Build.VERSION_CODES.O_MR1)
2930
public class GrooidShell {
3031

3132
private final ClassLoader classLoader;
@@ -34,13 +35,21 @@ public GrooidShell(ClassLoader classLoader) {
3435
this.classLoader = classLoader;
3536
}
3637

37-
@RequiresApi(api = Build.VERSION_CODES.O_MR1)
38-
public Object evaluate(String scriptText) {
38+
@SuppressWarnings("UnusedReturnValue")
39+
public Result evaluate(String scriptText) {
40+
try {
41+
return evaluateInternal(scriptText);
42+
} catch (CompilationFailedException e) {
43+
return Result.failed(e);
44+
}
45+
}
46+
47+
private Result evaluateInternal(String scriptText) throws CompilationFailedException {
3948
D8Command.Builder builder = D8Command.builder();
4049
builder.setDisableDesugaring(true);
4150
builder.setMinApiLevel(26);
4251

43-
final Set<String> classNames = new LinkedHashSet<>();
52+
Set<String> classNames = new LinkedHashSet<>();
4453
CompilerConfiguration config = new CompilerConfiguration();
4554
config.setBytecodePostprocessor((name, original) -> {
4655
builder.addClassProgramData(original, Origin.unknown());
@@ -51,9 +60,25 @@ public Object evaluate(String scriptText) {
5160
GrooidClassLoader gcl = new GrooidClassLoader(classLoader, config);
5261
gcl.parseClass(scriptText);
5362

54-
final ByteBuffer[] byteBuffer = new ByteBuffer[1];
63+
ByteBuffer[] byteBuffer = getTransformedDexByteBufferArray(builder);
64+
Map<String, Class<?>> classes = defineDynamic(classNames, byteBuffer);
65+
for (Class<?> scriptClass : classes.values()) {
66+
if (Script.class.isAssignableFrom(scriptClass)) {
67+
try {
68+
Script script = (Script) scriptClass.newInstance();
69+
return Result.success(script.run());
70+
} catch (IllegalAccessException | InstantiationException e) {
71+
return Result.failed(e);
72+
}
73+
}
74+
}
5575

56-
builder.setProgramConsumer(new DexIndexedConsumer() {
76+
return Result.failed(new IllegalArgumentException("Un-parsable argument provided."));
77+
}
78+
79+
private ByteBuffer[] getTransformedDexByteBufferArray(D8Command.Builder commandBuilder) throws CompilationFailedException {
80+
final ByteBuffer[] byteBuffer = new ByteBuffer[1];
81+
commandBuilder.setProgramConsumer(new DexIndexedConsumer() {
5782
@Override
5883
public void finished(DiagnosticsHandler diagnosticsHandler) {
5984

@@ -64,27 +89,8 @@ public void accept(int fileIndex, ByteDataView data, Set<String> descriptors, Di
6489
byteBuffer[0] = ByteBuffer.wrap(data.getBuffer());
6590
}
6691
});
67-
68-
try {
69-
D8.run(builder.build());
70-
} catch (CompilationFailedException e) {
71-
e.printStackTrace();
72-
}
73-
74-
Map<String, Class<?>> classes = defineDynamic(classNames, byteBuffer);
75-
for (Class<?> scriptClass : classes.values()) {
76-
if (Script.class.isAssignableFrom(scriptClass)) {
77-
Script script;
78-
try {
79-
script = (Script) scriptClass.newInstance();
80-
return script.run();
81-
} catch (IllegalAccessException | InstantiationException e) {
82-
e.printStackTrace();
83-
}
84-
}
85-
}
86-
87-
return null;
92+
D8.run(commandBuilder.build());
93+
return byteBuffer;
8894
}
8995

9096
@RequiresApi(api = Build.VERSION_CODES.O_MR1)
@@ -100,4 +106,37 @@ private Map<String, Class<?>> defineDynamic(Set<String> classNames, ByteBuffer[]
100106
}
101107
return result;
102108
}
109+
110+
@SuppressWarnings("unused")
111+
public static class Result {
112+
113+
public static Result failed(Throwable failure) {
114+
return new Result(null, failure);
115+
}
116+
117+
public static Result success(Object result) {
118+
return new Result(result, null);
119+
}
120+
121+
private final Throwable failure;
122+
123+
private final Object result;
124+
125+
private Result(Object result, Throwable failure) {
126+
this.result = result;
127+
this.failure = failure;
128+
}
129+
130+
public boolean isSuccessful() {
131+
return failure != null;
132+
}
133+
134+
public Throwable getFailure() {
135+
return failure;
136+
}
137+
138+
public Object getResult() {
139+
return result;
140+
}
141+
}
103142
}

0 commit comments

Comments
 (0)