wasm gc: fix issues with virtual calls

This commit is contained in:
Alexey Andreev 2024-08-14 17:20:19 +02:00
parent 2805631025
commit 40fbce0ddd
16 changed files with 245 additions and 60 deletions

View File

@ -74,6 +74,7 @@ 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();
}
private void contributeInitializerUtils() {

View File

@ -56,6 +56,8 @@ import org.teavm.backend.wasm.model.expression.WasmInt64Subtype;
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmIntType;
import org.teavm.backend.wasm.model.expression.WasmIntUnary;
import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation;
import org.teavm.backend.wasm.model.expression.WasmLoadFloat32;
import org.teavm.backend.wasm.model.expression.WasmLoadFloat64;
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
@ -302,6 +304,11 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
return new WasmInt32Constant(0);
}
@Override
protected WasmExpression genIsNull(WasmExpression value) {
return new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, value);
}
@Override
public void visit(SubscriptExpr expr) {
WasmExpression ptr = getArrayElementPointer(expr);

View File

@ -92,8 +92,6 @@ import org.teavm.backend.wasm.model.expression.WasmInt64Constant;
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmIntType;
import org.teavm.backend.wasm.model.expression.WasmIntUnary;
import org.teavm.backend.wasm.model.expression.WasmIntUnaryOperation;
import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
import org.teavm.backend.wasm.model.expression.WasmSwitch;
@ -902,9 +900,16 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
var block = new WasmBlock(false);
block.setType(mapType(reference.getReturnType()));
var instanceVar = tempVars.acquire(instanceWasmType);
WasmLocal instanceVar;
var isTemporary = false;
if (instance instanceof WasmGetLocal) {
instanceVar = ((WasmGetLocal) instance).getLocal();
} else {
instanceVar = tempVars.acquire(instanceWasmType);
block.getBody().add(new WasmSetLocal(instanceVar, instance));
instance = new WasmGetLocal(instanceVar);
isTemporary = true;
}
var arguments = new ArrayList<WasmExpression>();
arguments.add(instance);
@ -918,7 +923,9 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
var call = generateVirtualCall(instanceVar, reference, arguments);
block.getBody().add(call);
if (isTemporary) {
tempVars.release(instanceVar);
}
return block;
}
}
@ -1133,7 +1140,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
result.acceptVisitor(typeInference);
var cachedObject = exprCache.create(result, typeInference.getResult(), expr.getLocation(), block.getBody());
var ifNull = new WasmBranch(genIsZero(cachedObject.expr()), block);
var ifNull = new WasmBranch(genIsNull(cachedObject.expr()), block);
ifNull.setResult(new WasmInt32Constant(0));
block.getBody().add(new WasmDrop(ifNull));
@ -1171,7 +1178,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
var wasmSourceType = typeInference.getResult();
var valueToCast = exprCache.create(result, wasmSourceType, expr.getLocation(), block.getBody());
var nullCheck = new WasmBranch(genIsZero(valueToCast.expr()), block);
var nullCheck = new WasmBranch(genIsNull(valueToCast.expr()), block);
nullCheck.setResult(valueToCast.expr());
block.getBody().add(new WasmDrop(nullCheck));
@ -1544,9 +1551,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
}
}
private WasmExpression genIsZero(WasmExpression value) {
return new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, value);
}
protected abstract WasmExpression genIsNull(WasmExpression value);
protected abstract WasmType mapType(ValueType type);

View File

