From e5f4515cb1c3763aea203bc0b26bed937f921ef1 Mon Sep 17 00:00:00 2001 From: romanman Date: Sun, 25 May 2014 21:39:47 +0300 Subject: [PATCH] Draft_1 for compile assembly to a machine code --- .../main/java/org/ethereum/gui/ToolBar.java | 2 +- .../org/ethereum/serpent/SerpentCompiler.java | 98 +++++++++++++++++++ .../serpent/SerpentToAssemblyCompiler.java | 2 + .../main/java/org/ethereum/util/ByteUtil.java | 24 +++++ .../src/main/java/org/ethereum/vm/OpCode.java | 24 +++-- .../src/main/resources/system.properties | 4 - .../ethereum/serpent/SerpentCompileTest.java | 44 ++++++--- 7 files changed, 171 insertions(+), 27 deletions(-) diff --git a/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java b/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java index cf60034c..a7a58f20 100644 --- a/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java +++ b/ethereumj-core/src/main/java/org/ethereum/gui/ToolBar.java @@ -31,7 +31,7 @@ public class ToolBar extends JFrame { public ToolBar() throws HeadlessException { introLogger.info(""); - introLogger.info("♢ EthereumJ [v0.5.1] "); + introLogger.info("♢ EthereumJ [v0.5.1] by RomanJ"); introLogger.info("♢ Code by Roman Mandeleil, (c) 2014."); introLogger.info("♢ Contribution: Nick Savers "); introLogger.info("♢ Based on a design by Vitalik Buterin."); diff --git a/ethereumj-core/src/main/java/org/ethereum/serpent/SerpentCompiler.java b/ethereumj-core/src/main/java/org/ethereum/serpent/SerpentCompiler.java index dfdc77d2..9da1946e 100644 --- a/ethereumj-core/src/main/java/org/ethereum/serpent/SerpentCompiler.java +++ b/ethereumj-core/src/main/java/org/ethereum/serpent/SerpentCompiler.java @@ -1,6 +1,15 @@ package org.ethereum.serpent; import org.antlr.v4.runtime.tree.ParseTree; +import org.ethereum.util.ByteUtil; +import org.ethereum.vm.OpCode; +import org.spongycastle.util.BigIntegers; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; /** * www.ethereumJ.com @@ -20,4 +29,93 @@ public class SerpentCompiler { return result; } + + + public static String compileAssemblyToMachine(String code){ + + StringBuffer assemblyCode = new StringBuffer(); + String[] lexaArr = code.split("\\s+"); + + List lexaList = new ArrayList(); + Collections.addAll(lexaList, lexaArr); + + // Encode push_n numbers + for (int i = 0; i < lexaList.size(); ++i){ + + String lexa = lexaList.get(i); + + if (OpCode.contains(lexa) || + lexa.contains("REF_") || + lexa.contains("LABEL_")) continue; + + int bytesNum = ByteUtil.numBytes( lexa ); + + String num = lexaList.remove(i); + BigInteger bNum = new BigInteger(num); + byte[] bytes = BigIntegers.asUnsignedByteArray(bNum); + if (bytes.length == 0)bytes = new byte[]{0}; + + for (int j = bytes.length; j > 0 ; --j){ + + lexaList.add(i, (bytes[j-1] & 0xFF) +""); + } + + lexaList.add(i, "PUSH" + bytesNum); + i = i + bytesNum; + + } + + // calc label pos & remove labels + HashMap labels = new HashMap(); + for (int i = 0; i < lexaList.size(); ++i){ + + String lexa = lexaList.get(i); + if (!lexa.contains("LABEL_")) continue; + + String label = lexaList.remove(i); + String labelNum = label.split("LABEL_")[1]; + + labels.put(labelNum, i); + + ++i; + } + + // encode all ref occurrence + for (int i = 0; i < lexaList.size(); ++i){ + + String lexa = lexaList.get(i); + if (!lexa.contains("REF_")) continue; + + String ref = lexaList.remove(i); + String labelNum = ref.split("REF_")[1]; + + Integer pos = labels.get(labelNum); + int bytesNum = ByteUtil.numBytes( pos.toString() ); + + lexaList.add(i, pos.toString()); + + for (int j = 0; j < (4 - bytesNum) ; ++j) + lexaList.add(i, "0"); + + lexaList.add(i, "PUSH4"); + ++i; + } + + for (String lexa : lexaList){ + + if (OpCode.contains(lexa)) + assemblyCode.append( OpCode.byteVal(lexa) ); + else{ + + assemblyCode.append( lexa ); + } + assemblyCode.append(" "); + } + + return assemblyCode.toString(); + } + + + + } diff --git a/ethereumj-core/src/main/java/org/ethereum/serpent/SerpentToAssemblyCompiler.java b/ethereumj-core/src/main/java/org/ethereum/serpent/SerpentToAssemblyCompiler.java index 87303894..3849d404 100644 --- a/ethereumj-core/src/main/java/org/ethereum/serpent/SerpentToAssemblyCompiler.java +++ b/ethereumj-core/src/main/java/org/ethereum/serpent/SerpentToAssemblyCompiler.java @@ -170,6 +170,8 @@ public class SerpentToAssemblyCompiler extends SerpentBaseVisitor { @Override public String visitInt_val(@NotNull SerpentParser.Int_valContext ctx) { + // todo: (!!!) ensure that not encode value more than 32 bytes (!!!) + if (ctx.OP_NOT() != null) return visitExpression(ctx.expression()) + " NOT"; diff --git a/ethereumj-core/src/main/java/org/ethereum/util/ByteUtil.java b/ethereumj-core/src/main/java/org/ethereum/util/ByteUtil.java index 7cbb6e69..bd246a38 100644 --- a/ethereumj-core/src/main/java/org/ethereum/util/ByteUtil.java +++ b/ethereumj-core/src/main/java/org/ethereum/util/ByteUtil.java @@ -82,4 +82,28 @@ public class ByteUtil { return 0; return new BigInteger(1, b).intValue(); } + + + /** + * Calculate the number of bytes need + * to encode the number + * + * @param val - number + * @return number of min bytes used to encode the number + */ + public static int numBytes(String val){ + + BigInteger bInt = new BigInteger(val); + int bytes = 0; + + while(!bInt.equals(BigInteger.ZERO)){ + + bInt = bInt.shiftRight(8); + ++bytes; + } + + if (bytes == 0) ++bytes; + + return bytes; + } } diff --git a/ethereumj-core/src/main/java/org/ethereum/vm/OpCode.java b/ethereumj-core/src/main/java/org/ethereum/vm/OpCode.java index c54ecd40..2865598b 100644 --- a/ethereumj-core/src/main/java/org/ethereum/vm/OpCode.java +++ b/ethereumj-core/src/main/java/org/ethereum/vm/OpCode.java @@ -1,14 +1,13 @@ package org.ethereum.vm; -import java.util.HashMap; -import java.util.Map; +import java.util.*; /** * Instruction set for the Ethereum Virtual Machine */ public enum OpCode { - /** + /** * Stop and Arithmetic Operations */ @@ -136,9 +135,13 @@ public enum OpCode { private byte opcode; private static final Map intToTypeMap = new HashMap(); + private static final Map stringToByteMap = new HashMap(); + + static { for (OpCode type : OpCode.values()) { intToTypeMap.put(type.opcode, type); + stringToByteMap.put(type.name(), type.opcode); } } @@ -148,12 +151,21 @@ public enum OpCode { public static OpCode fromInt(int i) { OpCode type = intToTypeMap.get(i); - if (type == null) + if (type == null) return OpCode.STOP; return type; } - + public int asInt() { return opcode; - } + } + + public static boolean contains(String code){ + + return stringToByteMap.containsKey(code.trim()); + } + + public static byte byteVal(String code){ + return stringToByteMap.get(code); + } } diff --git a/ethereumj-core/src/main/resources/system.properties b/ethereumj-core/src/main/resources/system.properties index ad73bcb5..09519175 100644 --- a/ethereumj-core/src/main/resources/system.properties +++ b/ethereumj-core/src/main/resources/system.properties @@ -1,8 +1,4 @@ -#*** -# Starting to gather some properties the system going to support -#*** -client.name=EthereumJ [v0.5.1] by RomanJ # if the system will work as a server also # accept for incoming connections [true/false] diff --git a/ethereumj-core/src/test/java/org/ethereum/serpent/SerpentCompileTest.java b/ethereumj-core/src/test/java/org/ethereum/serpent/SerpentCompileTest.java index 1d4d7d50..574f3d97 100644 --- a/ethereumj-core/src/test/java/org/ethereum/serpent/SerpentCompileTest.java +++ b/ethereumj-core/src/test/java/org/ethereum/serpent/SerpentCompileTest.java @@ -1,6 +1,7 @@ package org.ethereum.serpent; import org.antlr.v4.runtime.tree.ParseTree; +import org.ethereum.util.ByteUtil; import org.junit.Assert; import org.junit.Test; @@ -1211,23 +1212,34 @@ public class SerpentCompileTest { Assert.assertEquals(expected, result); } -/* - * -a = msg.datasize -b = msg.sender -c = msg.value -d = tx.gasprice -e = tx.origin -f = tx.gas -g = contract.balance -h = block.prevhash -i = block.coinbase -j = block.timestamp -k = block.number -l = block.difficulty -m = block.gaslimit -*/ + + @Test // compile to machine code 1 + public void test42(){ + + String code = "x = 256 \n" + + "while x > 1: \n" + + " if (x % 2) == 0: \n" + + " x = x / 2 \n" + + " else: \n " + + " x = 3 * x + 1 \n" ; + + String expected = "97 1 0 96 0 84 96 1 96 0 83 11 12 13 99 0 0 0 53 89 96 0 96 2 96 0 83 6 12 13 99 0 0 0 39 89 96 2 96 0 83 4 96 0 84 99 0 0 0 51 88 96 1 96 0 83 96 3 2 1 96 0 84 99 0 0 0 6 88"; + + String asmResult = SerpentCompiler.compile(code); + String machineCode = SerpentCompiler.compileAssemblyToMachine(asmResult); + + Assert.assertEquals(expected, machineCode.trim()); + } + + + + @Test // todo delete this one + public void testFoo(){ + + System.out.println(ByteUtil.numBytes("65536")); + + } /** *