diff --git a/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java b/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java index e27dc2b..cd52c43 100644 --- a/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java +++ b/src/de/inetsoftware/jwebassembly/module/ModuleGenerator.java @@ -102,6 +102,8 @@ public class ModuleGenerator { optimizer = options.optimizer; javaCodeBuilder.init( options, classFileLoader ); ((WasmCodeBuilder)watParser).init( options, classFileLoader ); + types.init( classFileLoader ); + scanLibraries( libraries ); } @@ -279,7 +281,7 @@ public class ModuleGenerator { scanFunctions(); functCount = functions.size(); // scan the functions can find new needed types or only new needed fields in the known types scanForClinit(); - types.scanTypeHierarchy( classFileLoader ); // scan the type hierarchy can find new functions + types.scanTypeHierarchy(); // scan the type hierarchy can find new functions } while( functCount < functions.size() ); // write only the needed imports to the output @@ -319,10 +321,10 @@ public class ModuleGenerator { } // scan again if there are new types or new needed fields - types.scanTypeHierarchy( classFileLoader ); + types.scanTypeHierarchy(); JWebAssembly.LOGGER.fine( "scan finish" ); - types.prepareFinish( writer, classFileLoader ); + types.prepareFinish( writer ); functions.prepareFinish(); strings.prepareFinish( writer ); writer.prepareFinish(); diff --git a/src/de/inetsoftware/jwebassembly/module/TypeManager.java b/src/de/inetsoftware/jwebassembly/module/TypeManager.java index dce8e59..5795332 100644 --- a/src/de/inetsoftware/jwebassembly/module/TypeManager.java +++ b/src/de/inetsoftware/jwebassembly/module/TypeManager.java @@ -18,6 +18,7 @@ package de.inetsoftware.jwebassembly.module; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -159,6 +160,8 @@ public class TypeManager { private int typeTableOffset; + private ClassFileLoader classFileLoader; + /** * Initialize the type manager. * @@ -169,6 +172,14 @@ public class TypeManager { this.options = options; } + /** + * Initialize the type manager + * @param classFileLoader for loading the class files + */ + void init( ClassFileLoader classFileLoader ) { + this.classFileLoader = classFileLoader; + } + /** * Count of used types * @@ -190,12 +201,10 @@ public class TypeManager { /** * Scan the hierarchy of the types. * - * @param classFileLoader - * for loading the class files * @throws IOException * if any I/O error occur on loading or writing */ - void scanTypeHierarchy( ClassFileLoader classFileLoader ) throws IOException { + void scanTypeHierarchy() throws IOException { for( StructType type : structTypes.values() ) { type.scanTypeHierarchy( options.functions, this, classFileLoader ); } @@ -206,12 +215,10 @@ public class TypeManager { * * @param writer * the targets for the types - * @param classFileLoader - * for loading the class files * @throws IOException * if any I/O error occur on loading or writing */ - void prepareFinish( ModuleWriter writer, ClassFileLoader classFileLoader ) throws IOException { + void prepareFinish( ModuleWriter writer ) throws IOException { isFinish = true; for( StructType type : structTypes.values() ) { type.writeStructType( writer ); @@ -570,7 +577,9 @@ public class TypeManager { private final String name; - private final StructTypeKind kind; + private final StructTypeKind kind; + + private final TypeManager manager; private final int classIndex; @@ -595,7 +604,7 @@ public class TypeManager { * Create a reference to type * * @param name - * the Java class name + * the Java class name like "java/lang/String" * @param kind * the type kind * @param manager @@ -604,6 +613,7 @@ public class TypeManager { protected StructType( @Nonnull String name, @Nonnull StructTypeKind kind, @Nonnull TypeManager manager ) { this.name = name; this.kind = kind; + this.manager = manager; switch( kind ) { case array_native: this.classIndex = -1; @@ -943,8 +953,43 @@ public class TypeManager { */ @Override public boolean isSubTypeOf( AnyType type ) { - //TODO if type is StructType (class or interface) - return type == this || type == ValueType.externref || type == ValueType.anyref || type == ValueType.eqref; + if( type == this || type == ValueType.externref || type == ValueType.anyref || type == ValueType.eqref ) { + return true; + } + if( !(type instanceof StructType) ) { + return false; + } + StructType structType = (StructType)type; + if( kind != structType.kind ) { + return false; + } + + try { + ClassFile classFile = manager.classFileLoader.get( name ); + if( classFile != null ) { + for( ConstantClass interClass : classFile.getInterfaces() ) { + if( interClass.getName().equals( structType.name ) ) { + return true; + } + } + + while( classFile != null ) { + ConstantClass superClass = classFile.getSuperClass(); + if( superClass == null ) { + break; + } + String superName = superClass.getName(); + if( superName.equals( structType.name ) ) { + return true; + } + classFile = manager.classFileLoader.get( superName ); + } + } + } catch( IOException ex ) { + throw new UncheckedIOException( ex ); + } + + return false; } /** diff --git a/test/de/inetsoftware/jwebassembly/module/StructTypeTest.java b/test/de/inetsoftware/jwebassembly/module/StructTypeTest.java new file mode 100644 index 0000000..262021f --- /dev/null +++ b/test/de/inetsoftware/jwebassembly/module/StructTypeTest.java @@ -0,0 +1,102 @@ +/* + * Copyright 2021 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 static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; + +import org.junit.Before; +import org.junit.Test; + +import de.inetsoftware.jwebassembly.JWebAssembly; +import de.inetsoftware.jwebassembly.module.TypeManager.StructType; +import de.inetsoftware.jwebassembly.module.TypeManager.StructTypeKind; + +/** + * @author Volker Berlin + */ +public class StructTypeTest { + + private TypeManager manager; + + @Before + public void before() { + HashMap properties = new HashMap<>(); + properties.put( JWebAssembly.WASM_USE_GC, "true" ); + WasmOptions options = new WasmOptions( properties ); + manager = options.types; + manager.init( new ClassFileLoader( getClass().getClassLoader() ) ); + } + + @Test + public void isSubTypeOf_SuperClass() throws Throwable { + StructType typeInteger = manager.valueOf( "java/lang/Integer" ); + StructType typeNumber = manager.valueOf( "java/lang/Number" ); + StructType typeObject = manager.valueOf( "java/lang/Object" ); + + assertTrue( typeInteger.isSubTypeOf( typeInteger ) ); + assertTrue( typeInteger.isSubTypeOf( typeNumber ) ); + assertTrue( typeInteger.isSubTypeOf( typeObject ) ); + + assertFalse( typeNumber.isSubTypeOf( typeInteger ) ); + assertFalse( typeObject.isSubTypeOf( typeInteger ) ); + assertFalse( typeObject.isSubTypeOf( typeNumber ) ); + } + + @Test + public void isSubTypeOf_Primitives() throws Throwable { + StructType typeObject = manager.valueOf( "java/lang/Object" ); + StructType typeBoolean = manager.valueOf( "boolean" ); + StructType typeInt = manager.valueOf( "int" ); + + assertEquals( StructTypeKind.primitive, typeBoolean.getKind() ); + assertEquals( StructTypeKind.primitive, typeInt.getKind() ); + assertEquals( StructTypeKind.normal, typeObject.getKind() ); + + assertFalse( typeBoolean.isSubTypeOf( typeInt ) ); + assertFalse( typeInt.isSubTypeOf( typeBoolean ) ); + + assertFalse( typeBoolean.isSubTypeOf( typeObject ) ); + assertFalse( typeObject.isSubTypeOf( typeBoolean ) ); + } + + @Test + public void isSubTypeOf_Interfaces() throws Throwable { + StructType typeInteger = manager.valueOf( "java/lang/Integer" ); + StructType typeComparable = manager.valueOf( "java/lang/Comparable" ); + + assertTrue( typeInteger.isSubTypeOf( typeComparable ) ); + assertFalse( typeComparable.isSubTypeOf( typeInteger ) ); + } + + @Test + public void isSubTypeOf_Arrays() throws Throwable { + StructType typeObject = manager.valueOf( "java/lang/Object" ); + StructType typeInteger = manager.valueOf( "java/lang/Integer" ); + StructType typeObjArray = manager.arrayType( typeObject ); + StructType typeIntArray = manager.arrayType( typeInteger ); + + assertTrue( typeObjArray.isSubTypeOf( typeObject ) ); + assertFalse( typeObjArray.isSubTypeOf( typeInteger ) ); + + assertTrue( typeIntArray.isSubTypeOf( typeObjArray ) ); + assertFalse( typeObjArray.isSubTypeOf( typeIntArray ) ); + } + +}