Add support for complex block types

This commit is contained in:
Volker Berlin 2021-02-27 20:40:30 +01:00
parent 27199c53db
commit cf7e66901d
7 changed files with 235 additions and 90 deletions

View File

@ -32,6 +32,7 @@ import javax.annotation.Nullable;
import de.inetsoftware.jwebassembly.WasmException; import de.inetsoftware.jwebassembly.WasmException;
import de.inetsoftware.jwebassembly.module.FunctionName; import de.inetsoftware.jwebassembly.module.FunctionName;
import de.inetsoftware.jwebassembly.module.ModuleWriter; import de.inetsoftware.jwebassembly.module.ModuleWriter;
import de.inetsoftware.jwebassembly.module.TypeManager.BlockType;
import de.inetsoftware.jwebassembly.module.TypeManager.StructType; import de.inetsoftware.jwebassembly.module.TypeManager.StructType;
import de.inetsoftware.jwebassembly.module.TypeManager.StructTypeKind; import de.inetsoftware.jwebassembly.module.TypeManager.StructTypeKind;
import de.inetsoftware.jwebassembly.module.ValueTypeConvertion; import de.inetsoftware.jwebassembly.module.ValueTypeConvertion;
@ -525,6 +526,18 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
return typeId; return typeId;
} }
/**
* {@inheritDoc}
*/
@Override
protected int writeBlockType( BlockType type ) throws IOException {
FunctionTypeEntry entry = new FunctionTypeEntry();
entry.params.addAll( type.getParams() );
entry.results.addAll( type.getResults() );
functionTypes.add( entry );
return functionTypes.size() - 1;
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@ -532,19 +545,13 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
protected void writeException() throws IOException { protected void writeException() throws IOException {
if( exceptionSignatureIndex <= 0 ) { if( exceptionSignatureIndex <= 0 ) {
FunctionTypeEntry type = new FunctionTypeEntry(); FunctionTypeEntry type = new FunctionTypeEntry();
type.params.add( ValueType.externref ); AnyType eventType = options.useGC() ? options.types.valueOf( "java/lang/Throwable" ) : ValueType.externref;
type.params.add( eventType );
exceptionSignatureIndex = functionTypes.indexOf( type ); exceptionSignatureIndex = functionTypes.indexOf( type );
if( exceptionSignatureIndex < 0 ) { if( exceptionSignatureIndex < 0 ) {
exceptionSignatureIndex = functionTypes.size(); exceptionSignatureIndex = functionTypes.size();
functionTypes.add( type ); functionTypes.add( type );
} }
// result type of catch block for unboxing
type = new FunctionTypeEntry();
type.params.add( ValueType.exnref );
type.results.add( ValueType.externref );
options.setCatchType( functionTypes.size() );
functionTypes.add( type );
} }
} }

View File

@ -25,11 +25,14 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import de.inetsoftware.classparser.Code; import de.inetsoftware.classparser.Code;
import de.inetsoftware.classparser.CodeInputStream; import de.inetsoftware.classparser.CodeInputStream;
import de.inetsoftware.classparser.ConstantClass;
import de.inetsoftware.classparser.TryCatchFinally; import de.inetsoftware.classparser.TryCatchFinally;
import de.inetsoftware.jwebassembly.WasmException; import de.inetsoftware.jwebassembly.WasmException;
import de.inetsoftware.jwebassembly.module.TypeManager.BlockType;
import de.inetsoftware.jwebassembly.module.TypeManager.StructType; import de.inetsoftware.jwebassembly.module.TypeManager.StructType;
import de.inetsoftware.jwebassembly.module.WasmInstruction.Type; import de.inetsoftware.jwebassembly.module.WasmInstruction.Type;
import de.inetsoftware.jwebassembly.wasm.AnyType; import de.inetsoftware.jwebassembly.wasm.AnyType;
@ -1015,10 +1018,10 @@ class BranchManger {
// occur with a RETURN in a finally block // occur with a RETURN in a finally block
// We does not need to unbox if the value will be drop // We does not need to unbox if the value will be drop
} else { } else {
addUnboxExnref( catchNode ); addUnboxExnref( catchNode, tryCatch );
} }
} else { } else {
addUnboxExnref( catchNode ); addUnboxExnref( catchNode, tryCatch );
// add a "if $exception instanceof type" check to the WASM code // add a "if $exception instanceof type" check to the WASM code
int instrPos = findIdxOfCodePos( catchPos ) + 1; int instrPos = findIdxOfCodePos( catchPos ) + 1;
@ -1083,8 +1086,10 @@ class BranchManger {
* *
* @param catchNode * @param catchNode
* the catch node * the catch node
* @param tryCatch
* the catch or finally block
*/ */
private void addUnboxExnref( BranchNode catchNode ) { private void addUnboxExnref( BranchNode catchNode, TryCatchFinally tryCatch ) {
// unboxing the exnref on the stack to a reference of the exception // unboxing the exnref on the stack to a reference of the exception
int catchPos = catchNode.startPos; int catchPos = catchNode.startPos;
if( !options.useEH() ) { if( !options.useEH() ) {
@ -1092,7 +1097,9 @@ class BranchManger {
catchNode.add( 0, unBoxing ); catchNode.add( 0, unBoxing );
return; return;
} }
BranchNode unBoxing = new BranchNode( catchPos, catchPos, WasmBlockOperator.BLOCK, WasmBlockOperator.END, options.getCatchType() ); AnyType excepType = getCatchType( tryCatch );
BlockType blockType = options.types.blockType( Arrays.asList( ValueType.exnref ), Arrays.asList( excepType ) );
BranchNode unBoxing = new BranchNode( catchPos, catchPos, WasmBlockOperator.BLOCK, WasmBlockOperator.END, blockType );
catchNode.add( 0, unBoxing ); catchNode.add( 0, unBoxing );
//TODO localVariables.getTempVariable( ValueType.exnref, catchPos, endPos ); https://github.com/WebAssembly/wabt/issues/1388 //TODO localVariables.getTempVariable( ValueType.exnref, catchPos, endPos ); https://github.com/WebAssembly/wabt/issues/1388
@ -1100,20 +1107,30 @@ class BranchManger {
unBoxing.add( new BranchNode( catchPos, catchPos, WasmBlockOperator.RETHROW, null ) ); unBoxing.add( new BranchNode( catchPos, catchPos, WasmBlockOperator.RETHROW, null ) );
} }
private AnyType getCatchType( TryCatchFinally tryCatch ) {
if( options.useGC() ) {
ConstantClass excepClass = tryCatch.getType();
String excepName = excepClass != null ? excepClass.getName() : "java/lang/Throwable";
return options.types.valueOf( excepName );
}
return ValueType.externref;
}
/** /**
* Check if there are a start of a catch block on the code position. * Get the catch type if there are a start of a catch block on the code position.
* *
* @param codePosition * @param codePosition
* the code position * the code position
* @return true, if there is a catch block * @return the type or null
*/ */
boolean isCatch( int codePosition ) { @Nullable
AnyType getCatchType( int codePosition ) {
for( TryCatchFinally tryCatch : exceptionTable ) { for( TryCatchFinally tryCatch : exceptionTable ) {
if( tryCatch.getHandler() == codePosition ) { if( tryCatch.getHandler() == codePosition ) {
return true; return getCatchType( tryCatch );
} }
} }
return false; return null;
} }
/** /**

View File

@ -234,9 +234,10 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
addLoadStoreInstruction( ValueType.f64, false, byteCode.readUnsignedIndex( wide ), codePos, lineNumber ); addLoadStoreInstruction( ValueType.f64, false, byteCode.readUnsignedIndex( wide ), codePos, lineNumber );
break; break;
case 58: // astore case 58: // astore
if( branchManager.isCatch( codePos ) ) { storeType = branchManager.getCatchType( codePos );
addJumpPlaceholder( codePos, 0, ValueType.externref, codePos, lineNumber ); if( storeType != null ) {
storeType = ValueType.externref; // for the catch there are no previous instructions // for the catch there are no previous instructions
addJumpPlaceholder( codePos, 0, storeType, codePos, lineNumber );
} else { } else {
storeType = findValueTypeFromStack( 1, codePos ); storeType = findValueTypeFromStack( 1, codePos );
} }
@ -270,9 +271,10 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
case 76: // astore_1 case 76: // astore_1
case 77: // astore_2 case 77: // astore_2
case 78: // astore_3 case 78: // astore_3
if( branchManager.isCatch( codePos ) ) { storeType = branchManager.getCatchType( codePos );
addJumpPlaceholder( codePos, 0, ValueType.externref, codePos, lineNumber ); if( storeType != null ) {
storeType = ValueType.externref; // for the catch there are no previous instructions // for the catch there are no previous instructions
addJumpPlaceholder( codePos, 0, storeType, codePos, lineNumber );
} else { } else {
storeType = findValueTypeFromStack( 1, codePos ); storeType = findValueTypeFromStack( 1, codePos );
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2017 - 2020 Volker Berlin (i-net software) * Copyright 2017 - 2021 Volker Berlin (i-net software)
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,6 +23,7 @@ import javax.annotation.Nonnegative;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import de.inetsoftware.jwebassembly.module.TypeManager.BlockType;
import de.inetsoftware.jwebassembly.module.TypeManager.StructType; import de.inetsoftware.jwebassembly.module.TypeManager.StructType;
import de.inetsoftware.jwebassembly.wasm.AnyType; import de.inetsoftware.jwebassembly.wasm.AnyType;
import de.inetsoftware.jwebassembly.wasm.ArrayOperator; import de.inetsoftware.jwebassembly.wasm.ArrayOperator;
@ -78,7 +79,18 @@ public abstract class ModuleWriter implements Closeable {
* @throws IOException * @throws IOException
* if any I/O error occur * if any I/O error occur
*/ */
protected abstract int writeStructType( StructType type ) throws IOException; protected abstract int writeStructType( @Nonnull StructType type ) throws IOException;
/**
* Write a block type.
*
* @param type
* the type
* @return type ID
* @throws IOException
* if any I/O error occur
*/
protected abstract int writeBlockType( @Nonnull BlockType type ) throws IOException;
/** /**
* Mark to write exceptions * Mark to write exceptions

View File

@ -19,6 +19,7 @@ package de.inetsoftware.jwebassembly.module;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
@ -146,7 +147,9 @@ public class TypeManager {
*/ */
private static final String[] PRIMITIVE_CLASSES = { "boolean", "byte", "char", "double", "float", "int", "long", "short", "void" }; private static final String[] PRIMITIVE_CLASSES = { "boolean", "byte", "char", "double", "float", "int", "long", "short", "void" };
private Map<Object, StructType> structTypes = new LinkedHashMap<>(); private final Map<Object, StructType> structTypes = new LinkedHashMap<>();
private final Map<BlockType, BlockType> blockTypes = new LinkedHashMap<>();
private int typeIndexCounter; private int typeIndexCounter;
@ -211,7 +214,11 @@ public class TypeManager {
void prepareFinish( ModuleWriter writer, ClassFileLoader classFileLoader ) throws IOException { void prepareFinish( ModuleWriter writer, ClassFileLoader classFileLoader ) throws IOException {
isFinish = true; isFinish = true;
for( StructType type : structTypes.values() ) { for( StructType type : structTypes.values() ) {
type.writeStructType( writer, options.functions, this, classFileLoader ); type.writeStructType( writer );
}
for( BlockType type : blockTypes.values() ) {
type.code = writer.writeBlockType( type );
} }
// write type table // write type table
@ -380,6 +387,26 @@ public class TypeManager {
return type; return type;
} }
/**
* Create block type
*
* @param params
* the parameters
* @param results
* the results
* @return the type
*/
@Nonnull
BlockType blockType( List<AnyType> params, List<AnyType> results ) {
BlockType blockType = new BlockType( params, results );
BlockType type = blockTypes.get( blockType );
if( type != null ) {
return type;
}
blockTypes.put( blockType, blockType );
return blockType;
}
/** /**
* Create the FunctionName for a virtual call. The function has 2 parameters (THIS, * Create the FunctionName for a virtual call. The function has 2 parameters (THIS,
* virtualfunctionIndex) and returns the index of the function. * virtualfunctionIndex) and returns the index of the function.
@ -648,16 +675,10 @@ public class TypeManager {
* *
* @param writer * @param writer
* the targets for the types * the targets for the types
* @param functions
* the used functions for the vtables of the types
* @param types
* for types of fields
* @param classFileLoader
* for loading the class files
* @throws IOException * @throws IOException
* if any I/O error occur on loading or writing * if any I/O error occur on loading or writing
*/ */
private void writeStructType( ModuleWriter writer, FunctionManager functions, TypeManager types, ClassFileLoader classFileLoader ) throws IOException { private void writeStructType( ModuleWriter writer ) throws IOException {
JWebAssembly.LOGGER.fine( "write type: " + name ); JWebAssembly.LOGGER.fine( "write type: " + name );
code = writer.writeStructType( this ); code = writer.writeStructType( this );
} }
@ -1121,4 +1142,96 @@ public class TypeManager {
return interfaceMethodName; return interfaceMethodName;
} }
} }
/**
* A type that can use for a block
*/
public static class BlockType implements AnyType {
@Nonnull
private final List<AnyType> params;
@Nonnull
private final List<AnyType> results;
private int code;
private String name;
public BlockType(List<AnyType> params, List<AnyType> results) {
this.params = params;
this.results = results;
}
/**
* {@inheritDoc}
*/
@Override
public int getCode() {
return code;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isRefType() {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isSubTypeOf( AnyType type ) {
return type == this;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return params.hashCode() + results.hashCode();
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals( Object obj ) {
if( this == obj ) {
return true;
}
if( obj == null ) {
return false;
}
if( getClass() != obj.getClass() ) {
return false;
}
BlockType other = (BlockType)obj;
return params.equals( other.params ) && results.equals( other.results );
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
if( name != null ) {
return name;
}
return super.toString();
}
public List<AnyType> getParams() {
return Collections.unmodifiableList( params );
}
public List<AnyType> getResults() {
return Collections.unmodifiableList( results );
}
public void setName( String name ) {
this.name = name;
}
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2017 - 2020 Volker Berlin (i-net software) * Copyright 2017 - 2021 Volker Berlin (i-net software)
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,7 +21,6 @@ import javax.annotation.Nonnull;
import de.inetsoftware.jwebassembly.JWebAssembly; import de.inetsoftware.jwebassembly.JWebAssembly;
import de.inetsoftware.jwebassembly.javascript.JavaScriptSyntheticFunctionName; import de.inetsoftware.jwebassembly.javascript.JavaScriptSyntheticFunctionName;
import de.inetsoftware.jwebassembly.wasm.AnyType;
import de.inetsoftware.jwebassembly.wasm.ValueType; import de.inetsoftware.jwebassembly.wasm.ValueType;
/** /**
@ -63,30 +62,6 @@ public class WasmOptions {
private SyntheticFunctionName cast; private SyntheticFunctionName cast;
private int catchTypeCode;
private AnyType catchType = new AnyType() {
@Override
public int getCode() {
return catchTypeCode;
}
@Override
public boolean isRefType() {
return false;
}
@Override
public boolean isSubTypeOf( AnyType type ) {
return type == this;
}
@Override
public String toString() {
return "(param exnref)(result anyref)";
}
};
/** /**
* Create a new instance of options * Create a new instance of options
* *
@ -223,23 +198,4 @@ public class WasmOptions {
} }
return name; return name;
} }
/**
* The type for a catch block to unboxing the exnref into a anyref
*
* @return the type
*/
public AnyType getCatchType() {
return catchType;
}
/**
* Set the dynamic type id for the catch type
*
* @param catchTypeCode
* the positive id
*/
public void setCatchType( int catchTypeCode ) {
this.catchTypeCode = catchTypeCode;
}
} }

View File

@ -31,6 +31,7 @@ import javax.annotation.Nullable;
import de.inetsoftware.jwebassembly.WasmException; import de.inetsoftware.jwebassembly.WasmException;
import de.inetsoftware.jwebassembly.module.FunctionName; import de.inetsoftware.jwebassembly.module.FunctionName;
import de.inetsoftware.jwebassembly.module.ModuleWriter; import de.inetsoftware.jwebassembly.module.ModuleWriter;
import de.inetsoftware.jwebassembly.module.TypeManager.BlockType;
import de.inetsoftware.jwebassembly.module.TypeManager.StructType; import de.inetsoftware.jwebassembly.module.TypeManager.StructType;
import de.inetsoftware.jwebassembly.module.TypeManager.StructTypeKind; import de.inetsoftware.jwebassembly.module.TypeManager.StructTypeKind;
import de.inetsoftware.jwebassembly.module.ValueTypeConvertion; import de.inetsoftware.jwebassembly.module.ValueTypeConvertion;
@ -247,6 +248,24 @@ public class TextModuleWriter extends ModuleWriter {
return 0; return 0;
} }
/**
* {@inheritDoc}
*/
@Override
protected int writeBlockType( BlockType type ) throws IOException {
StringBuilder output = new StringBuilder();
for( AnyType valueType : type.getParams() ) {
writeParam( output, "param", valueType, null );
}
for( AnyType valueType : type.getResults() ) {
writeParam( output, "result", valueType, null );
}
String name = output.toString();
type.setName( name );
types.add( name );
return types.size() - 1;
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@ -257,11 +276,12 @@ public class TextModuleWriter extends ModuleWriter {
int oldInset = inset; int oldInset = inset;
inset = 1; inset = 1;
newline( output ); newline( output );
output.append( "(event (param externref))" ); if( options.useGC() ) {
output.append( "(event (param (ref null $java/lang/Throwable)))" );
} else {
output.append( "(event (param externref))" );
}
inset = oldInset; inset = oldInset;
options.setCatchType( types.size() );
types.add( options.getCatchType().toString() );
} }
} }
@ -383,19 +403,37 @@ public class TextModuleWriter extends ModuleWriter {
if( methodOutput == null ) { if( methodOutput == null ) {
return; return;
} }
newline( methodOutput ); writeParam( methodOutput, kind, valueType, name );
methodOutput.append( " (" ).append( kind ); }
if( options.debugNames() ) {
/**
* Write a parameter to the given output
*
* @param output
* the traget
* @param kind
* "param", "result" or "local"
* @param valueType
* the data type of the parameter
* @param name
* optional name of the parameter
* @throws IOException
* if any I/O error occur
*/
private void writeParam( StringBuilder output, String kind, AnyType valueType, @Nullable String name ) throws IOException {
newline( output );
output.append( " (" ).append( kind );
if( options.debugNames() ) {
if( name != null ) { if( name != null ) {
methodOutput.append( " $" ).append( name ); output.append( " $" ).append( name );
} }
if( kind != "result" ) { if( kind != "result" ) {
methodParamNames.add( name ); methodParamNames.add( name );
} }
} }
methodOutput.append( ' ' ); output.append( ' ' );
writeTypeName( methodOutput, valueType ); writeTypeName( output, valueType );
methodOutput.append( ')' ); output.append( ')' );
} }
/** /**