implements support for static class constructors

This commit is contained in:
Volker Berlin 2020-04-02 21:01:11 +02:00
parent 645996ceb0
commit 65a1f3ff76
6 changed files with 97 additions and 16 deletions

View File

@ -35,7 +35,7 @@ The project is currently not production ready but you can run already some tests
* [x] invoke default method calls
* [x] String support
* [x] Simple Class object support
* [ ] static constructors
* [x] static constructors
* [x] Optimizer - Optimize the WASM output of a single method after transpiling before writing to output
* [x] Hello World sample [(live)](https://i-net-software.github.io/JWebAssembly/samples/HelloWorld/HelloWorld.html), [(source code)](https://github.com/i-net-software/JWebAssembly/blob/master/docs/samples/HelloWorld/HelloWorld.java)

View File

@ -91,6 +91,8 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
private boolean callIndirect;
private FunctionName startFunction;
/**
* Create new instance.
*
@ -133,6 +135,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
writeEventSection();
writeSection( SectionType.Global, globals.values() );
writeSection( SectionType.Export, exports );
writeStartSection();
writeElementSection();
writeCodeSection();
writeDataSection();
@ -253,6 +256,22 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
}
}
/**
* Write a start section. The id of the function that should be automatically executed.
*
* @throws IOException
* if any I/O error occur
*/
private void writeStartSection() throws IOException {
if( startFunction == null ) {
return;
}
int id = getFunction( startFunction ).id;
WasmOutputStream stream = new WasmOutputStream();
stream.writeVaruint32( id );
wasm.writeSection( SectionType.Start, stream );
}
/**
* Write element section. This section create a matching between direct and indirect function call IDs.
*
@ -518,10 +537,15 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
*/
@Override
protected void writeMethodParamStart( FunctionName name, FunctionType funcType ) throws IOException {
if( funcType == FunctionType.Abstract ) {
abstracts.put( name.signatureName, function = new Function() );
} else {
function = getFunction( name );
switch( funcType ) {
case Abstract:
abstracts.put( name.signatureName, function = new Function() );
break;
case Start:
startFunction = name;
//$FALL-THROUGH$
default:
function = getFunction( name );
}
functionType = new FunctionTypeEntry();
locals.clear();

View File

@ -37,9 +37,9 @@ import de.inetsoftware.jwebassembly.WasmException;
*/
class FunctionManager {
private final Map<FunctionName, FunctionState> states = new LinkedHashMap<>();
private final Map<FunctionName, FunctionState> states = new LinkedHashMap<>();
private final Map<String, FunctionName> classesWithCInit = new HashMap<>();
private final Map<String, FunctionName> classesWithClinit = new HashMap<>();
private boolean isFinish;
@ -90,10 +90,10 @@ class FunctionManager {
* Mark that a class has static initializer.
*
* @param name
* the function name
* the "&lt;clinit&gt;" function name
*/
void markClassWithCInit( FunctionName name ) {
classesWithCInit.put( name.className, name );
void markClassWithClinit( FunctionName name ) {
classesWithClinit.put( name.className, name );
}
/**
@ -152,7 +152,7 @@ class FunctionManager {
}
state.state = State.Needed;
JWebAssembly.LOGGER.fine( "\t\tcall: " + name.signatureName );
FunctionName cInit = classesWithCInit.get( name.className );
FunctionName cInit = classesWithClinit.get( name.className );
if( cInit != null ) {
markAsNeeded( cInit );
}
@ -243,10 +243,23 @@ class FunctionManager {
return null;
}
/**
* Get all static constructor FunctionName of used classes.
*
* @return an iterator
*/
@Nullable
Iterator<FunctionName> getWriteLaterClinit() {
return classesWithClinit.values().stream().filter( ( name ) -> {
FunctionState state = states.get( name );
return state != null && (state.state == State.Needed || state.state == State.Scanned);
} ).iterator();
}
/**
* Get all FunctionName that is required but was not written.
*
* @return the FunctionName or null
* @return an iterator
*/
@Nullable
Iterator<FunctionName> getWriteLater() {

View File

@ -299,6 +299,9 @@ public class ModuleGenerator {
javaScript.addImport( importModule, importName, importAnannotation );
}
// add a start method for the static class constructors
prepareStartFunction();
// init/write the function types
for( Iterator<FunctionName> iterator = functions.getWriteLater(); iterator.hasNext(); ) {
FunctionName name = iterator.next();
@ -318,6 +321,39 @@ public class ModuleGenerator {
writer.prepareFinish();
}
/**
* Add a start method for the static class constructors
*
* @throws IOException
* if any I/O error occur
*/
private void prepareStartFunction() throws IOException {
// add the start function/section only if there are static code
Iterator<FunctionName> iterator = functions.getWriteLaterClinit();
if( iterator.hasNext() ) {
FunctionName cinit = new SyntheticFunctionName( "", "<clinit>", "()V" ) {
@Override
protected boolean hasWasmCode() {
return true;
}
@Override
protected WasmCodeBuilder getCodeBuilder( WatParser watParser ) {
watParser.reset( null, null, getSignature( null ) );
while( iterator.hasNext() ) {
FunctionName name = iterator.next();
//TODO if not in the debug mode then inlining would produce smaller output and should be faster
watParser.addCallInstruction( name, 0, -1 );
}
return watParser;
}
};
functions.markAsNeeded( cinit );
writeMethodSignature( cinit, FunctionType.Start, null );
}
}
/**
* Finish the code generation.
*
@ -405,7 +441,7 @@ public class ModuleGenerator {
try {
FunctionName name = new FunctionName( method );
if( "<clinit>".equals( name.methodName ) ) {
functions.markClassWithCInit( name );
functions.markClassWithClinit( name );
}
if( functions.isKnown( name ) ) {
return;

View File

@ -329,8 +329,14 @@ public class TextModuleWriter extends ModuleWriter {
*/
@Override
protected void writeMethodParamStart( @Nonnull FunctionName name, FunctionType funcType ) throws IOException {
if( funcType == FunctionType.Abstract ) {
abstracts.put( name.signatureName, new Function() );
switch( funcType ) {
case Abstract:
abstracts.put( name.signatureName, new Function() );
break;
case Start:
newline( imports );
imports.append( "(start $" ).append( normalizeName( name ) ).append( ")" );
break;
}
typeOutput.setLength( 0 );
methodParamNames.clear();

View File

@ -26,5 +26,7 @@ public enum FunctionType {
/** has real code */
Code,
/** abstract or interface, only used for indirrect call */
Abstract;
Abstract,
/** the function of start section, should occur only once */
Start,
}