wasm gc: support type nullability and global mutability

This commit is contained in:
Alexey Andreev 2024-08-31 20:16:09 +02:00
parent 9b601ac002
commit 1dc7bc653d
21 changed files with 209 additions and 128 deletions

View File

@ -180,7 +180,7 @@ public class WasmGCModuleGenerator {
initializer = new WasmFunction(functionType); initializer = new WasmFunction(functionType);
initializer.setReferenced(true); initializer.setReferenced(true);
declarationsGenerator.module.functions.add(initializer); declarationsGenerator.module.functions.add(initializer);
initializerRef = new WasmGlobal("teavm@initializer", functionType.getReference(), initializerRef = new WasmGlobal("teavm@initializer", functionType.getNonNullReference(),
new WasmFunctionReference(initializer)); new WasmFunctionReference(initializer));
declarationsGenerator.module.globals.add(initializerRef); declarationsGenerator.module.globals.add(initializerRef);
initializer.getBody().add(new WasmSetGlobal(initializerRef, initializer.getBody().add(new WasmSetGlobal(initializerRef,

View File

@ -69,7 +69,9 @@ public abstract class BaseDisassemblyListener {
break; break;
} }
} else if (type instanceof WasmHollowType.SpecialReference) { } else if (type instanceof WasmHollowType.SpecialReference) {
switch (((WasmHollowType.SpecialReference) type).kind) { var refType = (WasmHollowType.SpecialReference) type;
if (refType.isNullable()) {
switch (refType.kind) {
case ANY: case ANY:
writer.write("anyref"); writer.write("anyref");
return; return;
@ -91,9 +93,38 @@ public abstract class BaseDisassemblyListener {
default: default:
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
} else {
writer.write("(ref ");
switch (refType.kind) {
case ANY:
writer.write("any");
return;
case FUNC:
writer.write("func");
return;
case ARRAY:
writer.write("array");
return;
case EXTERN:
writer.write("extern");
return;
case STRUCT:
writer.write("struct");
return;
case I31:
writer.write("i31");
return;
default:
throw new IllegalArgumentException();
}
}
} else if (type instanceof WasmHollowType.CompositeReference) { } else if (type instanceof WasmHollowType.CompositeReference) {
writer.write("(ref null "); var refType = (WasmHollowType.CompositeReference) type;
writeTypeRef(((WasmHollowType.CompositeReference) type).index); writer.write("(ref ");
if (refType.isNullable()) {
writer.write("null");
}
writeTypeRef(refType.index);
writer.write(")"); writer.write(")");
return; return;
} }

View File

@ -755,23 +755,17 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements
} }
@Override @Override
public void cast(WasmHollowType.Reference type, boolean nullable) { public void cast(WasmHollowType.Reference type) {
writer.address().write("ref.cast (ref "); writer.address().write("ref.cast (ref ");
if (!nullable) {
writer.write("null ");
}
writeType(type); writeType(type);
writer.write(")").eol(); writer.eol();
} }
@Override @Override
public void test(WasmHollowType.Reference type, boolean nullable) { public void test(WasmHollowType.Reference type) {
writer.address().write("ref.test (ref "); writer.address().write("ref.test ");
if (!nullable) {
writer.write("null ");
}
writeType(type); writeType(type);
writer.write(")").eol(); writer.eol();
} }
@Override @Override

View File

@ -281,8 +281,9 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
} }
var classStructure = classInfo.virtualTableStructure; var classStructure = classInfo.virtualTableStructure;
classInfo.pointer = new WasmGlobal(pointerName, classStructure.getReference(), classInfo.pointer = new WasmGlobal(pointerName, classStructure.getNonNullReference(),
new WasmStructNewDefault(classStructure)); new WasmStructNewDefault(classStructure));
classInfo.pointer.setImmutable(true);
module.globals.add(classInfo.pointer); module.globals.add(classInfo.pointer);
if (type instanceof ValueType.Primitive) { if (type instanceof ValueType.Primitive) {
initPrimitiveClass(classInfo, (ValueType.Primitive) type); initPrimitiveClass(classInfo, (ValueType.Primitive) type);
@ -390,10 +391,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
if (cls != null && cls.getMethod(CLINIT_METHOD_DESC) != null) { if (cls != null && cls.getMethod(CLINIT_METHOD_DESC) != null) {
var clinitType = functionTypes.of(null); var clinitType = functionTypes.of(null);
var wasmName = names.topLevel(names.suggestForClass(name) + "@initializer"); var wasmName = names.topLevel(names.suggestForClass(name) + "@initializer");
var initFunction = functionProvider.forStaticMethod(new MethodReference(name, CLINIT_METHOD_DESC)); classInfo.initializerPointer = new WasmGlobal(wasmName, clinitType.getReference(),
initFunction.setReferenced(true); new WasmNullConstant(clinitType.getReference()));
var ref = new WasmFunctionReference(initFunction);
classInfo.initializerPointer = new WasmGlobal(wasmName, clinitType.getReference(), ref);
module.globals.add(classInfo.initializerPointer); module.globals.add(classInfo.initializerPointer);
} }
} }
@ -420,6 +419,11 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
if (virtualTable != null && virtualTable.isConcrete()) { if (virtualTable != null && virtualTable.isConcrete()) {
fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable); fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable);
} }
if (classInfo.initializerPointer != null) {
var initFunction = functionProvider.forStaticMethod(new MethodReference(name, CLINIT_METHOD_DESC));
initFunction.setReferenced(true);
classInfo.initializerPointer.setInitialValue(new WasmFunctionReference(initFunction));
}
}; };
} }
@ -490,7 +494,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
var objectLocal = new WasmLocal(standardClasses.objectClass().getType(), "object"); var objectLocal = new WasmLocal(standardClasses.objectClass().getType(), "object");
function.add(objectLocal); function.add(objectLocal);
var castObject = new WasmCast(new WasmGetLocal(objectLocal), objectStructure.getReference()); var castObject = new WasmCast(new WasmGetLocal(objectLocal), objectStructure.getNonNullReference());
var arrayField = new WasmStructGet(objectStructure, castObject, ARRAY_DATA_FIELD_OFFSET); var arrayField = new WasmStructGet(objectStructure, castObject, ARRAY_DATA_FIELD_OFFSET);
var result = new WasmArrayLength(arrayField); var result = new WasmArrayLength(arrayField);
function.getBody().add(new WasmReturn(result)); function.getBody().add(new WasmReturn(result));
@ -521,7 +525,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
arrayGetObjectFunction.add(objectLocal); arrayGetObjectFunction.add(objectLocal);
arrayGetObjectFunction.add(indexLocal); arrayGetObjectFunction.add(indexLocal);
var array = new WasmCast(new WasmGetLocal(objectLocal), arrayStruct.getReference()); var array = new WasmCast(new WasmGetLocal(objectLocal), arrayStruct.getNonNullReference());
var arrayData = new WasmStructGet(arrayStruct, array, ARRAY_DATA_FIELD_OFFSET); var arrayData = new WasmStructGet(arrayStruct, array, ARRAY_DATA_FIELD_OFFSET);
var result = new WasmArrayGet(arrayDataType, arrayData, new WasmGetLocal(indexLocal)); var result = new WasmArrayGet(arrayDataType, arrayData, new WasmGetLocal(indexLocal));
arrayGetObjectFunction.getBody().add(new WasmReturn(result)); arrayGetObjectFunction.getBody().add(new WasmReturn(result));
@ -545,7 +549,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
function.add(objectLocal); function.add(objectLocal);
function.add(indexLocal); function.add(indexLocal);
var array = new WasmCast(new WasmGetLocal(objectLocal), arrayStruct.getReference()); var array = new WasmCast(new WasmGetLocal(objectLocal), arrayStruct.getNonNullReference());
var arrayData = new WasmStructGet(arrayStruct, array, ARRAY_DATA_FIELD_OFFSET); var arrayData = new WasmStructGet(arrayStruct, array, ARRAY_DATA_FIELD_OFFSET);
var result = new WasmArrayGet(arrayDataType, arrayData, new WasmGetLocal(indexLocal)); var result = new WasmArrayGet(arrayDataType, arrayData, new WasmGetLocal(indexLocal));
Class<?> primitiveType; Class<?> primitiveType;
@ -626,7 +630,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
var call = new WasmCall(function); var call = new WasmCall(function);
var instanceParam = new WasmLocal(getClassInfo(virtualTable.getClassName()).getType()); var instanceParam = new WasmLocal(getClassInfo(virtualTable.getClassName()).getType());
wrapperFunction.add(instanceParam); wrapperFunction.add(instanceParam);
var castTarget = getClassInfo(implementor.getClassName()).getType(); var castTarget = getClassInfo(implementor.getClassName()).getStructure().getNonNullReference();
call.getArguments().add(new WasmCast(new WasmGetLocal(instanceParam), castTarget)); call.getArguments().add(new WasmCast(new WasmGetLocal(instanceParam), castTarget));
var params = new WasmLocal[entry.getMethod().parameterCount()]; var params = new WasmLocal[entry.getMethod().parameterCount()];
for (var i = 0; i < entry.getMethod().parameterCount(); ++i) { for (var i = 0; i < entry.getMethod().parameterCount(); ++i) {
@ -664,7 +668,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
function.add(dataCopyLocal); function.add(dataCopyLocal);
function.getBody().add(new WasmSetLocal(originalLocal, function.getBody().add(new WasmSetLocal(originalLocal,
new WasmCast(new WasmGetLocal(instanceLocal), objectStructure.getReference()))); new WasmCast(new WasmGetLocal(instanceLocal), objectStructure.getNonNullReference())));
function.getBody().add(new WasmSetLocal(resultLocal, new WasmStructNewDefault(objectStructure))); function.getBody().add(new WasmSetLocal(resultLocal, new WasmStructNewDefault(objectStructure)));
var classValue = new WasmStructGet(objectStructure, new WasmGetLocal(originalLocal), var classValue = new WasmStructGet(objectStructure, new WasmGetLocal(originalLocal),

View File

@ -279,7 +279,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
var index = context.classInfoProvider().getVirtualMethodsOffset() + entry.getIndex(); var index = context.classInfoProvider().getVirtualMethodsOffset() + entry.getIndex();
var expectedInstanceClassInfo = context.classInfoProvider().getClassInfo(vtable.getClassName()); var expectedInstanceClassInfo = context.classInfoProvider().getClassInfo(vtable.getClassName());
var vtableStruct = expectedInstanceClassInfo.getVirtualTableStructure(); var vtableStruct = expectedInstanceClassInfo.getVirtualTableStructure();
classRef = new WasmCast(classRef, vtableStruct.getReference()); classRef = new WasmCast(classRef, vtableStruct.getNonNullReference());
var functionRef = new WasmStructGet(vtableStruct, classRef, index); var functionRef = new WasmStructGet(vtableStruct, classRef, index);
var functionTypeRef = (WasmType.CompositeReference) vtableStruct.getFields().get(index).getUnpackedType(); var functionTypeRef = (WasmType.CompositeReference) vtableStruct.getFields().get(index).getUnpackedType();
@ -288,7 +288,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
var instanceType = (WasmType.CompositeReference) instance.getType(); var instanceType = (WasmType.CompositeReference) instance.getType();
var instanceStruct = (WasmStructure) instanceType.composite; var instanceStruct = (WasmStructure) instanceType.composite;
if (!expectedInstanceClassInfo.getStructure().isSupertypeOf(instanceStruct)) { if (!expectedInstanceClassInfo.getStructure().isSupertypeOf(instanceStruct)) {
instanceRef = new WasmCast(instanceRef, expectedInstanceClassInfo.getType()); instanceRef = new WasmCast(instanceRef, expectedInstanceClassInfo.getStructure().getNonNullReference());
} }
invoke.getArguments().add(instanceRef); invoke.getArguments().add(instanceRef);
@ -381,7 +381,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
WasmExpression exception = new WasmGetGlobal(context.exceptionGlobal()); WasmExpression exception = new WasmGetGlobal(context.exceptionGlobal());
if (exceptionClass != null && !exceptionClass.equals("java.lang.Throwable")) { if (exceptionClass != null && !exceptionClass.equals("java.lang.Throwable")) {
exception = new WasmCast(exception, context.classInfoProvider().getClassInfo(exceptionClass) exception = new WasmCast(exception, context.classInfoProvider().getClassInfo(exceptionClass)
.getStructure().getReference()); .getStructure().getNonNullReference());
} }
var save = new WasmSetLocal(local, exception); var save = new WasmSetLocal(local, exception);
save.setLocation(location); save.setLocation(location);

View File

@ -86,9 +86,10 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
var brief = string.length() > 16 ? string.substring(0, 16) : string; var brief = string.length() > 16 ? string.substring(0, 16) : string;
var globalName = names.topLevel("teavm@string<" + stringMap.size() + ">" var globalName = names.topLevel("teavm@string<" + stringMap.size() + ">"
+ WasmGCNameProvider.sanitize(brief)); + WasmGCNameProvider.sanitize(brief));
var globalType = standardClasses.stringClass().getType(); var globalType = standardClasses.stringClass().getStructure().getNonNullReference();
var global = new WasmGlobal(globalName, globalType, var global = new WasmGlobal(globalName, globalType,
new WasmStructNewDefault(standardClasses.stringClass().getStructure())); new WasmStructNewDefault(standardClasses.stringClass().getStructure()));
global.setImmutable(true);
module.globals.add(global); module.globals.add(global);
return new WasmGCStringConstant(stringMap.size(), global); return new WasmGCStringConstant(stringMap.size(), global);
}); });

