diff --git a/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java b/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java index 4d8973b..cf65d70 100644 --- a/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java +++ b/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java @@ -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 ); } } diff --git a/src/de/inetsoftware/jwebassembly/module/BranchManger.java b/src/de/inetsoftware/jwebassembly/module/BranchManger.java index cab11a9..a6757c7 100644 --- a/src/de/inetsoftware/jwebassembly/module/BranchManger.java +++ b/src/de/inetsoftware/jwebassembly/module/BranchManger.java @@ -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; } /** diff --git a/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java b/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java index b48a5c5..b25c463 100644 --- a/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java +++ b/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java @@ -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 ); } diff --git a/src/de/inetsoftware/jwebassembly/module/ModuleWriter.java b/src/de/inetsoftware/jwebassembly/module/ModuleWriter.java index 5601bdd..d437ee6 100644 --- a/src/de/inetsoftware/jwebassembly/module/ModuleWriter.java +++ b/src/de/inetsoftware/jwebassembly/module/ModuleWriter.java @@ -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 diff --git a/src/de/inetsoftware/jwebassembly/module/TypeManager.java b/src/de/inetsoftware/jwebassembly/module/TypeManager.java index 9d850df..fee4196 100644 --- a/src/de/inetsoftware/jwebassembly/module/TypeManager.java +++ b/src/de/inetsoftware/jwebassembly/module/TypeManager.java @@ -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 structTypes = new LinkedHashMap<>(); + private final Map structTypes = new LinkedHashMap<>(); + + private final Map 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 params, List 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 params; + @Nonnull + private final List results; + private int code; + private String name; + + public BlockType(List params, List 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 getParams() { + return Collections.unmodifiableList( params ); + } + + public List getResults() { + return Collections.unmodifiableList( results ); + } + + public void setName( String name ) { + this.name = name; + } + } + } diff --git a/src/de/inetsoftware/jwebassembly/module/WasmOptions.java b/src/de/inetsoftware/jwebassembly/module/WasmOptions.java index f66424a..9ce6fbe 100644 --- a/src/de/inetsoftware/jwebassembly/module/WasmOptions.java +++ b/src/de/inetsoftware/jwebassembly/module/WasmOptions.java @@ -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; - } } diff --git a/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java b/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java index f08f2e8..962df82 100644 --- a/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java +++ b/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java @@ -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( ')' ); } /**