From 87359fb4972f13f1bc7bd0a12f1602c678463ff7 Mon Sep 17 00:00:00 2001 From: Volker Berlin Date: Sat, 18 Apr 2020 13:09:11 +0200 Subject: [PATCH] Simplify the try/catch structure --- .../binary/BinaryModuleWriter.java | 49 ++++++++++-------- .../jwebassembly/binary/WasmOutputStream.java | 26 +++++++--- .../jwebassembly/module/BranchManger.java | 45 ++++++++-------- .../jwebassembly/module/TypeManager.java | 8 +++ .../jwebassembly/module/WasmOptions.java | 49 ++++++++++++++++-- .../jwebassembly/text/TextModuleWriter.java | 51 ++++++++++++------- .../jwebassembly/wasm/AnyType.java | 9 +++- .../jwebassembly/wasm/ArrayType.java | 10 +++- .../jwebassembly/wasm/ValueType.java | 10 +++- .../jwebassembly/module/WatParserTest.java | 2 +- 10 files changed, 184 insertions(+), 75 deletions(-) diff --git a/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java b/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java index 53cea13..a176441 100644 --- a/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java +++ b/src/de/inetsoftware/jwebassembly/binary/BinaryModuleWriter.java @@ -66,7 +66,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod private final boolean createSourceMap; - private WasmOutputStream codeStream = new WasmOutputStream(); + private WasmOutputStream codeStream = new WasmOutputStream( options ); private List functionTypes = new ArrayList<>(); @@ -116,7 +116,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod */ @Override public void close() throws IOException { - wasm = new WasmOutputStream( target.getWasmOutput() ); + wasm = new WasmOutputStream( options, target.getWasmOutput() ); wasm.write( WASM_BINARY_MAGIC ); wasm.writeInt32( WASM_BINARY_VERSION ); @@ -152,7 +152,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod private void writeSection( SectionType type, Collection entries ) throws IOException { int count = entries.size(); if( count > 0 ) { - WasmOutputStream stream = new WasmOutputStream(); + WasmOutputStream stream = new WasmOutputStream( options ); stream.writeVaruint32( count ); for( SectionEntry entry : entries ) { entry.writeSectionEntry( stream ); @@ -174,7 +174,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod return; } - WasmOutputStream stream = new WasmOutputStream(); + WasmOutputStream stream = new WasmOutputStream( options ); int count = 1; if( stringCount > 0 ) { count++; @@ -217,7 +217,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod private void writeMemorySection() throws IOException { int dataSize = dataStream.size(); if( dataSize > 0 ) { - WasmOutputStream stream = new WasmOutputStream(); + WasmOutputStream stream = new WasmOutputStream( options ); int pages = (dataSize + 0xFFFF) / 0x10000; // a page is defined with a size of 64KiB int count = 1; stream.writeVaruint32( count ); @@ -238,7 +238,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod */ private void writeEventSection() throws IOException { if( exceptionSignatureIndex >= 0 ) { - WasmOutputStream stream = new WasmOutputStream(); + WasmOutputStream stream = new WasmOutputStream( options ); stream.writeVaruint32( 1 ); // event declaration @@ -260,7 +260,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod return; } int id = getFunction( startFunction ).id; - WasmOutputStream stream = new WasmOutputStream(); + WasmOutputStream stream = new WasmOutputStream( options ); stream.writeVaruint32( id ); wasm.writeSection( SectionType.Start, stream ); } @@ -277,7 +277,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod } int elemCount = imports.size() + functions.size(); - WasmOutputStream stream = new WasmOutputStream(); + WasmOutputStream stream = new WasmOutputStream( options ); stream.writeVaruint32( 1 ); // count of element segments to follow // element_segment @@ -305,7 +305,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod } int start = wasm.size(); - WasmOutputStream stream = new WasmOutputStream(); + WasmOutputStream stream = new WasmOutputStream( options ); stream.writeVaruint32( size ); for( Entry entry : functions.entrySet() ) { try { @@ -345,7 +345,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod return; } - WasmOutputStream stream = new WasmOutputStream(); + WasmOutputStream stream = new WasmOutputStream( options ); stream.writeVaruint32( 1 ); // count, we use one large segment // one data segment @@ -368,12 +368,12 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod if( !options.debugNames() ) { return; } - WasmOutputStream stream = new WasmOutputStream(); + WasmOutputStream stream = new WasmOutputStream( options ); stream.writeString( "name" ); // Custom Section name "name", content is part of the section length // write function names stream.write( 1 ); // 1 - Function name - WasmOutputStream section = new WasmOutputStream(); + WasmOutputStream section = new WasmOutputStream( options ); section.writeVaruint32( imports.size() + functions.size() ); writeDebugFunctionNames( imports.entrySet(), section ); writeDebugFunctionNames( functions.entrySet(), section ); @@ -449,7 +449,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod if( url == null ) { return; } - WasmOutputStream stream = new WasmOutputStream(); + WasmOutputStream stream = new WasmOutputStream( options ); stream.writeString( "sourceMappingURL" ); // Custom Section name "sourceMappingURL", content is part of the section length stream.writeString( url ); wasm.writeSection( SectionType.Custom, stream ); @@ -465,7 +465,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod Package pack = getClass().getPackage(); String version = pack == null ? null : pack.getImplementationVersion(); - WasmOutputStream stream = new WasmOutputStream(); + WasmOutputStream stream = new WasmOutputStream( options ); stream.writeString( "producers" ); // Custom Section name "producers", content is part of the section length stream.writeVaruint32( 2 ); // field_count; number of fields that follow (language and processed-by) @@ -511,13 +511,20 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod @Override protected void writeException() throws IOException { if( exceptionSignatureIndex <= 0 ) { - FunctionTypeEntry exceptionType = new FunctionTypeEntry(); - exceptionType.params.add( ValueType.anyref ); - exceptionSignatureIndex = functionTypes.indexOf( exceptionType ); + FunctionTypeEntry type = new FunctionTypeEntry(); + type.params.add( ValueType.anyref ); + exceptionSignatureIndex = functionTypes.indexOf( type ); if( exceptionSignatureIndex < 0 ) { exceptionSignatureIndex = functionTypes.size(); - functionTypes.add( exceptionType ); + functionTypes.add( type ); } + + // result type of catch block for unboxing + type = new FunctionTypeEntry(); + type.params.add( ValueType.exnref ); + type.results.add( ValueType.anyref ); + options.setCatchType( functionTypes.size() ); + functionTypes.add( type ); } } @@ -639,7 +646,7 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod @Override protected void writeMethodFinish() throws IOException { @SuppressWarnings( "resource" ) - WasmOutputStream localsTypeStream = new WasmOutputStream(); + WasmOutputStream localsTypeStream = new WasmOutputStream( options ); int localEntryCount = 0; // number of local entries in output int varCount = locals.size(); for( int i = 0; i < varCount; ) { @@ -655,10 +662,10 @@ public class BinaryModuleWriter extends ModuleWriter implements InstructionOpcod } @SuppressWarnings( "resource" ) - WasmOutputStream localsStream = new WasmOutputStream(); + WasmOutputStream localsStream = new WasmOutputStream( options ); localsStream.writeVaruint32( localEntryCount ); - WasmOutputStream functionsStream = function.functionsStream = new WasmOutputStream(); + WasmOutputStream functionsStream = function.functionsStream = new WasmOutputStream( options ); functionsStream.writeVaruint32( localsStream.size() + localsTypeStream.size() + codeStream.size() + 1 ); localsStream.writeTo( functionsStream ); localsTypeStream.writeTo( functionsStream ); diff --git a/src/de/inetsoftware/jwebassembly/binary/WasmOutputStream.java b/src/de/inetsoftware/jwebassembly/binary/WasmOutputStream.java index b5ed845..0fc41ad 100644 --- a/src/de/inetsoftware/jwebassembly/binary/WasmOutputStream.java +++ b/src/de/inetsoftware/jwebassembly/binary/WasmOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2019 Volker Berlin (i-net software) + * Copyright 2017 - 2020 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. @@ -24,6 +24,7 @@ import javax.annotation.Nonnegative; import javax.annotation.Nonnull; import de.inetsoftware.jwebassembly.WasmException; +import de.inetsoftware.jwebassembly.module.WasmOptions; import de.inetsoftware.jwebassembly.wasm.AnyType; import de.inetsoftware.jwebassembly.wasm.LittleEndianOutputStream; import de.inetsoftware.jwebassembly.wasm.ValueType; @@ -33,11 +34,17 @@ import de.inetsoftware.jwebassembly.wasm.ValueType; */ class WasmOutputStream extends LittleEndianOutputStream { + private final WasmOptions options; + /** * Create a in memory stream. + * + * @param options + * compiler properties */ - WasmOutputStream() { + WasmOutputStream( WasmOptions options ) { super(); + this.options = options; } /** @@ -45,9 +52,12 @@ class WasmOutputStream extends LittleEndianOutputStream { * * @param output * the target of data + * @param options + * compiler properties */ - WasmOutputStream( OutputStream output ) { + WasmOutputStream( WasmOptions options, OutputStream output ) { super( output ); + this.options = options; } /** @@ -86,8 +96,12 @@ class WasmOutputStream extends LittleEndianOutputStream { * if an I/O error occurs. */ public void writeRefValueType( AnyType type ) throws IOException { - if( type.getCode() >= 0 ) { - writeValueType( ValueType.ref_type ); + if( type.isRefType() ) { + if( options.useGC() ) { + writeValueType( ValueType.ref_type ); + } else { + type = ValueType.anyref; + } } writeValueType( type ); } @@ -192,7 +206,7 @@ class WasmOutputStream extends LittleEndianOutputStream { * if an I/O error occurs. */ void writeDouble( double value ) throws IOException { - long l = Double.doubleToLongBits(value); + long l = Double.doubleToLongBits( value ); writeInt32( (int)l ); writeInt32( (int)(l >>> 32) ); } diff --git a/src/de/inetsoftware/jwebassembly/module/BranchManger.java b/src/de/inetsoftware/jwebassembly/module/BranchManger.java index cfb3cf7..1d69657 100644 --- a/src/de/inetsoftware/jwebassembly/module/BranchManger.java +++ b/src/de/inetsoftware/jwebassembly/module/BranchManger.java @@ -860,17 +860,21 @@ class BranchManger { * Should be converted to the follow Wasm code for try/catch: * *
-     * block
-     *   block (result anyref)
-     *     try
-     *       code1
-     *     catch
-     *       br_on_exn 1 0
-     *       rethrow
-     *     end
-     *     br 1
+     * try
+     *   code1
+     * catch
+     *   block (param exnref)(result anyref)
+     *     br_on_exn 0 0
+     *     rethrow
+     *   end
+     *   local.tee $ex
+     *   i32.const classIndex(Exception)
+     *   call $.instanceof
+     *   i32.eqz
+     *   if
+     *     local.get $ex
+     *     throw 0
      *   end
-     *   local.set x
      *   code2
      * end
      * code3
@@ -910,25 +914,24 @@ class BranchManger {
         }
         int startPos = tryBlock.startPosition;
         int catchPos = tryCatch.getHandler();
-        BranchNode outerBlock = new BranchNode( startPos, endPos, WasmBlockOperator.BLOCK, WasmBlockOperator.END );
-        parent.add( outerBlock );
-
-        BranchNode innerBlock = new BranchNode( startPos, catchPos, WasmBlockOperator.BLOCK, WasmBlockOperator.END, ValueType.anyref );
-        outerBlock.add( innerBlock );
 
         BranchNode tryNode = new BranchNode( startPos, catchPos, WasmBlockOperator.TRY, null );
-        innerBlock.add( tryNode );
+        parent.add( tryNode );
         calculate( tryNode, parsedOperations.subList( 0, idx ) );
 
-        BranchNode catchNode = new BranchNode( catchPos, catchPos, WasmBlockOperator.CATCH, WasmBlockOperator.END );
-        innerBlock.add( catchNode );
+        BranchNode catchNode = new BranchNode( catchPos, endPos, WasmBlockOperator.CATCH, WasmBlockOperator.END );
+        parent.add( catchNode );
 
         if( tryCatch.isFinally() ) {
             catchNode.add( new BranchNode( catchPos, catchPos, WasmBlockOperator.DROP, null ) );
         } else {
+            // unboxing the exnref on the stack to a reference of the exception
+            BranchNode unBoxing = new BranchNode( catchPos, catchPos, WasmBlockOperator.BLOCK, WasmBlockOperator.END, options.getCatchType() );
+            catchNode.add( unBoxing );
+
             //TODO localVariables.getTempVariable( ValueType.exnref, catchPos, endPos ); https://github.com/WebAssembly/wabt/issues/1388
-            catchNode.add( new BranchNode( catchPos, catchPos, WasmBlockOperator.BR_ON_EXN, null, 1 ) );
-            catchNode.add( new BranchNode( catchPos, catchPos, WasmBlockOperator.RETHROW, null ) );
+            unBoxing.add( new BranchNode( catchPos, catchPos, WasmBlockOperator.BR_ON_EXN, null, 0 ) );
+            unBoxing.add( new BranchNode( catchPos, catchPos, WasmBlockOperator.RETHROW, null ) );
 
             // add a "if $exception instanceof type" check to the WASM code
             StructType type = options.types.valueOf( tryCatch.getType().getName() );
@@ -946,8 +949,6 @@ class BranchManger {
             instructions.add( ++instrPos, new WasmBlockInstruction( WasmBlockOperator.THROW, null, catchPos, lineNumber ) );
             instructions.add( ++instrPos, new WasmBlockInstruction( WasmBlockOperator.END, null, catchPos, lineNumber ) );
         }
-
-        innerBlock.add( new BranchNode( catchPos, catchPos, WasmBlockOperator.BR, null, 1 ) );
     }
 
     /**
diff --git a/src/de/inetsoftware/jwebassembly/module/TypeManager.java b/src/de/inetsoftware/jwebassembly/module/TypeManager.java
index 247b49d..a39498b 100644
--- a/src/de/inetsoftware/jwebassembly/module/TypeManager.java
+++ b/src/de/inetsoftware/jwebassembly/module/TypeManager.java
@@ -506,6 +506,14 @@ public class TypeManager {
             return code;
         }
 
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public boolean isRefType() {
+            return true;
+        }
+
         /**
          * {@inheritDoc}
          */
diff --git a/src/de/inetsoftware/jwebassembly/module/WasmOptions.java b/src/de/inetsoftware/jwebassembly/module/WasmOptions.java
index d341a54..d6450da 100644
--- a/src/de/inetsoftware/jwebassembly/module/WasmOptions.java
+++ b/src/de/inetsoftware/jwebassembly/module/WasmOptions.java
@@ -21,11 +21,7 @@ import javax.annotation.Nonnull;
 
 import de.inetsoftware.jwebassembly.JWebAssembly;
 import de.inetsoftware.jwebassembly.javascript.JavaScriptSyntheticFunctionName;
-import de.inetsoftware.jwebassembly.module.CodeOptimizer;
-import de.inetsoftware.jwebassembly.module.FunctionManager;
-import de.inetsoftware.jwebassembly.module.FunctionName;
-import de.inetsoftware.jwebassembly.module.StringManager;
-import de.inetsoftware.jwebassembly.module.TypeManager;
+import de.inetsoftware.jwebassembly.wasm.AnyType;
 import de.inetsoftware.jwebassembly.wasm.ValueType;
 
 /**
@@ -65,6 +61,30 @@ public class WasmOptions {
 
     private FunctionName 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
      * 
@@ -184,4 +204,23 @@ 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 a08151a..9b36b9a 100644
--- a/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java
+++ b/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java
@@ -232,6 +232,9 @@ public class TextModuleWriter extends ModuleWriter {
             newline( output );
             output.append( "(event (param anyref))" );
             inset = oldInset;
+
+            options.setCatchType( types.size() );
+            types.add( options.getCatchType().toString() );
         }
     }
 
@@ -310,7 +313,7 @@ public class TextModuleWriter extends ModuleWriter {
      *             if any I/O error occur
      */
     private void writeTypeName( Appendable output, AnyType type ) throws IOException {
-        if( type instanceof ValueType ) {
+        if( !type.isRefType() ) {
             output.append( type.toString() );
         } else if( options.useGC() ) {
             output.append( "(optref " ).append( normalizeName( type.toString() ) ).append( ')' );
@@ -714,14 +717,7 @@ public class TextModuleWriter extends ModuleWriter {
                 name = "return";
                 break;
             case IF:
-                if( data == ValueType.empty ) {
-                    name = "if";
-                } else {
-                    StringBuilder builder = new StringBuilder("if (result ");
-                    writeTypeName( builder, (AnyType)data );
-                    builder.append( ")" );
-                    name = builder;
-                }
+                name = blockWithResult( "if", (AnyType)data );
                 insetAfter++;
                 break;
             case ELSE:
@@ -737,14 +733,7 @@ public class TextModuleWriter extends ModuleWriter {
                 name = "drop";
                 break;
             case BLOCK:
-                if( data == null ) {
-                    name = "block";
-                } else {
-                    StringBuilder builder = new StringBuilder("block (result ");
-                    writeTypeName( builder, (AnyType)data );
-                    builder.append( ")" );
-                    name = builder;
-                }
+                name = blockWithResult( "block", (AnyType)data );
                 insetAfter++;
                 break;
             case BR:
@@ -797,6 +786,34 @@ public class TextModuleWriter extends ModuleWriter {
         inset += insetAfter;
     }
 
+    /**
+     * Create a the result type for a block instruction
+     * 
+     * @param blockName
+     *            the name of the block for example "if" or "block"
+     * @param result
+     *            the result type of the block
+     * @return the block with result type
+     * @throws IOException
+     *             if any I/O error occur
+     */
+    @Nonnull
+    private CharSequence blockWithResult( String blockName, AnyType result ) throws IOException {
+        if( result == null || result == ValueType.empty ) {
+            return blockName;
+        } else {
+            StringBuilder builder = new StringBuilder( blockName );
+            if( result.toString().contains( "(" ) ) {
+                builder.append( result );
+            } else {
+                builder.append( "(result " );
+                writeTypeName( builder, result );
+                builder.append( ")" );
+            }
+            return builder;
+        }
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git a/src/de/inetsoftware/jwebassembly/wasm/AnyType.java b/src/de/inetsoftware/jwebassembly/wasm/AnyType.java
index 9d384f1..fb2c00d 100644
--- a/src/de/inetsoftware/jwebassembly/wasm/AnyType.java
+++ b/src/de/inetsoftware/jwebassembly/wasm/AnyType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2018 - 2019 Volker Berlin (i-net software)
+ * Copyright 2018 - 2020 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.
@@ -39,6 +39,13 @@ public interface AnyType {
      */
     public int getCode();
 
+    /**
+     * If the type is a reference type. A GC reference to the heap.
+     * 
+     * @return true, is GC type
+     */
+    public boolean isRefType();
+
     /**
      * Check if this is a sub type of given type.
      * 
diff --git a/src/de/inetsoftware/jwebassembly/wasm/ArrayType.java b/src/de/inetsoftware/jwebassembly/wasm/ArrayType.java
index bf1272e..ff5ea27 100644
--- a/src/de/inetsoftware/jwebassembly/wasm/ArrayType.java
+++ b/src/de/inetsoftware/jwebassembly/wasm/ArrayType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2019 Volker Berlin (i-net software)
+ * Copyright 2019 - 2020 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.
@@ -50,6 +50,14 @@ public class ArrayType implements AnyType {
         return ValueType.anyref.getCode();
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isRefType() {
+        return true;
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git a/src/de/inetsoftware/jwebassembly/wasm/ValueType.java b/src/de/inetsoftware/jwebassembly/wasm/ValueType.java
index 8dc20de..6f48286 100644
--- a/src/de/inetsoftware/jwebassembly/wasm/ValueType.java
+++ b/src/de/inetsoftware/jwebassembly/wasm/ValueType.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 - 2019 Volker Berlin (i-net software)
+ * Copyright 2017 - 2020 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.
@@ -58,6 +58,14 @@ public enum ValueType implements AnyType {
         return code;
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isRefType() {
+        return false;
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git a/test/de/inetsoftware/jwebassembly/module/WatParserTest.java b/test/de/inetsoftware/jwebassembly/module/WatParserTest.java
index 7bf324c..dd20931 100644
--- a/test/de/inetsoftware/jwebassembly/module/WatParserTest.java
+++ b/test/de/inetsoftware/jwebassembly/module/WatParserTest.java
@@ -336,7 +336,7 @@ public class WatParserTest {
 
     @Test
     public void ifElseEndI32() throws IOException {
-        test( "if (result i32) else end" );
+        test( "if(result i32) else end" );
     }
 
     @Test