View File

@ -59,7 +59,7 @@ public class ArrayIntrinsic implements WasmGCIntrinsic {
var object = context.exprCache().create(originalObject, objectStruct.getReference(), var object = context.exprCache().create(originalObject, objectStruct.getReference(),
invocation.getLocation(), block.getBody()); invocation.getLocation(), block.getBody());
var classRef = new WasmStructGet(objectStruct, object.expr(), WasmGCClassInfoProvider.CLASS_FIELD_OFFSET); var classRef = new WasmStructGet(objectStruct, object.expr(), WasmGCClassInfoProvider.CLASS_FIELD_OFFSET);
var vt = new WasmCast(classRef, vtStruct.getReference()); var vt = new WasmCast(classRef, vtStruct.getNonNullReference());
var function = new WasmStructGet(vtStruct, vt, offset); var function = new WasmStructGet(vtStruct, vt, offset);
var call = new WasmCallReference(function, functionType); var call = new WasmCallReference(function, functionType);
call.getArguments().add(object.expr()); call.getArguments().add(object.expr());

View File

@ -66,8 +66,10 @@ public class ObjectIntrinsic implements WasmGCIntrinsic {
} }
private WasmExpression generateGetMonitor(InvocationExpr invocation, WasmGCIntrinsicContext context) { private WasmExpression generateGetMonitor(InvocationExpr invocation, WasmGCIntrinsicContext context) {
var monitorType = context.classInfoProvider().getClassInfo(ValueType.object("java.lang.Object$Monitor")) var monitorStruct = context.classInfoProvider().getClassInfo(ValueType.object("java.lang.Object$Monitor"))
.getStructure().getReference(); .getStructure();
var monitorType = monitorStruct.getReference();
var monitorNotNullType = monitorStruct.getNonNullReference();
var objectStruct = context.classInfoProvider().getClassInfo(ValueType.object("java.lang.Object")) var objectStruct = context.classInfoProvider().getClassInfo(ValueType.object("java.lang.Object"))
.getStructure(); .getStructure();
var block = new WasmBlock(false); var block = new WasmBlock(false);
@ -77,13 +79,13 @@ public class ObjectIntrinsic implements WasmGCIntrinsic {
block.getBody().add(new WasmSetLocal(tmpVar, new WasmStructGet(objectStruct, instance, block.getBody().add(new WasmSetLocal(tmpVar, new WasmStructGet(objectStruct, instance,
WasmGCClassInfoProvider.MONITOR_FIELD_OFFSET))); WasmGCClassInfoProvider.MONITOR_FIELD_OFFSET)));
WasmExpression test = new WasmTest(new WasmGetLocal(tmpVar), monitorType, false); WasmExpression test = new WasmTest(new WasmGetLocal(tmpVar), monitorNotNullType);
test = new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, test); test = new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, test);
var branch = new WasmBranch(test, block); var branch = new WasmBranch(test, block);
branch.setResult(new WasmNullConstant(monitorType)); branch.setResult(new WasmNullConstant(monitorType));
block.getBody().add(new WasmDrop(branch)); block.getBody().add(new WasmDrop(branch));
block.getBody().add(new WasmCast(new WasmGetLocal(tmpVar), monitorType)); block.getBody().add(new WasmCast(new WasmGetLocal(tmpVar), monitorNotNullType));
context.tempVars().release(tmpVar); context.tempVars().release(tmpVar);
return block; return block;
} }
@ -106,13 +108,14 @@ public class ObjectIntrinsic implements WasmGCIntrinsic {
block.getBody().add(new WasmSetLocal(tmpVar, new WasmStructGet(objectStruct, instance, block.getBody().add(new WasmSetLocal(tmpVar, new WasmStructGet(objectStruct, instance,
WasmGCClassInfoProvider.MONITOR_FIELD_OFFSET))); WasmGCClassInfoProvider.MONITOR_FIELD_OFFSET)));
WasmExpression test = new WasmTest(new WasmGetLocal(tmpVar), WasmType.Reference.I31, false); WasmExpression test = new WasmTest(new WasmGetLocal(tmpVar),
WasmType.SpecialReferenceKind.I31.asNonNullType());
test = new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, test); test = new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, test);
var branch = new WasmBranch(test, block); var branch = new WasmBranch(test, block);
branch.setResult(new WasmInt32Constant(-1)); branch.setResult(new WasmInt32Constant(-1));
block.getBody().add(new WasmDrop(branch)); block.getBody().add(new WasmDrop(branch));
var i31ref = new WasmCast(new WasmGetLocal(tmpVar), WasmType.Reference.I31); var i31ref = new WasmCast(new WasmGetLocal(tmpVar), WasmType.SpecialReferenceKind.I31.asNonNullType());
block.getBody().add(new WasmInt31Get(i31ref, WasmSignedType.UNSIGNED)); block.getBody().add(new WasmInt31Get(i31ref, WasmSignedType.UNSIGNED));
context.tempVars().release(tmpVar); context.tempVars().release(tmpVar);
return block; return block;

