From 2d8556d0a28633a1be9d5601bc9f6b2d28ddb2fc Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 9 Sep 2024 20:44:40 +0200 Subject: [PATCH] wasm gc: implement enum constants --- .../impl/reflection/ClassSupport.java | 26 +++++++ .../org/teavm/classlib/java/lang/TClass.java | 9 ++- .../classlib/java/util/TGenericEnumSet.java | 12 +++- .../backend/wasm/gc/WasmGCDependencies.java | 4 +- .../gc/classes/WasmGCClassGenerator.java | 71 +++++++++++++++++-- .../gc/classes/WasmGCClassInfoProvider.java | 2 + .../gc/methods/WasmGCGenerationUtil.java | 18 ++++- .../wasm/intrinsics/gc/ClassIntrinsic.java | 3 +- .../intrinsics/gc/ClassSupportIntrinsic.java | 35 +++++++++ .../wasm/intrinsics/gc/WasmGCIntrinsics.java | 8 +++ .../backend/wasm/runtime/WasmGCSupport.java | 2 +- .../analysis/ClassMetadataRequirements.java | 30 ++++++++ .../plugin/EnumDependencySupport.java | 7 +- 13 files changed, 211 insertions(+), 16 deletions(-) create mode 100644 classlib/src/main/java/org/teavm/classlib/impl/reflection/ClassSupport.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassSupportIntrinsic.java diff --git a/classlib/src/main/java/org/teavm/classlib/impl/reflection/ClassSupport.java b/classlib/src/main/java/org/teavm/classlib/impl/reflection/ClassSupport.java new file mode 100644 index 000000000..fc39403b7 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/impl/reflection/ClassSupport.java @@ -0,0 +1,26 @@ +/* + * Copyright 2024 Alexey Andreev. + * + * 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 org.teavm.classlib.impl.reflection; + +import org.teavm.interop.NoSideEffects; + +public final class ClassSupport { + private ClassSupport() { + } + + @NoSideEffects + public static native Enum[] getEnumConstants(Class cls); +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java index afc2c3acd..f35be8e55 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java @@ -29,6 +29,7 @@ import java.util.Set; import org.teavm.backend.javascript.spi.GeneratedBy; import org.teavm.backend.javascript.spi.InjectedBy; import org.teavm.classlib.PlatformDetector; +import org.teavm.classlib.impl.reflection.ClassSupport; import org.teavm.classlib.impl.reflection.Flags; import org.teavm.classlib.impl.reflection.JSClass; import org.teavm.classlib.impl.reflection.JSField; @@ -692,8 +693,12 @@ public final class TClass extends TObject implements TAnnotatedElement, TType if (!isEnum()) { return null; } - Platform.initClass(platformClass); - return (T[]) Platform.getEnumConstants(platformClass).clone(); + if (PlatformDetector.isWebAssemblyGC()) { + return (T[]) ClassSupport.getEnumConstants((Class) (Object) this); + } else { + Platform.initClass(platformClass); + return (T[]) Platform.getEnumConstants(platformClass).clone(); + } } @SuppressWarnings("unchecked") diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/TGenericEnumSet.java b/classlib/src/main/java/org/teavm/classlib/java/util/TGenericEnumSet.java index 8ee30159b..df5208228 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/TGenericEnumSet.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/TGenericEnumSet.java @@ -18,6 +18,8 @@ package org.teavm.classlib.java.util; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; +import org.teavm.classlib.PlatformDetector; +import org.teavm.classlib.impl.reflection.ClassSupport; import org.teavm.classlib.java.lang.TClass; import org.teavm.platform.Platform; import org.teavm.platform.PlatformClass; @@ -43,9 +45,13 @@ class TGenericEnumSet> extends TEnumSet { } static Enum[] getConstants(Class cls) { - PlatformClass platformClass = ((TClass) (Object) cls).getPlatformClass(); - Platform.initClass(platformClass); - return Platform.getEnumConstants(platformClass); + if (PlatformDetector.isWebAssemblyGC()) { + return ClassSupport.getEnumConstants(cls); + } else { + PlatformClass platformClass = ((TClass) (Object) cls).getPlatformClass(); + Platform.initClass(platformClass); + return Platform.getEnumConstants(platformClass); + } } @Override diff --git a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java index 73d7c8888..6cbc2bb84 100644 --- a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java +++ b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCDependencies.java @@ -98,8 +98,8 @@ public class WasmGCDependencies { analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "aiiobe", ArrayIndexOutOfBoundsException.class)) .use(); analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "cce", ClassCastException.class)).use(); - analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "throwCloneNotSupportedException", - void.class)).use(); + analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "defaultClone", Object.class, + Object.class)).use(); } private void contributeInitializerUtils() { 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 fb6fe9ef6..298373dba 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 @@ -31,8 +31,10 @@ import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTable; import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTableEntry; import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTableProvider; +import org.teavm.backend.wasm.generate.TemporaryVariablePool; import org.teavm.backend.wasm.generate.gc.WasmGCInitializerContributor; import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider; +import org.teavm.backend.wasm.generate.gc.methods.WasmGCGenerationUtil; import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool; import org.teavm.backend.wasm.model.WasmArray; import org.teavm.backend.wasm.model.WasmField; @@ -48,7 +50,9 @@ import org.teavm.backend.wasm.model.expression.WasmArrayCopy; import org.teavm.backend.wasm.model.expression.WasmArrayGet; import org.teavm.backend.wasm.model.expression.WasmArrayLength; import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault; +import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmCall; +import org.teavm.backend.wasm.model.expression.WasmCallReference; import org.teavm.backend.wasm.model.expression.WasmCast; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmFloat32Constant; @@ -72,6 +76,7 @@ import org.teavm.backend.wasm.model.expression.WasmStructNewDefault; import org.teavm.backend.wasm.model.expression.WasmStructSet; import org.teavm.backend.wasm.runtime.WasmGCSupport; import org.teavm.model.ClassHierarchy; +import org.teavm.model.ClassReader; import org.teavm.model.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.FieldReference; @@ -131,6 +136,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private int classSupertypeFunctionOffset; private int classEnclosingClassOffset; private int virtualTableFieldOffset; + private int enumConstantsFunctionOffset; private int arrayLengthOffset = -1; private int arrayGetOffset = -1; private int cloneOffset = -1; @@ -140,6 +146,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private WasmFunctionType arrayGetType; private WasmFunctionType arrayLengthType; private List nonInitializedStructures = new ArrayList<>(); + private WasmArray objectArrayType; + private WasmArray enumConstantArray; public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource, ClassHierarchy hierarchy, @@ -494,11 +502,15 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit cloneFunction = generateCloneFunction(classInfo, name); } else { cloneFunction = functionProvider.forStaticMethod(new MethodReference( - WasmGCSupport.class, "throwCloneNotSupportedException", void.class)); + WasmGCSupport.class, "defaultClone", Object.class, Object.class)); } cloneFunction.setReferenced(true); target.add(setClassField(classInfo, cloneOffset, new WasmFunctionReference(cloneFunction))); } + if (metadataReq.enumConstants() && cls.hasModifier(ElementModifier.ENUM)) { + target.add(setClassField(classInfo, enumConstantsFunctionOffset, + new WasmFunctionReference(createEnumConstantsFunction(classInfo, cls)))); + } } if (virtualTable != null && virtualTable.isConcrete()) { fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable); @@ -881,6 +893,11 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit return arrayGetOffset; } + @Override + public int getEnumConstantsFunctionOffset() { + return enumConstantsFunctionOffset; + } + private void initArrayClass(WasmGCClassInfo classInfo, ValueType.Array type) { classInfo.initializer = target -> { var itemTypeInfo = getClassInfo(type.getItemType()); @@ -1047,6 +1064,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit 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(); + var enumConstantsType = functionTypes.of(enumArrayType); + fields.add(createClassField(enumConstantsType.getReference().asStorage(), "getEnumConstants")); + } virtualTableFieldOffset = fields.size(); } } @@ -1057,6 +1081,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private void fillArrayFields(WasmGCClassInfo classInfo, ValueType elementType) { WasmStorageType wasmElementType; + WasmArray wasmArray; if (elementType instanceof ValueType.Primitive) { switch (((ValueType.Primitive) elementType).getKind()) { case BOOLEAN: @@ -1082,12 +1107,21 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit default: throw new IllegalArgumentException(); } + var wasmArrayName = names.topLevel(names.suggestForType(classInfo.getValueType()) + "$Data"); + wasmArray = new WasmArray(wasmArrayName, wasmElementType); + module.types.add(wasmArray); } else { wasmElementType = standardClasses.objectClass().getType().asStorage(); + wasmArray = objectArrayType; + if (wasmArray == null) { + var wasmArrayName = names.topLevel(names.suggestForType(ValueType.arrayOf( + ValueType.object("java.lang.Object"))) + "$Data"); + wasmArray = new WasmArray(wasmArrayName, wasmElementType); + module.types.add(wasmArray); + objectArrayType = wasmArray; + } } - var wasmArrayName = names.topLevel(names.suggestForType(classInfo.getValueType()) + "$Data"); - var wasmArray = new WasmArray(wasmArrayName, wasmElementType); - module.types.add(wasmArray); + classInfo.structure.getFields().add(new WasmField(wasmArray.getReference().asStorage(), arrayDataFieldName())); } @@ -1214,6 +1248,35 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit return function; } + private WasmFunction createEnumConstantsFunction(WasmGCClassInfo classInfo, ClassReader cls) { + var enumArrayStruct = getClassInfo(ValueType.parse(Enum[].class)).structure; + var function = new WasmFunction(functionTypes.of(enumArrayStruct.getReference())); + function.setName(names.topLevel(cls.getName() + "@constants")); + module.functions.add(function); + function.setReferenced(true); + + var fields = cls.getFields().stream() + .filter(field -> field.hasModifier(ElementModifier.ENUM)) + .filter(field -> field.hasModifier(ElementModifier.STATIC)) + .map(field -> new WasmGetGlobal(getStaticFieldLocation(field.getReference()))) + .collect(Collectors.toList()); + + if (classInfo.getInitializerPointer() != null) { + function.getBody().add(new WasmCallReference(new WasmGetGlobal(classInfo.getInitializerPointer()), + functionTypes.of(null))); + } + + var tempVars = new TemporaryVariablePool(function); + var util = new WasmGCGenerationUtil(this, tempVars); + var local = tempVars.acquire(enumArrayStruct.getReference()); + var block = new WasmBlock(false); + block.setType(enumArrayStruct.getReference()); + util.allocateArray(ValueType.parse(Enum.class), fields, null, null, block.getBody()); + function.getBody().add(new WasmReturn(block)); + tempVars.release(local); + + return function; + } private WasmExpression setClassField(WasmGCClassInfo classInfo, int fieldIndex, WasmExpression value) { return new WasmStructSet( diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java index 574259aef..448875c92 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java @@ -54,6 +54,8 @@ public interface WasmGCClassInfoProvider { int getArrayLengthOffset(); + int getEnumConstantsFunctionOffset(); + int getCloneOffset(); default WasmGCClassInfo getClassInfo(String name) { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationUtil.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationUtil.java index 0d69e326f..daa3af067 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationUtil.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationUtil.java @@ -16,12 +16,14 @@ package org.teavm.backend.wasm.generate.gc.methods; import java.util.List; +import java.util.function.Function; import org.teavm.backend.wasm.generate.TemporaryVariablePool; import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.model.WasmArray; import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault; +import org.teavm.backend.wasm.model.expression.WasmArrayNewFixed; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmGetGlobal; @@ -43,6 +45,20 @@ public class WasmGCGenerationUtil { public void allocateArray(ValueType itemType, WasmExpression length, TextLocation location, WasmLocal local, List target) { + allocateArray(itemType, location, local, target, arrayType -> new WasmArrayNewDefault(arrayType, length)); + } + + public void allocateArray(ValueType itemType, List data, TextLocation location, + WasmLocal local, List target) { + allocateArray(itemType, location, local, target, arrayType -> { + var expr = new WasmArrayNewFixed(arrayType); + expr.getElements().addAll(data); + return expr; + }); + } + + public void allocateArray(ValueType itemType, TextLocation location, + WasmLocal local, List target, Function data) { var classInfo = classInfoProvider.getClassInfo(ValueType.arrayOf(itemType)); var block = new WasmBlock(false); block.setType(classInfo.getType()); @@ -68,7 +84,7 @@ public class WasmGCGenerationUtil { classInfo.getStructure(), new WasmGetLocal(targetVar), WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET, - new WasmArrayNewDefault(wasmArray, length) + data.apply(wasmArray) ); initArrayField.setLocation(location); target.add(initArrayField); diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassIntrinsic.java index 6ad0c89fc..992d87361 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassIntrinsic.java @@ -43,8 +43,7 @@ public class ClassIntrinsic implements WasmGCIntrinsic { case "getSuperclass": { var cls = context.generate(invocation.getArguments().get(0)); var clsStruct = context.classInfoProvider().getClassInfo("java.lang.Class").getStructure(); - var result = new WasmStructGet(clsStruct, cls, - context.classInfoProvider().getClassParentOffset()); + var result = new WasmStructGet(clsStruct, cls, context.classInfoProvider().getClassParentOffset()); result.setLocation(invocation.getLocation()); return result; } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassSupportIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassSupportIntrinsic.java new file mode 100644 index 000000000..91ca0b510 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ClassSupportIntrinsic.java @@ -0,0 +1,35 @@ +/* + * Copyright 2024 Alexey Andreev. + * + * 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 org.teavm.backend.wasm.intrinsics.gc; + +import org.teavm.ast.InvocationExpr; +import org.teavm.backend.wasm.model.expression.WasmCallReference; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmStructGet; +import org.teavm.model.ValueType; + +public class ClassSupportIntrinsic implements WasmGCIntrinsic { + @Override + public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) { + var enumArrayStruct = context.classInfoProvider().getClassInfo(ValueType.arrayOf( + ValueType.object("java.lang.Enum"))).getStructure(); + var clsStruct = context.classInfoProvider().getClassInfo("java.lang.Class").getStructure(); + var cls = context.generate(invocation.getArguments().get(0)); + var fieldIndex = context.classInfoProvider().getEnumConstantsFunctionOffset(); + var functionRef = new WasmStructGet(clsStruct, cls, fieldIndex); + return new WasmCallReference(functionRef, context.functionTypes().of(enumArrayStruct.getReference())); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java index 6b410aeef..83e4b3b64 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java @@ -39,6 +39,7 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { fillWasmRuntime(); fillObject(); fillClass(); + fillClassSupport(); fillSystem(); fillLongAndInteger(); fillFloat(); @@ -89,6 +90,13 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { add(new MethodReference(Class.class, "setSimpleNameCache", Class.class, String.class, void.class), intrinsic); } + private void fillClassSupport() { + var intrinsic = new ClassSupportIntrinsic(); + add(new MethodReference("org.teavm.classlib.impl.reflection.ClassSupport", + "getEnumConstants", ValueType.object("java.lang.Class"), + ValueType.arrayOf(ValueType.object("java.lang.Enum"))), intrinsic); + } + private void fillSystem() { add(new MethodReference(System.class, "arraycopy", Object.class, int.class, Object.class, int.class, int.class, void.class), new SystemArrayCopyIntrinsic()); diff --git a/core/src/main/java/org/teavm/backend/wasm/runtime/WasmGCSupport.java b/core/src/main/java/org/teavm/backend/wasm/runtime/WasmGCSupport.java index 62a2538a3..b51e5c2ad 100644 --- a/core/src/main/java/org/teavm/backend/wasm/runtime/WasmGCSupport.java +++ b/core/src/main/java/org/teavm/backend/wasm/runtime/WasmGCSupport.java @@ -35,7 +35,7 @@ public class WasmGCSupport { return new ClassCastException(); } - public static void throwCloneNotSupportedException() throws CloneNotSupportedException { + public static Object defaultClone(Object value) throws CloneNotSupportedException { throw new CloneNotSupportedException(); } 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 b8ccf4009..a2ee57967 100644 --- a/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java +++ b/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java @@ -16,6 +16,7 @@ package org.teavm.model.analysis; import java.lang.reflect.Array; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.teavm.dependency.DependencyInfo; @@ -45,6 +46,7 @@ public class ClassMetadataRequirements { private Map requirements = new HashMap<>(); private boolean hasArrayGet; private boolean hasArrayLength; + private boolean hasEnumConstants; public ClassMetadataRequirements(DependencyInfo dependencyInfo) { MethodDependencyInfo getNameMethod = dependencyInfo.getMethod(GET_NAME_METHOD); @@ -128,6 +130,22 @@ public class ClassMetadataRequirements { requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).cloneMethod = true; } } + + var enumConstants = Arrays.asList( + dependencyInfo.getMethod(new MethodReference("org.teavm.platform.Platform", "getEnumConstants", + ValueType.object("org.teavm.platform.PlatformClass"), ValueType.parse(Enum[].class))), + dependencyInfo.getMethod(new MethodReference("org.teavm.classlib.impl.reflection.ClassSupport", + "getEnumConstants", ValueType.parse(Class.class), ValueType.parse(Enum[].class))) + ); + for (var enumConstantsDep : enumConstants) { + if (enumConstantsDep != null) { + hasEnumConstants = true; + var classNames = enumConstantsDep.getVariable(1).getClassValueNode().getTypes(); + for (var className : classNames) { + requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).enumConstants = true; + } + } + } } public Info getInfo(String className) { @@ -150,6 +168,10 @@ public class ClassMetadataRequirements { return hasArrayLength; } + public boolean hasEnumConstants() { + return hasEnumConstants; + } + private void addClassesRequiringName(Map target, String[] source) { for (String typeName : source) { target.computeIfAbsent(decodeType(typeName), k -> new ClassInfo()).name = true; @@ -177,6 +199,7 @@ public class ClassMetadataRequirements { boolean arrayLength; boolean arrayGet; boolean cloneMethod; + boolean enumConstants; @Override public boolean name() { @@ -227,6 +250,11 @@ public class ClassMetadataRequirements { public boolean cloneMethod() { return cloneMethod; } + + @Override + public boolean enumConstants() { + return enumConstants; + } } public interface Info { @@ -249,5 +277,7 @@ public class ClassMetadataRequirements { boolean arrayGet(); boolean cloneMethod(); + + boolean enumConstants(); } } diff --git a/platform/src/main/java/org/teavm/platform/plugin/EnumDependencySupport.java b/platform/src/main/java/org/teavm/platform/plugin/EnumDependencySupport.java index 11ef489d7..04bbaef21 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/EnumDependencySupport.java +++ b/platform/src/main/java/org/teavm/platform/plugin/EnumDependencySupport.java @@ -46,7 +46,7 @@ public class EnumDependencySupport extends AbstractDependencyListener { @Override public void methodReached(DependencyAgent agent, MethodDependency method) { - if (method.getReference().getClassName().equals(Platform.class.getName()) + if (isEnumSupportClass(method.getMethod().getOwnerName()) && method.getReference().getName().equals("getEnumConstants")) { allEnums.connect(method.getResult().getArrayItem()); final MethodReference ref = method.getReference(); @@ -66,4 +66,9 @@ public class EnumDependencySupport extends AbstractDependencyListener { } } } + + private boolean isEnumSupportClass(String className) { + return className.equals(Platform.class.getName()) + || className.endsWith("org.teavm.classlib.impl.reflection.ClassSupport"); + } }