diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java index 4fc5b5d21..280034643 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java @@ -157,10 +157,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit function.getBody().addAll(initializerFunctionStatements); initializerFunctionStatements.clear(); for (var classInfo : classInfoMap.values()) { - var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType()); - supertypeFunction.setReferenced(true); - function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset, - new WasmFunctionReference(supertypeFunction))); + var req = metadataRequirements.getInfo(classInfo.getValueType()); + if (req != null && req.isAssignable()) { + var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType()); + supertypeFunction.setReferenced(true); + function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset, + new WasmFunctionReference(supertypeFunction))); + } function.getBody().add(setClassField(classInfo, CLASS_FIELD_OFFSET, new WasmGetGlobal(classClass.pointer))); if (classInfo.initializerPointer != null) { @@ -265,9 +268,11 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit default: throw new IllegalArgumentException(); } + var req = metadataRequirements.getInfo(type); + var name = req != null && req.name() ? ReflectionUtil.typeName(type.getKind()) : null; target.add(fillPrimitiveClass( classInfo.pointer, - ReflectionUtil.typeName(type.getKind()), + name, kind )); }; @@ -401,10 +406,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit } private WasmExpression fillPrimitiveClass(WasmGlobal global, String name, int kind) { + var str = name != null + ? new WasmGetGlobal(strings.getStringConstant(name).global) + : new WasmNullConstant(standardClasses.stringClass().getType()); return new WasmCall( getCreatePrimitiveClassFunction(), new WasmGetGlobal(global), - new WasmGetGlobal(strings.getStringConstant(name).global), + str, new WasmInt32Constant(kind) ); } diff --git a/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java b/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java index a5e56b260..037f45929 100644 --- a/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java +++ b/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java @@ -20,6 +20,7 @@ import java.util.Map; import org.teavm.dependency.DependencyInfo; import org.teavm.dependency.MethodDependencyInfo; import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; public class ClassMetadataRequirements { private static final MethodReference GET_NAME_METHOD = new MethodReference(Class.class, "getName", String.class); @@ -27,12 +28,14 @@ public class ClassMetadataRequirements { "getSimpleName", String.class); private static final MethodReference GET_SUPERCLASS_METHOD = new MethodReference(Class.class, "getSuperclass", Class.class); + private static final MethodReference IS_ASSIGNABLE_METHOD = new MethodReference(Class.class, "isAssignableFrom", + Class.class, boolean.class); private static final MethodReference GET_DECLARING_CLASS_METHOD = new MethodReference(Class.class, "getDeclaringClass", Class.class); private static final MethodReference GET_ENCLOSING_CLASS_METHOD = new MethodReference(Class.class, "getEnclosingClass", Class.class); private static final ClassInfo EMPTY_INFO = new ClassInfo(); - private Map requirements = new HashMap<>(); + private Map requirements = new HashMap<>(); public ClassMetadataRequirements(DependencyInfo dependencyInfo) { MethodDependencyInfo getNameMethod = dependencyInfo.getMethod(GET_NAME_METHOD); @@ -45,7 +48,7 @@ public class ClassMetadataRequirements { String[] classNames = getSimpleNameMethod.getVariable(0).getClassValueNode().getTypes(); addClassesRequiringName(requirements, classNames); for (String className : classNames) { - ClassInfo classInfo = requirements.computeIfAbsent(className, k -> new ClassInfo()); + ClassInfo classInfo = requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()); classInfo.simpleName = true; classInfo.enclosingClass = true; } @@ -53,9 +56,17 @@ public class ClassMetadataRequirements { var getSuperclassMethod = dependencyInfo.getMethod(GET_SUPERCLASS_METHOD); if (getSuperclassMethod != null) { - String[] classNames = getSuperclassMethod.getVariable(0).getClassValueNode().getTypes(); - for (String className : classNames) { - requirements.computeIfAbsent(className, k -> new ClassInfo()).declaringClass = true; + var classNames = getSuperclassMethod.getVariable(0).getClassValueNode().getTypes(); + for (var className : classNames) { + requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).declaringClass = true; + } + } + + var isAssignableMethod = dependencyInfo.getMethod(IS_ASSIGNABLE_METHOD); + if (isAssignableMethod != null) { + var classNames = getSuperclassMethod.getVariable(0).getClassValueNode().getTypes(); + for (var className : classNames) { + requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).isAssignable = true; } } @@ -63,7 +74,7 @@ public class ClassMetadataRequirements { if (getDeclaringClassMethod != null) { String[] classNames = getDeclaringClassMethod.getVariable(0).getClassValueNode().getTypes(); for (String className : classNames) { - requirements.computeIfAbsent(className, k -> new ClassInfo()).declaringClass = true; + requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).declaringClass = true; } } @@ -71,12 +82,16 @@ public class ClassMetadataRequirements { if (getEnclosingClassMethod != null) { String[] classNames = getEnclosingClassMethod.getVariable(0).getClassValueNode().getTypes(); for (String className : classNames) { - requirements.computeIfAbsent(className, k -> new ClassInfo()).enclosingClass = true; + requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).enclosingClass = true; } } } public Info getInfo(String className) { + return getInfo(ValueType.object(className)); + } + + public Info getInfo(ValueType className) { ClassInfo result = requirements.get(className); if (result == null) { result = EMPTY_INFO; @@ -84,19 +99,19 @@ public class ClassMetadataRequirements { return result; } - private void addClassesRequiringName(Map target, String[] source) { + private void addClassesRequiringName(Map target, String[] source) { for (String typeName : source) { - if (typeName.startsWith("[")) { - if (!typeName.endsWith(";")) { - continue; - } - int index = 0; - while (typeName.charAt(index) == '[') { - ++index; - } - typeName = typeName.substring(index, typeName.length() - 1).replace('/', '.'); - } - target.computeIfAbsent(typeName, k -> new ClassInfo()).name = true; + target.computeIfAbsent(decodeType(typeName), k -> new ClassInfo()).name = true; + } + } + + private ValueType decodeType(String typeName) { + if (typeName.startsWith("[")) { + return ValueType.parseIfPossible(typeName); + } else if (typeName.startsWith("~")) { + return ValueType.parseIfPossible(typeName.substring(1)); + } else { + return ValueType.object(typeName); } } @@ -106,6 +121,7 @@ public class ClassMetadataRequirements { boolean declaringClass; boolean enclosingClass; boolean superclass; + boolean isAssignable; @Override public boolean name() { @@ -131,6 +147,11 @@ public class ClassMetadataRequirements { public boolean superclass() { return superclass; } + + @Override + public boolean isAssignable() { + return isAssignable; + } } public interface Info { @@ -143,5 +164,7 @@ public class ClassMetadataRequirements { boolean enclosingClass(); boolean superclass(); + + boolean isAssignable(); } }