View File

@ -18,6 +18,7 @@ package org.teavm.backend.wasm.model;
public abstract class WasmCompositeType extends WasmEntity { public abstract class WasmCompositeType extends WasmEntity {
private String name; private String name;
private WasmType.CompositeReference reference; private WasmType.CompositeReference reference;
private WasmType.CompositeReference nonNullReference;
int recursiveTypeCount = -1; int recursiveTypeCount = -1;
WasmCompositeType(String name) { WasmCompositeType(String name) {
@ -30,11 +31,18 @@ public abstract class WasmCompositeType extends WasmEntity {
public WasmType.CompositeReference getReference() { public WasmType.CompositeReference getReference() {
if (reference == null) { if (reference == null) {
reference = new WasmType.CompositeReference(this); reference = new WasmType.CompositeReference(this, true);
} }
return reference; return reference;
} }
public WasmType.CompositeReference getNonNullReference() {
if (nonNullReference == null) {
nonNullReference = new WasmType.CompositeReference(this, false);
}
return nonNullReference;
}
public int getRecursiveTypeCount() { public int getRecursiveTypeCount() {
return recursiveTypeCount; return recursiveTypeCount;
} }

View File

@ -22,6 +22,7 @@ public class WasmGlobal extends WasmEntity {
private String name; private String name;
private WasmType type; private WasmType type;
private WasmExpression initialValue; private WasmExpression initialValue;
private boolean immutable;
public WasmGlobal(String name, WasmType type, WasmExpression initialValue) { public WasmGlobal(String name, WasmType type, WasmExpression initialValue) {
this.name = name; this.name = name;
@ -48,4 +49,12 @@ public class WasmGlobal extends WasmEntity {
public void setInitialValue(WasmExpression initialValue) { public void setInitialValue(WasmExpression initialValue) {
this.initialValue = Objects.requireNonNull(initialValue); this.initialValue = Objects.requireNonNull(initialValue);
} }
public boolean isImmutable() {
return immutable;
}
public void setImmutable(boolean immutable) {
this.immutable = immutable;
}
} }

View File

@ -63,12 +63,23 @@ public abstract class WasmType {
public static final SpecialReference STRUCT = SpecialReferenceKind.STRUCT.asType(); public static final SpecialReference STRUCT = SpecialReferenceKind.STRUCT.asType();
public static final SpecialReference ARRAY = SpecialReferenceKind.ARRAY.asType(); public static final SpecialReference ARRAY = SpecialReferenceKind.ARRAY.asType();
public static final SpecialReference I31 = SpecialReferenceKind.I31.asType(); public static final SpecialReference I31 = SpecialReferenceKind.I31.asType();
private final boolean nullable;
public Reference(boolean nullable) {
this.nullable = nullable;
}
public boolean isNullable() {
return nullable;
}
} }
public static final class CompositeReference extends Reference { public static final class CompositeReference extends Reference {
public final WasmCompositeType composite; public final WasmCompositeType composite;
CompositeReference(WasmCompositeType composite) { CompositeReference(WasmCompositeType composite, boolean nullable) {
super(nullable);
this.composite = composite; this.composite = composite;
} }
} }
@ -76,7 +87,8 @@ public abstract class WasmType {
public static final class SpecialReference extends Reference { public static final class SpecialReference extends Reference {
public final SpecialReferenceKind kind; public final SpecialReferenceKind kind;
private SpecialReference(SpecialReferenceKind kind) { private SpecialReference(SpecialReferenceKind kind, boolean nullable) {
super(nullable);
this.kind = kind; this.kind = kind;
} }
} }
@ -90,12 +102,20 @@ public abstract class WasmType {
I31; I31;
private SpecialReference type; private SpecialReference type;
private SpecialReference nonNullType;
final SpecialReference asType() { public final SpecialReference asType() {
if (type == null) { if (type == null) {
type = new SpecialReference(this); type = new SpecialReference(this, true);
} }
return type; return type;
} }
public final SpecialReference asNonNullType() {
if (nonNullType == null) {
nonNullType = new SpecialReference(this, false);
}
return nonNullType;
}
} }
} }

View File

@ -21,16 +21,10 @@ import org.teavm.backend.wasm.model.WasmType;
public class WasmCast extends WasmExpression { public class WasmCast extends WasmExpression {
private WasmExpression value; private WasmExpression value;
private WasmType.Reference targetType; private WasmType.Reference targetType;
private boolean nullable;
public WasmCast(WasmExpression value, WasmType.Reference targetType) { public WasmCast(WasmExpression value, WasmType.Reference targetType) {
this(value, targetType, true);
}
public WasmCast(WasmExpression value, WasmType.Reference targetType, boolean nullable) {
this.value = Objects.requireNonNull(value); this.value = Objects.requireNonNull(value);
this.targetType = Objects.requireNonNull(targetType); this.targetType = Objects.requireNonNull(targetType);
this.nullable = nullable;
} }
public WasmExpression getValue() { public WasmExpression getValue() {
@ -49,14 +43,6 @@ public class WasmCast extends WasmExpression {
this.targetType = Objects.requireNonNull(targetType); this.targetType = Objects.requireNonNull(targetType);
} }
public boolean isNullable() {
return nullable;
}
public void setNullable(boolean nullable) {
this.nullable = nullable;
}
@Override @Override
public void acceptVisitor(WasmExpressionVisitor visitor) { public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this); visitor.visit(this);

View File

@ -21,16 +21,10 @@ import org.teavm.backend.wasm.model.WasmType;
public class WasmTest extends WasmExpression { public class WasmTest extends WasmExpression {
private WasmExpression value; private WasmExpression value;
private WasmType.Reference testType; private WasmType.Reference testType;
private boolean nullable;
public WasmTest(WasmExpression value, WasmType.Reference testType) { public WasmTest(WasmExpression value, WasmType.Reference testType) {
this(value, testType, true);
}
public WasmTest(WasmExpression value, WasmType.Reference testType, boolean nullable) {
this.value = Objects.requireNonNull(value); this.value = Objects.requireNonNull(value);
this.testType = Objects.requireNonNull(testType); this.testType = Objects.requireNonNull(testType);
this.nullable = nullable;
} }
public WasmExpression getValue() { public WasmExpression getValue() {
@ -49,13 +43,6 @@ public class WasmTest extends WasmExpression {
this.testType = Objects.requireNonNull(testType); this.testType = Objects.requireNonNull(testType);
} }
public boolean isNullable() {
return nullable;
}
public void setNullable(boolean nullable) {
this.nullable = nullable;
}
@Override @Override
public void acceptVisitor(WasmExpressionVisitor visitor) { public void acceptVisitor(WasmExpressionVisitor visitor) {

View File

@ -135,10 +135,10 @@ public interface CodeListener {
default void nullConstant(WasmHollowType.Reference type) { default void nullConstant(WasmHollowType.Reference type) {
} }
default void cast(WasmHollowType.Reference type, boolean nullable) { default void cast(WasmHollowType.Reference type) {
} }
default void test(WasmHollowType.Reference type, boolean nullable) { default void test(WasmHollowType.Reference type) {
} }
default void structNew(int typeIndex) { default void structNew(int typeIndex) {

View File

@ -589,7 +589,7 @@ public class CodeParser extends BaseSectionParser {
break; break;
case 0xD0: case 0xD0:
codeListener.nullConstant(reader.readHeapType()); codeListener.nullConstant(reader.readHeapType(true));
break; break;
case 0xD2: case 0xD2:
@ -684,19 +684,19 @@ public class CodeParser extends BaseSectionParser {
return true; return true;
case 20: case 20:
codeListener.test(reader.readHeapType(), false); codeListener.test(reader.readHeapType(false));
return true; return true;
case 21: case 21:
codeListener.test(reader.readHeapType(), true); codeListener.test(reader.readHeapType(true));
return true; return true;
case 22: case 22:
codeListener.cast(reader.readHeapType(), false); codeListener.cast(reader.readHeapType(false));
return true; return true;
case 23: case 23:
codeListener.cast(reader.readHeapType(), true); codeListener.cast(reader.readHeapType(true));
return true; return true;
case 28: case 28:

View File

@ -16,6 +16,7 @@
package org.teavm.backend.wasm.parser; package org.teavm.backend.wasm.parser;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import org.teavm.backend.wasm.model.WasmType;
public class WasmBinaryReader { public class WasmBinaryReader {
private final AddressListener addressListener; private final AddressListener addressListener;
@ -63,43 +64,51 @@ public class WasmBinaryReader {
case 0x7C: case 0x7C:
return WasmHollowType.FLOAT64; return WasmHollowType.FLOAT64;
case 0x63: case 0x63:
return readHeapType(); return readHeapType(true);
case 0x64:
return readHeapType(false);
case 0x40: case 0x40:
return null; return null;
default: default:
return readAbsHeapType(typeId); return readAbsHeapType(typeId, true);
} }
} }
public WasmHollowType.Reference readHeapType() { public WasmHollowType.Reference readHeapType(boolean nullable) {
var typeId = data[ptr]; var typeId = data[ptr];
if ((typeId & 0xC0) == 0x40) { if ((typeId & 0xC0) == 0x40) {
var result = readAbsHeapType(typeId); var result = readAbsHeapType(typeId, nullable);
++ptr; ++ptr;
return result; return result;
} }
return new WasmHollowType.CompositeReference(readLEB()); return new WasmHollowType.CompositeReference(readLEB(), nullable);
} }
public WasmHollowType.SpecialReference readAbsHeapType(int typeId) { public WasmHollowType.SpecialReference readAbsHeapType(int typeId, boolean nullable) {
switch (typeId) { switch (typeId) {
case 0x70: case 0x70:
return WasmHollowType.Reference.FUNC; return special(WasmType.SpecialReferenceKind.FUNC, nullable);
case 0x6F: case 0x6F:
return WasmHollowType.Reference.EXTERN; return special(WasmType.SpecialReferenceKind.EXTERN, nullable);
case 0x6E: case 0x6E:
return WasmHollowType.Reference.ANY; return special(WasmType.SpecialReferenceKind.ANY, nullable);
case 0x6C: case 0x6C:
return WasmHollowType.Reference.I31; return special(WasmType.SpecialReferenceKind.I31, nullable);
case 0x6B: case 0x6B:
return WasmHollowType.Reference.STRUCT; return special(WasmType.SpecialReferenceKind.STRUCT, nullable);
case 0x6A: case 0x6A:
return WasmHollowType.Reference.ARRAY; return special(WasmType.SpecialReferenceKind.ARRAY, nullable);
default: default:
throw new ParseException("Unknown type", ptr); throw new ParseException("Unknown type", ptr);
} }
} }
private static WasmHollowType.SpecialReference special(WasmType.SpecialReferenceKind kind, boolean nullable) {
return nullable
? WasmHollowType.Reference.special(kind)
: WasmHollowType.SpecialReference.nonNullSpecial(kind);
}
public int readSignedLEB() { public int readSignedLEB() {
var result = 0; var result = 0;
var shift = 0; var shift = 0;

View File

@ -17,7 +17,6 @@ package org.teavm.backend.wasm.parser;
import java.util.Objects; import java.util.Objects;
import org.teavm.backend.wasm.model.WasmNumType; import org.teavm.backend.wasm.model.WasmNumType;
import org.teavm.backend.wasm.model.WasmStorageType;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
public class WasmHollowType { public class WasmHollowType {
@ -26,8 +25,6 @@ public class WasmHollowType {
public static final Number FLOAT32 = new Number(WasmNumType.FLOAT32); public static final Number FLOAT32 = new Number(WasmNumType.FLOAT32);
public static final Number FLOAT64 = new Number(WasmNumType.FLOAT64); public static final Number FLOAT64 = new Number(WasmNumType.FLOAT64);
private WasmStorageType.Regular storageType;
private WasmHollowType() { private WasmHollowType() {
} }
@ -56,18 +53,45 @@ public class WasmHollowType {
public static abstract class Reference extends WasmHollowType { public static abstract class Reference extends WasmHollowType {
public static final SpecialReference FUNC = new SpecialReference(WasmType.SpecialReferenceKind.FUNC); private static final SpecialReference[] references = new SpecialReference[
public static final SpecialReference ANY = new SpecialReference(WasmType.SpecialReferenceKind.ANY); WasmType.SpecialReferenceKind.values().length];
public static final SpecialReference EXTERN = new SpecialReference(WasmType.SpecialReferenceKind.EXTERN); private static final SpecialReference[] nonNullReferences = new SpecialReference[
public static final SpecialReference STRUCT = new SpecialReference(WasmType.SpecialReferenceKind.STRUCT); WasmType.SpecialReferenceKind.values().length];
public static final SpecialReference ARRAY = new SpecialReference(WasmType.SpecialReferenceKind.ARRAY);
public static final SpecialReference I31 = new SpecialReference(WasmType.SpecialReferenceKind.I31); private final boolean nullable;
public Reference(boolean nullable) {
this.nullable = nullable;
}
public boolean isNullable() {
return nullable;
}
public static SpecialReference special(WasmType.SpecialReferenceKind kind) {
var result = references[kind.ordinal()];
if (result == null) {
result = new SpecialReference(kind, true);
references[kind.ordinal()] = result;
}
return result;
}
public static SpecialReference nonNullSpecial(WasmType.SpecialReferenceKind kind) {
var result = nonNullReferences[kind.ordinal()];
if (result == null) {
result = new SpecialReference(kind, false);
nonNullReferences[kind.ordinal()] = result;
}
return result;
}
} }
public static final class CompositeReference extends WasmHollowType.Reference { public static final class CompositeReference extends WasmHollowType.Reference {
public final int index; public final int index;
public CompositeReference(int index) { public CompositeReference(int index, boolean nullable) {
super(nullable);
this.index = index; this.index = index;
} }
@ -80,19 +104,20 @@ public class WasmHollowType {
return false; return false;
} }
CompositeReference that = (CompositeReference) o; CompositeReference that = (CompositeReference) o;
return index == that.index; return index == that.index && isNullable() == that.isNullable();
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hashCode(index); return Objects.hash(index, isNullable());
} }
} }
public static final class SpecialReference extends WasmHollowType.Reference { public static final class SpecialReference extends WasmHollowType.Reference {
public final WasmType.SpecialReferenceKind kind; public final WasmType.SpecialReferenceKind kind;
SpecialReference(WasmType.SpecialReferenceKind kind) { SpecialReference(WasmType.SpecialReferenceKind kind, boolean nullable) {
super(nullable);
this.kind = kind; this.kind = kind;
} }
} }

View File

@ -219,7 +219,7 @@ public class WasmBinaryRenderer {
section.writeLEB(module.globals.size()); section.writeLEB(module.globals.size());
for (var global : module.globals) { for (var global : module.globals) {
section.writeType(global.getType(), module); section.writeType(global.getType(), module);
section.writeByte(1); // mutable section.writeByte(global.isImmutable() ? 0 : 1); // mutable
global.getInitialValue().acceptVisitor(visitor); global.getInitialValue().acceptVisitor(visitor);
section.writeByte(0x0b); section.writeByte(0x0b);
} }

View File

@ -1075,7 +1075,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
pushLocation(expression); pushLocation(expression);
expression.getValue().acceptVisitor(this); expression.getValue().acceptVisitor(this);
writer.writeByte(0xfb); writer.writeByte(0xfb);
writer.writeByte(expression.isNullable() ? 23 : 22); writer.writeByte(expression.getTargetType().isNullable() ? 23 : 22);
writer.writeHeapType(expression.getTargetType(), module); writer.writeHeapType(expression.getTargetType(), module);
popLocation(); popLocation();
} }
@ -1085,7 +1085,7 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor {
pushLocation(expression); pushLocation(expression);
expression.getValue().acceptVisitor(this); expression.getValue().acceptVisitor(this);
writer.writeByte(0xfb); writer.writeByte(0xfb);
writer.writeByte(expression.isNullable() ? 21 : 20); writer.writeByte(expression.getTestType().isNullable() ? 21 : 20);
writer.writeHeapType(expression.getTestType(), module); writer.writeHeapType(expression.getTestType(), module);
popLocation(); popLocation();
} }

View File

@ -37,11 +37,15 @@ public class WasmBinaryWriter {
if (type instanceof WasmType.Number) { if (type instanceof WasmType.Number) {
writeType(((WasmType.Number) type).number); writeType(((WasmType.Number) type).number);
} else if (type instanceof WasmType.SpecialReference) { } else if (type instanceof WasmType.SpecialReference) {
writeSpecialHeapType(((WasmType.SpecialReference) type).kind); var refType = (WasmType.SpecialReference) type;
if (!refType.isNullable()) {
writeByte(0x64);
}
writeSpecialHeapType(refType.kind);
} else if (type instanceof WasmType.CompositeReference) { } else if (type instanceof WasmType.CompositeReference) {
writeByte(0x63); var refType = (WasmType.CompositeReference) type;
var composite = ((WasmType.CompositeReference) type).composite; writeByte(refType.isNullable() ? 0x63 : 0x64);
var index = module.types.indexOf(composite); var index = module.types.indexOf(refType.composite);
writeSignedLEB(index); writeSignedLEB(index);
} }
} }