From 6bcda0daa59be5dae8b30eb5ae41a3f3ccb69fe2 Mon Sep 17 00:00:00 2001 From: Volker Berlin Date: Sun, 23 Jun 2019 20:50:11 +0200 Subject: [PATCH] import only the needed functions to reduce the needed JS glue code --- .../jwebassembly/module/FunctionManager.java | 89 +++++++++++++++++-- .../jwebassembly/module/ModuleGenerator.java | 50 +++++++---- .../jwebassembly/module/TypeManager.java | 3 +- .../module/WasmCallIndirectInstruction.java | 4 +- .../module/WasmCallInstruction.java | 6 +- 5 files changed, 125 insertions(+), 27 deletions(-) diff --git a/src/de/inetsoftware/jwebassembly/module/FunctionManager.java b/src/de/inetsoftware/jwebassembly/module/FunctionManager.java index 590cab8..9f40d15 100644 --- a/src/de/inetsoftware/jwebassembly/module/FunctionManager.java +++ b/src/de/inetsoftware/jwebassembly/module/FunctionManager.java @@ -17,6 +17,7 @@ package de.inetsoftware.jwebassembly.module; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; @@ -57,6 +58,19 @@ public class FunctionManager { return state; } + /** + * Mark the a function as a import function. Only if the function is also needed then it will imported from + * compiler. + * + * @param name + * the function name + * @param importAnannotation + * the annotation of the import + */ + void markAsImport( FunctionName name, Map importAnannotation ) { + getOrCreate( name ).importAnannotation = importAnannotation; + } + /** * Mark the a function as scanned in the prepare phase. This should only occur with needed functions. * @@ -82,16 +96,48 @@ public class FunctionManager { * * @param name * the function name + * @param isStatic + * true, if the method is static * @return the real function name */ - FunctionName markAsNeeded( FunctionName name ) { + FunctionName markAsNeeded( FunctionName name, boolean isStatic ) { FunctionState state = getOrCreate( name ); if( state.state == State.None ) { state.state = State.Needed; } + state.isStatic = isStatic; return state.alias == null ? name : state.alias; } + /** + * Get all FunctionNames that need imported + * + * @return an iterator + */ + Iterator getNeededImports() { + return states.entrySet().stream().filter( entry -> { + FunctionState state = entry.getValue(); + switch( state.state ) { + case Needed: + case Scanned: + return state.importAnannotation != null; + default: + } + return false; + } ).map( entry -> entry.getKey() ).iterator(); + } + + /** + * Get the annotation of an import function + * + * @param name + * the function name + * @return the annotation or null + */ + Map getImportAnannotation( FunctionName name ) { + return getOrCreate( name ).importAnannotation; + } + /** * Get the first FunctionName that is required but was not scanned. * @@ -125,6 +171,24 @@ public class FunctionManager { return null; } + /** + * Get all FunctionNames that need imported + * + * @return an iterator + */ + Iterator getNeededFunctions() { + return states.entrySet().stream().filter( entry -> { + FunctionState state = entry.getValue(); + switch( state.state ) { + case Needed: + case Scanned: + return true; + default: + } + return false; + } ).map( entry -> entry.getKey() ).iterator(); + } + /** * if the given function is required but was not scanned. * @@ -158,6 +222,17 @@ public class FunctionManager { } } + /** + * if the given function is static. + * + * @param name + * the function name + * @return true, if the function is static + */ + boolean isStatic( FunctionName name ) { + return getOrCreate( name ).isStatic; + } + /** * Add a replacement for a method * @@ -227,13 +302,17 @@ public class FunctionManager { * State of a function/method */ private static class FunctionState { - private State state = State.None; + private State state = State.None; - private MethodInfo method; + private MethodInfo method; - private FunctionName alias; + private FunctionName alias; - private int functionIdx = -1; + private Map importAnannotation; + + private int functionIdx = -1; + + private boolean isStatic; } private static enum State { diff --git a/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java b/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java index a85a6cc..57a0dfa 100644 --- a/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java +++ b/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java @@ -156,7 +156,6 @@ public class ModuleGenerator { ClassFile classFile = ClassFile.get( next.className, libraries ); if( classFile == null ) { if( next instanceof SyntheticFunctionName ) { - writeMethodSignature( next, true, null ); scanMethod( ((SyntheticFunctionName)next).getCodeBuilder( watParser ) ); functions.markAsScanned( next ); } @@ -165,7 +164,6 @@ public class ModuleGenerator { try { FunctionName name = new FunctionName( method ); if( functions.needToScan( name ) ) { - writeMethodSignature( name, method.isStatic(), null ); scanMethod( createInstructions( functions.replace( name, method ) ) ); functions.markAsScanned( name ); } @@ -182,7 +180,7 @@ public class ModuleGenerator { MethodInfo method = superClassFile.getMethod( next.methodName, next.signature ); if( method != null ) { FunctionName name = new FunctionName( method ); - functions.markAsNeeded( name ); + functions.markAsNeeded( name, method.isStatic() ); functions.setAlias( next, name ); continue NEXT; // we have found a super method } @@ -202,6 +200,25 @@ public class ModuleGenerator { */ public void prepareFinish() throws IOException { prepareFunctions(); + + // write only the needed imports to the output + for( Iterator iterator = functions.getNeededImports(); iterator.hasNext(); ) { + FunctionName name = iterator.next(); + + functions.markAsWritten( name ); + Map importAnannotation = functions.getImportAnannotation( name ); + String impoarModule = (String)importAnannotation.get( "module" ); + String importName = (String)importAnannotation.get( "name" ); + writer.prepareImport( name, impoarModule, importName ); + writeMethodSignature( name, true, null ); + } + + // init/write the function types + for( Iterator iterator = functions.getNeededFunctions(); iterator.hasNext(); ) { + FunctionName name = iterator.next(); + writeMethodSignature( name, functions.isStatic( name ), null ); + } + types.prepareFinish( writer, functions, libraries ); prepareFunctions(); // prepare of types can add some override methods as needed functions.prepareFinish(); @@ -224,8 +241,10 @@ public class ModuleGenerator { for( WasmInstruction instruction : instructions ) { switch( instruction.getType() ) { case Call: + ((WasmCallInstruction)instruction).markAsNeeded( functions, true ); + break; case CallIndirect: - ((WasmCallInstruction)instruction).markAsNeeded( functions ); + ((WasmCallInstruction)instruction).markAsNeeded( functions, false ); break; default: } @@ -315,28 +334,23 @@ public class ModuleGenerator { try { FunctionName name = new FunctionName( method ); Map annotationValues; + if( (annotationValues = method.getAnnotation( JWebAssembly.REPLACE_ANNOTATION )) != null ) { + String signatureName = (String)annotationValues.get( "value" ); + name = new FunctionName( signatureName ); + functions.addReplacement( name, method ); + } if( (annotationValues = method.getAnnotation( JWebAssembly.IMPORT_ANNOTATION )) != null ) { if( !method.isStatic() ) { throw new WasmException( "Import method must be static: " + name.fullName, -1 ); } - functions.markAsWritten( name ); - String impoarModule = (String)annotationValues.get( "module" ); - String importName = (String)annotationValues.get( "name" ); - writer.prepareImport( name, impoarModule, importName ); - writeMethodSignature( name, true, null ); + functions.markAsImport( name, annotationValues ); return; } if( (annotationValues = method.getAnnotation( JWebAssembly.EXPORT_ANNOTATION )) != null ) { if( !method.isStatic() ) { throw new WasmException( "Export method must be static: " + name.fullName, -1 ); } - functions.markAsNeeded( name ); - return; - } - if( (annotationValues = method.getAnnotation( JWebAssembly.REPLACE_ANNOTATION )) != null ) { - String signatureName = (String)annotationValues.get( "value" ); - name = new FunctionName( signatureName ); - functions.addReplacement( name, method ); + functions.markAsNeeded( name, true ); return; } } catch( Exception ioex ) { @@ -446,8 +460,10 @@ public class ModuleGenerator { } break; case Call: + ((WasmCallInstruction)instruction).markAsNeeded( functions, true ); + break; case CallIndirect: - ((WasmCallInstruction)instruction).markAsNeeded( functions ); + ((WasmCallInstruction)instruction).markAsNeeded( functions, false ); break; case Struct: WasmStructInstruction instr = (WasmStructInstruction)instruction; diff --git a/src/de/inetsoftware/jwebassembly/module/TypeManager.java b/src/de/inetsoftware/jwebassembly/module/TypeManager.java index 8f11501..f04dcd2 100644 --- a/src/de/inetsoftware/jwebassembly/module/TypeManager.java +++ b/src/de/inetsoftware/jwebassembly/module/TypeManager.java @@ -42,6 +42,7 @@ import de.inetsoftware.jwebassembly.wasm.ValueType; */ public class TypeManager { + /** name of virtual function table, start with a point for an invalid Java identifier */ static final String VTABLE = ".vtable"; private Map map = new LinkedHashMap<>(); @@ -204,7 +205,7 @@ public class TypeManager { FunctionName func = methods.get( idx ); if( func.methodName.equals( funcName.methodName ) && func.signature.equals( funcName.signature ) ) { methods.set( idx, funcName ); // use the override method - functions.markAsNeeded( funcName ); // mark all overridden methods also as needed if the super method is used + functions.markAsNeeded( funcName, false ); // mark all overridden methods also as needed if the super method is used break; } } diff --git a/src/de/inetsoftware/jwebassembly/module/WasmCallIndirectInstruction.java b/src/de/inetsoftware/jwebassembly/module/WasmCallIndirectInstruction.java index 278d77a..7ba8d82 100644 --- a/src/de/inetsoftware/jwebassembly/module/WasmCallIndirectInstruction.java +++ b/src/de/inetsoftware/jwebassembly/module/WasmCallIndirectInstruction.java @@ -74,8 +74,8 @@ class WasmCallIndirectInstruction extends WasmCallInstruction { * {@inheritDoc} */ @Override - void markAsNeeded( FunctionManager functions ) { - super.markAsNeeded( functions ); + void markAsNeeded( FunctionManager functions, boolean isStatic ) { + super.markAsNeeded( functions, isStatic ); virtualFunctionIdx = functions.getFunctionIndex( getFunctionName() ); } diff --git a/src/de/inetsoftware/jwebassembly/module/WasmCallInstruction.java b/src/de/inetsoftware/jwebassembly/module/WasmCallInstruction.java index ccf6b8a..7566a46 100644 --- a/src/de/inetsoftware/jwebassembly/module/WasmCallInstruction.java +++ b/src/de/inetsoftware/jwebassembly/module/WasmCallInstruction.java @@ -75,9 +75,11 @@ class WasmCallInstruction extends WasmInstruction { * * @param functions * the function manager + * @param isStatic + * true, if the method is static */ - void markAsNeeded( @Nonnull FunctionManager functions ) { - name = functions.markAsNeeded( name ); + void markAsNeeded( @Nonnull FunctionManager functions, boolean isStatic ) { + name = functions.markAsNeeded( name, isStatic ); } /**