wasm gc: implement enum constants

This commit is contained in:
Alexey Andreev 2024-09-09 20:44:40 +02:00
parent 8184c46bae
commit 2d8556d0a2
13 changed files with 211 additions and 16 deletions

View File

@ -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);
}

View File

@ -29,6 +29,7 @@ import java.util.Set;
import org.teavm.backend.javascript.spi.GeneratedBy; import org.teavm.backend.javascript.spi.GeneratedBy;
import org.teavm.backend.javascript.spi.InjectedBy; import org.teavm.backend.javascript.spi.InjectedBy;
import org.teavm.classlib.PlatformDetector; 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.Flags;
import org.teavm.classlib.impl.reflection.JSClass; import org.teavm.classlib.impl.reflection.JSClass;
import org.teavm.classlib.impl.reflection.JSField; import org.teavm.classlib.impl.reflection.JSField;
@ -692,9 +693,13 @@ public final class TClass<T> extends TObject implements TAnnotatedElement, TType
if (!isEnum()) { if (!isEnum()) {
return null; return null;
} }
if (PlatformDetector.isWebAssemblyGC()) {
return (T[]) ClassSupport.getEnumConstants((Class<?>) (Object) this);
} else {
Platform.initClass(platformClass); Platform.initClass(platformClass);
return (T[]) Platform.getEnumConstants(platformClass).clone(); return (T[]) Platform.getEnumConstants(platformClass).clone();
} }
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public T cast(TObject obj) { public T cast(TObject obj) {

View File

@ -18,6 +18,8 @@ package org.teavm.classlib.java.util;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; 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.classlib.java.lang.TClass;
import org.teavm.platform.Platform; import org.teavm.platform.Platform;
import org.teavm.platform.PlatformClass; import org.teavm.platform.PlatformClass;
@ -43,10 +45,14 @@ class TGenericEnumSet<E extends Enum<E>> extends TEnumSet<E> {
} }
static Enum<?>[] getConstants(Class<?> cls) { static Enum<?>[] getConstants(Class<?> cls) {
if (PlatformDetector.isWebAssemblyGC()) {
return ClassSupport.getEnumConstants(cls);
} else {
PlatformClass platformClass = ((TClass<?>) (Object) cls).getPlatformClass(); PlatformClass platformClass = ((TClass<?>) (Object) cls).getPlatformClass();
Platform.initClass(platformClass); Platform.initClass(platformClass);
return Platform.getEnumConstants(platformClass); return Platform.getEnumConstants(platformClass);
} }
}
@Override @Override
public Iterator<E> iterator() { public Iterator<E> iterator() {

View File

@ -98,8 +98,8 @@ public class WasmGCDependencies {
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "aiiobe", ArrayIndexOutOfBoundsException.class)) analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "aiiobe", ArrayIndexOutOfBoundsException.class))
.use(); .use();
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "cce", ClassCastException.class)).use(); analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "cce", ClassCastException.class)).use();
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "throwCloneNotSupportedException", analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "defaultClone", Object.class,
void.class)).use(); Object.class)).use();
} }
private void contributeInitializerUtils() { private void contributeInitializerUtils() {

View File

@ -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.WasmGCVirtualTable;
import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTableEntry; import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTableEntry;
import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTableProvider; 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.WasmGCInitializerContributor;
import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider; 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.generate.gc.strings.WasmGCStringPool;
import org.teavm.backend.wasm.model.WasmArray; import org.teavm.backend.wasm.model.WasmArray;
import org.teavm.backend.wasm.model.WasmField; 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.WasmArrayGet;
import org.teavm.backend.wasm.model.expression.WasmArrayLength; import org.teavm.backend.wasm.model.expression.WasmArrayLength;
import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault; 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.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmCast; import org.teavm.backend.wasm.model.expression.WasmCast;
import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmFloat32Constant; 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.model.expression.WasmStructSet;
import org.teavm.backend.wasm.runtime.WasmGCSupport; import org.teavm.backend.wasm.runtime.WasmGCSupport;
import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHierarchy;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
@ -131,6 +136,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private int classSupertypeFunctionOffset; private int classSupertypeFunctionOffset;
private int classEnclosingClassOffset; private int classEnclosingClassOffset;
private int virtualTableFieldOffset; private int virtualTableFieldOffset;
private int enumConstantsFunctionOffset;
private int arrayLengthOffset = -1; private int arrayLengthOffset = -1;
private int arrayGetOffset = -1; private int arrayGetOffset = -1;
private int cloneOffset = -1; private int cloneOffset = -1;
@ -140,6 +146,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private WasmFunctionType arrayGetType; private WasmFunctionType arrayGetType;
private WasmFunctionType arrayLengthType; private WasmFunctionType arrayLengthType;
private List<WasmStructure> nonInitializedStructures = new ArrayList<>(); private List<WasmStructure> nonInitializedStructures = new ArrayList<>();
private WasmArray objectArrayType;
private WasmArray enumConstantArray;
public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource, public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource,
ClassHierarchy hierarchy, ClassHierarchy hierarchy,
@ -494,11 +502,15 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
cloneFunction = generateCloneFunction(classInfo, name); cloneFunction = generateCloneFunction(classInfo, name);
} else { } else {
cloneFunction = functionProvider.forStaticMethod(new MethodReference( cloneFunction = functionProvider.forStaticMethod(new MethodReference(
WasmGCSupport.class, "throwCloneNotSupportedException", void.class)); WasmGCSupport.class, "defaultClone", Object.class, Object.class));
} }
cloneFunction.setReferenced(true); cloneFunction.setReferenced(true);
target.add(setClassField(classInfo, cloneOffset, new WasmFunctionReference(cloneFunction))); 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()) { if (virtualTable != null && virtualTable.isConcrete()) {
fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable); fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable);
@ -881,6 +893,11 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
return arrayGetOffset; return arrayGetOffset;
} }
@Override
public int getEnumConstantsFunctionOffset() {
return enumConstantsFunctionOffset;
}
private void initArrayClass(WasmGCClassInfo classInfo, ValueType.Array type) { private void initArrayClass(WasmGCClassInfo classInfo, ValueType.Array type) {
classInfo.initializer = target -> { classInfo.initializer = target -> {
var itemTypeInfo = getClassInfo(type.getItemType()); var itemTypeInfo = getClassInfo(type.getItemType());
@ -1047,6 +1064,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
cloneOffset = fields.size(); cloneOffset = fields.size();
fields.add(createClassField(functionTypes.of(standardClasses.objectClass().getType(), fields.add(createClassField(functionTypes.of(standardClasses.objectClass().getType(),
standardClasses.objectClass().getType()).getReference().asStorage(), "clone")); 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(); virtualTableFieldOffset = fields.size();
} }
} }
@ -1057,6 +1081,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private void fillArrayFields(WasmGCClassInfo classInfo, ValueType elementType) { private void fillArrayFields(WasmGCClassInfo classInfo, ValueType elementType) {
WasmStorageType wasmElementType; WasmStorageType wasmElementType;
WasmArray wasmArray;
if (elementType instanceof ValueType.Primitive) { if (elementType instanceof ValueType.Primitive) {
switch (((ValueType.Primitive) elementType).getKind()) { switch (((ValueType.Primitive) elementType).getKind()) {
case BOOLEAN: case BOOLEAN:
@ -1082,12 +1107,21 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
default: default:
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
var wasmArrayName = names.topLevel(names.suggestForType(classInfo.getValueType()) + "$Data");
wasmArray = new WasmArray(wasmArrayName, wasmElementType);
module.types.add(wasmArray);
} else { } else {
wasmElementType = standardClasses.objectClass().getType().asStorage(); wasmElementType = standardClasses.objectClass().getType().asStorage();
} wasmArray = objectArrayType;
var wasmArrayName = names.topLevel(names.suggestForType(classInfo.getValueType()) + "$Data"); if (wasmArray == null) {
var wasmArray = new WasmArray(wasmArrayName, wasmElementType); var wasmArrayName = names.topLevel(names.suggestForType(ValueType.arrayOf(
ValueType.object("java.lang.Object"))) + "$Data");
wasmArray = new WasmArray(wasmArrayName, wasmElementType);
module.types.add(wasmArray); module.types.add(wasmArray);
objectArrayType = wasmArray;
}
}
classInfo.structure.getFields().add(new WasmField(wasmArray.getReference().asStorage(), classInfo.structure.getFields().add(new WasmField(wasmArray.getReference().asStorage(),
arrayDataFieldName())); arrayDataFieldName()));
} }
@ -1214,6 +1248,35 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
return function; 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) { private WasmExpression setClassField(WasmGCClassInfo classInfo, int fieldIndex, WasmExpression value) {
return new WasmStructSet( return new WasmStructSet(

View File

@ -54,6 +54,8 @@ public interface WasmGCClassInfoProvider {
int getArrayLengthOffset(); int getArrayLengthOffset();
int getEnumConstantsFunctionOffset();
int getCloneOffset(); int getCloneOffset();
default WasmGCClassInfo getClassInfo(String name) { default WasmGCClassInfo getClassInfo(String name) {

View File

@ -16,12 +16,14 @@
package org.teavm.backend.wasm.generate.gc.methods; package org.teavm.backend.wasm.generate.gc.methods;
import java.util.List; import java.util.List;
import java.util.function.Function;
import org.teavm.backend.wasm.generate.TemporaryVariablePool; import org.teavm.backend.wasm.generate.TemporaryVariablePool;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.model.WasmArray; import org.teavm.backend.wasm.model.WasmArray;
import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault; 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.WasmBlock;
import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmGetGlobal; 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, public void allocateArray(ValueType itemType, WasmExpression length, TextLocation location, WasmLocal local,
List<WasmExpression> target) { List<WasmExpression> target) {
allocateArray(itemType, location, local, target, arrayType -> new WasmArrayNewDefault(arrayType, length));
}
public void allocateArray(ValueType itemType, List<? extends WasmExpression> data, TextLocation location,
WasmLocal local, List<WasmExpression> 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<WasmExpression> target, Function<WasmArray, WasmExpression> data) {
var classInfo = classInfoProvider.getClassInfo(ValueType.arrayOf(itemType)); var classInfo = classInfoProvider.getClassInfo(ValueType.arrayOf(itemType));
var block = new WasmBlock(false); var block = new WasmBlock(false);
block.setType(classInfo.getType()); block.setType(classInfo.getType());
@ -68,7 +84,7 @@ public class WasmGCGenerationUtil {
classInfo.getStructure(), classInfo.getStructure(),
new WasmGetLocal(targetVar), new WasmGetLocal(targetVar),
WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET, WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET,
new WasmArrayNewDefault(wasmArray, length) data.apply(wasmArray)
); );
initArrayField.setLocation(location); initArrayField.setLocation(location);
target.add(initArrayField); target.add(initArrayField);

View File

@ -43,8 +43,7 @@ public class ClassIntrinsic implements WasmGCIntrinsic {
case "getSuperclass": { case "getSuperclass": {
var cls = context.generate(invocation.getArguments().get(0)); var cls = context.generate(invocation.getArguments().get(0));
var clsStruct = context.classInfoProvider().getClassInfo("java.lang.Class").getStructure(); var clsStruct = context.classInfoProvider().getClassInfo("java.lang.Class").getStructure();
var result = new WasmStructGet(clsStruct, cls, var result = new WasmStructGet(clsStruct, cls, context.classInfoProvider().getClassParentOffset());
context.classInfoProvider().getClassParentOffset());
result.setLocation(invocation.getLocation()); result.setLocation(invocation.getLocation());
return result; return result;
} }

View File

@ -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()));
}
}

View File

@ -39,6 +39,7 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
fillWasmRuntime(); fillWasmRuntime();
fillObject(); fillObject();
fillClass(); fillClass();
fillClassSupport();
fillSystem(); fillSystem();
fillLongAndInteger(); fillLongAndInteger();
fillFloat(); fillFloat();
@ -89,6 +90,13 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
add(new MethodReference(Class.class, "setSimpleNameCache", Class.class, String.class, void.class), intrinsic); 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() { private void fillSystem() {
add(new MethodReference(System.class, "arraycopy", Object.class, int.class, Object.class, add(new MethodReference(System.class, "arraycopy", Object.class, int.class, Object.class,
int.class, int.class, void.class), new SystemArrayCopyIntrinsic()); int.class, int.class, void.class), new SystemArrayCopyIntrinsic());

View File

@ -35,7 +35,7 @@ public class WasmGCSupport {
return new ClassCastException(); return new ClassCastException();
} }
public static void throwCloneNotSupportedException() throws CloneNotSupportedException { public static Object defaultClone(Object value) throws CloneNotSupportedException {
throw new CloneNotSupportedException(); throw new CloneNotSupportedException();
} }

View File

@ -16,6 +16,7 @@
package org.teavm.model.analysis; package org.teavm.model.analysis;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.teavm.dependency.DependencyInfo; import org.teavm.dependency.DependencyInfo;
@ -45,6 +46,7 @@ public class ClassMetadataRequirements {
private Map<ValueType, ClassInfo> requirements = new HashMap<>(); private Map<ValueType, ClassInfo> requirements = new HashMap<>();
private boolean hasArrayGet; private boolean hasArrayGet;
private boolean hasArrayLength; private boolean hasArrayLength;
private boolean hasEnumConstants;
public ClassMetadataRequirements(DependencyInfo dependencyInfo) { public ClassMetadataRequirements(DependencyInfo dependencyInfo) {
MethodDependencyInfo getNameMethod = dependencyInfo.getMethod(GET_NAME_METHOD); MethodDependencyInfo getNameMethod = dependencyInfo.getMethod(GET_NAME_METHOD);
@ -128,6 +130,22 @@ public class ClassMetadataRequirements {
requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).cloneMethod = true; 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) { public Info getInfo(String className) {
@ -150,6 +168,10 @@ public class ClassMetadataRequirements {
return hasArrayLength; return hasArrayLength;
} }
public boolean hasEnumConstants() {
return hasEnumConstants;
}
private void addClassesRequiringName(Map<ValueType, ClassInfo> target, String[] source) { private void addClassesRequiringName(Map<ValueType, ClassInfo> target, String[] source) {
for (String typeName : source) { for (String typeName : source) {
target.computeIfAbsent(decodeType(typeName), k -> new ClassInfo()).name = true; target.computeIfAbsent(decodeType(typeName), k -> new ClassInfo()).name = true;
@ -177,6 +199,7 @@ public class ClassMetadataRequirements {
boolean arrayLength; boolean arrayLength;
boolean arrayGet; boolean arrayGet;
boolean cloneMethod; boolean cloneMethod;
boolean enumConstants;
@Override @Override
public boolean name() { public boolean name() {
@ -227,6 +250,11 @@ public class ClassMetadataRequirements {
public boolean cloneMethod() { public boolean cloneMethod() {
return cloneMethod; return cloneMethod;
} }
@Override
public boolean enumConstants() {
return enumConstants;
}
} }
public interface Info { public interface Info {
@ -249,5 +277,7 @@ public class ClassMetadataRequirements {
boolean arrayGet(); boolean arrayGet();
boolean cloneMethod(); boolean cloneMethod();
boolean enumConstants();
} }
} }

View File

@ -46,7 +46,7 @@ public class EnumDependencySupport extends AbstractDependencyListener {
@Override @Override
public void methodReached(DependencyAgent agent, MethodDependency method) { 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")) { && method.getReference().getName().equals("getEnumConstants")) {
allEnums.connect(method.getResult().getArrayItem()); allEnums.connect(method.getResult().getArrayItem());
final MethodReference ref = method.getReference(); 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");
}
} }