From c52bc2a0fe22afb419f4b3416917c4c6f0cc7a02 Mon Sep 17 00:00:00 2001 From: Volker Berlin Date: Sun, 29 Mar 2020 14:31:58 +0200 Subject: [PATCH] Rewrite stack analyze to handle conditional parameters on the stack. --- .../module/JavaMethodWasmCodeBuilder.java | 8 +- .../jwebassembly/module/StackInspector.java | 100 ++++++++++++++++++ .../jwebassembly/module/WasmCodeBuilder.java | 45 ++------ .../jwebassembly/runtime/StructsNonGC.java | 13 +++ 4 files changed, 128 insertions(+), 38 deletions(-) create mode 100644 src/de/inetsoftware/jwebassembly/module/StackInspector.java diff --git a/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java b/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java index 00c7e98..21fefd2 100644 --- a/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java +++ b/src/de/inetsoftware/jwebassembly/module/JavaMethodWasmCodeBuilder.java @@ -194,7 +194,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder { addArrayInstruction( ArrayOperator.GET, ValueType.f64, codePos, lineNumber ); break; case 50: // aaload - AnyType storeType = findValueTypeFromStack( 2 ); + AnyType storeType = findValueTypeFromStack( 2, codePos ); addArrayInstruction( ArrayOperator.GET, storeType, codePos, lineNumber ); break; case 51: // baload @@ -252,7 +252,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder { if( branchManager.isCatch( codePos ) ) { storeType = ValueType.anyref; // for the catch there are no previous instructions } else { - storeType = findValueTypeFromStack( 1 ); + storeType = findValueTypeFromStack( 1, codePos ); } addLoadStoreInstruction( storeType, false, op - 75, codePos, lineNumber ); break; @@ -269,7 +269,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder { addArrayInstruction( ArrayOperator.SET, ValueType.f64, codePos, lineNumber ); break; case 83: // aastore - storeType = findValueTypeFromStack( 1 ); + storeType = findValueTypeFromStack( 1, codePos ); addArrayInstruction( ArrayOperator.SET, storeType, codePos, lineNumber ); break; case 84: // bastore @@ -646,7 +646,7 @@ class JavaMethodWasmCodeBuilder extends WasmCodeBuilder { addArrayInstruction( ArrayOperator.NEW, type, codePos, lineNumber ); break; case 190: // arraylength - type = ((ArrayType)findValueTypeFromStack( 1 )).getArrayType(); + type = ((ArrayType)findValueTypeFromStack( 1, codePos )).getArrayType(); addArrayInstruction( ArrayOperator.LEN, type, codePos, lineNumber ); break; case 191: // athrow diff --git a/src/de/inetsoftware/jwebassembly/module/StackInspector.java b/src/de/inetsoftware/jwebassembly/module/StackInspector.java new file mode 100644 index 0000000..d1b6752 --- /dev/null +++ b/src/de/inetsoftware/jwebassembly/module/StackInspector.java @@ -0,0 +1,100 @@ +/* + Copyright 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. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +package de.inetsoftware.jwebassembly.module; + +import java.util.ArrayDeque; +import java.util.List; +import java.util.NoSuchElementException; + +import de.inetsoftware.jwebassembly.WasmException; +import de.inetsoftware.jwebassembly.module.WasmInstruction.Type; +import de.inetsoftware.jwebassembly.wasm.AnyType; + +/** + * Inspect the current parsed instructions to find details over specific stack information. + * + * @author Volker Berlin + */ +class StackInspector { + + /** + * Inspect the instructions to find details over a specific stack position. + * + * @param instructions + * the parsed instructions + * @param count + * the count of values on the stack back. 1 means the last value. 2 means the penultimate value. + * @param javaCodePos + * the current code position, important to follow jumps in the code + * @return details of the stack position + */ + static StackValue findInstructionThatPushValue( List instructions, int count, int javaCodePos ) { + // because there can be jumps (GOTO) we can analyze the stack only forward. If we iterate backward we will not see that we are in a jump. + ArrayDeque stack = new ArrayDeque<>(); + int size = instructions.size(); + for( int i = 0; i < size; i++ ) { + WasmInstruction instr = instructions.get( i ); + int popCount = instr.getPopCount(); + for( int p = 0; p < popCount; p++ ) { + stack.pop(); + } + AnyType pushValue = instr.getPushValueType(); + if( pushValue != null ) { + StackValue el = new StackValue(); + el.idx = i; + el.instr = instr; + stack.push( el ); + } + if( instr.getType() == Type.Jump ) { + if( popCount == 0 ) { // GOTO, for example on the end of the THEN branch + JumpInstruction jump = (JumpInstruction)instr; + int jumpPos = jump.getJumpPosition(); + if( jumpPos > javaCodePos ) { + // we need a stack position inside a branch, we can remove all outside + stack.clear(); + } else if( jumpPos > instr.getCodePosition() ) { + while( ++i < size && jumpPos > instructions.get( i ).getCodePosition() ) { + //nothing + } + i--; // we are on the right position but the loop increment + } + } + } + } + + try { + StackValue stackValue = null; + for( int p = 0; p < count; p++ ) { + stackValue = stack.pop(); + } + return stackValue; + } catch( NoSuchElementException ex ) { + throw new WasmException( "Push instruction not found", -1 ); // should never occur + } + } + + /** + * Hold the state of the stack. + */ + static class StackValue { + /** the instruction index that push the stack value */ + int idx; + + /** the instruction that push the stack value */ + WasmInstruction instr; + } +} diff --git a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java index d828c9f..4e5be44 100644 --- a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java +++ b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java @@ -151,11 +151,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. + * @param javaCodePos + * current code position for which the stack is inspected * @return the type of the last push value */ @Nonnull - AnyType findValueTypeFromStack( int count ) { - return findInstructionThatPushValue( count, (idx,instr ) -> instr.getPushValueType() ); + AnyType findValueTypeFromStack( int count, int javaCodePos ) { + return StackInspector.findInstructionThatPushValue( instructions, count, javaCodePos ).instr.getPushValueType(); } /** @@ -163,37 +165,12 @@ public abstract class WasmCodeBuilder { * * @param count * the count of values on the stack back. 1 means the last value. 2 means the penultimate value. + * @param javaCodePos + * current code position for which the stack is inspected * @return the instruction */ - private WasmInstruction findInstructionThatPushValue( int count ) { - return findInstructionThatPushValue( count, (index,instruction) -> instruction ); - } - - /** - * Find the instruction that push the x-th value to the stack. - * - * @param - * the return type - * @param count - * the count of values on the stack back. 1 means the last value. 2 means the penultimate value. - * @param funct - * the return value from the instruction - * @return the function value - */ - T findInstructionThatPushValue( int count, BiFunction funct ) { - int valueCount = 0; - List instructions = this.instructions; - for( int i = instructions.size() - 1; i >= 0; i-- ) { - WasmInstruction instr = instructions.get( i ); - AnyType valueType = instr.getPushValueType(); - if( valueType != null ) { - if( ++valueCount == count ) { - return funct.apply( i, instr ); - } - } - valueCount -= instr.getPopCount(); - } - throw new WasmException( "Push instruction not found", -1 ); // should never occur + private WasmInstruction findInstructionThatPushValue( int count, int javaCodePos ) { + return StackInspector.findInstructionThatPushValue( instructions, count, javaCodePos ).instr; } /** @@ -299,7 +276,7 @@ public abstract class WasmCodeBuilder { switch( op ) { case set: case tee: - AnyType valueType = findValueTypeFromStack( 1 ); + AnyType valueType = findValueTypeFromStack( 1, javaCodePos ); localVariables.useIndex( valueType, wasmIdx ); } instructions.add( new WasmLocalInstruction( op, wasmIdx, localVariables, javaCodePos, lineNumber ) ); @@ -314,7 +291,7 @@ public abstract class WasmCodeBuilder { * the line number in the Java source code */ protected void addDupInstruction( int javaCodePos, int lineNumber ) { - WasmInstruction instr = findInstructionThatPushValue( 1 ); + WasmInstruction instr = findInstructionThatPushValue( 1, javaCodePos ); AnyType type = instr.getPushValueType(); int varIndex = -1; // if it is a GET to a local variable then we can use it @@ -521,7 +498,7 @@ public abstract class WasmCodeBuilder { // find the instruction that this push on stack int count = indirectCall.getPopCount(); - WasmInstruction instr = findInstructionThatPushValue( count ); + WasmInstruction instr = findInstructionThatPushValue( count, indirectCall.getCodePosition() ); int varIndex = -1; // if it is a GET to a local variable then we can use it if( instr.getType() == Type.Local ) { diff --git a/test/de/inetsoftware/jwebassembly/runtime/StructsNonGC.java b/test/de/inetsoftware/jwebassembly/runtime/StructsNonGC.java index 92f6c14..79804f0 100644 --- a/test/de/inetsoftware/jwebassembly/runtime/StructsNonGC.java +++ b/test/de/inetsoftware/jwebassembly/runtime/StructsNonGC.java @@ -218,6 +218,15 @@ public class StructsNonGC extends AbstractBaseTest { Integer val = val1 == val2 ? val1 : val2; return val; } + + /** + * To find the instruction that push the object of the method call we need to consider an IF THEN ELSE when analyzing the stack. + */ + @Export + static int callParameterFromCondition() { + Abc abc = new Abc(); + return abc.add( 42, abc == null ? 7 : 13 ); + } } interface TestDefault { @@ -247,6 +256,10 @@ public class StructsNonGC extends AbstractBaseTest { int abstractBar() { return 2; } + + int add( int a, int b ) { + return a + b; + } } static class Abc2 extends Abc {