Add ArrayType and improve the Java type parser.

This commit is contained in:
Volker Berlin 2019-08-14 20:07:39 +02:00
parent dd9f03df99
commit 0b28099674
15 changed files with 173 additions and 118 deletions

View File

@ -147,10 +147,12 @@ public class FunctionName {
/**
* Get the method signature iterator for parameter and return values.
*
* @param types
* the type manager
* @return the iterator
*/
@Nonnull
public Iterator<AnyType> getSignature() {
return new ValueTypeParser( signature );
public Iterator<AnyType> getSignature( TypeManager types ) {
return new ValueTypeParser( signature, types );
}
}

View File

@ -16,7 +16,6 @@
package de.inetsoftware.jwebassembly.module;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import javax.annotation.Nonnull;
@ -45,16 +44,6 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
private BranchManger branchManager = new BranchManger( getInstructions() );
/**
* Create a new code builder.
*
* @param properties
* compiler properties
*/
public JavaMethodWasmCodeBuilder( HashMap<String, String> properties ) {
super( properties );
}
/**
* Build the wasm instructions
*
@ -104,7 +93,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
case 0: // nop
break;
case 1: // aconst_null
addStructInstruction( StructOperator.NULL, (StructType)null, null, codePos, lineNumber );
addStructInstruction( StructOperator.NULL, null, null, codePos, lineNumber );
break;
case 2: // iconst_m1
case 3: // iconst_0

View File

@ -19,6 +19,7 @@ package de.inetsoftware.jwebassembly.module;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -29,6 +30,7 @@ import de.inetsoftware.classparser.LocalVariableTable;
import de.inetsoftware.jwebassembly.WasmException;
import de.inetsoftware.jwebassembly.wasm.AnyType;
import de.inetsoftware.jwebassembly.wasm.ValueType;
import de.inetsoftware.jwebassembly.wasm.ValueTypeParser;
/**
* This manager monitor the locale variables of a method to create a translation from the slot based index in Java to
@ -40,6 +42,8 @@ import de.inetsoftware.jwebassembly.wasm.ValueType;
*/
class LocaleVariableManager {
private TypeManager types;
private Variable[] variables;
private int size;
@ -59,6 +63,16 @@ class LocaleVariableManager {
}
}
/**
* Initialize the variable manager;
*
* @param types
* the type manager
*/
void init( TypeManager types ) {
this.types = types;
}
/**
* Reset the manager to an initial state
*
@ -85,7 +99,7 @@ class LocaleVariableManager {
for( int i = 0; i < vars.length; i++ ) {
LocalVariable local = vars[i];
Variable var = variables[size];
var.valueType = ValueType.getValueType( local.getSignature() );
var.valueType = new ValueTypeParser( local.getSignature(), types ).next();
var.name = local.getName();
var.idx = local.getIndex();
var.startPos = local.getStartPosition() - 2;
@ -199,8 +213,10 @@ class LocaleVariableManager {
if( valueType.getCode() >= 0 && var.valueType == ValueType.anyref ) {
// set the more specific type
} else {
throw new WasmException( "Redefine local variable '" + var.name + "' type from " + var.valueType + " to " + valueType + " in slot "
+ slot + ". Compile the Java code with debug information to correct this problem.", null, null, -1 );
return;// TODO we need a better check
// throw new WasmException( "Redefine local variable '" + var.name + "' type from " + var.valueType + " to " + valueType + " in slot "
// + slot + ". Compile the Java code with debug information to correct this problem.", null, null, -1 );
}
}
var.valueType = valueType;

View File

@ -87,13 +87,13 @@ public class ModuleGenerator {
* compiler properties
*/
public ModuleGenerator( @Nonnull ModuleWriter writer, WasmTarget target, @Nonnull List<URL> libraries, HashMap<String, String> properties ) {
this.javaCodeBuilder = new JavaMethodWasmCodeBuilder( properties );
this.watParser = new WatParser( properties );
this.javaCodeBuilder = new JavaMethodWasmCodeBuilder();
this.watParser = new WatParser();
this.writer = writer;
this.javaScript = new JavaScriptWriter( target );
this.libraries = new URLClassLoader( libraries.toArray( new URL[libraries.size()] ) );
javaCodeBuilder.init( types );
((WasmCodeBuilder)watParser).init( types );
javaCodeBuilder.init( types, properties );
((WasmCodeBuilder)watParser).init( types, properties );
scanLibraries( libraries );
}
@ -560,7 +560,7 @@ public class ModuleGenerator {
StructType instanceType = types.valueOf( name.className );
writer.writeMethodParam( "param", instanceType, "this" );
}
Iterator<AnyType> parser = name.getSignature();
Iterator<AnyType> parser = name.getSignature( types );
AnyType type;
for( String kind : new String[] {"param","result"}) {
while( parser.hasNext() && (type = parser.next()) != null ) {

View File

@ -48,7 +48,7 @@ abstract class SyntheticFunctionName extends FunctionName {
* {@inheritDoc}
*/
@Override
public Iterator<AnyType> getSignature() {
public Iterator<AnyType> getSignature( TypeManager types ) {
return Arrays.asList( signature ).iterator();
}

View File

@ -19,6 +19,7 @@ package de.inetsoftware.jwebassembly.module;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@ -32,6 +33,7 @@ import de.inetsoftware.classparser.MethodInfo;
import de.inetsoftware.jwebassembly.JWebAssembly;
import de.inetsoftware.jwebassembly.WasmException;
import de.inetsoftware.jwebassembly.wasm.AnyType;
import de.inetsoftware.jwebassembly.wasm.ArrayType;
import de.inetsoftware.jwebassembly.wasm.NamedStorageType;
import de.inetsoftware.jwebassembly.wasm.ValueType;
@ -45,7 +47,9 @@ 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<>();
private Map<String, StructType> structTypes = new LinkedHashMap<>();
private Map<AnyType, ArrayType> arrayTypes = new LinkedHashMap<>();
private boolean isFinish;
@ -63,7 +67,7 @@ public class TypeManager {
*/
void prepareFinish( ModuleWriter writer, FunctionManager functions, ClassLoader libraries ) throws IOException {
isFinish = true;
for( StructType type : map.values() ) {
for( StructType type : structTypes.values() ) {
type.writeStructType( writer, functions, this, libraries );
}
}
@ -90,7 +94,7 @@ public class TypeManager {
*/
@Nonnull
Collection<StructType> getTypes() {
return map.values();
return structTypes.values();
}
/**
@ -101,14 +105,34 @@ public class TypeManager {
* @return the struct type
*/
public StructType valueOf( String name ) {
StructType type = map.get( name );
StructType type = structTypes.get( name );
if( type == null ) {
JWebAssembly.LOGGER.fine( "\t\ttype: " + name );
if( isFinish ) {
throw new WasmException( "Register needed type after scanning: " + name, -1 );
}
type = new StructType( name );
map.put( name, type );
structTypes.put( name, type );
}
return type;
}
/**
* Get the array type for the given component type.
*
* @param arrayType
* the component type of the array
* @return the array type
*/
public ArrayType arrayType( AnyType arrayType ) {
ArrayType type = arrayTypes.get( arrayType );
if( type == null ) {
JWebAssembly.LOGGER.fine( "\t\ttype: " + arrayType );
if( isFinish ) {
throw new WasmException( "Register needed array type after scanning: " + arrayType, -1 );
}
type = new ArrayType( arrayType );
arrayTypes.put( arrayType, type );
}
return type;
}
@ -124,6 +148,8 @@ public class TypeManager {
private int code = Integer.MAX_VALUE;
private HashSet<String> neededFields = new HashSet<>();
private List<NamedStorageType> fields;
private List<FunctionName> methods;
@ -143,6 +169,10 @@ public class TypeManager {
this.name = name;
}
void useFieldName( NamedStorageType fieldName ) {
neededFields.add( fieldName.getName());
}
/**
* Write this struct type and initialize internal structures
*
@ -161,7 +191,8 @@ public class TypeManager {
JWebAssembly.LOGGER.fine( "write type: " + name );
fields = new ArrayList<>();
methods = new ArrayList<>();
listStructFields( name, functions, types, libraries );
HashSet<String> allNeededFields = new HashSet<>();
listStructFields( name, functions, types, libraries, allNeededFields );
code = writer.writeStructType( this );
}
@ -179,16 +210,18 @@ public class TypeManager {
* @throws IOException
* if any I/O error occur on loading or writing
*/
private void listStructFields( String className, FunctionManager functions, TypeManager types, ClassLoader libraries ) throws IOException {
private void listStructFields( String className, FunctionManager functions, TypeManager types, ClassLoader libraries, HashSet<String> allNeededFields ) throws IOException {
ClassFile classFile = ClassFile.get( className, libraries );
if( classFile == null ) {
throw new WasmException( "Missing class: " + className, -1 );
}
allNeededFields.addAll( neededFields );
ConstantClass superClass = classFile.getSuperClass();
if( superClass != null ) {
String superClassName = superClass.getName();
listStructFields( superClassName, functions, types, libraries );
listStructFields( superClassName, functions, types, libraries, allNeededFields );
} else {
fields.add( new NamedStorageType( ValueType.i32, className, VTABLE ) );
}
@ -197,6 +230,9 @@ public class TypeManager {
if( field.isStatic() ) {
continue;
}
if( !allNeededFields.contains( field.getName() ) ) {
continue;
}
fields.add( new NamedStorageType( className, field, types ) );
}

View File

@ -34,6 +34,9 @@ class WasmCallIndirectInstruction extends WasmCallInstruction {
private final StructType type;
/**
* the slot of a temporary variable of type "type" to duplicate "this"
*/
private final int tempVar;
private final LocaleVariableManager localVariables;
@ -43,10 +46,6 @@ class WasmCallIndirectInstruction extends WasmCallInstruction {
*
* @param name
* the function name that should be called
* @param type
* the type with the virtual method/function
* @param tempVar
* the slot of a temporary variable of type "type" to duplicate "this"
* @param localVariables
* the manager for local variables to translate the Java slot of the temporary variable into wasm local
* position
@ -54,11 +53,13 @@ class WasmCallIndirectInstruction extends WasmCallInstruction {
* the code position/offset in the Java method
* @param lineNumber
* the line number in the Java source code
* @param types
* the type manager
*/
WasmCallIndirectInstruction( FunctionName name, @Nonnull StructType type, int tempVar, LocaleVariableManager localVariables, int javaCodePos, int lineNumber ) {
super( name, javaCodePos, lineNumber );
this.type = type;
this.tempVar = tempVar;
WasmCallIndirectInstruction( FunctionName name, LocaleVariableManager localVariables, int javaCodePos, int lineNumber, TypeManager types ) {
super( name, javaCodePos, lineNumber, types );
this.type = types.valueOf( name.className );
this.tempVar = localVariables.getTempVariable( type, javaCodePos, javaCodePos + 1 );
this.localVariables = localVariables;
}

View File

@ -37,6 +37,8 @@ class WasmCallInstruction extends WasmInstruction {
private int paramCount = -1;
private TypeManager types;
/**
* Create an instance of a function call instruction
*
@ -46,10 +48,13 @@ class WasmCallInstruction 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
*/
WasmCallInstruction( FunctionName name, int javaCodePos, int lineNumber ) {
WasmCallInstruction( FunctionName name, int javaCodePos, int lineNumber, TypeManager types ) {
super( javaCodePos, lineNumber );
this.name = name;
this.types = types;
}
/**
@ -113,7 +118,7 @@ class WasmCallInstruction extends WasmInstruction {
if( paramCount >= 0 ) {
return;
}
Iterator<AnyType> parser = name.getSignature();
Iterator<AnyType> parser = name.getSignature( types );
paramCount = 0;
while( parser.next() != null ) {
paramCount++;

View File

@ -55,17 +55,7 @@ public abstract class WasmCodeBuilder {
private TypeManager types;
private final boolean useGC;
/**
* Create a new code builder.
*
* @param properties
* compiler properties
*/
public WasmCodeBuilder( HashMap<String, String> properties ) {
useGC = Boolean.parseBoolean( properties.getOrDefault( JWebAssembly.WASM_USE_GC, "false" ) );
}
private boolean useGC;
/**
* Get the list of instructions
@ -94,9 +84,13 @@ public abstract class WasmCodeBuilder {
*
* @param types
* the type manager
* @param properties
* compiler properties
*/
void init( TypeManager types ) {
void init( TypeManager types, HashMap<String, String> properties ) {
this.localVariables.init( types );
this.types = types;
this.useGC = Boolean.parseBoolean( properties.getOrDefault( JWebAssembly.WASM_USE_GC, "false" ) );
}
/**
@ -289,7 +283,7 @@ public abstract class WasmCodeBuilder {
* the line number in the Java source code
*/
protected void addCallInstruction( FunctionName name, int javaCodePos, int lineNumber ) {
instructions.add( new WasmCallInstruction( name, javaCodePos, lineNumber ) );
instructions.add( new WasmCallInstruction( name, javaCodePos, lineNumber, types ) );
}
/**
@ -303,9 +297,7 @@ public abstract class WasmCodeBuilder {
* the line number in the Java source code
*/
protected void addCallVirtualInstruction( FunctionName name, int javaCodePos, int lineNumber ) {
StructType type = types.valueOf( name.className );
int tempVar = localVariables.getTempVariable( type, javaCodePos, javaCodePos + 1 );
instructions.add( new WasmCallIndirectInstruction( name, type, tempVar, localVariables, javaCodePos, lineNumber ) );
instructions.add( new WasmCallIndirectInstruction( name, localVariables, javaCodePos, lineNumber, types ) );
}
/**
@ -385,24 +377,6 @@ 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 ) {
addStructInstruction( op, types.valueOf( typeName ), fieldName, javaCodePos, lineNumber );
}
/**
* Add an array operation to the instruction list as marker on the code position.
*
* @param op
* the operation
* @param structType
* the type
* @param fieldName
* the name of field if needed for the operation
* @param javaCodePos
* the code position/offset in the Java method
* @param lineNumber
* the line number in the Java source code
*/
protected void addStructInstruction( StructOperator op, @Nullable StructType structType, @Nullable NamedStorageType fieldName, int javaCodePos, int lineNumber ) {
instructions.add( new WasmStructInstruction( op, structType, fieldName, javaCodePos, lineNumber ) );
instructions.add( new WasmStructInstruction( op, typeName, fieldName, javaCodePos, lineNumber, types ) );
}
}

View File

@ -48,8 +48,8 @@ class WasmStructInstruction extends WasmInstruction {
*
* @param op
* the struct operation
* @param type
* the type of the parameters
* @param typeName
* the type name of the parameters
* @param fieldName
* the name of field if needed for the operation
* @param javaCodePos
@ -57,11 +57,14 @@ class WasmStructInstruction extends WasmInstruction {
* @param lineNumber
* the line number in the Java source code
*/
WasmStructInstruction( @Nullable StructOperator op, @Nullable StructType type, @Nullable NamedStorageType fieldName, int javaCodePos, int lineNumber ) {
WasmStructInstruction( @Nullable StructOperator op, @Nullable String typeName, @Nullable NamedStorageType fieldName, int javaCodePos, int lineNumber, TypeManager types ) {
super( javaCodePos, lineNumber );
this.op = op;
this.type = type;
this.type = typeName == null ? null : types.valueOf( typeName );
this.fieldName = fieldName;
if( type != null && fieldName != null ) {
type.useFieldName( fieldName );
}
}
/**
@ -98,7 +101,7 @@ class WasmStructInstruction extends WasmInstruction {
if( type != null && fieldName != null ) {
// The fieldName of the struct operation does not contain the class name in which the field was declared. It contains the class name of the variable. This can be the class or a subclass.
List<NamedStorageType> fields = type.getFields();
boolean classNameMatched = false;
boolean classNameMatched = type.getName().equals( fieldName.geClassName() );
for( int i = fields.size()-1; i >= 0; i-- ) {
NamedStorageType field = fields.get( i );
if( !classNameMatched && field.geClassName().equals( fieldName.geClassName() ) ) {
@ -109,6 +112,16 @@ class WasmStructInstruction extends WasmInstruction {
break;
}
}
if( !classNameMatched ) {
// special case, the type self does not add a needed field, that we search in all fields
for( int i = fields.size()-1; i >= 0; i-- ) {
NamedStorageType field = fields.get( i );
if( field.getName().equals( fieldName.getName() ) ) {
idx = i;
break;
}
}
}
}
writer.writeStructOperator( op, type, fieldName, idx );
}

View File

@ -0,0 +1,53 @@
/*
* 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.wasm;
/**
* A reference to an array type
*
* @author Volker Berlin
*
*/
public class ArrayType implements AnyType {
private AnyType arrayType;
/**
* Create a new array type
* @param arrayType the type of the array
*/
public ArrayType( AnyType arrayType ) {
this.arrayType = arrayType;
}
/**
* {@inheritDoc}
*/
@Override
public int getCode() {
// until there is a real type definition we will define write it as anyref
return ValueType.anyref.getCode();
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
// until there is a real type definition we will define write it as anyref
return ValueType.anyref.toString();
}
}

View File

@ -55,16 +55,4 @@ public enum ValueType implements AnyType {
public int getCode() {
return code;
}
/**
* Get the WebAssembly value type from a Java signature.
*
* @param javaSignature
* the signature
* @return the value type
*/
public static ValueType getValueType( String javaSignature ) {
return (ValueType)new ValueTypeParser( javaSignature ).next();
}
}

View File

@ -33,16 +33,6 @@ public class ValueTypeParser implements Iterator<AnyType> {
private TypeManager types;
/**
* Create a new parser.
*
* @param javaSignature
* the Java signature
*/
public ValueTypeParser( String javaSignature ) {
this( javaSignature, null );
}
/**
* Create a new parser.
*
@ -79,13 +69,12 @@ public class ValueTypeParser implements Iterator<AnyType> {
case ')':
return null;
case '[': // array
next();
return ValueType.anyref;
return types.arrayType( next() );
case 'L':
int idx2 = sig.indexOf( ';', idx );
String name = sig.substring( idx, idx2 );
idx = idx2 + 1;
return types == null ? ValueType.anyref : types.valueOf( name );
return "java/lang/Object".equals( name ) ? ValueType.anyref : types.valueOf( name );
case 'Z': // boolean
case 'B': // byte
case 'C': // char

View File

@ -17,7 +17,6 @@
package de.inetsoftware.jwebassembly.watparser;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.annotation.Nonnegative;
@ -38,16 +37,6 @@ import de.inetsoftware.jwebassembly.wasm.WasmBlockOperator;
*/
public class WatParser extends WasmCodeBuilder {
/**
* Create a new code builder.
*
* @param properties
* compiler properties
*/
public WatParser( HashMap<String, String> properties ) {
super( properties );
}
/**
* Parse the given wasm text format and generate a list of WasmInstuctions
*

View File

@ -36,7 +36,7 @@ import de.inetsoftware.jwebassembly.watparser.WatParser;
public class WatParserTest {
private void test( String wat ) throws IOException {
WatParser parser = new WatParser( new HashMap<>() );
WatParser parser = new WatParser();
parser.parse( wat, 100 );
WasmCodeBuilder codeBuilder = parser;
StringBuilder builder = new StringBuilder();