From 349ed8fc2da45c9ad692da70fa9bb374ebce42e6 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 9 Sep 2024 14:23:58 +0200 Subject: [PATCH] wasm gc: implement Class.getSuperclass and Object.clone, fix issue with resource maps --- .../org/teavm/classlib/java/lang/TObject.java | 4 +- .../backend/wasm/gc/WasmGCDependencies.java | 3 +- .../gc/vtable/WasmGCVirtualTableBuilder.java | 3 +- .../gc/WasmGCDeclarationsGenerator.java | 1 + .../gc/classes/WasmGCClassGenerator.java | 87 ++++++++++++++++--- .../gc/classes/WasmGCClassInfoProvider.java | 4 + .../wasm/intrinsics/gc/ClassIntrinsic.java | 8 ++ .../wasm/intrinsics/gc/ObjectIntrinsic.java | 22 +++++ .../wasm/intrinsics/gc/WasmGCIntrinsics.java | 2 + .../backend/wasm/runtime/WasmGCSupport.java | 4 +- .../gc/BaseClassesTransformation.java | 8 +- .../analysis/ClassMetadataRequirements.java | 16 ++++ .../plugin/wasmgc/ResourceMapHelper.java | 2 +- 13 files changed, 139 insertions(+), 25 deletions(-) diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java index 0db13182b..10569d90d 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java @@ -344,7 +344,7 @@ public class TObject { @PluggableDependency(ObjectDependencyPlugin.class) protected Object clone() throws TCloneNotSupportedException { if (PlatformDetector.isWebAssemblyGC()) { - throw new TCloneNotSupportedException(); + return cloneObject(); } if (!(this instanceof TCloneable) && Platform.getPlatformObject(this) .getPlatformClass().getMetadata().getArrayItem() == null) { @@ -355,6 +355,8 @@ public class TObject { return result; } + private native TObject cloneObject(); + @SuppressWarnings("unused") private static RuntimeObject cloneLowLevel(RuntimeObject self) { RuntimeClass cls = RuntimeClass.getClass(self); 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 cd4c17e02..73d7c8888 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,7 +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, "cnse", CloneNotSupportedException.class)).use(); + analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "throwCloneNotSupportedException", + void.class)).use(); } private void contributeInitializerUtils() { diff --git a/core/src/main/java/org/teavm/backend/wasm/gc/vtable/WasmGCVirtualTableBuilder.java b/core/src/main/java/org/teavm/backend/wasm/gc/vtable/WasmGCVirtualTableBuilder.java index 67acf3e2d..bdc547a95 100644 --- a/core/src/main/java/org/teavm/backend/wasm/gc/vtable/WasmGCVirtualTableBuilder.java +++ b/core/src/main/java/org/teavm/backend/wasm/gc/vtable/WasmGCVirtualTableBuilder.java @@ -34,6 +34,7 @@ import org.teavm.model.MethodDescriptor; import org.teavm.model.MethodReference; class WasmGCVirtualTableBuilder { + private static final MethodReference CLONE_METHOD = new MethodReference(Object.class, "clone", Object.class); ListableClassReaderSource classes; Collection methodsAtCallSites; Predicate isVirtual; @@ -170,7 +171,7 @@ class WasmGCVirtualTableBuilder { if (method.getProgram() == null && !method.hasModifier(ElementModifier.NATIVE)) { continue; } - if (!isVirtual.test(method.getReference())) { + if (!isVirtual.test(method.getReference()) && !method.getReference().equals(CLONE_METHOD)) { continue; } table.currentImplementors.put(method.getDescriptor(), method.getReference()); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java index 60f0febb6..5327892f8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java @@ -82,6 +82,7 @@ public class WasmGCDeclarationsGenerator { classGenerator = new WasmGCClassGenerator( module, classes, + hierarchy, functionTypes, tags, metadataRequirements, 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 1c995b17c..fb6fe9ef6 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 @@ -67,8 +67,11 @@ import org.teavm.backend.wasm.model.expression.WasmSetGlobal; import org.teavm.backend.wasm.model.expression.WasmSetLocal; import org.teavm.backend.wasm.model.expression.WasmSignedType; import org.teavm.backend.wasm.model.expression.WasmStructGet; +import org.teavm.backend.wasm.model.expression.WasmStructNew; 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.ClassReaderSource; import org.teavm.model.ElementModifier; import org.teavm.model.FieldReference; @@ -93,6 +96,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private final WasmModule module; private ClassReaderSource classSource; + private ClassHierarchy hierarchy; private WasmFunctionTypes functionTypes; private TagRegistry tagRegistry; private ClassMetadataRequirements metadataRequirements; @@ -129,6 +133,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private int virtualTableFieldOffset; private int arrayLengthOffset = -1; private int arrayGetOffset = -1; + private int cloneOffset = -1; private WasmStructure arrayVirtualTableStruct; private WasmFunction arrayGetObjectFunction; private WasmFunction arrayLengthObjectFunction; @@ -137,6 +142,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private List nonInitializedStructures = new ArrayList<>(); public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource, + ClassHierarchy hierarchy, WasmFunctionTypes functionTypes, TagRegistry tagRegistry, ClassMetadataRequirements metadataRequirements, WasmGCVirtualTableProvider virtualTables, BaseWasmFunctionRepository functionProvider, WasmGCNameProvider names, @@ -144,6 +150,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit List customTypeMapperFactories) { this.module = module; this.classSource = classSource; + this.hierarchy = hierarchy; this.functionTypes = functionTypes; this.tagRegistry = tagRegistry; this.metadataRequirements = metadataRequirements; @@ -359,6 +366,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit return classEnclosingClassOffset; } + @Override + public int getClassParentOffset() { + standardClasses.classClass().getStructure().init(); + return classParentOffset; + } + @Override public int getClassNameOffset() { standardClasses.classClass().getStructure().init(); @@ -383,6 +396,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit return virtualTableFieldOffset; } + @Override + public int getCloneOffset() { + standardClasses.classClass().getStructure().init(); + return cloneOffset; + } + private void initPrimitiveClass(WasmGCClassInfo classInfo, ValueType.Primitive type) { classInfo.initializer = target -> { int kind; @@ -469,6 +488,17 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit var owner = getClassInfo(cls.getOwnerName()); target.add(setClassField(classInfo, classEnclosingClassOffset, new WasmGetGlobal(owner.pointer))); } + if (metadataReq.cloneMethod()) { + WasmFunction cloneFunction; + if (hierarchy.isSuperType("java.lang.Cloneable", name, false)) { + cloneFunction = generateCloneFunction(classInfo, name); + } else { + cloneFunction = functionProvider.forStaticMethod(new MethodReference( + WasmGCSupport.class, "throwCloneNotSupportedException", void.class)); + } + cloneFunction.setReferenced(true); + target.add(setClassField(classInfo, cloneOffset, new WasmFunctionReference(cloneFunction))); + } } if (virtualTable != null && virtualTable.isConcrete()) { fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable); @@ -481,6 +511,38 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit }; } + private WasmFunction generateCloneFunction(WasmGCClassInfo classInfo, String className) { + var function = new WasmFunction(functionTypes.of(standardClasses.objectClass().getType(), + standardClasses.objectClass().getType())); + function.setName(names.topLevel(className + "@clone")); + module.functions.add(function); + + var objLocal = new WasmLocal(standardClasses.objectClass().getType(), "obj"); + var castObjLocal = new WasmLocal(classInfo.getType(), "castObj"); + function.add(objLocal); + function.add(castObjLocal); + + var cast = new WasmCast(new WasmGetLocal(objLocal), classInfo.getStructure().getReference()); + function.getBody().add(new WasmSetLocal(castObjLocal, cast)); + + var copy = new WasmStructNew(classInfo.structure); + for (var i = 0; i < classInfo.structure.getFields().size(); ++i) { + if (i == MONITOR_FIELD_OFFSET) { + copy.getInitializers().add(new WasmNullConstant(WasmType.Reference.EQ)); + } else { + var fieldType = classInfo.structure.getFields().get(i).getType(); + var getExpr = new WasmStructGet(classInfo.structure, new WasmGetLocal(castObjLocal), i); + if (fieldType instanceof WasmStorageType.Packed) { + getExpr.setSignedType(WasmSignedType.UNSIGNED); + } + copy.getInitializers().add(getExpr); + } + } + + function.getBody().add(new WasmReturn(copy)); + return function; + } + private void fillVirtualTableMethods(List target, WasmStructure structure, WasmGlobal global, WasmGCVirtualTable virtualTable) { var usedVt = virtualTable.getFirstUsed(); @@ -500,15 +562,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit var itemType = ((ValueType.Array) type).getItemType(); for (var entry : virtualTable.getEntries()) { - if (entry.getMethod().getName().equals("clone")) { - var function = generateArrayCloneMethod(objectStructure, itemType); - function.setReferenced(true); - var ref = new WasmFunctionReference(function); - var fieldIndex = virtualTableFieldOffset + entry.getIndex(); - target.add(new WasmStructSet(structure, new WasmGetGlobal(global), fieldIndex, ref)); - } else { - fillVirtualTableEntry(target, global, structure, virtualTable, entry); - } + fillVirtualTableEntry(target, global, structure, virtualTable, entry); } var info = metadataRequirements.getInfo(type); @@ -773,7 +827,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit var classField = new WasmField(standardClasses.classClass().getType().asStorage()); classField.setName(names.forMemberField(FAKE_CLASS_FIELD)); fields.add(classField); - var monitorField = new WasmField(WasmType.Reference.ANY.asStorage()); + var monitorField = new WasmField(WasmType.Reference.EQ.asStorage()); monitorField.setName(names.forMemberField(FAKE_MONITOR_FIELD)); fields.add(monitorField); } @@ -836,6 +890,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit new WasmGetGlobal(itemTypeInfo.pointer) )); fillArrayVirtualTableMethods(classInfo.getValueType(), target, classInfo.pointer, classInfo.structure); + var metadataReq = metadataRequirements.getInfo(type); + if (metadataReq.cloneMethod()) { + var cloneFunction = generateArrayCloneMethod(classInfo.structure, type.getItemType()); + cloneFunction.setReferenced(true); + target.add(setClassField(classInfo, cloneOffset, new WasmFunctionReference(cloneFunction))); + } }; } @@ -983,7 +1043,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit classNameOffset = fields.size(); fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "name")); classSimpleNameOffset = fields.size(); - fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "simpleNAme")); + fields.add(createClassField(standardClasses.stringClass().getType().asStorage(), "simpleName")); + cloneOffset = fields.size(); + fields.add(createClassField(functionTypes.of(standardClasses.objectClass().getType(), + standardClasses.objectClass().getType()).getReference().asStorage(), "clone")); virtualTableFieldOffset = fields.size(); } } @@ -1146,7 +1209,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit standardClasses.classClass().getStructure(), new WasmGetLocal(targetVar), classParentOffset, - new WasmGetGlobal(standardClasses.classClass().pointer) + new WasmGetGlobal(standardClasses.objectClass().pointer) )); return function; } 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 7a537471b..574259aef 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 @@ -42,6 +42,8 @@ public interface WasmGCClassInfoProvider { int getClassEnclosingClassOffset(); + int getClassParentOffset(); + int getNewArrayFunctionOffset(); int getClassNameOffset(); @@ -52,6 +54,8 @@ public interface WasmGCClassInfoProvider { int getArrayLengthOffset(); + int getCloneOffset(); + default WasmGCClassInfo getClassInfo(String name) { return getClassInfo(ValueType.object(name)); } 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 94c232633..6ad0c89fc 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 @@ -40,6 +40,14 @@ public class ClassIntrinsic implements WasmGCIntrinsic { result.setLocation(invocation.getLocation()); return result; } + 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()); + result.setLocation(invocation.getLocation()); + return result; + } case "getNameImpl": return generateGetName(invocation, context); case "setNameImpl": diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ObjectIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ObjectIntrinsic.java index ee6c8ff48..ca86614a3 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ObjectIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/ObjectIntrinsic.java @@ -20,6 +20,7 @@ import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; +import org.teavm.backend.wasm.model.expression.WasmCallReference; import org.teavm.backend.wasm.model.expression.WasmCast; import org.teavm.backend.wasm.model.expression.WasmDrop; import org.teavm.backend.wasm.model.expression.WasmExpression; @@ -52,6 +53,8 @@ public class ObjectIntrinsic implements WasmGCIntrinsic { return generateGetIdentity(invocation, context); case "setWasmGCIdentity": return generateSetIdentity(invocation, context); + case "cloneObject": + return generateClone(invocation, context); default: throw new IllegalArgumentException(); } @@ -130,4 +133,23 @@ public class ObjectIntrinsic implements WasmGCIntrinsic { return new WasmStructSet(objectStruct, instance, WasmGCClassInfoProvider.MONITOR_FIELD_OFFSET, identityWrapper); } + + private WasmExpression generateClone(InvocationExpr invocation, WasmGCIntrinsicContext context) { + var objectStruct = context.classInfoProvider().getClassInfo("java.lang.Object").getStructure(); + var classStruct = context.classInfoProvider().getClassInfo("java.lang.Class").getStructure(); + + var block = new WasmBlock(false); + block.setType(objectStruct.getReference()); + var obj = context.exprCache().create(context.generate(invocation.getArguments().get(0)), + objectStruct.getReference(), invocation.getLocation(), block.getBody()); + var cls = new WasmStructGet(objectStruct, obj.expr(), WasmGCClassInfoProvider.CLASS_FIELD_OFFSET); + var functionRef = new WasmStructGet(classStruct, cls, context.classInfoProvider().getCloneOffset()); + var call = new WasmCallReference(functionRef, context.functionTypes().of( + objectStruct.getReference(), objectStruct.getReference())); + call.getArguments().add(obj.expr()); + block.getBody().add(call); + + obj.release(); + return block; + } } 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 bd664d980..6b410aeef 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 @@ -68,6 +68,7 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { private void fillObject() { var intrinsic = new ObjectIntrinsic(); add(new MethodReference(Object.class, "getClass", Class.class), intrinsic); + add(new MethodReference(Object.class, "cloneObject", Object.class), intrinsic); add(new MethodReference(Object.class.getName(), "getMonitor", ValueType.object("java.lang.Object$Monitor")), intrinsic); add(new MethodReference(Object.class.getName(), "setMonitor", @@ -83,6 +84,7 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { add(new MethodReference(Class.class, "getNameImpl", String.class), intrinsic); add(new MethodReference(Class.class, "setNameImpl", String.class, void.class), intrinsic); add(new MethodReference(Class.class, "getEnclosingClass", Class.class), intrinsic); + add(new MethodReference(Class.class, "getSuperclass", Class.class), intrinsic); add(new MethodReference(Class.class, "getSimpleNameCache", Class.class, String.class), intrinsic); add(new MethodReference(Class.class, "setSimpleNameCache", Class.class, String.class, void.class), intrinsic); } 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 193b09384..62a2538a3 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,8 +35,8 @@ public class WasmGCSupport { return new ClassCastException(); } - public static CloneNotSupportedException cnse() { - return new CloneNotSupportedException(); + public static void throwCloneNotSupportedException() throws CloneNotSupportedException { + throw new CloneNotSupportedException(); } public static int nextObjectId() { diff --git a/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java b/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java index ca9bb7b30..962ea7dd6 100644 --- a/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java +++ b/core/src/main/java/org/teavm/backend/wasm/transformation/gc/BaseClassesTransformation.java @@ -15,7 +15,6 @@ */ package org.teavm.backend.wasm.transformation.gc; -import org.teavm.backend.wasm.runtime.WasmGCSupport; import org.teavm.interop.Import; import org.teavm.model.AccessLevel; import org.teavm.model.AnnotationHolder; @@ -29,7 +28,6 @@ import org.teavm.model.MethodHolder; import org.teavm.model.MethodReference; import org.teavm.model.Program; import org.teavm.model.ValueType; -import org.teavm.model.emit.ProgramEmitter; import org.teavm.model.instructions.GetFieldInstruction; import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvokeInstruction; @@ -51,11 +49,6 @@ public class BaseClassesTransformation implements ClassHolderTransformer { method.setProgram(null); method.getModifiers().add(ElementModifier.NATIVE); break; - case "clone": { - var em = ProgramEmitter.create(method, context.getHierarchy()); - em.invoke(WasmGCSupport.class, "cnse", CloneNotSupportedException.class).raise(); - break; - } default: if (method.getProgram() != null) { transformMonitorFieldAccess(method.getProgram()); @@ -80,6 +73,7 @@ public class BaseClassesTransformation implements ClassHolderTransformer { case "getEnclosingClass": case "getSimpleNameCache": case "setSimpleNameCache": + case "getSuperclass": method.setProgram(null); method.getModifiers().add(ElementModifier.NATIVE); break; 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 b74a2e136..b8ccf4009 100644 --- a/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java +++ b/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java @@ -120,6 +120,14 @@ public class ClassMetadataRequirements { requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).arrayLength = true; } } + + var clone = dependencyInfo.getMethod(new MethodReference(Object.class, "cloneObject", Object.class)); + if (clone != null) { + var classNames = clone.getVariable(0).getTypes(); + for (var className : classNames) { + requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).cloneMethod = true; + } + } } public Info getInfo(String className) { @@ -168,6 +176,7 @@ public class ClassMetadataRequirements { boolean newArray; boolean arrayLength; boolean arrayGet; + boolean cloneMethod; @Override public boolean name() { @@ -213,6 +222,11 @@ public class ClassMetadataRequirements { public boolean arrayGet() { return arrayGet; } + + @Override + public boolean cloneMethod() { + return cloneMethod; + } } public interface Info { @@ -233,5 +247,7 @@ public class ClassMetadataRequirements { boolean arrayLength(); boolean arrayGet(); + + boolean cloneMethod(); } } diff --git a/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceMapHelper.java b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceMapHelper.java index abf404f00..efa77b38b 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceMapHelper.java +++ b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceMapHelper.java @@ -28,7 +28,7 @@ public final class ResourceMapHelper { public static Resource get(ResourceMap map, String key) { var count = entryCount(map); - var initialIndex = Math.abs(key.hashCode()) % count; + var initialIndex = Integer.remainderUnsigned(key.hashCode(), count); for (var i = initialIndex; i < count; ++i) { var entry = entry(map, i); if (entry == null) {