Skip to content

Commit

Permalink
wasm gc: implement Class.getSuperclass and Object.clone, fix issue wi…
Browse files Browse the repository at this point in the history
…th resource maps
  • Loading branch information
konsoletyper committed Sep 9, 2024
1 parent fe0304e commit 349ed8f
Show file tree
Hide file tree
Showing 13 changed files with 139 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ private static void setIdentityLowLevel(RuntimeObject object, int id) {
@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) {
Expand All @@ -355,6 +355,8 @@ protected Object clone() throws TCloneNotSupportedException {
return result;
}

private native TObject cloneObject();

@SuppressWarnings("unused")
private static RuntimeObject cloneLowLevel(RuntimeObject self) {
RuntimeClass cls = RuntimeClass.getClass(self);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ private void contributeExceptionUtils() {
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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
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;
Expand Down Expand Up @@ -170,7 +171,7 @@ private void fillTable(Table table) {
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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public WasmGCDeclarationsGenerator(
classGenerator = new WasmGCClassGenerator(
module,
classes,
hierarchy,
functionTypes,
tags,
metadataRequirements,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,11 @@
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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -137,13 +142,15 @@ 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,
ClassInitializerInfo classInitializerInfo,
List<WasmGCCustomTypeMapperFactory> customTypeMapperFactories) {
this.module = module;
this.classSource = classSource;
this.hierarchy = hierarchy;
this.functionTypes = functionTypes;
this.tagRegistry = tagRegistry;
this.metadataRequirements = metadataRequirements;
Expand Down Expand Up @@ -359,6 +366,12 @@ public int getClassEnclosingClassOffset() {
return classEnclosingClassOffset;
}

@Override
public int getClassParentOffset() {
standardClasses.classClass().getStructure().init();
return classParentOffset;
}

@Override
public int getClassNameOffset() {
standardClasses.classClass().getStructure().init();
Expand All @@ -383,6 +396,12 @@ public int getVirtualMethodsOffset() {
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;
Expand Down Expand Up @@ -469,6 +488,17 @@ private void initRegularClass(WasmGCClassInfo classInfo, WasmGCVirtualTable virt
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);
Expand All @@ -481,6 +511,38 @@ private void initRegularClass(WasmGCClassInfo classInfo, WasmGCVirtualTable virt
};
}

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();
Expand All @@ -500,15 +562,7 @@ private void fillArrayVirtualTableMethods(ValueType type, List<WasmExpression> t
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);
Expand Down Expand Up @@ -773,7 +827,7 @@ private void addSystemFields(List<WasmField> fields) {
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);
}
Expand Down Expand Up @@ -836,6 +890,12 @@ private void initArrayClass(WasmGCClassInfo classInfo, ValueType.Array type) {
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)));
}
};
}

Expand Down Expand Up @@ -983,7 +1043,10 @@ private void fillSimpleClassFields(List<WasmField> fields, String className) {
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();
}
}
Expand Down Expand Up @@ -1146,7 +1209,7 @@ private WasmFunction createCreateArrayClassFunction() {
standardClasses.classClass().getStructure(),
new WasmGetLocal(targetVar),
classParentOffset,
new WasmGetGlobal(standardClasses.classClass().pointer)
new WasmGetGlobal(standardClasses.objectClass().pointer)
));
return function;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public interface WasmGCClassInfoProvider {

int getClassEnclosingClassOffset();

int getClassParentOffset();

int getNewArrayFunctionOffset();

int getClassNameOffset();
Expand All @@ -52,6 +54,8 @@ public interface WasmGCClassInfoProvider {

int getArrayLengthOffset();

int getCloneOffset();

default WasmGCClassInfo getClassInfo(String name) {
return getClassInfo(ValueType.object(name));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext co
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":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
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;
Expand Down Expand Up @@ -52,6 +53,8 @@ public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext co
return generateGetIdentity(invocation, context);
case "setWasmGCIdentity":
return generateSetIdentity(invocation, context);
case "cloneObject":
return generateClone(invocation, context);
default:
throw new IllegalArgumentException();
}
Expand Down Expand Up @@ -130,4 +133,23 @@ private WasmExpression generateSetIdentity(InvocationExpr invocation, WasmGCIntr
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ private void fillWasmRuntime() {
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",
Expand All @@ -83,6 +84,7 @@ private void fillClass() {
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public static ClassCastException cce() {
return new ClassCastException();
}

public static CloneNotSupportedException cnse() {
return new CloneNotSupportedException();
public static void throwCloneNotSupportedException() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}

public static int nextObjectId() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -29,7 +28,6 @@
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;
Expand All @@ -51,11 +49,6 @@ public void transformClass(ClassHolder cls, ClassHolderTransformerContext contex
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());
Expand All @@ -80,6 +73,7 @@ public void transformClass(ClassHolder cls, ClassHolderTransformerContext contex
case "getEnclosingClass":
case "getSimpleNameCache":
case "setSimpleNameCache":
case "getSuperclass":
method.setProgram(null);
method.getModifiers().add(ElementModifier.NATIVE);
break;
Expand Down
Loading

0 comments on commit 349ed8f

Please sign in to comment.