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.module.FunctionName;
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.StructTypeKind;
import de.inetsoftware.jwebassembly.module.ValueTypeConvertion;
@ -525,6 +526,18 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
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}
*/
@ -532,19 +545,13 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod
protected void writeException() throws IOException {
if( exceptionSignatureIndex <= 0 ) {
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 );
if( exceptionSignatureIndex < 0 ) {
exceptionSignatureIndex = functionTypes.size();
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 javax.annotation.Nonnull;
import javax.annotation.Nullable;
import de.inetsoftware.classparser.Code;
import de.inetsoftware.classparser.CodeInputStream;
import de.inetsoftware.classparser.ConstantClass;
import de.inetsoftware.classparser.TryCatchFinally;
import de.inetsoftware.jwebassembly.WasmException;
import de.inetsoftware.jwebassembly.module.TypeManager.BlockType;
import de.inetsoftware.jwebassembly.module.TypeManager.StructType;
import de.inetsoftware.jwebassembly.module.WasmInstruction.Type;
import de.inetsoftware.jwebassembly.wasm.AnyType;
@ -1015,10 +1018,10 @@ class BranchManger {
// occur with a RETURN in a finally block
// We does not need to unbox if the value will be drop
} else {
addUnboxExnref( catchNode );
addUnboxExnref( catchNode, tryCatch );
}
} else {
addUnboxExnref( catchNode );
addUnboxExnref( catchNode, tryCatch );
// add a "if $exception instanceof type" check to the WASM code
int instrPos = findIdxOfCodePos( catchPos ) + 1;
@ -1083,8 +1086,10 @@ class BranchManger {
*
* @param catchNode
* 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
int catchPos = catchNode.startPos;
if( !options.useEH() ) {
@ -1092,7 +1097,9 @@ class BranchManger {
catchNode.add( 0, unBoxing );
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 );
//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 ) );
}
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
* 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 ) {
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 );
break;
case 58: // astore
if( branchManager.isCatch( codePos ) ) {
addJumpPlaceholder( codePos, 0, ValueType.externref, codePos, lineNumber );
storeType = ValueType.externref; // for the catch there are no previous instructions
storeType = branchManager.getCatchType( codePos );
if( storeType != null ) {
// for the catch there are no previous instructions
addJumpPlaceholder( codePos, 0, storeType, codePos, lineNumber );
} else {
storeType = findValueTypeFromStack( 1, codePos );
}
@ -270,9 +271,10 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder {
case 76: // astore_1
case 77: // astore_2
case 78: // astore_3
if( branchManager.isCatch( codePos ) ) {
addJumpPlaceholder( codePos, 0, ValueType.externref, codePos, lineNumber );
storeType = ValueType.externref; // for the catch there are no previous instructions
storeType = branchManager.getCatchType( codePos );
if( storeType != null ) {
// for the catch there are no previous instructions
addJumpPlaceholder( codePos, 0, storeType, codePos, lineNumber );
} else {
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");
* 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.Nullable;
import de.inetsoftware.jwebassembly.module.TypeManager.BlockType;
import de.inetsoftware.jwebassembly.module.TypeManager.StructType;
import de.inetsoftware.jwebassembly.wasm.AnyType;
import de.inetsoftware.jwebassembly.wasm.ArrayOperator;
@ -78,7 +79,18 @@ public abstract class ModuleWriter implements Closeable {
* @throws IOException
* 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

View File

@ -19,6 +19,7 @@ package de.inetsoftware.jwebassembly.module;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
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 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;
@ -211,7 +214,11 @@ public class TypeManager {
void prepareFinish( ModuleWriter writer, ClassFileLoader classFileLoader ) throws IOException {
isFinish = true;
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
@ -380,6 +387,26 @@ public class TypeManager {
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,
* virtualfunctionIndex) and returns the index of the function.
@ -648,16 +675,10 @@ public class TypeManager {
*
* @param writer
* 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
* 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 );
code = writer.writeStructType( this );
}
@ -1121,4 +1142,96 @@ public class TypeManager {
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");
* 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.javascript.JavaScriptSyntheticFunctionName;
import de.inetsoftware.jwebassembly.wasm.AnyType;
import de.inetsoftware.jwebassembly.wasm.ValueType;
/**
@ -63,30 +62,6 @@ public class WasmOptions {
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
*
@ -223,23 +198,4 @@ public class WasmOptions {
}
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.module.FunctionName;
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.StructTypeKind;
import de.inetsoftware.jwebassembly.module.ValueTypeConvertion;
@ -247,6 +248,24 @@ public class TextModuleWriter extends ModuleWriter {
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}
*/
@ -257,11 +276,12 @@ public class TextModuleWriter extends ModuleWriter {
int oldInset = inset;
inset = 1;
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;
options.setCatchType( types.size() );
types.add( options.getCatchType().toString() );
}
}
@ -383,19 +403,37 @@ public class TextModuleWriter extends ModuleWriter {
if( methodOutput == null ) {
return;
}
newline( methodOutput );
methodOutput.append( " (" ).append( kind );
if( options.debugNames() ) {
writeParam( methodOutput, kind, valueType, name );
}
/**
* 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 ) {
methodOutput.append( " $" ).append( name );
output.append( " $" ).append( name );
}
if( kind != "result" ) {
methodParamNames.add( name );
}
}
methodOutput.append( ' ' );
writeTypeName( methodOutput, valueType );
methodOutput.append( ')' );
output.append( ' ' );
writeTypeName( output, valueType );
output.append( ')' );
}
/**