Rewrite stack analyze to handle conditional parameters on the stack.

This commit is contained in:
Volker Berlin 2020-03-29 14:31:58 +02:00
parent 7fd8d16403
commit c52bc2a0fe
4 changed files with 128 additions and 38 deletions

View File

@ -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

View File

@ -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<WasmInstruction> 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<StackValue> 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;
}
}

View File

@ -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 <T>
* 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> T findInstructionThatPushValue( int count, BiFunction<Integer, WasmInstruction, T> funct ) {
int valueCount = 0;
List<WasmInstruction> 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 ) {

View File

@ -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 {