implements INSTANCEOF operation

This commit is contained in:
Volker Berlin 2020-02-26 18:02:59 +01:00
parent 0138eb5dc0
commit 6c820ba5e6
5 changed files with 121 additions and 28 deletions

View File

@ -52,6 +52,11 @@ public class TypeManager {
/** name of virtual function table, start with a point for an invalid Java identifier */
static final String VTABLE = ".vtable";
/**
* The reserved position on start of the vtable:
* <li>offset of interface call table (itable)
* <li>offset of instanceof list
*/
private static final int VTABLE_FIRST_FUNCTION_INDEX = 2;
private Map<String, StructType> structTypes = new LinkedHashMap<>();
@ -132,13 +137,13 @@ public class TypeManager {
}
/**
* Get the FunctionName for a virtual call and mark it as used. The function has 2 parameters (THIS,
* Create the FunctionName for a virtual call and mark it as used. The function has 2 parameters (THIS,
* virtualfunctionIndex) and returns the index of the function.
*
* @return the name
*/
@Nonnull
WatCodeSyntheticFunctionName getCallVirtualGC() {
WatCodeSyntheticFunctionName createCallVirtualGC() {
return new WatCodeSyntheticFunctionName( //
"callVirtual", "local.get 0 " // THIS
+ "struct.get java/lang/Object .vtable " // vtable is on index 0
@ -149,6 +154,49 @@ public class TypeManager {
, valueOf( "java/lang/Object" ), ValueType.i32, null, ValueType.i32 ); //
}
/**
* Create the FunctionName for the INSTANCEOF operation and mark it as used. The function has 2 parameters (THIS,
* classIndex) and returns true if there is a match.
*
* @return the name
*/
WatCodeSyntheticFunctionName createInstanceOf() {
return new WatCodeSyntheticFunctionName( //
"instanceof", "local.get 0 " // THIS
+ "struct.get java/lang/Object .vtable " // vtable is on index 0
+ "local.tee 2 " // save the vtable location
+ "i32.load offset=4 align=4 " // get offset of instanceof inside vtable (int position 1, byte position 4)
+ "local.get 2 " // get the vtable location
+ "i32.add " //
+ "local.tee 2 " // save the instanceof location
+ "i32.load offset=0 align=4 " // count of instanceof entries
+ "i32.const 4 " //
+ "i32.mul " //
+ "local.get 2 " // get the instanceof location
+ "i32.add " //
+ "local.set 3 " // save end position
+ "loop" //
+ " local.get 2 " // get the instanceof location pointer
+ " local.get 3 " // get the end location
+ " i32.eq" //
+ " if" // current offset == end offset
+ " i32.const 0" // not found
+ " return" //
+ " end" //
+ " local.get 2" // get the instanceof location pointer
+ " i32.const 4" //
+ " i32.add" // increment offset
+ " local.tee 2" // save the instanceof location pointer
+ " i32.load offset=0 align=4" //
+ " local.get 1" // the class index that we search
+ " i32.ne" //
+ " br_if 0 " //
+ "end " //
+ "i32.const 1 " // class/interface found
+ "return " //
, valueOf( "java/lang/Object" ), ValueType.i32, null, ValueType.i32 ); //
}
/**
* A reference to a type.
*
@ -259,9 +307,9 @@ public class TypeManager {
}
}
// add all interfaces to the instanceof
// add all interfaces to the instanceof set
for(ConstantClass interClass : classFile.getInterfaces() ) {
StructType type = types.structTypes.get( className );
StructType type = types.structTypes.get( interClass.getName() );
if( type != null ) {
instanceOFs.add( type );
}

View File

@ -52,11 +52,6 @@ import de.inetsoftware.jwebassembly.wasm.WasmBlockOperator;
*/
public abstract class WasmCodeBuilder {
/**
* declare for frequently use of virtual call with non GC mode.
*/
static final SyntheticFunctionName GET_I32 = new JavaScriptSyntheticFunctionName( "NonGC", "get_i32", () -> "(a,i) => a[i]", ValueType.anyref, ValueType.i32, null, ValueType.i32 );
private final LocaleVariableManager localVariables = new LocaleVariableManager();
private final List<WasmInstruction> instructions = new ArrayList<>();
@ -497,11 +492,7 @@ public abstract class WasmCodeBuilder {
}
indirectCall.setVariableIndexOfThis( varIndex );
instructions.add( indirectCall );
if( !options.useGC() ) {
// for later access of the vtable
functions.markAsNeeded( GET_I32 );
functions.markAsImport( GET_I32, GET_I32.getAnnotation() );
}
options.registerGet_i32(); // for later access of the vtable
}
/**
@ -643,6 +634,12 @@ public abstract class WasmCodeBuilder {
* the line number in the Java source code
*/
protected void addStructInstruction( StructOperator op, @Nonnull String typeName, @Nullable NamedStorageType fieldName, int javaCodePos, int lineNumber ) {
if( op == StructOperator.INSTANCEOF ) {
instructions.add( new WasmConstInstruction( types.valueOf( typeName ).getClassIndex(), javaCodePos, lineNumber ) );
FunctionName name = options.getInstanceOf();
instructions.add( new WasmCallInstruction( name, javaCodePos, lineNumber, types, false ) );
return;
}
WasmStructInstruction structInst = new WasmStructInstruction( op, typeName, fieldName, javaCodePos, lineNumber, types );
instructions.add( structInst );
if( !options.useGC() ) {

View File

@ -20,11 +20,13 @@ import java.util.HashMap;
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.ValueType;
/**
* The option/properties for the behavior of the compiler.
@ -55,9 +57,12 @@ public class WasmOptions {
*/
public FunctionName ref_eq;
private FunctionName get_i32;
private FunctionName callVirtual;
private FunctionName instanceOf;
/**
* Create a new instance of options
*
@ -112,6 +117,21 @@ public class WasmOptions {
return sourceMapBase;
}
/**
* Register FunctionName "NonGC.get_i32" for frequently access to vtable with non GC mode.
*/
void registerGet_i32() {
if( useGC ) {
return;
}
if( get_i32 == null ) {
SyntheticFunctionName name;
get_i32 = name = new JavaScriptSyntheticFunctionName( "NonGC", "get_i32", () -> "(a,i) => a[i]", ValueType.anyref, ValueType.i32, null, ValueType.i32 );
functions.markAsNeeded( name );
functions.markAsImport( name, name.getAnnotation() );
}
}
/**
* Get the FunctionName for a virtual call and mark it as used. The function has 2 parameters (THIS,
* virtualfunctionIndex) and returns the index of the function.
@ -122,8 +142,26 @@ public class WasmOptions {
FunctionName getCallVirtual() {
FunctionName name = callVirtual;
if( name == null ) {
callVirtual = name = types.getCallVirtualGC();
callVirtual = name = types.createCallVirtualGC();
functions.markAsNeeded( name );
registerGet_i32();
}
return name;
}
/**
* Get the FunctionName for an INSTANCEOF check and mark it as used. The function has 2 parameters (THIS,
* classIndex) and returns true or false.
*
* @return the name
*/
@Nonnull
FunctionName getInstanceOf() {
FunctionName name = instanceOf;
if( name == null ) {
instanceOf = name = types.createInstanceOf();
functions.markAsNeeded( name );
registerGet_i32();
}
return name;
}

View File

@ -126,19 +126,6 @@ public class RuntimeErrors {
}
}
@Test
public void instanceofCall() throws IOException {
compileErrorTest( "Unknown operator: INSTANCEOF", InstanceofMethod.class );
}
static class InstanceofMethod {
@Export
static boolean runnable() {
Object obj = new Object();
return obj instanceof Integer;
}
}
@Test
public void checkcast() throws IOException {
compileErrorTest( "Unknown operator: CAST", CheckcastMethod.class );

View File

@ -17,6 +17,8 @@ package de.inetsoftware.jwebassembly.runtime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import org.junit.ClassRule;
import org.junit.runners.Parameterized.Parameters;
@ -50,6 +52,9 @@ public class StructsNonGC extends AbstractBaseTest {
addParam( list, script, "useGlobalObject" );
addParam( list, script, "multipleAssign" );
addParam( list, script, "getDefaultValue" );
addParam( list, script, "instanceof1" );
addParam( list, script, "instanceof2" );
addParam( list, script, "instanceof3" );
}
rule.setTestParameters( list );
return list;
@ -143,6 +148,24 @@ public class StructsNonGC extends AbstractBaseTest {
Abc2 val = new Abc2();
return val.getDefault();
}
@Export
static boolean instanceof1() {
Object obj = new Object();
return obj instanceof Integer;
}
@Export
static boolean instanceof2() {
Object obj = new Object();
return obj instanceof Object;
}
@Export
static boolean instanceof3() {
Object obj = new LinkedList();
return obj instanceof List;
}
}
interface TestDefault {