diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassFlags.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassFlags.java index debe36b38..943ec7b71 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassFlags.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassFlags.java @@ -32,6 +32,7 @@ public final class WasmGCClassFlags { public static final int VARARGS = 1 << 13; public static final int VOLATILE = 1 << 14; public static final int PRIMITIVE = 1 << 15; + public static final int ARRAY = 1 << 16; public static final int PRIMITIVE_KIND_SHIFT = 16; public static final int PRIMITIVE_BOOLEAN = 0; 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 82e4fdca7..594c3356f 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 @@ -121,12 +121,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private List initializerFunctionStatements = new ArrayList<>(); private WasmFunction createPrimitiveClassFunction; private WasmFunction createArrayClassFunction; + private WasmFunction fillRegularClassFunction; private final WasmGCSupertypeFunctionGenerator supertypeGenerator; private final WasmGCNewArrayFunctionGenerator newArrayGenerator; private String arrayDataFieldName; private int classTagOffset; - private int classFlagsOffset; + private int classFlagsOffset = -1; private int classNameOffset; private int classSimpleNameOffset; private int classCanonicalNameOffset; @@ -149,7 +150,6 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private WasmFunctionType arrayLengthType; private List nonInitializedStructures = new ArrayList<>(); private WasmArray objectArrayType; - private WasmArray enumConstantArray; public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource, ClassHierarchy hierarchy, @@ -242,7 +242,6 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit @Override public void contributeToInitializer(WasmFunction function) { - var classClass = standardClasses.classClass(); function.getBody().addAll(initializerFunctionStatements); initializerFunctionStatements.clear(); for (var classInfo : classInfoMap.values()) { @@ -261,8 +260,6 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit new WasmFunctionReference(newArrayFunction))); } } - function.getBody().add(setClassField(classInfo, CLASS_FIELD_OFFSET, - new WasmGetGlobal(classClass.pointer))); } for (var consumer : staticFieldInitializers) { consumer.accept(function); @@ -497,14 +494,15 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit standardClasses.classClass().getStructure().init(); var ranges = tagRegistry.getRanges(name); int tag = ranges.stream().mapToInt(range -> range.lower).min().orElse(0); - target.add(setClassField(classInfo, classTagOffset, new WasmInt32Constant(tag))); + int flags = cls != null ? getFlags(cls) : 0; + target.add(new WasmCall(getFillRegularClassFunction(), new WasmGetGlobal(classInfo.pointer), + new WasmInt32Constant(tag), new WasmInt32Constant(flags))); var metadataReq = metadataRequirements.getInfo(name); if (metadataReq.name()) { var namePtr = strings.getStringConstant(name).global; target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr))); } if (cls != null) { - target.add(setClassField(classInfo, classFlagsOffset, new WasmInt32Constant(getFlags(cls)))); if (metadataReq.simpleName() && cls.getSimpleName() != null) { var namePtr = strings.getStringConstant(cls.getSimpleName()).global; target.add(setClassField(classInfo, classSimpleNameOffset, new WasmGetGlobal(namePtr))); @@ -1091,36 +1089,52 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit fields.add(wasmField); } if (className.equals("java.lang.Class")) { + var cls = classSource.get("java.lang.Class"); classFlagsOffset = fields.size(); - fields.add(createClassField(WasmType.INT32.asStorage(), "lowerIndex")); + fields.add(createClassField(WasmType.INT32.asStorage(), "flags")); classTagOffset = fields.size(); - fields.add(createClassField(WasmType.INT32.asStorage(), "upperIndex")); - classParentOffset = fields.size(); - fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "parent")); + fields.add(createClassField(WasmType.INT32.asStorage(), "id")); + if (metadataRequirements.hasSuperclass()) { + classParentOffset = fields.size(); + fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "parent")); + } classArrayItemOffset = fields.size(); fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "arrayItem")); classArrayOffset = fields.size(); fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "array")); - classSupertypeFunctionOffset = fields.size(); - fields.add(createClassField(supertypeGenerator.getFunctionType().getReference().asStorage(), - "isSupertype")); - classNewArrayOffset = fields.size(); - fields.add(createClassField(newArrayGenerator.getNewArrayFunctionType().getReference().asStorage(), - "createArrayInstance")); - classEnclosingClassOffset = fields.size(); - fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "enclosingClass")); - classDeclaringClassOffset = fields.size(); - fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "declaringClass")); - classNameOffset = fields.size(); - fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "name")); - classSimpleNameOffset = fields.size(); - fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "simpleName")); - classCanonicalNameOffset = fields.size(); - fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "canonicalName")); + if (metadataRequirements.hasIsAssignable()) { + classSupertypeFunctionOffset = fields.size(); + fields.add(createClassField(supertypeGenerator.getFunctionType().getReference().asStorage(), + "isSupertype")); + } + if (metadataRequirements.hasArrayNewInstance()) { + classNewArrayOffset = fields.size(); + fields.add(createClassField(newArrayGenerator.getNewArrayFunctionType().getReference().asStorage(), + "createArrayInstance")); + } + if (metadataRequirements.hasEnclosingClass()) { + classEnclosingClassOffset = fields.size(); + fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "enclosingClass")); + } + if (metadataRequirements.hasDeclaringClass()) { + classDeclaringClassOffset = fields.size(); + fields.add(createClassField(standardClasses.classClass().getType().asStorage(), "declaringClass")); + } + if (metadataRequirements.hasName()) { + classNameOffset = fields.size(); + fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "name")); + } + if (metadataRequirements.hasSimpleName()) { + classSimpleNameOffset = fields.size(); + fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "simpleName")); + } + if (cls != null && cls.getMethod(new MethodDescriptor("getCanonicalName", String.class)) != null) { + classCanonicalNameOffset = fields.size(); + fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "canonicalName")); + } cloneOffset = fields.size(); fields.add(createClassField(functionTypes.of(standardClasses.objectClass().getType(), standardClasses.objectClass().getType()).getReference().asStorage(), "clone")); - fields.add(createClassField(WasmType.INT32.asStorage(), "enumConstantCount")); if (metadataRequirements.hasEnumConstants()) { enumConstantsFunctionOffset = fields.size(); var enumArrayType = getClassInfo(ValueType.arrayOf(ValueType.object("java.lang.Enum"))).getType(); @@ -1214,7 +1228,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit function.add(nameVar); function.add(kindVar); - standardClasses.classClass().getStructure().getFields().size(); + standardClasses.classClass().getStructure().init(); + function.getBody().add(new WasmStructSet( + standardClasses.classClass().getStructure(), + new WasmGetLocal(targetVar), + CLASS_FIELD_OFFSET, + new WasmGetGlobal(standardClasses.classClass().pointer) + )); var flagsExpr = new WasmIntBinary( WasmIntType.INT32, WasmIntBinaryOperation.SHL, @@ -1248,6 +1268,54 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit return function; } + private WasmFunction getFillRegularClassFunction() { + if (fillRegularClassFunction == null) { + fillRegularClassFunction = createFillRegularClassFunction(); + } + return fillRegularClassFunction; + } + + private WasmFunction createFillRegularClassFunction() { + var functionType = functionTypes.of( + null, + standardClasses.classClass().getType(), + WasmType.INT32, + WasmType.INT32 + ); + var function = new WasmFunction(functionType); + module.functions.add(function); + function.setName(names.topLevel("teavm@fillRegularClass")); + + var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target"); + var idVar = new WasmLocal(standardClasses.classClass().getType(), "id"); + var flagsVar = new WasmLocal(standardClasses.classClass().getType(), "flags"); + function.add(targetVar); + function.add(idVar); + function.add(flagsVar); + standardClasses.classClass().getStructure().init(); + + function.getBody().add(new WasmStructSet( + standardClasses.classClass().getStructure(), + new WasmGetLocal(targetVar), + CLASS_FIELD_OFFSET, + new WasmGetGlobal(standardClasses.classClass().pointer) + )); + function.getBody().add(new WasmStructSet( + standardClasses.classClass().getStructure(), + new WasmGetLocal(targetVar), + classTagOffset, + new WasmGetLocal(idVar) + )); + function.getBody().add(new WasmStructSet( + standardClasses.classClass().getStructure(), + new WasmGetLocal(targetVar), + classFlagsOffset, + new WasmGetLocal(flagsVar) + )); + + return function; + } + private WasmFunction getCreateArrayClassFunction() { if (createArrayClassFunction == null) { createArrayClassFunction = createCreateArrayClassFunction(); @@ -1271,6 +1339,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit function.add(itemVar); standardClasses.classClass().getStructure().init(); + function.getBody().add(new WasmStructSet( + standardClasses.classClass().getStructure(), + new WasmGetLocal(targetVar), + CLASS_FIELD_OFFSET, + new WasmGetGlobal(standardClasses.classClass().pointer) + )); function.getBody().add(new WasmStructSet( standardClasses.classClass().getStructure(), new WasmGetLocal(targetVar), diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java index de9667143..bf73a591f 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCSupertypeFunctionGenerator.java @@ -174,7 +174,6 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction return functionType; } - private WasmExpression getClassField(WasmExpression instance, int fieldIndex) { return new WasmStructGet( classGenerator.standardClasses.classClass().getStructure(), 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 547e26735..a29885f43 100644 --- a/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java +++ b/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java @@ -47,15 +47,24 @@ public class ClassMetadataRequirements { private boolean hasArrayGet; private boolean hasArrayLength; private boolean hasEnumConstants; + private boolean hasSuperclass; + private boolean hasIsAssignable; + private boolean hasNewInstance; + private boolean hasEnclosingClass; + private boolean hasDeclaringClass; + private boolean hasSimpleName; + private boolean hasName; public ClassMetadataRequirements(DependencyInfo dependencyInfo) { MethodDependencyInfo getNameMethod = dependencyInfo.getMethod(GET_NAME_METHOD); if (getNameMethod != null) { + hasName = true; addClassesRequiringName(requirements, getNameMethod.getVariable(0).getClassValueNode().getTypes()); } MethodDependencyInfo getSimpleNameMethod = dependencyInfo.getMethod(GET_SIMPLE_NAME_METHOD); if (getSimpleNameMethod != null) { + hasSimpleName = true; String[] classNames = getSimpleNameMethod.getVariable(0).getClassValueNode().getTypes(); addClassesRequiringName(requirements, classNames); for (String className : classNames) { @@ -67,6 +76,7 @@ public class ClassMetadataRequirements { var getSuperclassMethod = dependencyInfo.getMethod(GET_SUPERCLASS_METHOD); if (getSuperclassMethod != null) { + hasSuperclass = true; var classNames = getSuperclassMethod.getVariable(0).getClassValueNode().getTypes(); for (var className : classNames) { requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).superclass = true; @@ -75,6 +85,7 @@ public class ClassMetadataRequirements { var isAssignableMethod = dependencyInfo.getMethod(IS_ASSIGNABLE_METHOD); if (isAssignableMethod != null) { + hasIsAssignable = true; var classNames = isAssignableMethod.getVariable(0).getClassValueNode().getTypes(); for (var className : classNames) { requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).isAssignable = true; @@ -83,6 +94,7 @@ public class ClassMetadataRequirements { MethodDependencyInfo getDeclaringClassMethod = dependencyInfo.getMethod(GET_DECLARING_CLASS_METHOD); if (getDeclaringClassMethod != null) { + hasDeclaringClass = true; String[] classNames = getDeclaringClassMethod.getVariable(0).getClassValueNode().getTypes(); for (String className : classNames) { requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).declaringClass = true; @@ -91,6 +103,7 @@ public class ClassMetadataRequirements { MethodDependencyInfo getEnclosingClassMethod = dependencyInfo.getMethod(GET_ENCLOSING_CLASS_METHOD); if (getEnclosingClassMethod != null) { + hasEnclosingClass = true; String[] classNames = getEnclosingClassMethod.getVariable(0).getClassValueNode().getTypes(); for (String className : classNames) { requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).enclosingClass = true; @@ -99,6 +112,7 @@ public class ClassMetadataRequirements { var newArrayMethod = dependencyInfo.getMethod(NEW_ARRAY); if (newArrayMethod != null) { + hasNewInstance = true; var classNames = newArrayMethod.getVariable(1).getClassValueNode().getTypes(); for (var className : classNames) { requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).newArray = true; @@ -172,6 +186,34 @@ public class ClassMetadataRequirements { return hasEnumConstants; } + public boolean hasSuperclass() { + return hasSuperclass; + } + + public boolean hasIsAssignable() { + return hasIsAssignable; + } + + public boolean hasArrayNewInstance() { + return hasNewInstance; + } + + public boolean hasEnclosingClass() { + return hasEnclosingClass; + } + + public boolean hasDeclaringClass() { + return hasDeclaringClass; + } + + public boolean hasSimpleName() { + return hasSimpleName; + } + + public boolean hasName() { + return hasName; + } + private void addClassesRequiringName(Map target, String[] source) { for (String typeName : source) { target.computeIfAbsent(decodeType(typeName), k -> new ClassInfo()).name = true;