1
0
mirror of https://github.com/i-net-software/JWebAssembly.git synced 2025-03-25 23:47:51 +01:00
2019-04-14 15:42:37 +02:00

351 lines
11 KiB
Java

/*
Copyright 2018 - 2019 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.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import javax.annotation.Nullable;
import de.inetsoftware.classparser.LocalVariable;
import de.inetsoftware.classparser.LocalVariableTable;
import de.inetsoftware.jwebassembly.WasmException;
import de.inetsoftware.jwebassembly.wasm.AnyType;
import de.inetsoftware.jwebassembly.wasm.ValueType;
/**
* This manager monitor the locale variables of a method to create a translation from the slot based index in Java to
* the variable based index in WebAssembly. An 8-byte variable of type long and double consumes in Java 2 slots, but
* only one index in WebAssmenbly.
*
* @author Volker Berlin
*
*/
class LocaleVariableManager {
private Variable[] variables;
private int size;
private final ArrayList<AnyType> localTypes = new ArrayList<>();
private final HashSet<String> names = new HashSet<>();
/**
* Create a new instance.
*/
LocaleVariableManager() {
// initialize with a initial capacity that should be enough for the most methods
variables = new Variable[8];
for( int i = 0; i < variables.length; i++ ) {
variables[i] = new Variable();
}
}
/**
* Reset the manager to an initial state
*
* @param variableTable
* variable table of the Java method.
*/
void reset( LocalVariableTable variableTable ) {
size = 0;
if( variableTable == null ) {
return;
}
/**
* Java can use reuse a variable slot in a different block. The type can be different in the block. WebAssembly
* does not support a type change for a local variable. That we need to create 2 variables. This try the follow
* complex code.
*/
LocalVariable[] vars = variableTable.getTable();
ensureCapacity( vars.length );
// transfer all declarations from the LocalVariableTable
for( int i = 0; i < vars.length; i++ ) {
LocalVariable local = vars[i];
Variable var = variables[size];
var.valueType = ValueType.getValueType( local.getSignature() );
var.name = local.getName();
var.idx = local.getIndex();
var.startPos = local.getStartPosition() - 2;
var.endPos = local.getStartPosition() + local.getLengthPosition();
size++;
}
// sort to make sure but it should already sorted
Arrays.sort( variables, 0, size, (Comparator<Variable>)( v1, v2 ) -> {
int comp = Integer.compare( v1.idx, v2.idx );
if( comp != 0 ) {
return comp;
}
return Integer.compare( v1.startPos, v2.startPos );
} );
// reduce all duplications if there are no conflicts and expands startPos and endPos
for( int i = 0; i < size - 1; i++ ) {
Variable var = variables[i];
int j = i + 1;
Variable var2 = variables[j];
if( var.idx == var2.idx ) {
if( var.valueType == var2.valueType ) {
var.endPos = var2.endPos;
size--;
int count = size - j;
if( count > 0 ) {
System.arraycopy( variables, j + 1, variables, j, count );
variables[size] = var2;
}
i--;
continue;
}
} else {
var.endPos = Integer.MAX_VALUE;
var2.startPos = 0;
}
}
if( size > 0 ) {
variables[0].startPos = 0;
variables[size - 1].endPos = Integer.MAX_VALUE;
}
// make the names unique if there conflicts. Java can use the same variable name in different blocks. WebAssembly text output does not accept this.
names.clear();
for( int i = 0; i < size; i++ ) {
Variable var = variables[i];
var.valueType = null; // TODO temporary hack
var.name = findUniqueVarName( var.name );
}
// add all missing slots that we can add self temporary variables
int maxLocals = variableTable.getMaxLocals();
NEXT: for( int i = 0; i < maxLocals; i++ ) {
for( int j = 0; j < size; j++ ) {
Variable var = variables[j];
if( var.idx == i ) {
continue NEXT;
}
}
ensureCapacity( size + 1 );
Variable var = variables[size];
var.valueType = null;
var.name = null;
var.idx = i;
var.startPos = 0;
var.endPos = Integer.MAX_VALUE;
size++;
}
}
/**
* Find a unique variable name.
*
* @param name
* the suggested name
* @return a name that not was used before
*/
private String findUniqueVarName( String name ) {
if( names.contains( name ) ) {
// duplicate name for a variable in a different block
int id = 1;
do {
String name2 = name + '_' + ++id;
if( !names.contains( name2 ) ) {
name = name2;
break;
}
} while( true );
}
names.add( name );
return name;
}
/**
* Mark a variable slot as used with its type.
*
* @param valueType
* the type of the local variable
* @param slot
* the memory/slot index of the local variable
* @param javaCodePos
* the code position/offset in the Java method
*/
void use( AnyType valueType, int slot, int javaCodePos ) {
int idx = get( slot, javaCodePos );
Variable var = variables[idx];
if( var.valueType != null && var.valueType != valueType ) {
if( var.valueType.getCode() >= 0 && valueType == ValueType.anyref ) {
return;
}
if( valueType.getCode() >= 0 && var.valueType == ValueType.anyref ) {
// set the more specific type
} else {
throw new WasmException( "Redefine local variable '" + var.name + "' type from " + var.valueType + " to " + valueType + " in slot "
+ slot, null, null, -1 );
}
}
var.valueType = valueType;
}
/**
* Calculate the WebAssembly index position on the consumed data.
*/
void calculate() {
for( int i = 0; i < size; i++ ) {
Variable var = variables[i];
if( var.valueType == null ) {
size--;
System.arraycopy( variables, i + 1, variables, i, size - i );
variables[size] = var;
i--;
}
}
}
/**
* Get the data types of the local variables. The value is only valid until the next call.
*
* @param paramCount
* the count of method parameter which should be exclude
* @return the reused list with fresh values
*/
List<AnyType> getLocalTypes( int paramCount ) {
localTypes.clear();
for( int i = paramCount; i < size; i++ ) {
Variable var = variables[i];
localTypes.add( var.valueType );
}
return localTypes;
}
/**
* Get the name of the variable or null if no name available
*
* @param idx
* the wasm variable index
* @return the name
*/
@Nullable
String getLocalName( int idx ) {
return variables[idx].name;
}
/**
* Get the slot of the temporary variable.
*
* @param valueType
* the valueType for the variable
* @param startCodePosition
* the start of the Java code position
* @param endCodePosition
* the end of the Java code position
* @return the slot
*/
int getTempVariable( AnyType valueType, int startCodePosition, int endCodePosition ) {
ensureCapacity( size + 1 );
Variable var = variables[size];
var.valueType = valueType;
var.name = null;
var.idx = size;
var.startPos = startCodePosition;
var.endPos = endCodePosition;
return size++;
}
/**
* Get the WebAssembly variable index of the given Java Slot.
*
* @param slot
* the memory/slot index of the local variable in Java
* @param javaCodePos the current code position in the Java method
* @return the variable index in WebAssembly
*/
int get( int slot, int javaCodePos ) {
for( int i = 0; i < size; i++ ) {
Variable var = variables[i];
if( slot != var.idx ) {
continue;
}
if( var.matchCodePosition( javaCodePos ) ) {
return i;
}
}
throw new WasmException( "Can not find local variable for slot: " + slot + " on code position " + javaCodePos, -1 );
}
/**
* Get the ValueType of the variable.
*
* @param slot
* the memory/slot index of the local variable in Java
* @return the ValueType
*/
AnyType getValueType( int slot ) {
return variables[slot].valueType;
}
/**
* Ensure that there is enough capacity.
*
* @param slot
* the needed slot
*/
private void ensureCapacity( int slot ) {
if( slot >= variables.length ) {
int i = variables.length;
variables = Arrays.copyOf( variables, slot + 1 );
for( ; i < variables.length; i++ ) {
variables[i] = new Variable();
}
}
}
/**
* The state of a single local variable slot.
*/
private static class Variable {
private AnyType valueType;
private String name;
private int idx = -1;
private int startPos;
private int endPos;
/**
* If the variable is valid at this position
*
* @param codePosition
* the position to check
* @return true, if this variable match
*/
public boolean matchCodePosition( int codePosition ) {
return startPos <= codePosition && codePosition <= endPos;
}
}
}