mirror of
https://github.com/i-net-software/JWebAssembly.git
synced 2025-03-15 02:44:47 +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.Entry;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
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
|
||||
*/
|
||||
@Nullable
|
||||
FunctionName nextWriteLater() {
|
||||
for( Entry<FunctionName, FunctionState> entry : states.entrySet() ) {
|
||||
Iterator<FunctionName> getWriteLater() {
|
||||
return iterator( entry -> {
|
||||
switch( entry.getValue().state ) {
|
||||
case Needed:
|
||||
case Scanned:
|
||||
return entry.getKey();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -207,16 +208,25 @@ public class FunctionManager {
|
||||
* @return an iterator
|
||||
*/
|
||||
Iterator<FunctionName> getNeededFunctions() {
|
||||
return states.entrySet().stream().filter( entry -> {
|
||||
return iterator( entry -> {
|
||||
FunctionState state = entry.getValue();
|
||||
switch( state.state ) {
|
||||
case Needed:
|
||||
case Scanned:
|
||||
return true;
|
||||
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 ) {
|
||||
return false;
|
||||
}
|
||||
if( getClass() != obj.getClass() ) {
|
||||
// synthetic functions should be replace/equals to real functions.
|
||||
if( !(obj instanceof FunctionName) ) {
|
||||
return false;
|
||||
}
|
||||
FunctionName other = (FunctionName)obj;
|
||||
|
@ -292,41 +292,40 @@ public class ModuleGenerator {
|
||||
* if any I/O error occur
|
||||
*/
|
||||
public void finish() throws IOException {
|
||||
FunctionName next;
|
||||
while( (next = functions.nextWriteLater()) != null ) {
|
||||
ClassFile classFile = ClassFile.get( next.className, libraries );
|
||||
if( classFile == null ) {
|
||||
if( next instanceof SyntheticFunctionName ) {
|
||||
writeMethodImpl( next, true, ((SyntheticFunctionName)next).getCodeBuilder( watParser ) );
|
||||
} else {
|
||||
throw new WasmException( "Missing function: " + next.signatureName, -1 );
|
||||
}
|
||||
for( Iterator<FunctionName> it = functions.getWriteLater(); it.hasNext(); ) {
|
||||
FunctionName next = it.next();
|
||||
if( next instanceof SyntheticFunctionName ) {
|
||||
writeMethodImpl( next, true, ((SyntheticFunctionName)next).getCodeBuilder( watParser ) );
|
||||
} else {
|
||||
iterateMethods( classFile, method -> {
|
||||
try {
|
||||
FunctionName name;
|
||||
Map<String, Object> wat = method.getAnnotation( JWebAssembly.TEXTCODE_ANNOTATION );
|
||||
if( wat != null ) {
|
||||
String signature = (String)wat.get( "signature" );
|
||||
if( signature == null ) {
|
||||
signature = method.getType();
|
||||
ClassFile classFile = ClassFile.get( next.className, libraries );
|
||||
if( classFile == null ) {
|
||||
throw new WasmException( "Missing function: " + next.signatureName, -1 );
|
||||
} else {
|
||||
MethodInfo method = classFile.getMethod( next.methodName, next.signature );
|
||||
if( method != null ) {
|
||||
try {
|
||||
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 );
|
||||
} else {
|
||||
name = new FunctionName( method );
|
||||
method = functions.replace( name, method );
|
||||
if( functions.needToWrite( next ) ) {
|
||||
writeMethod( next, method );
|
||||
}
|
||||
} catch (IOException ioex){
|
||||
throw WasmException.create( ioex, sourceFile, className, -1 );
|
||||
}
|
||||
if( functions.needToWrite( name ) ) {
|
||||
writeMethod( name, method );
|
||||
} else {
|
||||
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();
|
||||
|
@ -23,6 +23,7 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
import javax.annotation.Nonnegative;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import de.inetsoftware.jwebassembly.api.annotation.WasmTextCode;
|
||||
import de.inetsoftware.jwebassembly.wasm.ValueType;
|
||||
@ -34,12 +35,51 @@ import de.inetsoftware.jwebassembly.wasm.ValueType;
|
||||
*/
|
||||
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 ) {
|
||||
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.
|
||||
*
|
||||
@ -82,10 +122,9 @@ class StringManager extends LinkedHashMap<String, Integer> {
|
||||
ByteArrayOutputStream stringOut = new ByteArrayOutputStream();
|
||||
ByteArrayOutputStream dataStream = writer.dataStream;
|
||||
|
||||
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;
|
||||
// save the offset of the string data for later code inlining
|
||||
stringMemoryOffset = dataStream.size();
|
||||
int offset = stringMemoryOffset + size * 4;
|
||||
|
||||
for( String str : this.keySet() ) {
|
||||
// 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.
|
||||
* @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.
|
||||
* WASM code<p>
|
||||
* 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
|
||||
* @see #STRING_CONSTANT_SIGNATURE
|
||||
* @see #STRING_CONSTANT_FUNCTION
|
||||
*/
|
||||
private static String stringConstant( int strIdx ) {
|
||||
String str = getStringFromTable( strIdx );
|
||||
@ -148,25 +182,30 @@ class StringManager extends LinkedHashMap<String, Integer> {
|
||||
return str;
|
||||
}
|
||||
|
||||
// read the compact string length
|
||||
int offset = getIntFromMemory( strIdx * 4 + stringsMemoryOffset() );
|
||||
int length = 0;
|
||||
int b;
|
||||
int shift = 0;
|
||||
do {
|
||||
b = getUnsignedByteFromMemory( offset++ );
|
||||
length = (length << 7) + (b & 0x7F);
|
||||
length += (b & 0x7F) << shift;
|
||||
shift += 7;
|
||||
} while( b >= 0x80 );
|
||||
|
||||
// copy the bytes from the data section
|
||||
byte[] bytes = new byte[length];
|
||||
for( int i = 0; i < length; i++ ) {
|
||||
bytes[i] = getUnsignedByteFromMemory( i + offset );
|
||||
}
|
||||
str = new String( bytes );
|
||||
// save the string for future use
|
||||
setStringIntoTable( strIdx, str );
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* WASM code
|
||||
* WASM code<p>
|
||||
* Get a string from the string table. Should be inlined from the optimizer.
|
||||
*
|
||||
* @param strIdx
|
||||
@ -179,7 +218,7 @@ class StringManager extends LinkedHashMap<String, Integer> {
|
||||
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.
|
||||
*
|
||||
* @param strIdx
|
||||
@ -193,12 +232,18 @@ class StringManager extends LinkedHashMap<String, Integer> {
|
||||
"return" )
|
||||
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" )
|
||||
private static native int stringsMemoryOffset();
|
||||
|
||||
/**
|
||||
* WASM code
|
||||
* Load an i32 from memory. The offset must be aligned.
|
||||
* WASM code<p>
|
||||
* Load an i32 from memory. The offset must be aligned. Should be inlined from the optimizer.
|
||||
*
|
||||
* @param pos
|
||||
* the memory position
|
||||
@ -210,8 +255,8 @@ class StringManager extends LinkedHashMap<String, Integer> {
|
||||
private static native int getIntFromMemory( int pos );
|
||||
|
||||
/**
|
||||
* WASM code
|
||||
* Load a byte from the memory.
|
||||
* WASM code<p>
|
||||
* Load a byte from the memory. Should be inlined from the optimizer.
|
||||
*
|
||||
* @param pos
|
||||
* the memory position
|
||||
|
@ -357,7 +357,7 @@ public abstract class WasmCodeBuilder {
|
||||
if( id == null ) {
|
||||
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 ) );
|
||||
addCallInstruction( name, javaCodePos, lineNumber );
|
||||
} else {
|
||||
|
@ -16,6 +16,8 @@
|
||||
*/
|
||||
package de.inetsoftware.jwebassembly.module;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import de.inetsoftware.jwebassembly.wasm.AnyType;
|
||||
import de.inetsoftware.jwebassembly.watparser.WatParser;
|
||||
|
||||
@ -57,11 +59,21 @@ class WatCodeSyntheticFunctionName extends ArraySyntheticFunctionName {
|
||||
* @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 ) {
|
||||
public WatCodeSyntheticFunctionName( String className, String name, String signature, @Nonnull String code, AnyType... signatureTypes ) {
|
||||
super( className, name, signature, signatureTypes );
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Wat code, can be overridden.
|
||||
*
|
||||
* @return the code
|
||||
*/
|
||||
@Nonnull
|
||||
protected String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@ -75,7 +87,7 @@ class WatCodeSyntheticFunctionName extends ArraySyntheticFunctionName {
|
||||
*/
|
||||
@Override
|
||||
protected WasmCodeBuilder getCodeBuilder( WatParser watParser ) {
|
||||
watParser.parse( code, null, -1 );
|
||||
watParser.parse( getCode(), null, -1 );
|
||||
return watParser;
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ public class StringOperations extends AbstractBaseTest {
|
||||
for( ScriptEngine script : ScriptEngine.testEngines() ) {
|
||||
addParam( list, script, "newFromChars" );
|
||||
addParam( list, script, "newFromBytes" );
|
||||
addParam( list, script, "constant" );
|
||||
}
|
||||
rule.setTestParameters( list );
|
||||
return list;
|
||||
@ -60,5 +61,15 @@ public class StringOperations extends AbstractBaseTest {
|
||||
byte[] bytes = {(byte)0xC3,(byte)0xA4};
|
||||
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