@ -52,6 +52,7 @@ import org.teavm.backend.wasm.model.expression.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmIntType;
import org.teavm.backend.wasm.model.expression.WasmNullConstant;
import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
import org.teavm.backend.wasm.model.expression.WasmStructSet;
@ -122,7 +123,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
standardClasses = new WasmGCStandardClasses(this);
strings = new WasmGCStringPool(standardClasses, module, functionProvider);
supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes);
typeMapper = new WasmGCTypeMapper(this);
typeMapper = new WasmGCTypeMapper(this, functionTypes, module);
}
public WasmGCSupertypeFunctionProvider getSupertypeProvider() {
@ -143,6 +144,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
@Override
public void contributeToInitializerDefinitions(WasmFunction function) {
fillVirtualTableSupertypes();
for (var classInfo : classInfoMap.values()) {
var classInstanceType = classInfo.virtualTableStructure != null
? classInfo.virtualTableStructure
@ -152,6 +154,35 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
}
}
private void fillVirtualTableSupertypes() {
for (var classInfo : classInfoMap.values()) {
if (classInfo.virtualTableStructure != null && classInfo.getValueType() instanceof ValueType.Object
&& classInfo.hasOwnVirtualTable) {
var className = ((ValueType.Object) classInfo.getValueType()).getClassName();
classInfo.virtualTableStructure.setSupertype(findVirtualTableSupertype(className));
}
}
}
private WasmStructure findVirtualTableSupertype(String className) {
while (className != null) {
var cls = classSource.get(className);
if (cls == null) {
break;
}
className = cls.getParent();
if (className == null) {
break;
}
var parentInfo = classInfoMap.get(ValueType.object(className));
if (parentInfo != null && parentInfo.virtualTableStructure != null) {
return parentInfo.virtualTableStructure;
}
}
var classClass = classInfoMap.get(ValueType.object("java.lang.Class"));
return classClass != null ? classClass.structure : null;
}
@Override
public void contributeToInitializer(WasmFunction function) {
var classClass = standardClasses.classClass();
@ -209,7 +240,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
fillFields(classInfo, type);
}
var pointerName = names.forClassInstance(type);
var classStructure = virtualTable != null && virtualTable.hasValidEntries()
classInfo.hasOwnVirtualTable = virtualTable != null && virtualTable.hasValidEntries();
var classStructure = classInfo.hasOwnVirtualTable
? initRegularClassStructure(((ValueType.Object) type).getClassName())
: standardClasses.classClass().getStructure();
classInfo.virtualTableStructure = classStructure;
@ -330,21 +362,30 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
}
}
if (virtualTable != null && virtualTable.hasValidEntries()) {
fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable,
virtualTableFieldOffset, name, new HashSet<>());
fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable, virtualTable,
new HashSet<>());
}
};
}
private int fillVirtualTableMethods(List<WasmExpression> target, WasmStructure structure, WasmGlobal global,
VirtualTable virtualTable, int index, String origin, Set<MethodDescriptor> filled) {
private void fillVirtualTableMethods(List<WasmExpression> target, WasmStructure structure, WasmGlobal global,
VirtualTable virtualTable, VirtualTable original, Set<MethodDescriptor> filled) {
if (virtualTable.getParent() != null) {
fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), original, filled);
}
for (var method : virtualTable.getMethods()) {
var entry = virtualTable.getEntry(method);
var entry = original.getEntry(method);
if (entry != null && entry.getImplementor() != null && filled.add(method)
&& !method.equals(GET_CLASS_METHOD)) {
var fieldIndex = virtualTableFieldOffset + entry.getIndex();
var expectedType = (WasmType.CompositeReference) structure.getFields().get(fieldIndex)
.asUnpackedType();
var expectedFunctionType = (WasmFunctionType) expectedType.composite;
var function = functionProvider.forInstanceMethod(entry.getImplementor());
if (!origin.equals(entry.getImplementor().getClassName())) {
var functionType = getFunctionType(virtualTable.getClassName(), method);
if (!virtualTable.getClassName().equals(entry.getImplementor().getClassName())
|| expectedFunctionType != function.getType()) {
var functionType = typeMapper.getFunctionType(virtualTable.getClassName(), method, true);
functionType.getSupertypes().add(expectedFunctionType);
var wrapperFunction = new WasmFunction(functionType);
module.functions.add(wrapperFunction);
var call = new WasmCall(function);
@ -358,17 +399,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
call.getArguments().add(new WasmGetLocal(params[i]));
wrapperFunction.add(params[i]);
}
wrapperFunction.getBody().add(new WasmReturn(call));
function = wrapperFunction;
}
function.setReferenced(true);
var ref = new WasmFunctionReference(function);
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index + entry.getIndex(), ref));
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), fieldIndex, ref));
}
}
if (virtualTable.getParent() != null) {
index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin,
filled);
}
return index;
}
private WasmStructure initRegularClassStructure(String className) {
@ -391,23 +429,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
if (methodDesc == null) {
structure.getFields().add(WasmType.Reference.FUNC.asStorage());
} else {
var functionType = getFunctionType(virtualTable.getClassName(), methodDesc);
var functionType = typeMapper.getFunctionType(virtualTable.getClassName(), methodDesc, false);
structure.getFields().add(functionType.getReference().asStorage());
}
}
}
private WasmFunctionType getFunctionType(String className, MethodDescriptor methodDesc) {
var returnType = typeMapper.mapType(methodDesc.getResultType());
var javaParamTypes = methodDesc.getParameterTypes();
var paramTypes = new WasmType[javaParamTypes.length + 1];
paramTypes[0] = getClassInfo(className).getType();
for (var i = 0; i < javaParamTypes.length; ++i) {
paramTypes[i + 1] = typeMapper.mapType(javaParamTypes[i]);
}
return functionTypes.of(returnType, paramTypes);
}
private void initArrayClass(WasmGCClassInfo classInfo, ValueType.Array type) {
classInfo.initializer = target -> {
var itemTypeInfo = getClassInfo(type.getItemType());
@ -497,6 +524,9 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
if (className.equals("java.lang.Object") && field.getName().equals("monitor")) {
continue;
}
if (className.equals("java.lang.Class") && field.getName().equals("platformClass")) {
continue;
}
if (field.hasModifier(ElementModifier.STATIC)) {
continue;
}

View File

@ -28,6 +28,7 @@ public class WasmGCClassInfo {
private ValueType valueType;
WasmStructure structure;
WasmArray array;
boolean hasOwnVirtualTable;
WasmStructure virtualTableStructure;
WasmGlobal pointer;
WasmGlobal initializerPointer;

View File

@ -15,16 +15,26 @@
*/
package org.teavm.backend.wasm.generate.gc.classes;
import java.util.List;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.model.WasmFunctionType;
import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmPackedType;
import org.teavm.backend.wasm.model.WasmStorageType;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.ValueType;
public class WasmGCTypeMapper {
private WasmGCClassInfoProvider classInfoProvider;
private WasmFunctionTypes functionTypes;
private WasmModule module;
WasmGCTypeMapper(WasmGCClassInfoProvider classInfoProvider) {
WasmGCTypeMapper(WasmGCClassInfoProvider classInfoProvider, WasmFunctionTypes functionTypes,
WasmModule module) {
this.classInfoProvider = classInfoProvider;
this.functionTypes = functionTypes;
this.module = module;
}
public WasmStorageType mapStorageType(ValueType type) {
@ -76,4 +86,21 @@ public class WasmGCTypeMapper {
return classInfoProvider.getClassInfo(type).getType();
}
}
public WasmFunctionType getFunctionType(String className, MethodDescriptor methodDesc, boolean fresh) {
var returnType = mapType(methodDesc.getResultType());
var javaParamTypes = methodDesc.getParameterTypes();
var paramTypes = new WasmType[javaParamTypes.length + 1];
paramTypes[0] = classInfoProvider.getClassInfo(className).getType();
for (var i = 0; i < javaParamTypes.length; ++i) {
paramTypes[i + 1] = mapType(javaParamTypes[i]);
}
if (fresh) {
var type = new WasmFunctionType(null, returnType, List.of(paramTypes));
module.types.add(type);
return type;
} else {
return functionTypes.of(returnType, paramTypes);
}
}
}

View File

@ -211,6 +211,11 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
: WasmType.Reference.STRUCT);
}
@Override
protected WasmExpression genIsNull(WasmExpression value) {
return new WasmReferencesEqual(value, new WasmNullConstant(WasmType.Reference.STRUCT));
}
@Override
protected CallSiteIdentifier generateCallSiteId(TextLocation location) {
return new SimpleCallSite();
@ -268,12 +273,19 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
}
var instanceStruct = context.classInfoProvider().getClassInfo(vtable.getClassName()).getStructure();
var actualInstanceType = (WasmType.CompositeReference) instance.getType();
var actualInstanceStruct = (WasmStructure) actualInstanceType.composite;
var actualVtableType = (WasmType.CompositeReference) actualInstanceStruct.getFields().get(0).asUnpackedType();
var actualVtableStruct = (WasmStructure) actualVtableType.composite;
WasmExpression classRef = new WasmStructGet(instanceStruct, new WasmGetLocal(instance),
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET);
var index = context.classInfoProvider().getVirtualMethodsOffset() + vtableIndex;
var vtableStruct = context.classInfoProvider().getClassInfo(vtable.getClassName())
.getVirtualTableStructure();
if (!vtableStruct.isSupertypeOf(actualVtableStruct)) {
classRef = new WasmCast(classRef, vtableStruct.getReference());
}
var functionRef = new WasmStructGet(vtableStruct, classRef, index);
var functionTypeRef = (WasmType.CompositeReference) vtableStruct.getFields().get(index).asUnpackedType();
@ -432,7 +444,6 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
result = invocation(expr, null, false);
}
@Override
protected WasmExpression invocation(InvocationExpr expr, List<WasmExpression> resultConsumer, boolean willDrop) {
if (expr.getType() == InvocationType.SPECIAL || expr.getType() == InvocationType.STATIC) {

View File

@ -72,6 +72,9 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
var value = new WasmCall(nextCharArrayFunction);
function.getBody().add(new WasmStructSet(stringStruct, new WasmGetGlobal(str.global),
WasmGCClassInfoProvider.CUSTOM_FIELD_OFFSETS, value));
function.getBody().add(new WasmStructSet(stringStruct, new WasmGetGlobal(str.global),
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET,
new WasmGetGlobal(standardClasses.stringClass().getPointer())));
}
}

View File

@ -15,7 +15,9 @@
*/
package org.teavm.backend.wasm.model;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
public class WasmFunctionType extends WasmCompositeType {
@ -23,6 +25,7 @@ public class WasmFunctionType extends WasmCompositeType {
private WasmType returnType;
private Supplier<List<? extends WasmType>> parameterTypesSupplier;
private Supplier<WasmType> returnTypeSupplier;
private Set<WasmFunctionType> supertypes = new LinkedHashSet<>();
public WasmFunctionType(String name, WasmType returnType, List<? extends WasmType> parameterTypes) {
super(name);
@ -53,6 +56,10 @@ public class WasmFunctionType extends WasmCompositeType {
return returnType;
}
public Set<WasmFunctionType> getSupertypes() {
return supertypes;
}
@Override
public void acceptVisitor(WasmCompositeTypeVisitor visitor) {
visitor.visit(this);

View File

@ -121,6 +121,7 @@ public class WasmModule {
sorting.original = types;
sorting.graph = typeGraph;
sorting.visited = new boolean[types.size()];
sorting.sccVisited = new boolean[types.size()];
sorting.sccMap = sccStartNode;
sorting.sccsByIndex = sccsByIndex;
for (var i = 0; i < types.size(); ++i) {
@ -137,6 +138,7 @@ public class WasmModule {
WasmCollection<WasmCompositeType> original;
Graph graph;
boolean[] visited;
boolean[] sccVisited;
int[] sccMap;
int[][] sccsByIndex;
List<WasmCompositeType> sorted = new ArrayList<>();
@ -146,18 +148,46 @@ public class WasmModule {
if (visited[typeIndex]) {
return;
}
var scc = sccsByIndex[typeIndex];
if (scc == null) {
visited[typeIndex] = true;
for (var outgoing : graph.outgoingEdges(typeIndex)) {
visit(outgoing);
}
var scc = sccsByIndex[typeIndex];
if (scc == null) {
sorted.add(original.get(typeIndex));
} else {
visited[typeIndex] = true;
for (var index : scc) {
sorted.add(original.get(index));
for (var outgoing : graph.outgoingEdges(index)) {
visit(outgoing);
}
}
for (var index : scc) {
visitScc(index, typeIndex);
}
}
}
void visitScc(int index, int sccBase) {
if (sccVisited[index]) {
return;
}
sccVisited[index] = true;
var type = original.get(index);
if (type instanceof WasmStructure) {
var supertype = ((WasmStructure) type).getSupertype();
if (supertype != null && sccMap[supertype.index] == sccBase) {
visitScc(supertype.index, sccBase);
}
} else if (type instanceof WasmFunctionType) {
var supertypes = ((WasmFunctionType) type).getSupertypes();
for (var supertype : supertypes) {
if (sccMap[supertype.index] == sccBase) {
visitScc(supertype.index, sccBase);
}
}
}
sorted.add(type);
}
}
}

View File

@ -57,6 +57,9 @@ final class WasmTypeGraphBuilder {
@Override
public void visit(WasmFunctionType type) {
for (var supertype : type.getSupertypes()) {
addEdge(supertype.getReference());
}
for (var parameter : type.getParameterTypes()) {
addEdge(parameter);
}

View File

@ -381,6 +381,9 @@ public class WasmBinaryRenderer {
var visitor = new WasmBinaryRenderingVisitor(code, module, dwarfGenerator,
function.getJavaMethod() != null ? debugLines : null, offset);
for (var part : function.getBody()) {
visitor.preprocess(part);
}
for (var part : function.getBody()) {
part.acceptVisitor(visitor);
}

View File

@ -18,9 +18,11 @@ package org.teavm.backend.wasm.render;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.teavm.backend.wasm.debug.DebugLines;
import org.teavm.backend.wasm.generate.DwarfGenerator;
import org.teavm.backend.wasm.model.WasmModule;
@ -39,6 +41,7 @@ import org.teavm.backend.wasm.model.expression.WasmCast;
import org.teavm.backend.wasm.model.expression.WasmConditional;
import org.teavm.backend.wasm.model.expression.WasmConversion;
import org.teavm.backend.wasm.model.expression.WasmCopy;
import org.teavm.backend.wasm.model.expression.WasmDefaultExpressionVisitor;
import org.teavm.backend.wasm.model.expression.WasmDrop;
import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.backend.wasm.model.expression.WasmExpressionVisitor;
@ -95,6 +98,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
private TextLocation lastEmittedLocation;
private int positionToEmit;
private List<TextLocation> locationStack = new ArrayList<>();
private Set<WasmBlock> blocksToPreserve = new HashSet<>();
WasmBinaryRenderingVisitor(WasmBinaryWriter writer, WasmModule module,
DwarfGenerator dwarfGenerator, DebugLines debugLines, int addressOffset) {
@ -105,8 +109,38 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
this.debugLines = debugLines;
}
void preprocess(WasmExpression expression) {
expression.acceptVisitor(new WasmDefaultExpressionVisitor() {
@Override
public void visit(WasmBranch expression) {
super.visit(expression);
register(expression.getTarget());
}
@Override
public void visit(WasmBreak expression) {
super.visit(expression);
register(expression.getTarget());
}
@Override
public void visit(WasmSwitch expression) {
super.visit(expression);
for (WasmBlock target : expression.getTargets()) {
register(target);
}
register(expression.getDefaultTarget());
}
private void register(WasmBlock block) {
blocksToPreserve.add(block);
}
});
}
@Override
public void visit(WasmBlock expression) {
if (blocksToPreserve.contains(expression) || expression.isLoop()) {
pushLocation(expression);
pushLocation(expression);
int blockDepth = 1;
@ -122,6 +156,13 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
popLocation();
blockDepths.remove(expression);
depth -= blockDepth;
} else {
pushLocation(expression);
for (var part : expression.getBody()) {
part.acceptVisitor(this);
}
popLocation();
}
}
private void writeBlockType(WasmType type) {

View File

@ -57,6 +57,11 @@ public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor
@Override
public void visit(WasmFunctionType type) {
section.writeByte(0x50);
section.writeLEB(type.getSupertypes().size());
for (var supertype : type.getSupertypes()) {
section.writeLEB(module.types.indexOf(supertype));
}
section.writeByte(0x60);
section.writeLEB(type.getParameterTypes().size());
for (var inputType : type.getParameterTypes()) {

View File

@ -33,6 +33,10 @@ public class WasmGCSupport {
return new ClassCastException();
}
public static CloneNotSupportedException cnse() {
return new CloneNotSupportedException();
}
@Import(name = "putcharStdout")
public static native void putCharStdout(char c);

View File

@ -15,10 +15,12 @@
*/
package org.teavm.backend.wasm.transformation.gc;
import org.teavm.backend.wasm.runtime.WasmGCSupport;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassHolderTransformerContext;
import org.teavm.model.ElementModifier;
import org.teavm.model.emit.ProgramEmitter;
public class BaseClassesTransformation implements ClassHolderTransformer {
@Override
@ -30,6 +32,11 @@ 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;
}
}
}
} else if (cls.getName().equals("java.lang.Class")) {