mirror of
https://github.com/i-net-software/JWebAssembly.git
synced 2025-03-25 07:27:52 +01:00
Fix the memory offset of strings with a dynamic synthetic function.
This commit is contained in:
parent
f61cebd285
commit
8668d71b6c
@ -20,6 +20,7 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -184,21 +185,21 @@ public class FunctionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the first FunctionName that is required but was not written.
|
* Get all FunctionName that is required but was not written.
|
||||||
*
|
*
|
||||||
* @return the FunctionName or null
|
* @return the FunctionName or null
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
FunctionName nextWriteLater() {
|
Iterator<FunctionName> getWriteLater() {
|
||||||
for( Entry<FunctionName, FunctionState> entry : states.entrySet() ) {
|
return iterator( entry -> {
|
||||||
switch( entry.getValue().state ) {
|
switch( entry.getValue().state ) {
|
||||||
case Needed:
|
case Needed:
|
||||||
case Scanned:
|
case Scanned:
|
||||||
return entry.getKey();
|
return true;
|
||||||
default:
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
} );
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -207,16 +208,25 @@ public class FunctionManager {
|
|||||||
* @return an iterator
|
* @return an iterator
|
||||||
*/
|
*/
|
||||||
Iterator<FunctionName> getNeededFunctions() {
|
Iterator<FunctionName> getNeededFunctions() {
|
||||||
return states.entrySet().stream().filter( entry -> {
|
return iterator( entry -> {
|
||||||
FunctionState state = entry.getValue();
|
FunctionState state = entry.getValue();
|
||||||
switch( state.state ) {
|
switch( state.state ) {
|
||||||
case Needed:
|
case Needed:
|
||||||
case Scanned:
|
case Scanned:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
} );
|
||||||
} ).map( entry -> entry.getKey() ).iterator();
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get a iterator for function names
|
||||||
|
* @param filter the filter
|
||||||
|
* @return the iterator
|
||||||
|
*/
|
||||||
|
private Iterator<FunctionName> iterator( Predicate<Entry<FunctionName, FunctionState>> filter ) {
|
||||||
|
return states.entrySet().stream().filter( filter ).map( entry -> entry.getKey() ).iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -137,7 +137,8 @@ public class FunctionName {
|
|||||||
if( obj == null ) {
|
if( obj == null ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if( getClass() != obj.getClass() ) {
|
// synthetic functions should be replace/equals to real functions.
|
||||||
|
if( !(obj instanceof FunctionName) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
FunctionName other = (FunctionName)obj;
|
FunctionName other = (FunctionName)obj;
|
||||||
|
@ -292,41 +292,40 @@ public class ModuleGenerator {
|
|||||||
* if any I/O error occur
|
* if any I/O error occur
|
||||||
*/
|
*/
|
||||||
public void finish() throws IOException {
|
public void finish() throws IOException {
|
||||||
FunctionName next;
|
for( Iterator<FunctionName> it = functions.getWriteLater(); it.hasNext(); ) {
|
||||||
while( (next = functions.nextWriteLater()) != null ) {
|
FunctionName next = it.next();
|
||||||
ClassFile classFile = ClassFile.get( next.className, libraries );
|
if( next instanceof SyntheticFunctionName ) {
|
||||||
if( classFile == null ) {
|
writeMethodImpl( next, true, ((SyntheticFunctionName)next).getCodeBuilder( watParser ) );
|
||||||
if( next instanceof SyntheticFunctionName ) {
|
|
||||||
writeMethodImpl( next, true, ((SyntheticFunctionName)next).getCodeBuilder( watParser ) );
|
|
||||||
} else {
|
|
||||||
throw new WasmException( "Missing function: " + next.signatureName, -1 );
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
iterateMethods( classFile, method -> {
|
ClassFile classFile = ClassFile.get( next.className, libraries );
|
||||||
try {
|
if( classFile == null ) {
|
||||||
FunctionName name;
|
throw new WasmException( "Missing function: " + next.signatureName, -1 );
|
||||||
Map<String, Object> wat = method.getAnnotation( JWebAssembly.TEXTCODE_ANNOTATION );
|
} else {
|
||||||
if( wat != null ) {
|
MethodInfo method = classFile.getMethod( next.methodName, next.signature );
|
||||||
String signature = (String)wat.get( "signature" );
|
if( method != null ) {
|
||||||
if( signature == null ) {
|
try {
|
||||||
signature = method.getType();
|
Map<String, Object> wat = method.getAnnotation( JWebAssembly.TEXTCODE_ANNOTATION );
|
||||||
|
if( wat != null ) {
|
||||||
|
String signature = (String)wat.get( "signature" );
|
||||||
|
if( signature == null ) {
|
||||||
|
signature = method.getType();
|
||||||
|
}
|
||||||
|
next = new FunctionName( method, signature );
|
||||||
|
} else {
|
||||||
|
method = functions.replace( next, method );
|
||||||
}
|
}
|
||||||
name = new FunctionName( method, signature );
|
if( functions.needToWrite( next ) ) {
|
||||||
} else {
|
writeMethod( next, method );
|
||||||
name = new FunctionName( method );
|
}
|
||||||
method = functions.replace( name, method );
|
} catch (IOException ioex){
|
||||||
|
throw WasmException.create( ioex, sourceFile, className, -1 );
|
||||||
}
|
}
|
||||||
if( functions.needToWrite( name ) ) {
|
} else {
|
||||||
writeMethod( name, method );
|
if( functions.needToWrite( next ) ) {
|
||||||
|
throw new WasmException( "Missing function: " + next.signatureName, -1 );
|
||||||
}
|
}
|
||||||
} catch (IOException ioex){
|
|
||||||
throw WasmException.create( ioex, sourceFile, className, -1 );
|
|
||||||
}
|
}
|
||||||
} );
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if( functions.needToWrite( next ) ) {
|
|
||||||
throw new WasmException( "Missing function: " + next.signatureName, -1 );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
javaScript.finish();
|
javaScript.finish();
|
||||||
|
@ -23,6 +23,7 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
import javax.annotation.Nonnegative;
|
import javax.annotation.Nonnegative;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
import de.inetsoftware.jwebassembly.api.annotation.WasmTextCode;
|
import de.inetsoftware.jwebassembly.api.annotation.WasmTextCode;
|
||||||
import de.inetsoftware.jwebassembly.wasm.ValueType;
|
import de.inetsoftware.jwebassembly.wasm.ValueType;
|
||||||
@ -34,12 +35,51 @@ import de.inetsoftware.jwebassembly.wasm.ValueType;
|
|||||||
*/
|
*/
|
||||||
class StringManager extends LinkedHashMap<String, Integer> {
|
class StringManager extends LinkedHashMap<String, Integer> {
|
||||||
|
|
||||||
private FunctionManager functions;
|
/**
|
||||||
|
* Signature of method stringConstant.
|
||||||
|
*
|
||||||
|
* @see #stringConstant(int)
|
||||||
|
*/
|
||||||
|
private static final FunctionName STRING_CONSTANT_FUNCTION =
|
||||||
|
new FunctionName( "de/inetsoftware/jwebassembly/module/StringManager.stringConstant(I)Ljava/lang/String;" );
|
||||||
|
|
||||||
|
private FunctionManager functions;
|
||||||
|
|
||||||
|
private int stringMemoryOffset = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the string manager.
|
||||||
|
*
|
||||||
|
* @param functions
|
||||||
|
* the function manager
|
||||||
|
*/
|
||||||
void init( FunctionManager functions ) {
|
void init( FunctionManager functions ) {
|
||||||
this.functions = functions;
|
this.functions = functions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the function name object for the {@link #stringConstant(int)}.
|
||||||
|
*
|
||||||
|
* @see #stringConstant(int)
|
||||||
|
* @return the name
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
FunctionName getStringConstantFunction() {
|
||||||
|
if( stringMemoryOffset < 0 ) {
|
||||||
|
// register the function stringsMemoryOffset() as synthetic function
|
||||||
|
stringMemoryOffset = 0;
|
||||||
|
FunctionName offsetFunction =
|
||||||
|
new WatCodeSyntheticFunctionName( "de/inetsoftware/jwebassembly/module/StringManager", "stringsMemoryOffset", "()I", "", null, ValueType.i32 ) {
|
||||||
|
protected String getCode() {
|
||||||
|
return "i32.const " + stringMemoryOffset;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
functions.markAsNeeded( offsetFunction, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
return STRING_CONSTANT_FUNCTION;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finish the prepare. Now no new strings should be added.
|
* Finish the prepare. Now no new strings should be added.
|
||||||
*
|
*
|
||||||
@ -82,10 +122,9 @@ class StringManager extends LinkedHashMap<String, Integer> {
|
|||||||
ByteArrayOutputStream stringOut = new ByteArrayOutputStream();
|
ByteArrayOutputStream stringOut = new ByteArrayOutputStream();
|
||||||
ByteArrayOutputStream dataStream = writer.dataStream;
|
ByteArrayOutputStream dataStream = writer.dataStream;
|
||||||
|
|
||||||
int offset = dataStream.size();
|
// save the offset of the string data for later code inlining
|
||||||
WatCodeSyntheticFunctionName stringMemoryOffset = new WatCodeSyntheticFunctionName( "de/inetsoftware/jwebassembly/module/StringManager", "stringsMemoryOffset", "()I", "i32.const " + offset, null, ValueType.i32 );
|
stringMemoryOffset = dataStream.size();
|
||||||
// functions.markAsNeeded( stringMemoryOffset, true );
|
int offset = stringMemoryOffset + size * 4;
|
||||||
offset += size * 4;
|
|
||||||
|
|
||||||
for( String str : this.keySet() ) {
|
for( String str : this.keySet() ) {
|
||||||
// write the position where the string starts in the data section
|
// write the position where the string starts in the data section
|
||||||
@ -129,18 +168,13 @@ class StringManager extends LinkedHashMap<String, Integer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signature of method stringConstant.
|
* WASM code<p>
|
||||||
* @see #stringConstant(int)
|
* Get a constant string from the table.
|
||||||
*/
|
|
||||||
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.
|
* @param strIdx
|
||||||
|
* the id/index of the string.
|
||||||
* @return the string
|
* @return the string
|
||||||
* @see #STRING_CONSTANT_SIGNATURE
|
* @see #STRING_CONSTANT_FUNCTION
|
||||||
*/
|
*/
|
||||||
private static String stringConstant( int strIdx ) {
|
private static String stringConstant( int strIdx ) {
|
||||||
String str = getStringFromTable( strIdx );
|
String str = getStringFromTable( strIdx );
|
||||||
@ -148,25 +182,30 @@ class StringManager extends LinkedHashMap<String, Integer> {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read the compact string length
|
||||||
int offset = getIntFromMemory( strIdx * 4 + stringsMemoryOffset() );
|
int offset = getIntFromMemory( strIdx * 4 + stringsMemoryOffset() );
|
||||||
int length = 0;
|
int length = 0;
|
||||||
int b;
|
int b;
|
||||||
|
int shift = 0;
|
||||||
do {
|
do {
|
||||||
b = getUnsignedByteFromMemory( offset++ );
|
b = getUnsignedByteFromMemory( offset++ );
|
||||||
length = (length << 7) + (b & 0x7F);
|
length += (b & 0x7F) << shift;
|
||||||
|
shift += 7;
|
||||||
} while( b >= 0x80 );
|
} while( b >= 0x80 );
|
||||||
|
|
||||||
|
// copy the bytes from the data section
|
||||||
byte[] bytes = new byte[length];
|
byte[] bytes = new byte[length];
|
||||||
for( int i = 0; i < length; i++ ) {
|
for( int i = 0; i < length; i++ ) {
|
||||||
bytes[i] = getUnsignedByteFromMemory( i + offset );
|
bytes[i] = getUnsignedByteFromMemory( i + offset );
|
||||||
}
|
}
|
||||||
str = new String( bytes );
|
str = new String( bytes );
|
||||||
|
// save the string for future use
|
||||||
setStringIntoTable( strIdx, str );
|
setStringIntoTable( strIdx, str );
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WASM code
|
* WASM code<p>
|
||||||
* Get a string from the string table. Should be inlined from the optimizer.
|
* Get a string from the string table. Should be inlined from the optimizer.
|
||||||
*
|
*
|
||||||
* @param strIdx
|
* @param strIdx
|
||||||
@ -179,7 +218,7 @@ class StringManager extends LinkedHashMap<String, Integer> {
|
|||||||
private static native String getStringFromTable( int strIdx );
|
private static native String getStringFromTable( int strIdx );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WASM code
|
* WASM code<p>
|
||||||
* Set a string from the string table. Should be inlined from the optimizer.
|
* Set a string from the string table. Should be inlined from the optimizer.
|
||||||
*
|
*
|
||||||
* @param strIdx
|
* @param strIdx
|
||||||
@ -193,12 +232,18 @@ class StringManager extends LinkedHashMap<String, Integer> {
|
|||||||
"return" )
|
"return" )
|
||||||
private static native void setStringIntoTable( int strIdx, String str );
|
private static native void setStringIntoTable( int strIdx, String str );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WASM code<p>
|
||||||
|
* Placeholder for a synthetic function. Should be inlined from the optimizer.
|
||||||
|
* @return the memory offset of the string data in the element section
|
||||||
|
*/
|
||||||
|
//TODO the annotation can be removed if ModuleGenerator.prepareFunctions() can detect Synthetic functions correctly
|
||||||
@WasmTextCode( "i32.const 0" )
|
@WasmTextCode( "i32.const 0" )
|
||||||
private static native int stringsMemoryOffset();
|
private static native int stringsMemoryOffset();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WASM code
|
* WASM code<p>
|
||||||
* Load an i32 from memory. The offset must be aligned.
|
* Load an i32 from memory. The offset must be aligned. Should be inlined from the optimizer.
|
||||||
*
|
*
|
||||||
* @param pos
|
* @param pos
|
||||||
* the memory position
|
* the memory position
|
||||||
@ -210,8 +255,8 @@ class StringManager extends LinkedHashMap<String, Integer> {
|
|||||||
private static native int getIntFromMemory( int pos );
|
private static native int getIntFromMemory( int pos );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WASM code
|
* WASM code<p>
|
||||||
* Load a byte from the memory.
|
* Load a byte from the memory. Should be inlined from the optimizer.
|
||||||
*
|
*
|
||||||
* @param pos
|
* @param pos
|
||||||
* the memory position
|
* the memory position
|
||||||
|
@ -357,7 +357,7 @@ public abstract class WasmCodeBuilder {
|
|||||||
if( id == null ) {
|
if( id == null ) {
|
||||||
strings.put( (String)value, id = strings.size() );
|
strings.put( (String)value, id = strings.size() );
|
||||||
}
|
}
|
||||||
FunctionName name = new FunctionName( StringManager.STRING_CONSTANT_SIGNATURE );
|
FunctionName name = strings.getStringConstantFunction();
|
||||||
instructions.add( new WasmConstInstruction( id, ValueType.i32, javaCodePos, lineNumber ) );
|
instructions.add( new WasmConstInstruction( id, ValueType.i32, javaCodePos, lineNumber ) );
|
||||||
addCallInstruction( name, javaCodePos, lineNumber );
|
addCallInstruction( name, javaCodePos, lineNumber );
|
||||||
} else {
|
} else {
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package de.inetsoftware.jwebassembly.module;
|
package de.inetsoftware.jwebassembly.module;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
import de.inetsoftware.jwebassembly.wasm.AnyType;
|
import de.inetsoftware.jwebassembly.wasm.AnyType;
|
||||||
import de.inetsoftware.jwebassembly.watparser.WatParser;
|
import de.inetsoftware.jwebassembly.watparser.WatParser;
|
||||||
|
|
||||||
@ -57,11 +59,21 @@ class WatCodeSyntheticFunctionName extends ArraySyntheticFunctionName {
|
|||||||
* @param signatureTypes
|
* @param signatureTypes
|
||||||
* the method signature, first the parameters, then null and the the return types
|
* 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 ) {
|
public WatCodeSyntheticFunctionName( String className, String name, String signature, @Nonnull String code, AnyType... signatureTypes ) {
|
||||||
super( className, name, signature, signatureTypes );
|
super( className, name, signature, signatureTypes );
|
||||||
this.code = code;
|
this.code = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Wat code, can be overridden.
|
||||||
|
*
|
||||||
|
* @return the code
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
protected String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@ -75,7 +87,7 @@ class WatCodeSyntheticFunctionName extends ArraySyntheticFunctionName {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected WasmCodeBuilder getCodeBuilder( WatParser watParser ) {
|
protected WasmCodeBuilder getCodeBuilder( WatParser watParser ) {
|
||||||
watParser.parse( code, null, -1 );
|
watParser.parse( getCode(), null, -1 );
|
||||||
return watParser;
|
return watParser;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ public class StringOperations extends AbstractBaseTest {
|
|||||||
for( ScriptEngine script : ScriptEngine.testEngines() ) {
|
for( ScriptEngine script : ScriptEngine.testEngines() ) {
|
||||||
addParam( list, script, "newFromChars" );
|
addParam( list, script, "newFromChars" );
|
||||||
addParam( list, script, "newFromBytes" );
|
addParam( list, script, "newFromBytes" );
|
||||||
|
addParam( list, script, "constant" );
|
||||||
}
|
}
|
||||||
rule.setTestParameters( list );
|
rule.setTestParameters( list );
|
||||||
return list;
|
return list;
|
||||||
@ -60,5 +61,15 @@ public class StringOperations extends AbstractBaseTest {
|
|||||||
byte[] bytes = {(byte)0xC3,(byte)0xA4};
|
byte[] bytes = {(byte)0xC3,(byte)0xA4};
|
||||||
return new String( bytes );
|
return new String( bytes );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Export
|
||||||
|
static String constant() {
|
||||||
|
// string larger as 128 bytes
|
||||||
|
String constant = "1234567890 äöüäöüß " // umlaute
|
||||||
|
+ "𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡 𝒥𝒶𝓋𝒶𝓈𝒸𝓇𝒾𝓅𝓉 " // surrogate chars
|
||||||
|
+ "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
return constant;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user