From 6c820ba5e659c8a810b66ef6633734780bccce00 Mon Sep 17 00:00:00 2001 From: Volker Berlin Date: Wed, 26 Feb 2020 18:02:59 +0100 Subject: [PATCH] implements INSTANCEOF operation --- .../jwebassembly/module/TypeManager.java | 56 +++++++++++++++++-- .../jwebassembly/module/WasmCodeBuilder.java | 17 +++--- .../jwebassembly/module/WasmOptions.java | 40 ++++++++++++- .../jwebassembly/runtime/RuntimeErrors.java | 13 ----- .../jwebassembly/runtime/StructsNonGC.java | 23 ++++++++ 5 files changed, 121 insertions(+), 28 deletions(-) diff --git a/src/de/inetsoftware/jwebassembly/module/TypeManager.java b/src/de/inetsoftware/jwebassembly/module/TypeManager.java index 7f023bb..7199322 100644 --- a/src/de/inetsoftware/jwebassembly/module/TypeManager.java +++ b/src/de/inetsoftware/jwebassembly/module/TypeManager.java @@ -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: + *
  • offset of interface call table (itable) + *
  • offset of instanceof list + */ private static final int VTABLE_FIRST_FUNCTION_INDEX = 2; private Map 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 ); } diff --git a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java index ed864ad..b443d2e 100644 --- a/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java +++ b/src/de/inetsoftware/jwebassembly/module/WasmCodeBuilder.java @@ -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 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() ) { diff --git a/src/de/inetsoftware/jwebassembly/module/WasmOptions.java b/src/de/inetsoftware/jwebassembly/module/WasmOptions.java index be90bef..aefcaae 100644 --- a/src/de/inetsoftware/jwebassembly/module/WasmOptions.java +++ b/src/de/inetsoftware/jwebassembly/module/WasmOptions.java @@ -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; } diff --git a/test/de/inetsoftware/jwebassembly/runtime/RuntimeErrors.java b/test/de/inetsoftware/jwebassembly/runtime/RuntimeErrors.java index 45b848c..a3afda9 100644 --- a/test/de/inetsoftware/jwebassembly/runtime/RuntimeErrors.java +++ b/test/de/inetsoftware/jwebassembly/runtime/RuntimeErrors.java @@ -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 ); diff --git a/test/de/inetsoftware/jwebassembly/runtime/StructsNonGC.java b/test/de/inetsoftware/jwebassembly/runtime/StructsNonGC.java index 302e974..5243829 100644 --- a/test/de/inetsoftware/jwebassembly/runtime/StructsNonGC.java +++ b/test/de/inetsoftware/jwebassembly/runtime/StructsNonGC.java @@ -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 {