From f61cebd2853b187b0e34e05ee1c4ebb42b3a1cb8 Mon Sep 17 00:00:00 2001 From: Volker Berlin Date: Sat, 23 Nov 2019 20:39:10 +0100 Subject: [PATCH] implements reading from constant strings from memory --- .../module/ArraySyntheticFunctionName.java | 26 +++- .../jwebassembly/module/ModuleGenerator.java | 1 + .../jwebassembly/module/StringManager.java | 115 +++++++++++++++++- .../jwebassembly/module/WasmCodeBuilder.java | 15 +-- .../module/WatCodeSyntheticFunctionName.java | 25 +++- 5 files changed, 158 insertions(+), 24 deletions(-) diff --git a/src/de/inetsoftware/jwebassembly/module/ArraySyntheticFunctionName.java b/src/de/inetsoftware/jwebassembly/module/ArraySyntheticFunctionName.java index 440af2d..4df7594 100644 --- a/src/de/inetsoftware/jwebassembly/module/ArraySyntheticFunctionName.java +++ b/src/de/inetsoftware/jwebassembly/module/ArraySyntheticFunctionName.java @@ -28,7 +28,21 @@ import de.inetsoftware.jwebassembly.wasm.AnyType; */ public abstract class ArraySyntheticFunctionName extends SyntheticFunctionName { - private final AnyType[] signature; + private final AnyType[] signatureTypes; + + /** + * Create a new instance. + * + * @param className + * the Java class name + * @param name + * the function name + * @param signatureTypes + * the method signature, first the parameters, then null and the the return types + */ + public ArraySyntheticFunctionName( String className, String name, AnyType... signatureTypes ) { + this( className, name, "()V", signatureTypes ); //TODO better signature name + } /** * Create a new instance. @@ -38,11 +52,13 @@ public abstract class ArraySyntheticFunctionName extends SyntheticFunctionName { * @param name * the function name * @param signature + * the string signature + * @param signatureTypes * the method signature, first the parameters, then null and the the return types */ - public ArraySyntheticFunctionName( String className, String name, AnyType... signature ) { - super( className, name, "()V" ); //TODO better signature name - this.signature = signature; + public ArraySyntheticFunctionName( String className, String name, String signature, AnyType... signatureTypes ) { + super( className, name, signature ); + this.signatureTypes = signatureTypes; } /** @@ -50,6 +66,6 @@ public abstract class ArraySyntheticFunctionName extends SyntheticFunctionName { */ @Override public Iterator getSignature( TypeManager types ) { - return Arrays.asList( signature ).iterator(); + return Arrays.asList( signatureTypes ).iterator(); } } diff --git a/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java b/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java index eca96b6..5ad17dd 100644 --- a/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java +++ b/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java @@ -95,6 +95,7 @@ public class ModuleGenerator { this.libraries = new URLClassLoader( libraries.toArray( new URL[libraries.size()] ) ); WasmOptions options = writer.options; types.init( options ); + strings.init( functions ); javaCodeBuilder.init( types, functions, strings, options ); ((WasmCodeBuilder)watParser).init( types, functions, strings, options ); scanLibraries( libraries ); diff --git a/src/de/inetsoftware/jwebassembly/module/StringManager.java b/src/de/inetsoftware/jwebassembly/module/StringManager.java index a89c31e..440dcb0 100644 --- a/src/de/inetsoftware/jwebassembly/module/StringManager.java +++ b/src/de/inetsoftware/jwebassembly/module/StringManager.java @@ -24,6 +24,9 @@ import java.util.LinkedHashMap; import javax.annotation.Nonnegative; +import de.inetsoftware.jwebassembly.api.annotation.WasmTextCode; +import de.inetsoftware.jwebassembly.wasm.ValueType; + /** * Handle all the constant strings. The constant strings will be write into the data section. * @@ -31,6 +34,12 @@ import javax.annotation.Nonnegative; */ class StringManager extends LinkedHashMap { + private FunctionManager functions; + + void init( FunctionManager functions ) { + this.functions = functions; + } + /** * Finish the prepare. Now no new strings should be added. * @@ -41,7 +50,12 @@ class StringManager extends LinkedHashMap { */ void prepareFinish( ModuleWriter writer ) throws IOException { // inform the writer of string count that it can allocate a table of type anyref for the constant strings - writer.setStringCount( size() ); + int size = size(); + writer.setStringCount( size ); + if( size == 0 ) { + // no strings, nothing to do + return; + } /* Write the strings to the data sections. first there is a index table, then follows the strings @@ -68,7 +82,10 @@ class StringManager extends LinkedHashMap { ByteArrayOutputStream stringOut = new ByteArrayOutputStream(); ByteArrayOutputStream dataStream = writer.dataStream; - int offset = dataStream.size() + size() * 4; + int offset = dataStream.size(); + WatCodeSyntheticFunctionName stringMemoryOffset = new WatCodeSyntheticFunctionName( "de/inetsoftware/jwebassembly/module/StringManager", "stringsMemoryOffset", "()I", "i32.const " + offset, null, ValueType.i32 ); +// functions.markAsNeeded( stringMemoryOffset, true ); + offset += size * 4; for( String str : this.keySet() ) { // write the position where the string starts in the data section @@ -110,4 +127,98 @@ class StringManager extends LinkedHashMap { out.write( b ); } while( value != 0 ); } + + /** + * Signature of method stringConstant. + * @see #stringConstant(int) + */ + static final String STRING_CONSTANT_SIGNATURE = "de/inetsoftware/jwebassembly/module/StringManager.stringConstant(I)Ljava/lang/String;"; + + /** + * WASM code + * Get a constant string from the table. + * + * @param strIdx the id/index of the string. + * @return the string + * @see #STRING_CONSTANT_SIGNATURE + */ + private static String stringConstant( int strIdx ) { + String str = getStringFromTable( strIdx ); + if( str != null ) { + return str; + } + + int offset = getIntFromMemory( strIdx * 4 + stringsMemoryOffset() ); + int length = 0; + int b; + do { + b = getUnsignedByteFromMemory( offset++ ); + length = (length << 7) + (b & 0x7F); + } while( b >= 0x80 ); + + byte[] bytes = new byte[length]; + for( int i = 0; i < length; i++ ) { + bytes[i] = getUnsignedByteFromMemory( i + offset ); + } + str = new String( bytes ); + setStringIntoTable( strIdx, str ); + return str; + } + + /** + * WASM code + * Get a string from the string table. Should be inlined from the optimizer. + * + * @param strIdx + * the id/index of the string. + * @return the string or null if not already set. + */ + @WasmTextCode( "local.get 0 " + // + "table.get 1 " + // + "return" ) + private static native String getStringFromTable( int strIdx ); + + /** + * WASM code + * Set a string from the string table. Should be inlined from the optimizer. + * + * @param strIdx + * the id/index of the string. + * @param str + * the string + */ + @WasmTextCode( "local.get 0 " + // + "local.get 1 " + // + "table.set 1 " + // + "return" ) + private static native void setStringIntoTable( int strIdx, String str ); + + @WasmTextCode( "i32.const 0" ) + private static native int stringsMemoryOffset(); + + /** + * WASM code + * Load an i32 from memory. The offset must be aligned. + * + * @param pos + * the memory position + * @return the value from the memory + */ + @WasmTextCode( "local.get 0 " + // + "i32.load offset=0 align=4 " + // + "return" ) + private static native int getIntFromMemory( int pos ); + + /** + * WASM code + * Load a byte from the memory. + * + * @param pos + * the memory position + * @return the value from the memory + */ + @WasmTextCode( "local.get 0 " + // + "i32.load8_u offset=0 " + // + "return" ) + private static native byte getUnsignedByteFromMemory( int pos ); } diff --git a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java index 143c51a..23f72d7 100644 --- a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java +++ b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java @@ -357,20 +357,7 @@ public abstract class WasmCodeBuilder { if( id == null ) { strings.put( (String)value, id = strings.size() ); } - String wat = "local.get 0 " + // - "table.get 1 " + // strings table - "local.tee 1 " + // - "ref.is_null " + // - "if " + // - "local.get 0 " + // - "i32.const 4 " + // - "i32.mul " + // - "i32.load offset=0 " + // - "drop " + // - "end " + // - "local.get 1 " + // - "return"; - FunctionName name = new WatCodeSyntheticFunctionName( "stringConstant", wat, ValueType.i32, null, ValueType.anyref ); + FunctionName name = new FunctionName( StringManager.STRING_CONSTANT_SIGNATURE ); instructions.add( new WasmConstInstruction( id, ValueType.i32, javaCodePos, lineNumber ) ); addCallInstruction( name, javaCodePos, lineNumber ); } else { diff --git a/src/de/inetsoftware/jwebassembly/module/WatCodeSyntheticFunctionName.java b/src/de/inetsoftware/jwebassembly/module/WatCodeSyntheticFunctionName.java index 5dd3746..7e2f47e 100644 --- a/src/de/inetsoftware/jwebassembly/module/WatCodeSyntheticFunctionName.java +++ b/src/de/inetsoftware/jwebassembly/module/WatCodeSyntheticFunctionName.java @@ -35,11 +35,30 @@ class WatCodeSyntheticFunctionName extends ArraySyntheticFunctionName { * the function name * @param code * the WAT code (WASM in text form) - * @param signature + * @param signatureTypes * the method signature, first the parameters, then null and the the return types */ - public WatCodeSyntheticFunctionName( String name, String code, AnyType... signature ) { - super( "", name, signature ); + public WatCodeSyntheticFunctionName( String name, String code, AnyType... signatureTypes ) { + super( "", name, signatureTypes ); + this.code = code; + } + + /** + * Create a new instance. + * + * @param className + * the Java class name + * @param name + * the function name + * @param code + * the WAT code (WASM in text form) + * @param signature + * the string signature + * @param signatureTypes + * the method signature, first the parameters, then null and the the return types + */ + public WatCodeSyntheticFunctionName( String className, String name, String signature, String code, AnyType... signatureTypes ) { + super( className, name, signature, signatureTypes ); this.code = code; }