Skip to content

Commit

Permalink
Added ABI writing support for ABICompiler embedded interface
Browse files Browse the repository at this point in the history
-also adjusted the lifecycle of the ABICompiler so that any stateful call sequence requirements are hidden in factory methods
  • Loading branch information
jeff-aion committed Apr 17, 2019
1 parent ad82c3d commit f760df0
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 56 deletions.
4 changes: 1 addition & 3 deletions org.aion.avm.tooling/src/org/aion/avm/tooling/AvmRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
public final class AvmRule implements TestRule {

private boolean debugMode;
private final ABICompiler compiler;
private final JarOptimizer jarOptimizer;
public TestingKernel kernel;
public AvmImpl avm;
Expand All @@ -39,7 +38,6 @@ public final class AvmRule implements TestRule {
public AvmRule(boolean debugMode) {
this.debugMode = debugMode;
this.kernel = new TestingKernel(block);
compiler = new ABICompiler();
jarOptimizer = new JarOptimizer(debugMode);
}

Expand Down Expand Up @@ -72,7 +70,7 @@ public void evaluate() throws Throwable {
*/
public byte[] getDappBytes(Class<?> mainClass, byte[] arguments, Class<?>... otherClasses) {
byte[] jar = JarBuilder.buildJarForMainAndClasses(mainClass, otherClasses);
compiler.compile(jar);
ABICompiler compiler = ABICompiler.compileJarBytes(jar);
byte[] optimizedDappBytes = jarOptimizer.optimize(compiler.getJarFileBytes());
return new CodeAndArguments(optimizedDappBytes, arguments).encodeToBytes();
}
Expand Down
49 changes: 36 additions & 13 deletions org.aion.avm.tooling/src/org/aion/avm/tooling/abi/ABICompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Arrays;
import org.aion.avm.core.dappreading.JarBuilder;
import org.aion.avm.core.util.Helpers;
Expand Down Expand Up @@ -65,16 +67,7 @@ public static void main(String[] args) {

compiler.compile(fileInputStream);

System.out.println(VERSION_NUMBER);
System.out.println(compiler.mainClassName);
System.out.print("Clinit: ");
for (Type t: compiler.initializables) {
System.out.print(ABIUtils.shortenClassName(t.getClassName()) + " ");
}
System.out.println();
for (String s : compiler.callables) {
System.out.println(s);
}
compiler.writeAbi(System.out);

try {
DataOutputStream dout =
Expand All @@ -90,11 +83,26 @@ private static void usage() {
System.out.println("Usage: ABICompiler <DApp jar path>");
}

public void compile(byte[] jarBytes) {
compile(new ByteArrayInputStream(jarBytes));
public static ABICompiler compileJar(InputStream byteReader) {
ABICompiler compiler = new ABICompiler();
compiler.compile(byteReader);
return compiler;
}

public static ABICompiler compileJarBytes(byte[] rawBytes) {
ABICompiler compiler = new ABICompiler();
compiler.compile(new ByteArrayInputStream(rawBytes));
return compiler;
}

/**
* We only want to expose the ABICompiler object once it is fully populated (_has_ compiled something) so we hide the constructor.
* This can only be meaningfully called by our factory methods.
*/
private ABICompiler() {
}

public void compile(InputStream byteReader) {
private void compile(InputStream byteReader) {
try {
safeLoadFromBytes(byteReader);
} catch (Exception e) {
Expand Down Expand Up @@ -126,6 +134,21 @@ public void compile(InputStream byteReader) {
// }
}

public void writeAbi(OutputStream rawStream) {
// We want this to know about new lines so use a PrintStream.
PrintStream abiStream = new PrintStream(rawStream);
abiStream.println(VERSION_NUMBER);
abiStream.println(this.mainClassName);
abiStream.print("Clinit: ");
for (Type t: this.initializables) {
abiStream.print(ABIUtils.shortenClassName(t.getClassName()) + " ");
}
abiStream.println();
for (String s : this.callables) {
abiStream.println(s);
}
}

private void safeLoadFromBytes(InputStream byteReader) throws Exception {
classMap = new HashMap<>();
mainClassName = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
Expand All @@ -28,6 +29,10 @@


public class ABICompilerTest {
private static final String CHATTY_CALCULATOR_ABI = ABICompiler.getVersionNumber()
+ "\norg.aion.avm.tooling.abi.ChattyCalculatorTarget"
+ "\nClinit: "
+ "\npublic static String amIGreater(int, int)\n";

private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private final PrintStream originalOut = System.out;
Expand Down Expand Up @@ -91,17 +96,28 @@ public void testMainMethodCalculator() throws IOException {
dout.close();

ABICompiler.main(new String[]{tempDir.toString() + "/dapp.jar"});
Assert.assertEquals(
ABICompiler.getVersionNumber()
+ "\norg.aion.avm.tooling.abi.ChattyCalculatorTarget"
+ "\nClinit: "
+ "\npublic static String amIGreater(int, int)\n",
Assert.assertEquals(CHATTY_CALCULATOR_ABI,
outContent.toString());
File outputJar = new File(System.getProperty("user.dir") + "/outputJar.jar");
boolean didDelete = outputJar.delete();
Assert.assertTrue(didDelete);
}

@Test
public void testEmbeddedUsesOfCalculator() {
byte[] jar =
JarBuilder
.buildJarForMainAndClasses(ChattyCalculatorTarget.class,
SilentCalculatorTarget.class);

ABICompiler embeddedCompiler = ABICompiler.compileJarBytes(jar);
// Write the ABI to a stream we can explore.
ByteArrayOutputStream abiStream = new ByteArrayOutputStream();
embeddedCompiler.writeAbi(abiStream);
String abiString = new String(abiStream.toByteArray(), StandardCharsets.UTF_8);
Assert.assertEquals(CHATTY_CALCULATOR_ABI, abiString);
}

@Test
public void testStaticInitializers() throws IOException {

Expand Down Expand Up @@ -131,29 +147,27 @@ public void testStaticInitializers() throws IOException {

@Test
public void testGetMissingUserlibClasses() {
ABICompiler compiler = new ABICompiler();
byte[] jar =
JarBuilder
.buildJarForMainAndClasses(ChattyCalculatorTarget.class,
SilentCalculatorTarget.class, AionList.class, AionBuffer.class);

Class[] expectedMissingClasses = new Class[] {ABIDecoder.class, ABIEncoder.class, ABIException.class, ABIToken.class, AionMap.class, AionSet.class};

compiler.compile(jar);
ABICompiler compiler = ABICompiler.compileJarBytes(jar);
Class[] actualMissingClasses = compiler.getMissingUserlibClasses();
checkArrays(expectedMissingClasses, actualMissingClasses);
}

// Compilation should fail because of garbage AionList.class
@Test(expected = ABICompilerException.class)
public void testGetMissingUserlibClassesFail() {
ABICompiler compiler = new ABICompiler();
Map<String, byte[]> classMap = new HashMap<>();
classMap.put("org.aion.avm.userlib.AionList", new byte[10]);
byte[] jar =
JarBuilder
.buildJarForMainClassAndExplicitClassNamesAndBytecode(ChattyCalculatorTarget.class, classMap);
compiler.compile(jar);
ABICompiler.compileJarBytes(jar);
}

private void checkArrays(Class[] expectedArray, Class[] actualArray) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,17 @@

import static org.junit.Assert.assertTrue;

import java.io.ByteArrayInputStream;
import org.aion.avm.core.dappreading.JarBuilder;
import org.junit.Test;


public class CheckTypesTest {

private static ABICompiler compiler = new ABICompiler();

// Compilation should fail because of boxed-type parameters
@Test(expected = ABICompilerException.class)
public void testBadParams() {
byte[] jar = JarBuilder.buildJarForMainAndClasses(DAppForbiddenParameterTarget.class);
try {
compiler.compile(new ByteArrayInputStream(jar));
ABICompiler.compileJarBytes(jar);
} catch(ABICompilerException e) {
System.out.println(e.getMessage());
assertTrue(e.getMessage().contains("badParams"));
Expand All @@ -29,7 +25,7 @@ public void testBadParams() {
public void testBadReturnType() {
byte[] jar = JarBuilder.buildJarForMainAndClasses(DAppForbiddenReturnTypeTarget.class);
try {
compiler.compile(new ByteArrayInputStream(jar));
ABICompiler.compileJarBytes(jar);
} catch(ABICompilerException e) {
assertTrue(e.getMessage().contains("badReturn"));
throw e;
Expand All @@ -41,7 +37,7 @@ public void testBadReturnType() {
public void testBadIntArray() {
byte[] jar = JarBuilder.buildJarForMainAndClasses(DAppForbiddenIntArrayTarget.class);
try {
compiler.compile(new ByteArrayInputStream(jar));
ABICompiler.compileJarBytes(jar);
} catch(ABICompilerException e) {
assertTrue(e.getMessage().contains("badIntArray"));
throw e;
Expand All @@ -53,7 +49,7 @@ public void testBadIntArray() {
public void testBadStringArray() {
byte[] jar = JarBuilder.buildJarForMainAndClasses(DAppForbiddenStringArrayTarget.class);
try {
compiler.compile(new ByteArrayInputStream(jar));
ABICompiler.compileJarBytes(jar);
} catch(ABICompilerException e) {
assertTrue(e.getMessage().contains("badStringArray"));
throw e;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,20 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
import org.aion.avm.core.dappreading.JarBuilder;
import org.junit.Before;
import org.junit.Test;


public class ExtractAnnotationsTest {

private static ABICompiler compiler;

@Before
public void setup() {
compiler = new ABICompiler();
}

@Test
public void testOneClass() {
List<String> callables = new ArrayList<>();
try {
byte[] jar = JarBuilder.buildJarForMainAndClasses(DAppWithMainNoFallbackTarget.class);

compiler.compile(new ByteArrayInputStream(jar));
ABICompiler compiler = ABICompiler.compileJarBytes(jar);
callables = compiler.getCallables();

} catch (Throwable e) {
Expand All @@ -43,7 +33,7 @@ public void testOneClass() {
public void testNonPublicCallable() {
byte[] jar = JarBuilder.buildJarForMainAndClasses(DAppProtectedCallableTarget.class);
try {
compiler.compile(new ByteArrayInputStream(jar));
ABICompiler.compileJarBytes(jar);
} catch(ABICompilerException e) {
assertTrue(e.getMessage().contains("test4"));
throw e;
Expand All @@ -54,7 +44,7 @@ public void testNonPublicCallable() {
public void testNonStaticCallable() {
byte[] jar = JarBuilder.buildJarForMainAndClasses(DAppNonstaticCallableTarget.class);
try {
compiler.compile(new ByteArrayInputStream(jar));
ABICompiler.compileJarBytes(jar);
} catch(ABICompilerException e) {
assertTrue(e.getMessage().contains("test2"));
throw e;
Expand All @@ -67,7 +57,7 @@ public void testMultiClasses() {
try {
byte[] jar = JarBuilder.buildJarForMainAndClasses(DAppWithMainNoFallbackTarget.class, DAppNoMainWithFallbackTarget.class);

compiler.compile(new ByteArrayInputStream(jar));
ABICompiler compiler = ABICompiler.compileJarBytes(jar);
callables = compiler.getCallables();

} catch (Throwable e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,17 @@

import static org.junit.Assert.assertTrue;

import java.io.ByteArrayInputStream;
import org.aion.avm.core.dappreading.JarBuilder;
import org.junit.Test;


public class OverloadedCallablesTest {

private static ABICompiler compiler = new ABICompiler();

// Compilation should fail because @Callable methods cannot be overloaded
@Test(expected = ABICompilerException.class)
public void testBadParams() {
byte[] jar = JarBuilder.buildJarForMainAndClasses(OverloadedCallablesTarget.class);
try {
compiler.compile(new ByteArrayInputStream(jar));
ABICompiler.compileJarBytes(jar);
} catch(ABICompilerException e) {
System.out.println(e.getMessage());
assertTrue(e.getMessage().contains("test1"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,8 @@ private Address doInitialDeploymentAndSetup(Block block) {
// The assertions in this method depends on the gas charged, which in turn depends on the exact size of the jar file.
// The AvmRule invokes the ABICompiler on all input jars.
// As a result, we have to run the ABICompiler on the input jar to get the correct expected gas values.
ABICompiler compiler = new ABICompiler();
JarOptimizer optimizer = new JarOptimizer(false);
compiler.compile(JarBuilder.buildJarForMainAndClasses(GraphReachabilityIntegrationTestTarget.class));
ABICompiler compiler = ABICompiler.compileJarBytes(JarBuilder.buildJarForMainAndClasses(GraphReachabilityIntegrationTestTarget.class));
byte[] optimizedJar = optimizer.optimize(compiler.getJarFileBytes());
byte[] txData = new CodeAndArguments(optimizedJar, new byte[0]).encodeToBytes();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ public class EnumValuesTest {
public AvmRule avmRule = new AvmRule(false);

private Address from = avmRule.getPreminedAccount();
private ABICompiler compiler = new ABICompiler();


@Test
Expand All @@ -39,7 +38,7 @@ private void runCommonTestOnBytecode(byte[] clazz) {
Map<String, byte[]> classMap = new HashMap<>();
classMap.put(TestEnumForValues.class.getName(), clazz);
byte[] jar = JarBuilder.buildJarForMainClassAndExplicitClassNamesAndBytecode(TestResourceForValues.class, classMap);
compiler.compile(jar);
ABICompiler compiler = ABICompiler.compileJarBytes(jar);
byte[] txData = new CodeAndArguments(compiler.getJarFileBytes(), new byte[0]).encodeToBytes();

Address dappAddr = avmRule.deploy(from, BigInteger.ZERO, txData).getDappAddress();
Expand Down

0 comments on commit f760df0

Please sign in to comment.