mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 00:04:10 -08:00
wasm gc: implement Class.getSuperclass and Object.clone, fix issue with resource maps
This commit is contained in:
parent
fe0304ee67
commit
349ed8fc2d
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -82,6 +82,7 @@ public class WasmGCDeclarationsGenerator {
|
|||
classGenerator = new WasmGCClassGenerator(
|
||||
module,
|
||||
classes,
|
||||
hierarchy,
|
||||
functionTypes,
|
||||
tags,
|
||||
metadataRequirements,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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":
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user