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] invoke default method calls
* [x] String support * [x] String support
* [x] Simple Class object 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] 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) * [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 boolean callIndirect;
private FunctionName startFunction;
/** /**
* Create new instance. * Create new instance.
* *
@ -133,6 +135,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
writeEventSection(); writeEventSection();
writeSection( SectionType.Global, globals.values() ); writeSection( SectionType.Global, globals.values() );
writeSection( SectionType.Export, exports ); writeSection( SectionType.Export, exports );
writeStartSection();
writeElementSection(); writeElementSection();
writeCodeSection(); writeCodeSection();
writeDataSection(); 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. * 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 @Override
protected void writeMethodParamStart( FunctionName name, FunctionType funcType ) throws IOException { protected void writeMethodParamStart( FunctionName name, FunctionType funcType ) throws IOException {
if( funcType == FunctionType.Abstract ) { switch( funcType ) {
abstracts.put( name.signatureName, function = new Function() ); case Abstract:
} else { abstracts.put( name.signatureName, function = new Function() );
function = getFunction( name ); break;
case Start:
startFunction = name;
//$FALL-THROUGH$
default:
function = getFunction( name );
} }
functionType = new FunctionTypeEntry(); functionType = new FunctionTypeEntry();
locals.clear(); locals.clear();

View File

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

View File

@ -299,6 +299,9 @@ public class ModuleGenerator {
javaScript.addImport( importModule, importName, importAnannotation ); javaScript.addImport( importModule, importName, importAnannotation );
} }
// add a start method for the static class constructors
prepareStartFunction();
// init/write the function types // init/write the function types
for( Iterator<FunctionName> iterator = functions.getWriteLater(); iterator.hasNext(); ) { for( Iterator<FunctionName> iterator = functions.getWriteLater(); iterator.hasNext(); ) {
FunctionName name = iterator.next(); FunctionName name = iterator.next();
@ -318,6 +321,39 @@ public class ModuleGenerator {
writer.prepareFinish(); 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. * Finish the code generation.
* *
@ -405,7 +441,7 @@ public class ModuleGenerator {
try { try {
FunctionName name = new FunctionName( method ); FunctionName name = new FunctionName( method );
if( "<clinit>".equals( name.methodName ) ) { if( "<clinit>".equals( name.methodName ) ) {
functions.markClassWithCInit( name ); functions.markClassWithClinit( name );
} }
if( functions.isKnown( name ) ) { if( functions.isKnown( name ) ) {
return; return;

View File

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

View File

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