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;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
@ -37,9 +38,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> classesWithClinit = new HashMap<>();
private final Set<String> usedClasses = new LinkedHashSet<>();
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
* the "&lt;clinit&gt;" function name
* @param className
* the name of the class like "java/lang/Object"
*/
void markClassWithClinit( FunctionName name ) {
classesWithClinit.put( name.className, name );
void markClassAsUsed( String className ) {
usedClasses.add( className );
}
/**
@ -152,10 +153,7 @@ class FunctionManager {
}
state.state = State.Needed;
JWebAssembly.LOGGER.fine( "\t\tcall: " + name.signatureName );
FunctionName cInit = classesWithClinit.get( name.className );
if( cInit != null ) {
markAsNeeded( cInit );
}
usedClasses.add( name.className );
}
return state.alias == null ? name : state.alias;
}
@ -243,6 +241,16 @@ class FunctionManager {
return null;
}
/**
* Get all used classes.
*
* @return an iterator
*/
@Nullable
Iterator<String> getUsedClasses() {
return usedClasses.iterator();
}
/**
* Get all static constructor FunctionName of used classes.
*
@ -250,10 +258,7 @@ class FunctionManager {
*/
@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();
return iterator( entry -> entry.getKey().methodName.equals( "<clinit>" ) );
}
/**

View File

@ -196,8 +196,8 @@ public class ModuleGenerator {
NEXT:
while( (next = functions.nextScannLater()) != null ) {
className = next.className;
JWebAssembly.LOGGER.fine( "scan " + next.signatureName );
if( next instanceof SyntheticFunctionName ) {
JWebAssembly.LOGGER.fine( '\t' + next.methodName + next.signature );
SyntheticFunctionName synth = (SyntheticFunctionName)next;
if( synth.hasWasmCode() ) {
synth.getCodeBuilder( watParser );
@ -208,7 +208,6 @@ public class ModuleGenerator {
continue;
}
JWebAssembly.LOGGER.fine( "scan " + next.signatureName );
MethodInfo method = null;
ClassFile classFile = classFileLoader.get( next.className );
if( classFile != null ) {
@ -275,6 +274,7 @@ public class ModuleGenerator {
do {
scanFunctions();
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
} while( functCount < functions.size() );
@ -321,6 +321,26 @@ public class ModuleGenerator {
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
*
@ -329,8 +349,7 @@ public class ModuleGenerator {
*/
private void prepareStartFunction() throws IOException {
// add the start function/section only if there are static code
Iterator<FunctionName> iterator = functions.getWriteLaterClinit();
if( iterator.hasNext() ) {
if( functions.getWriteLaterClinit().hasNext() ) {
FunctionName cinit = new SyntheticFunctionName( "", "<clinit>", "()V" ) {
@Override
protected boolean hasWasmCode() {
@ -341,6 +360,7 @@ public class ModuleGenerator {
protected WasmCodeBuilder getCodeBuilder( WatParser watParser ) {
watParser.reset( null, null, getSignature( null ) );
Iterator<FunctionName> iterator = functions.getWriteLaterClinit();
while( iterator.hasNext() ) {
FunctionName name = iterator.next();
//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 {
try {
FunctionName name = new FunctionName( method );
if( "<clinit>".equals( name.methodName ) ) {
functions.markClassWithClinit( name );
}
if( functions.isKnown( name ) ) {
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
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 );
}
} catch( Exception ioex ) {

View File

@ -329,6 +329,7 @@ public abstract class WasmCodeBuilder {
FunctionName name = new FunctionName( ref );
AnyType type = new ValueTypeParser( ref.getType(), types ).next();
instructions.add( new WasmGlobalInstruction( load, name, type, javaCodePos, lineNumber ) );
functions.markClassAsUsed( name.className );
}
/**
@ -486,6 +487,7 @@ public abstract class WasmCodeBuilder {
}
instructions.add( instruction );
functions.markClassAsUsed( name.className );
}
/**
@ -535,6 +537,7 @@ public abstract class WasmCodeBuilder {
name = functions.markAsNeeded( name );
addCallIndirectInstruction( new WasmCallVirtualInstruction( name, javaCodePos, lineNumber, types, options ) );
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 ) {
//TODO name = functions.markAsNeeded( name );
addCallIndirectInstruction( new WasmCallInterfaceInstruction( name, javaCodePos, lineNumber, types, options ) );
functions.markClassAsUsed( name.className );
}
/**