Also static code from Java VM code must be registered. Disable static code to find a solution for the cyclic dependency between static code.

This commit is contained in:
Volker Berlin 2020-04-05 21:03:13 +02:00
parent c1b3e34eb7
commit 87f986e30b
3 changed files with 53 additions and 23 deletions

View File

@ -15,11 +15,12 @@
*/ */
package de.inetsoftware.jwebassembly.module; package de.inetsoftware.jwebassembly.module;
import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -37,9 +38,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> classesWithClinit = new HashMap<>(); private final Set<String> usedClasses = new LinkedHashSet<>();
private boolean isFinish; private boolean isFinish;
@ -87,13 +88,13 @@ class FunctionManager {
} }
/** /**
* Mark that a class has static initializer. * Mark a class as used. This means the static initializer must be used.
* *
* @param name * @param className
* the "&lt;clinit&gt;" function name * the name of the class like "java/lang/Object"
*/ */
void markClassWithClinit( FunctionName name ) { void markClassAsUsed( String className ) {
classesWithClinit.put( name.className, name ); usedClasses.add( className );
} }
/** /**
@ -152,10 +153,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 = classesWithClinit.get( name.className ); usedClasses.add( name.className );
if( cInit != null ) {
markAsNeeded( cInit );
}
} }
return state.alias == null ? name : state.alias; return state.alias == null ? name : state.alias;
} }
@ -243,6 +241,16 @@ class FunctionManager {
return null; return null;
} }
/**
* Get all used classes.
*
* @return an iterator
*/
@Nullable
Iterator<String> getUsedClasses() {
return usedClasses.iterator();
}
/** /**
* Get all static constructor FunctionName of used classes. * Get all static constructor FunctionName of used classes.
* *
@ -250,10 +258,7 @@ class FunctionManager {
*/ */
@Nullable @Nullable
Iterator<FunctionName> getWriteLaterClinit() { Iterator<FunctionName> getWriteLaterClinit() {
return classesWithClinit.values().stream().filter( ( name ) -> { return iterator( entry -> entry.getKey().methodName.equals( "<clinit>" ) );
FunctionState state = states.get( name );
return state != null && (state.state == State.Needed || state.state == State.Scanned);
} ).iterator();
} }
/** /**

View File

@ -196,8 +196,8 @@ public class ModuleGenerator {
NEXT: NEXT:
while( (next = functions.nextScannLater()) != null ) { while( (next = functions.nextScannLater()) != null ) {
className = next.className; className = next.className;
JWebAssembly.LOGGER.fine( "scan " + next.signatureName );
if( next instanceof SyntheticFunctionName ) { if( next instanceof SyntheticFunctionName ) {
JWebAssembly.LOGGER.fine( '\t' + next.methodName + next.signature );
SyntheticFunctionName synth = (SyntheticFunctionName)next; SyntheticFunctionName synth = (SyntheticFunctionName)next;
if( synth.hasWasmCode() ) { if( synth.hasWasmCode() ) {
synth.getCodeBuilder( watParser ); synth.getCodeBuilder( watParser );
@ -208,7 +208,6 @@ public class ModuleGenerator {
continue; continue;
} }
JWebAssembly.LOGGER.fine( "scan " + next.signatureName );
MethodInfo method = null; MethodInfo method = null;
ClassFile classFile = classFileLoader.get( next.className ); ClassFile classFile = classFileLoader.get( next.className );
if( classFile != null ) { if( classFile != null ) {
@ -275,6 +274,7 @@ public class ModuleGenerator {
do { do {
scanFunctions(); scanFunctions();
functCount = functions.size(); // scan the functions can find new needed types functCount = functions.size(); // scan the functions can find new needed types
//TODO static code disabled: scanForClinit();
types.scanTypeHierarchy( classFileLoader ); // scan the type hierarchy can find new functions types.scanTypeHierarchy( classFileLoader ); // scan the type hierarchy can find new functions
} while( functCount < functions.size() ); } while( functCount < functions.size() );
@ -321,6 +321,26 @@ public class ModuleGenerator {
writer.prepareFinish(); writer.prepareFinish();
} }
/**
* Scan for needed static constructors. The static code of all classes that used in any form must be executed.
*
* @throws IOException
* if any I/O error occur
*/
private void scanForClinit() throws IOException {
JWebAssembly.LOGGER.fine( "scan for needed <clinit>" );
for( Iterator<String> iterator = functions.getUsedClasses(); iterator.hasNext(); ) {
String className = iterator.next();
ClassFile classFile = classFileLoader.get( className );
if( classFile != null ) {
MethodInfo method = classFile.getMethod( "<clinit>", "()V" );
if( method != null ) {
functions.markAsNeeded( new FunctionName( method ) );
}
}
}
}
/** /**
* Add a start method for the static class constructors * Add a start method for the static class constructors
* *
@ -329,8 +349,7 @@ public class ModuleGenerator {
*/ */
private void prepareStartFunction() throws IOException { private void prepareStartFunction() throws IOException {
// add the start function/section only if there are static code // add the start function/section only if there are static code
Iterator<FunctionName> iterator = functions.getWriteLaterClinit(); if( functions.getWriteLaterClinit().hasNext() ) {
if( iterator.hasNext() ) {
FunctionName cinit = new SyntheticFunctionName( "", "<clinit>", "()V" ) { FunctionName cinit = new SyntheticFunctionName( "", "<clinit>", "()V" ) {
@Override @Override
protected boolean hasWasmCode() { protected boolean hasWasmCode() {
@ -341,6 +360,7 @@ public class ModuleGenerator {
protected WasmCodeBuilder getCodeBuilder( WatParser watParser ) { protected WasmCodeBuilder getCodeBuilder( WatParser watParser ) {
watParser.reset( null, null, getSignature( null ) ); watParser.reset( null, null, getSignature( null ) );
Iterator<FunctionName> iterator = functions.getWriteLaterClinit();
while( iterator.hasNext() ) { while( iterator.hasNext() ) {
FunctionName name = iterator.next(); FunctionName name = iterator.next();
//TODO if not in the debug mode then inlining would produce smaller output and should be faster //TODO if not in the debug mode then inlining would produce smaller output and should be faster
@ -440,9 +460,6 @@ public class ModuleGenerator {
private void prepareMethod( MethodInfo method ) throws WasmException { private void prepareMethod( MethodInfo method ) throws WasmException {
try { try {
FunctionName name = new FunctionName( method ); FunctionName name = new FunctionName( method );
if( "<clinit>".equals( name.methodName ) ) {
functions.markClassWithClinit( name );
}
if( functions.isKnown( name ) ) { if( functions.isKnown( name ) ) {
return; return;
} }
@ -533,6 +550,10 @@ public class ModuleGenerator {
strings.getStringConstantFunction(); // we will need also the string constant function for the Class Name, in the other case a program with only new Object().getClass().getName() will fail to compile strings.getStringConstantFunction(); // we will need also the string constant function for the Class Name, in the other case a program with only new Object().getClass().getName() will fail to compile
return types.getTypeTableMemoryOffsetFunctionName().getCodeBuilder( watParser ); return types.getTypeTableMemoryOffsetFunctionName().getCodeBuilder( watParser );
} }
if( "de/inetsoftware/jwebassembly/module/StringManager.stringsMemoryOffset()I".equals( name.signatureName ) ) {
strings.getStringConstantFunction();
return null;
}
throw new WasmException( "Abstract or native method can not be used: " + name.signatureName, -1 ); throw new WasmException( "Abstract or native method can not be used: " + name.signatureName, -1 );
} }
} catch( Exception ioex ) { } catch( Exception ioex ) {

View File

@ -329,6 +329,7 @@ public abstract class WasmCodeBuilder {
FunctionName name = new FunctionName( ref ); FunctionName name = new FunctionName( ref );
AnyType type = new ValueTypeParser( ref.getType(), types ).next(); AnyType type = new ValueTypeParser( ref.getType(), types ).next();
instructions.add( new WasmGlobalInstruction( load, name, type, javaCodePos, lineNumber ) ); instructions.add( new WasmGlobalInstruction( load, name, type, javaCodePos, lineNumber ) );
functions.markClassAsUsed( name.className );
} }
/** /**
@ -486,6 +487,7 @@ public abstract class WasmCodeBuilder {
} }
instructions.add( instruction ); instructions.add( instruction );
functions.markClassAsUsed( name.className );
} }
/** /**
@ -535,6 +537,7 @@ public abstract class WasmCodeBuilder {
name = functions.markAsNeeded( name ); name = functions.markAsNeeded( name );
addCallIndirectInstruction( new WasmCallVirtualInstruction( name, javaCodePos, lineNumber, types, options ) ); addCallIndirectInstruction( new WasmCallVirtualInstruction( name, javaCodePos, lineNumber, types, options ) );
options.getCallVirtual(); // mark the function as needed options.getCallVirtual(); // mark the function as needed
functions.markClassAsUsed( name.className );
} }
/** /**
@ -549,6 +552,7 @@ public abstract class WasmCodeBuilder {
protected void addCallInterfaceInstruction( FunctionName name, int javaCodePos, int lineNumber ) { protected void addCallInterfaceInstruction( FunctionName name, int javaCodePos, int lineNumber ) {
//TODO name = functions.markAsNeeded( name ); //TODO name = functions.markAsNeeded( name );
addCallIndirectInstruction( new WasmCallInterfaceInstruction( name, javaCodePos, lineNumber, types, options ) ); addCallIndirectInstruction( new WasmCallInterfaceInstruction( name, javaCodePos, lineNumber, types, options ) );
functions.markClassAsUsed( name.className );
} }
/** /**