From 87f986e30bd1876fe47190ee45855a559da6779f Mon Sep 17 00:00:00 2001 From: Volker Berlin Date: Sun, 5 Apr 2020 21:03:13 +0200 Subject: [PATCH] Also static code from Java VM code must be registered. Disable static code to find a solution for the cyclic dependency between static code. --- .../jwebassembly/module/FunctionManager.java | 37 +++++++++++-------- .../jwebassembly/module/ModuleGenerator.java | 35 ++++++++++++++---- .../jwebassembly/module/WasmCodeBuilder.java | 4 ++ 3 files changed, 53 insertions(+), 23 deletions(-) diff --git a/src/de/inetsoftware/jwebassembly/module/FunctionManager.java b/src/de/inetsoftware/jwebassembly/module/FunctionManager.java index 8fbad7f..8c8501e 100644 --- a/src/de/inetsoftware/jwebassembly/module/FunctionManager.java +++ b/src/de/inetsoftware/jwebassembly/module/FunctionManager.java @@ -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 states = new LinkedHashMap<>(); + private final Map states = new LinkedHashMap<>(); - private final Map classesWithClinit = new HashMap<>(); + private final Set 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 "<clinit>" 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 getUsedClasses() { + return usedClasses.iterator(); + } + /** * Get all static constructor FunctionName of used classes. * @@ -250,10 +258,7 @@ class FunctionManager { */ @Nullable Iterator 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( "" ) ); } /** diff --git a/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java b/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java index 40dfbe7..121fd4e 100644 --- a/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java +++ b/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java @@ -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 " ); + for( Iterator iterator = functions.getUsedClasses(); iterator.hasNext(); ) { + String className = iterator.next(); + ClassFile classFile = classFileLoader.get( className ); + if( classFile != null ) { + MethodInfo method = classFile.getMethod( "", "()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 iterator = functions.getWriteLaterClinit(); - if( iterator.hasNext() ) { + if( functions.getWriteLaterClinit().hasNext() ) { FunctionName cinit = new SyntheticFunctionName( "", "", "()V" ) { @Override protected boolean hasWasmCode() { @@ -341,6 +360,7 @@ public class ModuleGenerator { protected WasmCodeBuilder getCodeBuilder( WatParser watParser ) { watParser.reset( null, null, getSignature( null ) ); + Iterator 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( "".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 ) { diff --git a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java index 385805d..2bd89ca 100644 --- a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java +++ b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java @@ -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 ); } /**