diff --git a/src/de/inetsoftware/jwebassembly/binary/WasmOutputStream.java b/src/de/inetsoftware/jwebassembly/binary/WasmOutputStream.java index d792202..5b8c1c8 100644 --- a/src/de/inetsoftware/jwebassembly/binary/WasmOutputStream.java +++ b/src/de/inetsoftware/jwebassembly/binary/WasmOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 - 2021 Volker Berlin (i-net software) + * Copyright 2017 - 2022 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. @@ -84,12 +84,8 @@ class WasmOutputStream extends LittleEndianOutputStream { * if an I/O error occurs. */ public void writeValueType( AnyType type ) throws IOException { - if( !type.isRefType() && !options.useGC() ) { - switch( (ValueType)type ) { - case eqref: - type = ValueType.externref; - break; - } + if( !options.useGC() && type == ValueType.eqref ) { + type = ValueType.externref; } writeVarint( type.getCode() ); } diff --git a/src/de/inetsoftware/jwebassembly/module/BranchManager.java b/src/de/inetsoftware/jwebassembly/module/BranchManager.java index e57bafd..9ff111d 100644 --- a/src/de/inetsoftware/jwebassembly/module/BranchManager.java +++ b/src/de/inetsoftware/jwebassembly/module/BranchManager.java @@ -32,6 +32,7 @@ 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; @@ -64,6 +65,7 @@ class BranchManager { private final ArrayList breakOperations = new ArrayList<>(); + private BlockType switchType; /** * Create a branch manager. @@ -534,7 +536,7 @@ class BranchManager { // find the code position where the condition values are push on the stack List instructions = this.instructions; int idx = instructions.indexOf( startBlock.instr ); - startPos = WasmCodeBuilder.findBlockStart( 1, true, instructions, idx + 1 ); + startPos = WasmCodeBuilder.findBlockStart( 1, instructions, idx + 1 ); if( parent.overlapped( startPos ) ) { branch = addMiddleNode( parent, parent.startPos, endPos ); } else { @@ -853,6 +855,10 @@ class BranchManager { * the not consumed operations in the parent branch */ private void calculateSwitch( BranchNode parent, SwitchParsedBlock switchBlock, List parsedOperations ) { + BlockType switchType = this.switchType; + if( switchType == null ) { + this.switchType = switchType = options.types.blockType( Arrays.asList( ValueType.i32 ), Collections.emptyList() ); + } int startPosition = ((ParsedBlock)switchBlock).startPosition; int posCount = switchBlock.positions.length; boolean isTable = switchBlock.keys == null; @@ -883,7 +889,7 @@ class BranchManager { } lastPosition = currentPosition; blockCount++; - BranchNode node = new BranchNode( startPosition, currentPosition, WasmBlockOperator.BLOCK, WasmBlockOperator.END ); + BranchNode node = new BranchNode( startPosition, currentPosition, WasmBlockOperator.BLOCK, WasmBlockOperator.END, switchType ); if( blockNode != null ) { node.add( blockNode ); } @@ -914,7 +920,7 @@ class BranchManager { while( parentNode != null && end > parentNode.endPos ) { parentNode = parentNode.parent; } - BranchNode middleNode = new BranchNode( startPosition, end, WasmBlockOperator.BLOCK, WasmBlockOperator.END ); + BranchNode middleNode = new BranchNode( startPosition, end, WasmBlockOperator.BLOCK, WasmBlockOperator.END, switchType ); if( parentNode != null ) { BranchNode child = parentNode.remove( 0 ); parentNode.add( middleNode ); @@ -980,7 +986,7 @@ class BranchManager { } // Create the main block around the switch - BranchNode switchNode = new BranchNode( startPosition, lastPosition, WasmBlockOperator.BLOCK, WasmBlockOperator.END ); + BranchNode switchNode = new BranchNode( startPosition, lastPosition, WasmBlockOperator.BLOCK, WasmBlockOperator.END, switchType ); switchNode.add( blockNode ); parent.add( switchNode ); @@ -1467,7 +1473,8 @@ class BranchManager { * @return the new node */ private BranchNode addMiddleNode( BranchNode parent, int startPos, int endPos ) { - BranchNode middleNode = new BranchNode( startPos, endPos, WasmBlockOperator.BLOCK, WasmBlockOperator.END ); + Object data = parent.data == switchType && parent.startPos == startPos ? switchType : null; + BranchNode middleNode = new BranchNode( startPos, endPos, WasmBlockOperator.BLOCK, WasmBlockOperator.END, data ); int idx = 0; for( Iterator it = parent.iterator(); it.hasNext(); ) { BranchNode child = it.next(); @@ -1510,7 +1517,6 @@ class BranchManager { } while( codePosition < nextCodePosition ); } root.handle( byteCode.getCodePosition(), instructions, instructions.size(), byteCode.getLineNumber() ); - root.calculateBlockType( instructions ); } @@ -1725,9 +1731,20 @@ class BranchManager { @Override public boolean add( BranchNode node ) { node.parent = this; - assert node.startOp == null || (node.startPos >= startPos && node.endPos <= endPos): "Node outside parent: " + this + " + " + node; - assert node.startOp == null || !overlapped( node.startPos ) : "Node on wrong level: " + node + "; parent: " + this + "; last: " + get( size() - 1 ); - return super.add( node ); + assert node.startOp == null || (node.startPos >= startPos && node.endPos <= endPos) : "Node outside parent: " + this + " + " + node; + + int size = size(); + if( size > 0 && node.startOp != null ) { + int nodeStartPos = node.startPos; + for( int i = size - 1; i >= 0; i-- ) { + if( get( i ).endPos <= nodeStartPos ) { + super.add( i + 1, node ); + return true; + } + } + } + super.add( 0, node ); + return true; } /** @@ -1746,7 +1763,16 @@ class BranchManager { * @return true, if the position is already consumed from a child */ boolean overlapped( int startPos ) { - return size() > 0 && get( size() - 1 ).endPos > startPos; + for( int i = size() - 1; i >= 0; i-- ) { + BranchNode node = get( i ); + if( node.endPos <= startPos ) { + return false; + } + if( node.startPos < startPos ) { + return true; + } + } + return false; } /** @@ -1801,6 +1827,14 @@ class BranchManager { try { ArrayDeque stack = new ArrayDeque<>(); stack.push( ValueType.empty ); + + BlockType blockType = startBlock.getData() instanceof BlockType ? (BlockType)startBlock.getData() : null; + if( blockType != null ) { + for( AnyType param : blockType.getParams() ) { + stack.push( param ); + } + } + INSTRUCTIONS: for( int i = startIdx; i < instructions.size(); i++ ) { WasmInstruction instr = instructions.get( i ); if( instr.getType() == Type.Jump ) { @@ -1852,7 +1886,13 @@ class BranchManager { } } } - startBlock.setData( stack.pop() ); + + AnyType result = stack.pop(); + if( blockType == null ) { + startBlock.setData( result ); + } else if( result != ValueType.empty) { + throw new WasmException( "block with parameter has return parameter", startBlock.getLineNumber() ); + } } catch( Throwable th ) { throw WasmException.create( th, startBlock.getLineNumber() ); } diff --git a/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java b/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java index 5eb89d8..cae9df4 100644 --- a/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java +++ b/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java @@ -787,7 +787,6 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder { byteCode.skip( 4 - padding ); } startPosition--; - int switchValuestartPosition = findBlockStartCodePosition( 1 ); int defaultPosition = startPosition + byteCode.readInt(); int[] keys; @@ -846,7 +845,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder { addNumericInstruction( NumericOperator.sub, ValueType.i32, codePos, lineNumber ); } } - branchManager.addSwitchOperator( switchValuestartPosition, 0, lineNumber, keys, positions, defaultPosition ); + branchManager.addSwitchOperator( startPosition, 0, lineNumber, keys, positions, defaultPosition ); } /** diff --git a/src/de/inetsoftware/jwebassembly/module/WasmBlockInstruction.java b/src/de/inetsoftware/jwebassembly/module/WasmBlockInstruction.java index 86dd63c..3a403bc 100644 --- a/src/de/inetsoftware/jwebassembly/module/WasmBlockInstruction.java +++ b/src/de/inetsoftware/jwebassembly/module/WasmBlockInstruction.java @@ -17,10 +17,12 @@ package de.inetsoftware.jwebassembly.module; import java.io.IOException; +import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import de.inetsoftware.jwebassembly.module.TypeManager.BlockType; import de.inetsoftware.jwebassembly.wasm.AnyType; import de.inetsoftware.jwebassembly.wasm.ValueType; import de.inetsoftware.jwebassembly.wasm.WasmBlockOperator; @@ -104,7 +106,14 @@ class WasmBlockInstruction extends WasmInstruction { switch( op ) { case IF: case BLOCK: - return data != ValueType.empty ? (AnyType)data : null; + if( data == ValueType.empty || data == null ) { + return null; + } + if( data instanceof BlockType ) { + List results = ((BlockType)data).getResults(); + return results.isEmpty() ? null : results.get( 0 ); + } + return (AnyType)data; case RETURN: return (AnyType)data; default: @@ -125,6 +134,8 @@ class WasmBlockInstruction extends WasmInstruction { case THROW: case RETHROW: return 1; + case BLOCK: + return data instanceof BlockType ? ((BlockType)data).getParams().size() : 0; case RETURN: return data == null ? 0 : 1; default: @@ -146,6 +157,8 @@ class WasmBlockInstruction extends WasmInstruction { case THROW: case RETHROW: return new AnyType[] { ValueType.exnref }; + case BLOCK: + return data instanceof BlockType ? ((BlockType)data).getParams().toArray( new AnyType[0] ) : null; case RETURN: return data == null ? null : new AnyType[] { (AnyType)data }; default: diff --git a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java index 8ab3b74..427fc70 100644 --- a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java +++ b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java @@ -162,41 +162,13 @@ public abstract class WasmCodeBuilder { * * @param count * the count of values on the stack back. 1 means the last value. 2 means the penultimate value. - * @return the code position that push the last instruction - */ - int findBlockStartCodePosition( int count ) { - return findBlockStart( count, true ); - } - - /** - * We need a value (or several) from the stack inside a block. We need to find the WasmInstruction where the block - * can can begin. If it is a function call or a numeric expression, it can be complicated to find the right point. - * - * @param count - * the count of values on the stack back. 1 means the last value. 2 means the penultimate value. - * @param codePosition - * true, get the code position; false, get the index in the instructions - * @return the code position that push the last instruction - */ - private int findBlockStart( int count, boolean codePosition ) { - return findBlockStart( count, codePosition, instructions, instructions.size() ); - } - - /** - * We need a value (or several) from the stack inside a block. We need to find the WasmInstruction where the block - * can can begin. If it is a function call or a numeric expression, it can be complicated to find the right point. - * - * @param count - * the count of values on the stack back. 1 means the last value. 2 means the penultimate value. - * @param codePosition - * true, get the code position; false, get the index in the instructions * @param instructions * the instruction list for searching * @param idx * the start index for the search. Between 0 and instructions.size(). * @return the code position that push the last instruction */ - static int findBlockStart( int count, boolean codePosition, List instructions, int idx ) { + static int findBlockStart( int count, List instructions, int idx ) { int valueCount = 0; for( int i = idx - 1; i >= 0; i-- ) { WasmInstruction instr = instructions.get( i ); @@ -204,12 +176,13 @@ public abstract class WasmCodeBuilder { if( valueType != null ) { valueCount++; } - valueCount -= instr.getPopCount(); - if( valueCount == count ) { + int popCount = instr.getPopCount(); + valueCount -= popCount; + if( valueCount == count && popCount == 0 ) { int codePos = instr.getCodePosition(); if( i == 0 || instructions.get( i - 1 ).getCodePosition() < codePos ) { // if the same codePos is used from multiple instructions then it is not an atomic operation in Java - return codePosition ? codePos : i; + return codePos; } } } diff --git a/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java b/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java index 3e5c46a..7e8dbec 100644 --- a/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java +++ b/src/de/inetsoftware/jwebassembly/text/TextModuleWriter.java @@ -361,7 +361,7 @@ public class TextModuleWriter extends ModuleWriter { * if any I/O error occur */ private void writeTypeName( Appendable output, AnyType type ) throws IOException { - if( !type.isRefType() ) { + if( type instanceof ValueType ) { String name; switch( (ValueType)type ) { case u16: