From 4e3c11553f39b7d82fa1218f5770d3a3b1e99a5c Mon Sep 17 00:00:00 2001 From: Volker Berlin Date: Sun, 8 Sep 2019 13:55:22 +0200 Subject: [PATCH] prepare the polyfill of struct/Object instruction --- .../JavaScriptSyntheticFunctionName.java | 74 +++++++++++++++++++ .../jwebassembly/module/ModuleGenerator.java | 4 +- .../module/SyntheticFunctionName.java | 8 +- .../jwebassembly/module/TypeManager.java | 2 +- .../jwebassembly/module/WasmCodeBuilder.java | 18 ++++- .../module/WasmStructInstruction.java | 40 +++++++++- .../module/WatCodeSyntheticFunctionName.java | 4 +- .../jwebassembly/text/TextModuleWriter.java | 33 ++++++--- .../jwebassembly/runtime/Structs.java | 8 +- 9 files changed, 164 insertions(+), 27 deletions(-) create mode 100644 src/de/inetsoftware/jwebassembly/javascript/JavaScriptSyntheticFunctionName.java diff --git a/src/de/inetsoftware/jwebassembly/javascript/JavaScriptSyntheticFunctionName.java b/src/de/inetsoftware/jwebassembly/javascript/JavaScriptSyntheticFunctionName.java new file mode 100644 index 0000000..1d68a1d --- /dev/null +++ b/src/de/inetsoftware/jwebassembly/javascript/JavaScriptSyntheticFunctionName.java @@ -0,0 +1,74 @@ +/* + Copyright 2019 Volker Berlin (i-net software) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +package de.inetsoftware.jwebassembly.javascript; + +import java.util.function.Function; +import java.util.function.Supplier; + +import de.inetsoftware.jwebassembly.module.SyntheticFunctionName; +import de.inetsoftware.jwebassembly.wasm.AnyType; + +/** + * Synthetic JavaScript import function. + * + * @author Volker Berlin + * + */ +public class JavaScriptSyntheticFunctionName extends SyntheticFunctionName { + + private final Supplier js; + + /** + * Create a synthetic function which based on imported, dynamic generated JavaScript. + * + * @param module + * the module name + * @param functionName + * the name of the function + * @param js + * the dynamic JavaScript as a lambda expression + * @param signature + * the types of the signature + */ + public JavaScriptSyntheticFunctionName( String module, String functionName, Supplier js, AnyType... signature ) { + super( module, functionName, signature ); + this.js = js; + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean hasWasmCode() { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + protected Function getAnnotation() { + return ( key ) -> { + switch( key ) { + case JavaScriptWriter.JAVA_SCRIPT_CONTENT: + return js.get(); + default: + } + return null; + }; + } +} diff --git a/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java b/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java index 37c4839..61e7996 100644 --- a/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java +++ b/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java @@ -94,8 +94,8 @@ public class ModuleGenerator { this.javaScript = new JavaScriptWriter( target ); this.libraries = new URLClassLoader( libraries.toArray( new URL[libraries.size()] ) ); types.init( properties ); - javaCodeBuilder.init( types, properties ); - ((WasmCodeBuilder)watParser).init( types, properties ); + javaCodeBuilder.init( types, functions, properties ); + ((WasmCodeBuilder)watParser).init( types, functions, properties ); scanLibraries( libraries ); } diff --git a/src/de/inetsoftware/jwebassembly/module/SyntheticFunctionName.java b/src/de/inetsoftware/jwebassembly/module/SyntheticFunctionName.java index 82c83d3..57609ab 100644 --- a/src/de/inetsoftware/jwebassembly/module/SyntheticFunctionName.java +++ b/src/de/inetsoftware/jwebassembly/module/SyntheticFunctionName.java @@ -28,7 +28,7 @@ import de.inetsoftware.jwebassembly.watparser.WatParser; * * @author Volker Berlin */ -abstract class SyntheticFunctionName extends FunctionName { +public abstract class SyntheticFunctionName extends FunctionName { private final AnyType[] signature; @@ -60,7 +60,7 @@ abstract class SyntheticFunctionName extends FunctionName { * * @return true, if WASM code */ - abstract boolean hasWasmCode(); + protected abstract boolean hasWasmCode(); /** * Get the WasmCodeBuilder for the synthetic WASM code. @@ -69,7 +69,7 @@ abstract class SyntheticFunctionName extends FunctionName { * a helping WatParser * @return the code */ - WasmCodeBuilder getCodeBuilder( WatParser watParser ) { + protected WasmCodeBuilder getCodeBuilder( WatParser watParser ) { return null; } @@ -78,7 +78,7 @@ abstract class SyntheticFunctionName extends FunctionName { * * @return the annotation */ - Function getAnnotation() { + protected Function getAnnotation() { return null; } } diff --git a/src/de/inetsoftware/jwebassembly/module/TypeManager.java b/src/de/inetsoftware/jwebassembly/module/TypeManager.java index fb7eb88..ebee427 100644 --- a/src/de/inetsoftware/jwebassembly/module/TypeManager.java +++ b/src/de/inetsoftware/jwebassembly/module/TypeManager.java @@ -206,7 +206,7 @@ public class TypeManager { methods = new ArrayList<>(); HashSet allNeededFields = new HashSet<>(); listStructFields( name, functions, types, libraries, allNeededFields ); - code = writer.writeStructType( this ); + code = types.useGC ? writer.writeStructType( this ) : ValueType.anyref.getCode(); } /** diff --git a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java index d4ed513..38d4a38 100644 --- a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java +++ b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java @@ -31,7 +31,6 @@ import de.inetsoftware.classparser.MethodInfo; import de.inetsoftware.jwebassembly.JWebAssembly; import de.inetsoftware.jwebassembly.WasmException; import de.inetsoftware.jwebassembly.javascript.NonGC; -import de.inetsoftware.jwebassembly.module.TypeManager.StructType; import de.inetsoftware.jwebassembly.module.WasmInstruction.Type; import de.inetsoftware.jwebassembly.wasm.AnyType; import de.inetsoftware.jwebassembly.wasm.ArrayOperator; @@ -55,6 +54,8 @@ public abstract class WasmCodeBuilder { private TypeManager types; + private FunctionManager functions; + private boolean useGC; /** @@ -84,12 +85,15 @@ public abstract class WasmCodeBuilder { * * @param types * the type manager + * @param functions + * the function manager * @param properties * compiler properties */ - void init( TypeManager types, HashMap properties ) { + void init( TypeManager types, FunctionManager functions, HashMap properties ) { this.localVariables.init( types ); this.types = types; + this.functions = functions; this.useGC = Boolean.parseBoolean( properties.getOrDefault( JWebAssembly.WASM_USE_GC, "false" ) ); } @@ -380,6 +384,14 @@ public abstract class WasmCodeBuilder { * the line number in the Java source code */ protected void addStructInstruction( StructOperator op, @Nonnull String typeName, @Nullable NamedStorageType fieldName, int javaCodePos, int lineNumber ) { - instructions.add( new WasmStructInstruction( op, typeName, fieldName, javaCodePos, lineNumber, types ) ); + WasmStructInstruction structInst = new WasmStructInstruction( op, typeName, fieldName, javaCodePos, lineNumber, types ); + instructions.add( structInst ); + if( !useGC ) { + SyntheticFunctionName name = structInst.createNonGcFunction(); + if( name != null ) { + functions.markAsNeeded( name, true ); + functions.markAsImport( name, name.getAnnotation() ); + } + } } } diff --git a/src/de/inetsoftware/jwebassembly/module/WasmStructInstruction.java b/src/de/inetsoftware/jwebassembly/module/WasmStructInstruction.java index 0c17cf6..aae030d 100644 --- a/src/de/inetsoftware/jwebassembly/module/WasmStructInstruction.java +++ b/src/de/inetsoftware/jwebassembly/module/WasmStructInstruction.java @@ -23,6 +23,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import de.inetsoftware.jwebassembly.WasmException; +import de.inetsoftware.jwebassembly.javascript.JavaScriptSyntheticFunctionName; import de.inetsoftware.jwebassembly.module.TypeManager.StructType; import de.inetsoftware.jwebassembly.wasm.AnyType; import de.inetsoftware.jwebassembly.wasm.NamedStorageType; @@ -43,6 +44,8 @@ class WasmStructInstruction extends WasmInstruction { private final NamedStorageType fieldName; + private SyntheticFunctionName functionName; + /** * Create an instance of numeric operation. * @@ -56,6 +59,8 @@ class WasmStructInstruction extends WasmInstruction { * the code position/offset in the Java method * @param lineNumber * the line number in the Java source code + * @param types + * the type manager */ WasmStructInstruction( @Nullable StructOperator op, @Nullable String typeName, @Nullable NamedStorageType fieldName, int javaCodePos, int lineNumber, TypeManager types ) { super( javaCodePos, lineNumber ); @@ -67,6 +72,34 @@ class WasmStructInstruction extends WasmInstruction { } } + /** + * Create the synthetic polyfill function of this instruction for nonGC mode. + * + * @return the function or null if not needed + */ + SyntheticFunctionName createNonGcFunction() { + switch( op ) { + case NEW: + case NEW_DEFAULT: + functionName = new JavaScriptSyntheticFunctionName( "NonGC", "new_" + getSciptTypeName(), () -> "{}", null, type ); + break; + case SET: + AnyType type = fieldName.getType(); + functionName = new JavaScriptSyntheticFunctionName( "NonGC", "set_" + getSciptTypeName(), () -> "(a,i,v) => a[i]=v", type, null, null ); + break; + case GET: + type = fieldName.getType(); + functionName = new JavaScriptSyntheticFunctionName( "NonGC", "get_" + getSciptTypeName(), () -> "(a,i) => a[i]", type, null, type ); + break; + default: + } + return functionName; + } + + private String getSciptTypeName() { + return type.getName().replace( '/', '_' ); + } + /** * Get the StructOperator * @@ -123,7 +156,12 @@ class WasmStructInstruction extends WasmInstruction { } } } - writer.writeStructOperator( op, type, fieldName, idx ); + if( functionName != null ) { // nonGC + //TODO idx + writer.writeFunctionCall( functionName ); + } else { + writer.writeStructOperator( op, type, fieldName, idx ); + } } /** diff --git a/src/de/inetsoftware/jwebassembly/module/WatCodeSyntheticFunctionName.java b/src/de/inetsoftware/jwebassembly/module/WatCodeSyntheticFunctionName.java index d102ed8..c2e58e4 100644 --- a/src/de/inetsoftware/jwebassembly/module/WatCodeSyntheticFunctionName.java +++ b/src/de/inetsoftware/jwebassembly/module/WatCodeSyntheticFunctionName.java @@ -47,7 +47,7 @@ class WatCodeSyntheticFunctionName extends SyntheticFunctionName { * {@inheritDoc} */ @Override - boolean hasWasmCode() { + protected boolean hasWasmCode() { return true; } @@ -55,7 +55,7 @@ class WatCodeSyntheticFunctionName extends SyntheticFunctionName { * {@inheritDoc} */ @Override - public WasmCodeBuilder getCodeBuilder( WatParser watParser ) { + protected WasmCodeBuilder getCodeBuilder( WatParser watParser ) { watParser.parse( code, -1 ); return watParser; } diff --git a/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java b/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java index 52f30a4..29f24c3 100644 --- a/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java +++ b/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java @@ -22,6 +22,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import javax.annotation.Nonnull; @@ -79,12 +80,14 @@ public class TextModuleWriter extends ModuleWriter { private boolean isPrepared; - private HashSet globals = new HashSet<>(); + private HashMap globals = new HashMap<>(); private boolean useExceptions; private boolean callIndirect; + private boolean useGC; + /** * Create a new instance. * @@ -100,7 +103,8 @@ public class TextModuleWriter extends ModuleWriter { debugNames = Boolean.parseBoolean( properties.get( JWebAssembly.DEBUG_NAMES ) ); output.append( "(module" ); inset++; - if( spiderMonkey ) { + useGC = Boolean.parseBoolean( properties.getOrDefault( JWebAssembly.WASM_USE_GC, "false" ) ); + if( spiderMonkey && useGC ) { output.append( " (gc_feature_opt_in 3)" ); // enable GcFeatureOptIn for SpiderMonkey https://github.com/lars-t-hansen/moz-gc-experiments/blob/master/version2.md } } @@ -117,6 +121,15 @@ public class TextModuleWriter extends ModuleWriter { output.append( imports ); + for( Entry entry : globals.entrySet() ) { + output.append( "\n " ); + output.append( "(global $" ).append( entry.getKey() ).append( " (mut " ); + writeTypeName( output, entry.getValue() ); + output.append( ')' ); + writeDefaultValue( output, entry.getValue() ); + output.append( ')' ); + } + for( Function func : functions.values() ) { output.append( func.output ); } @@ -285,10 +298,12 @@ public class TextModuleWriter extends ModuleWriter { * if any I/O error occur */ private void writeTypeName( Appendable output, AnyType type ) throws IOException { - if( type.getCode() < 0 ) { + if( type instanceof ValueType ) { output.append( type.toString() ); - } else { + } else if( useGC ) { output.append( "(ref " ).append( normalizeName( type.toString() ) ).append( ')' ); + } else { + output.append( ValueType.anyref.toString() ); } } @@ -445,15 +460,9 @@ public class TextModuleWriter extends ModuleWriter { @Override protected void writeGlobalAccess( boolean load, FunctionName name, AnyType type ) throws IOException { String fullName = normalizeName( name.fullName ); - if( !globals.contains( fullName ) ) { + if( !globals.containsKey( fullName ) ) { // declare global variable if not already declared. - output.append( "\n " ); - output.append( "(global $" ).append( fullName ).append( " (mut " ); - writeTypeName( output, type ); - output.append( ')' ); - writeDefaultValue( output, type ); - output.append( ')' ); - globals.add( fullName ); + globals.put( fullName, type ); } newline( methodOutput ); methodOutput.append( load ? "global.get $" : "global.set $" ).append( fullName ); diff --git a/test/de/inetsoftware/jwebassembly/runtime/Structs.java b/test/de/inetsoftware/jwebassembly/runtime/Structs.java index 286d76c..cdf7e7f 100644 --- a/test/de/inetsoftware/jwebassembly/runtime/Structs.java +++ b/test/de/inetsoftware/jwebassembly/runtime/Structs.java @@ -18,6 +18,7 @@ package de.inetsoftware.jwebassembly.runtime; import static org.junit.Assume.assumeTrue; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import org.junit.ClassRule; @@ -40,7 +41,10 @@ public class Structs extends AbstractBaseTest { @Parameters( name = "{0}-{1}" ) public static Collection data() { ArrayList list = new ArrayList<>(); - for( ScriptEngine script : ScriptEngine.testEngines() ) { + ScriptEngine[] engines = ScriptEngine.testEngines(); + engines = Arrays.copyOf( engines, engines.length + 1 ); + engines[engines.length - 1] = ScriptEngine.SpiderMonkeyGC; + for( ScriptEngine script : engines ) { addParam( list, script, "isNull" ); addParam( list, script, "isNotNull" ); addParam( list, script, "isSame" ); @@ -57,7 +61,7 @@ public class Structs extends AbstractBaseTest { @Test public void test() { - assumeTrue( getScriptEngine() == ScriptEngine.SpiderMonkey || getScriptEngine() == ScriptEngine.SpiderMonkeyWat ); + assumeTrue( getScriptEngine() == ScriptEngine.SpiderMonkeyGC ); super.test(); }