prepare the polyfill of struct/Object instruction

This commit is contained in:
Volker Berlin 2019-09-08 13:55:22 +02:00
parent c029ef2a88
commit 4e3c11553f
9 changed files with 164 additions and 27 deletions

View File

@ -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<String> 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<String> js, AnyType... signature ) {
super( module, functionName, signature );
this.js = js;
}
/**
* {@inheritDoc}
*/
@Override
protected boolean hasWasmCode() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
protected Function<String, Object> getAnnotation() {
return ( key ) -> {
switch( key ) {
case JavaScriptWriter.JAVA_SCRIPT_CONTENT:
return js.get();
default:
}
return null;
};
}
}

View File

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

View File

@ -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<String, Object> getAnnotation() {
protected Function<String, Object> getAnnotation() {
return null;
}
}

View File

@ -206,7 +206,7 @@ public class TypeManager {
methods = new ArrayList<>();
HashSet<String> allNeededFields = new HashSet<>();
listStructFields( name, functions, types, libraries, allNeededFields );
code = writer.writeStructType( this );
code = types.useGC ? writer.writeStructType( this ) : ValueType.anyref.getCode();
}
/**

View File

@ -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<String, String> properties ) {
void init( TypeManager types, FunctionManager functions, HashMap<String, String> 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() );
}
}
}
}

View File

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

View File

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

View File

@ -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<String> globals = new HashSet<>();
private HashMap<String,AnyType> 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<String, AnyType> 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 );

View File

@ -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<Object[]> data() {
ArrayList<Object[]> 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();
}