wasm gc: implement Class.getSuperclass and Object.clone, fix issue with resource maps

This commit is contained in:
Alexey Andreev 2024-09-09 14:23:58 +02:00
parent fe0304ee67
commit 349ed8fc2d
13 changed files with 139 additions and 25 deletions

View File

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

View File

@ -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() {

View File

@ -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<MethodReference> methodsAtCallSites;
Predicate<MethodReference> 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());

View File

@ -82,6 +82,7 @@ public class WasmGCDeclarationsGenerator {
classGenerator = new WasmGCClassGenerator(
module,
classes,
hierarchy,
functionTypes,
tags,
metadataRequirements,

View File

@ -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<WasmStructure> 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<WasmGCCustomTypeMapperFactory> 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<WasmExpression> 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;
}

View File

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

View File

@ -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":

View File

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

View File

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

View File

@ -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() {

View File

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

View File

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

View File

@ -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) {