2525import groovy .lang .GrooidClassLoader ;
2626import 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 )
2930public 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