import only the needed functions to reduce the needed JS glue code

This commit is contained in:
Volker Berlin 2019-06-23 20:50:11 +02:00
parent b90d7e1dea
commit 6bcda0daa5
5 changed files with 125 additions and 27 deletions

View File

@ -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<String,Object> 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<FunctionName> 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<String, Object> 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<FunctionName> 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<String, Object> importAnannotation;
private int functionIdx = -1;
private boolean isStatic;
}
private static enum State {

View File

@ -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<FunctionName> iterator = functions.getNeededImports(); iterator.hasNext(); ) {
FunctionName name = iterator.next();
functions.markAsWritten( name );
Map<String, Object> 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<FunctionName> 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<String,Object> 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;

View File

@ -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<String, StructType> 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;
}
}

View File

@ -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() );
}

View File

@ -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 );
}
/**