mirror of
https://github.com/i-net-software/JWebAssembly.git
synced 2025-03-25 15:37:52 +01:00
270 lines
9.5 KiB
Java
270 lines
9.5 KiB
Java
/*
|
|
Copyright 2019 Volker Berlin (i-net software)
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
*/
|
|
package de.inetsoftware.jwebassembly.module;
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
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;
|
|
|
|
/**
|
|
* Handle all the constant strings. The constant strings will be write into the data section.
|
|
*
|
|
* @author Volker Berlin
|
|
*/
|
|
class StringManager extends LinkedHashMap<String, Integer> {
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @param writer
|
|
* the targets for the strings
|
|
* @throws IOException
|
|
* if any I/O error occur
|
|
*/
|
|
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
|
|
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
|
|
| ..... |
|
|
├────────────────────────────────┤
|
|
| start index string 1 (4 bytes) |
|
|
├────────────────────────────────┤
|
|
| start index string 2 (4 bytes) |
|
|
├────────────────────────────────┤
|
|
| start index string 3 (4 bytes) |
|
|
├────────────────────────────────┤
|
|
| ..... |
|
|
├────────────────────────────────┤
|
|
| length string 1 (1-x bytes) |
|
|
├────────────────────────────────┤
|
|
| string 1 (UTF8 encoded) |
|
|
├────────────────────────────────┤
|
|
| length string 2 (1-x bytes) |
|
|
├────────────────────────────────┤
|
|
| string 2 (UTF8 encoded) |
|
|
├────────────────────────────────┤
|
|
| ..... |
|
|
*/
|
|
ByteArrayOutputStream stringOut = new ByteArrayOutputStream();
|
|
ByteArrayOutputStream dataStream = writer.dataStream;
|
|
|
|
// 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
|
|
// little-endian byte order
|
|
int position = offset + stringOut.size();
|
|
dataStream.write( position >>> 0 );
|
|
dataStream.write( position >>> 8 );
|
|
dataStream.write( position >>> 16 );
|
|
dataStream.write( position >>> 24 );
|
|
|
|
byte[] bytes = str.getBytes( StandardCharsets.UTF_8 );
|
|
writeVaruint32( bytes.length, stringOut );
|
|
stringOut.write( bytes );
|
|
}
|
|
|
|
stringOut.writeTo( dataStream );
|
|
}
|
|
|
|
/**
|
|
* Write an unsigned integer.
|
|
*
|
|
* @param value
|
|
* the value
|
|
* @param out
|
|
* target stream
|
|
* @throws IOException
|
|
* if an I/O error occurs.
|
|
*/
|
|
private static void writeVaruint32( @Nonnegative int value, OutputStream out ) throws IOException {
|
|
if( value < 0 ) {
|
|
throw new IOException( "Invalid negative value" );
|
|
}
|
|
do {
|
|
int b = value & 0x7F; // low 7 bits
|
|
value >>= 7;
|
|
if( value != 0 ) { /* more bytes to come */
|
|
b |= 0x80;
|
|
}
|
|
out.write( b );
|
|
} while( value != 0 );
|
|
}
|
|
|
|
/**
|
|
* WASM code<p>
|
|
* Get a constant string from the table.
|
|
*
|
|
* @param strIdx
|
|
* the id/index of the string.
|
|
* @return the string
|
|
* @see #STRING_CONSTANT_FUNCTION
|
|
*/
|
|
private static String stringConstant( int strIdx ) {
|
|
String str = getStringFromTable( strIdx );
|
|
if( str != null ) {
|
|
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 += (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<p>
|
|
* 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<p>
|
|
* 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 );
|
|
|
|
/**
|
|
* 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<p>
|
|
* Load an i32 from memory. The offset must be aligned. Should be inlined from the optimizer.
|
|
*
|
|
* @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<p>
|
|
* Load a byte from the memory. Should be inlined from the optimizer.
|
|
*
|
|
* @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 );
|
|
}
|