From 92dc727962583ebcac39a7a660afbca950b929aa Mon Sep 17 00:00:00 2001 From: Volker Berlin Date: Sun, 26 Mar 2017 13:40:50 +0200 Subject: [PATCH] Write locals to binary format. --- .../binary/BinaryModuleWriter.java | 67 +++++++++++---- .../jwebassembly/module/ModuleWriter.java | 82 ++++++++++++++----- .../jwebassembly/text/TextModuleWriter.java | 11 +-- 3 files changed, 119 insertions(+), 41 deletions(-) diff --git a/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java b/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java index 2c5d444..848348d 100644 --- a/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java +++ b/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java @@ -32,20 +32,23 @@ import de.inetsoftware.jwebassembly.module.ValueType; */ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcodes { - private static final byte[] WASM_BINARY_MAGIC = { 0, 'a', 's', 'm' }; + private static final byte[] WASM_BINARY_MAGIC = { 0, 'a', 's', 'm' }; - private static final int WASM_BINARY_VERSION = 1; + private static final int WASM_BINARY_VERSION = 1; - private WasmOutputStream wasm; + private WasmOutputStream wasm; - private WasmOutputStream codeStream = new WasmOutputStream(); + private WasmOutputStream codeStream = new WasmOutputStream(); - private List functionTypes = new ArrayList<>(); + private WasmOutputStream functionsStream = new WasmOutputStream(); - private Map functions = new LinkedHashMap<>(); - - private Function function; - private FunctionType functionType; + private List functionTypes = new ArrayList<>(); + + private Map functions = new LinkedHashMap<>(); + + private Function function; + + private FunctionType functionType; /** * Create new instance. @@ -74,6 +77,12 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod wasm.close(); } + /** + * Write the type section to the output. This section contains the signatures of the functions. + * + * @throws IOException + * if any I/O error occur + */ private void writeTypeSection() throws IOException { int count = functionTypes.size(); if( count > 0 ) { @@ -96,6 +105,12 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod } } + /** + * Write the function section to the output. This section contains a mapping from the function index to the type signature index. + * + * @throws IOException + * if any I/O error occur + */ private void writeFunctionSection() throws IOException { int count = functions.size(); if( count > 0 ) { @@ -108,10 +123,20 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod } } + /** + * Write the code section to the output. This section contains the byte code. + * + * @throws IOException + * if any I/O error occur + */ private void writeCodeSection() throws IOException { + int size = functions.size(); + if( size == 0 ) { + return; + } WasmOutputStream stream = new WasmOutputStream(); - stream.writeVaruint32( functions.size() ); - codeStream.writeTo( stream ); + stream.writeVaruint32( size ); + functionsStream.writeTo( stream ); wasm.writeSection( SectionType.Code, stream, null ); } @@ -124,6 +149,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod function.id = functions.size(); functions.put( name, function ); functionType = new FunctionType(); + codeStream.reset(); } /** @@ -145,11 +171,22 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod * {@inheritDoc} */ @Override - protected void writeMethodFinish() throws IOException { + protected void writeMethodFinish( List locals ) throws IOException { // TODO optimize and search for duplicates function.typeId = functionTypes.size(); functionTypes.add( functionType ); - codeStream.write( END ); + + + WasmOutputStream localsStream = new WasmOutputStream(); + localsStream.writeVaruint32( locals.size() ); + for( ValueType valueType : locals ) { + localsStream.writeVaruint32( 1 ); // TODO optimize, write the count of same types. + localsStream.writeVarint32( valueType.getCode() ); + } + functionsStream.writeVaruint32( localsStream.size() + codeStream.size() + 1 ); + localsStream.writeTo( functionsStream ); + codeStream.writeTo( functionsStream ); + functionsStream.write( END ); } /** @@ -165,7 +202,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod * {@inheritDoc} */ @Override - protected void writeLoadInt( int idx ) throws IOException { + protected void writeLoad( int idx ) throws IOException { codeStream.write( GET_LOCAL ); codeStream.writeVaruint32( idx ); } @@ -174,7 +211,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod * {@inheritDoc} */ @Override - protected void writeStoreInt( int idx ) throws IOException { + protected void writeStore( int idx ) throws IOException { codeStream.write( SET_LOCAL ); codeStream.writeVaruint32( idx ); } diff --git a/src/de/inetsoftware/jwebassembly/module/ModuleWriter.java b/src/de/inetsoftware/jwebassembly/module/ModuleWriter.java index 04a4a28..450ce91 100644 --- a/src/de/inetsoftware/jwebassembly/module/ModuleWriter.java +++ b/src/de/inetsoftware/jwebassembly/module/ModuleWriter.java @@ -17,6 +17,11 @@ package de.inetsoftware.jwebassembly.module; import java.io.Closeable; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; import de.inetsoftware.classparser.ClassFile; import de.inetsoftware.classparser.Code; @@ -32,6 +37,10 @@ import de.inetsoftware.jwebassembly.WasmException; */ public abstract class ModuleWriter implements Closeable { + private int paramCount; + + private ArrayList locals = new ArrayList<>(); + /** * Write the content of the class to the * @@ -65,10 +74,11 @@ public abstract class ModuleWriter implements Closeable { * if some Java code can't converted */ private void writeMethod( MethodInfo method ) throws IOException, WasmException { - writeMethodStart( method.getName() ); - writeMethodSignature( method ); Code code = method.getCode(); - if( code != null ) { + if( code != null ) { // abstract methods and interface methods does not have code + writeMethodStart( method.getName() ); + writeMethodSignature( method ); + locals.clear(); LineNumberTable lineNumberTable = code.getLineNumberTable(); if( lineNumberTable != null ) { int lineNumber; @@ -85,8 +95,11 @@ public abstract class ModuleWriter implements Closeable { CodeInputStream byteCode = code.getByteCode(); writeCodeChunk( byteCode, -1 ); } + for( int i = 0; i < paramCount; i++ ) { + locals.remove( 0 ); + } + writeMethodFinish( locals ); } - writeMethodFinish(); } /** @@ -112,7 +125,9 @@ public abstract class ModuleWriter implements Closeable { private void writeMethodSignature( MethodInfo method ) throws IOException, WasmException { String signature = method.getDescription(); String kind = "param"; + int paramCount = 0; for( int i = 1; i < signature.length(); i++ ) { + paramCount++; String javaType; switch( signature.charAt( i ) ) { case '[': // array @@ -142,13 +157,13 @@ public abstract class ModuleWriter implements Closeable { case 'V': // void continue; case ')': + this.paramCount = paramCount - 1; kind = "return"; continue; default: javaType = signature.substring( i, i + 1 ); } - Code code = method.getCode(); - int lineNumber = code == null ? -1 : code.getFirstLineNr(); + int lineNumber = method.getCode().getFirstLineNr(); throw new WasmException( "Not supported Java data type in method signature: " + javaType, lineNumber ); } } @@ -168,10 +183,13 @@ public abstract class ModuleWriter implements Closeable { /** * Complete the method * + * @param locals + * a list with types of local variables + * * @throws IOException * if any I/O error occur */ - protected abstract void writeMethodFinish() throws IOException; + protected abstract void writeMethodFinish( List locals ) throws IOException; /** * Write a chunk of byte code. @@ -192,10 +210,16 @@ public abstract class ModuleWriter implements Closeable { writeConstInt( 1 ); break; case 26: // iload_0 - writeLoadInt( 0 ); + case 27: // iload_1 + case 28: // iload_2 + case 29: // iload_3 + writeLoadStore( true, ValueType.i32, op - 26 ); break; + case 59: // istore_0 case 60: // istore_1 - writeStoreInt( 1 ); + case 61: // istore_2 + case 62: // istore_3 + writeLoadStore( false, ValueType.i32, op - 59 ); break; case 96: // iadd writeAddInt(); @@ -213,7 +237,7 @@ public abstract class ModuleWriter implements Closeable { } /** - * Write a const integer value + * Write a constant integer value * * @param value * the value @@ -222,25 +246,41 @@ public abstract class ModuleWriter implements Closeable { */ protected abstract void writeConstInt( int value ) throws IOException; - /** - * Write a load integer - * - * @param idx - * the index of the parameter variable - * @throws IOException - * if any I/O error occur - */ - protected abstract void writeLoadInt( int idx ) throws IOException; + private void writeLoadStore( boolean load, @Nonnull ValueType valueType, @Nonnegative int idx ) throws WasmException, IOException { + while( locals.size() <= idx ) { + locals.add( null ); + } + ValueType oldType = locals.get( idx ); + if( oldType != null && oldType != valueType ) { + throw new WasmException( "Redefine local variable type from " + oldType + " to " + valueType, -1 ); + } + locals.set( idx, valueType ); + if( load ) { + writeLoad( idx ); + } else { + writeStore( idx ); + } + } /** - * Write a store integer. + * Write a variable load. * * @param idx * the index of the parameter variable * @throws IOException * if any I/O error occur */ - protected abstract void writeStoreInt( int idx ) throws IOException; + protected abstract void writeLoad( int idx ) throws IOException; + + /** + * Write a variable store. + * + * @param idx + * the index of the parameter variable + * @throws IOException + * if any I/O error occur + */ + protected abstract void writeStore( int idx ) throws IOException; /** * Write a add operator diff --git a/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java b/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java index 4540fac..f7d245b 100644 --- a/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java +++ b/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java @@ -16,6 +16,7 @@ package de.inetsoftware.jwebassembly.text; import java.io.IOException; +import java.util.List; import de.inetsoftware.jwebassembly.module.ModuleWriter; import de.inetsoftware.jwebassembly.module.ValueType; @@ -79,7 +80,7 @@ public class TextModuleWriter extends ModuleWriter { * {@inheritDoc} */ @Override - protected void writeMethodFinish() throws IOException { + protected void writeMethodFinish( List locals ) throws IOException { inset--; newline(); output.append( ')' ); @@ -91,14 +92,14 @@ public class TextModuleWriter extends ModuleWriter { @Override protected void writeConstInt( int value ) throws IOException { newline(); - output.append( "i32.const " ).append( Integer.toString( value )); + output.append( "i32.const " ).append( Integer.toString( value ) ); } - + /** * {@inheritDoc} */ @Override - protected void writeLoadInt( int idx ) throws IOException { + protected void writeLoad( int idx ) throws IOException { newline(); output.append( "get_local " ).append( Integer.toString( idx ) ); } @@ -107,7 +108,7 @@ public class TextModuleWriter extends ModuleWriter { * {@inheritDoc} */ @Override - protected void writeStoreInt( int idx ) throws IOException { + protected void writeStore( int idx ) throws IOException { newline(); output.append( "set_local " ).append( Integer.toString( idx ) ); }