wasm gc: fix bugs related to casts and implement runtime method for converting date to string

This commit is contained in:
Alexey Andreev 2024-09-12 20:16:01 +02:00
parent 8e2cf27e94
commit bfd2c8479c
8 changed files with 112 additions and 36 deletions

View File

@ -373,7 +373,7 @@ public class TDate implements TComparable<TDate> {
public String toString() {
if (PlatformDetector.isC()) {
return toStringC(value);
} else if (PlatformDetector.isWebAssembly()) {
} else if (PlatformDetector.isWebAssembly() || PlatformDetector.isWebAssemblyGC()) {
return toStringWebAssembly(value);
} else {
return JSDate.create(value).stringValue();

View File

@ -170,6 +170,16 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
return value;
}
@Override
protected WasmType mapCastSourceType(WasmType type) {
return type;
}
@Override
protected boolean validateCastTypes(WasmType sourceType, WasmType targetType, TextLocation location) {
return true;
}
@Override
protected WasmExpression peekException() {
return new WasmCall(context.functions().forStaticMethod(PEEK_EXCEPTION_METHOD));

View File

@ -1184,40 +1184,53 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
@Override
public void visit(CastExpr expr) {
if (expr.isWeak()) {
acceptWithType(expr.getValue(), expr.getTarget());
result = generateCast(result, mapType(expr.getTarget()));
return;
}
var block = new WasmBlock(false);
var wasmTargetType = mapType(expr.getTarget());
block.setType(wasmTargetType);
block.setLocation(expr.getLocation());
var wasmTargetType = (WasmType.CompositeReference) mapType(expr.getTarget());
acceptWithType(expr.getValue(), expr.getTarget());
result.acceptVisitor(typeInference);
var wasmSourceType = typeInference.getResult();
var valueToCast = exprCache.create(result, wasmSourceType, expr.getLocation(), block.getBody());
if (!expr.isWeak()) {
result.acceptVisitor(typeInference);
var wasmSourceType = typeInference.getResult();
if (wasmSourceType == null) {
return;
}
var nullCheck = new WasmBranch(genIsNull(valueToCast.expr()), block);
nullCheck.setResult(nullLiteral(wasmTargetType));
block.getBody().add(new WasmDrop(nullCheck));
wasmSourceType = mapCastSourceType(wasmSourceType);
var supertypeCall = generateInstanceOf(valueToCast.expr(), expr.getTarget());
if (!validateCastTypes(wasmSourceType, wasmTargetType, expr.getLocation())) {
return;
}
var breakIfPassed = new WasmBranch(supertypeCall, block);
breakIfPassed.setResult(generateCast(valueToCast.expr(), wasmTargetType));
block.getBody().add(new WasmDrop(breakIfPassed));
var block = new WasmBlock(false);
block.setType(wasmSourceType);
block.setLocation(expr.getLocation());
acceptWithType(expr.getValue(), expr.getTarget());
var valueToCast = exprCache.create(result, wasmSourceType, expr.getLocation(), block.getBody());
var callSiteId = generateCallSiteId(expr.getLocation());
callSiteId.generateRegister(block.getBody(), expr.getLocation());
generateThrowCCE(expr.getLocation(), block.getBody());
callSiteId.generateThrow(block.getBody(), expr.getLocation());
var nullCheck = new WasmBranch(genIsNull(valueToCast.expr()), block);
nullCheck.setResult(nullLiteral(wasmTargetType));
block.getBody().add(new WasmDrop(nullCheck));
valueToCast.release();
result = block;
var supertypeCall = generateInstanceOf(valueToCast.expr(), expr.getTarget());
var breakIfPassed = new WasmBranch(supertypeCall, block);
breakIfPassed.setResult(valueToCast.expr());
block.getBody().add(new WasmDrop(breakIfPassed));
var callSiteId = generateCallSiteId(expr.getLocation());
callSiteId.generateRegister(block.getBody(), expr.getLocation());
generateThrowCCE(expr.getLocation(), block.getBody());
callSiteId.generateThrow(block.getBody(), expr.getLocation());
valueToCast.release();
result = block;
}
result = generateCast(result, wasmTargetType);
result.setLocation(expr.getLocation());
}
protected abstract WasmType mapCastSourceType(WasmType type);
protected abstract boolean validateCastTypes(WasmType sourceType, WasmType targetType, TextLocation location);
protected abstract WasmExpression generateCast(WasmExpression value, WasmType targetType);
@Override

View File

@ -222,7 +222,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
}
return new WasmNullConstant(type instanceof WasmType.Reference
? (WasmType.Reference) type
: WasmType.Reference.STRUCT);
: context.classInfoProvider().getClassInfo("java.lang.Object").getType());
}
@Override
@ -362,6 +362,39 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
return new WasmCast(value, (WasmType.Reference) targetType);
}
@Override
protected WasmType mapCastSourceType(WasmType type) {
if (!(type instanceof WasmType.CompositeReference)) {
return type;
}
var refType = (WasmType.CompositeReference) type;
return refType.isNullable() ? refType : refType.composite.getReference();
}
@Override
protected boolean validateCastTypes(WasmType sourceType, WasmType targetType, TextLocation location) {
if (!(sourceType instanceof WasmType.CompositeReference)
|| !(targetType instanceof WasmType.CompositeReference)) {
return false;
}
var sourceRefType = (WasmType.CompositeReference) sourceType;
var targetRefType = (WasmType.CompositeReference) targetType;
if (sourceRefType.composite instanceof WasmStructure
&& targetRefType.composite instanceof WasmStructure) {
var sourceStruct = (WasmStructure) sourceRefType.composite;
var targetStruct = (WasmStructure) targetRefType.composite;
if (targetStruct.isSupertypeOf(sourceStruct)) {
return false;
}
if (!sourceStruct.isSupertypeOf(targetStruct)) {
result = new WasmUnreachable();
result.setLocation(location);
return false;
}
}
return true;
}
@Override
protected boolean needsClassInitializer(String className) {
return context.classInfoProvider().getClassInfo(className).getInitializerPointer() != null;
@ -516,6 +549,11 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
target.acceptVisitor(typeInference);
var type = (WasmType.CompositeReference) typeInference.getResult();
if (type == null) {
result = new WasmUnreachable();
result.setLocation(expr.getLocation());
return;
}
var struct = (WasmStructure) type.composite;
var fieldIndex = context.classInfoProvider().getFieldIndex(expr.getField());
if (fieldIndex >= 0) {

View File

@ -15,6 +15,8 @@
*/
package org.teavm.backend.wasm.generate.gc.methods;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.HashMap;
@ -209,7 +211,11 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
generateNativeMethodBody(method, function);
}
} catch (RuntimeException e) {
throw new RuntimeException("Failed generating method body: " + method.getReference(), e);
var buffer = new StringWriter();
var printWriter = new PrintWriter(buffer);
e.printStackTrace(printWriter);
diagnostics.error(new CallLocation(method.getReference()),
"Failed generating method body due to internal exception: " + buffer.toString());
}
}

View File

@ -67,7 +67,7 @@ public abstract class WasmType {
private final boolean nullable;
public Reference(boolean nullable) {
Reference(boolean nullable) {
this.nullable = nullable;
}

View File

@ -19,6 +19,7 @@ TeaVM.wasm = function() {
function defaults(imports) {
let stderr = "";
let stdout = "";
let exports;
imports.teavm = {
putcharStderr(c) {
if (c === 10) {
@ -38,6 +39,9 @@ TeaVM.wasm = function() {
},
currentTimeMillis() {
return new Date().getTime();
},
dateToString(timestamp, controller) {
return stringToJava(new Date(timestamp).toString());
}
};
imports.teavmMath = Math;
@ -62,21 +66,25 @@ TeaVM.wasm = function() {
}));
}
function stringToJava(str) {
let sb = exports.createStringBuilder();
for (let i = 0; i < str.length; ++i) {
exports.appendChar(sb, str.charCodeAt(i));
}
return exports.buildString(sb);
}
function createMain(instance) {
return args => {
if (typeof args === "undefined") {
args = [];
}
return new Promise((resolve, reject) => {
let exports = instance.exports;
exports = instance.exports;
let javaArgs = exports.createStringArray(args.length);
for (let i = 0; i < args.length; ++i) {
let arg = args[i];
let javaArg = exports.createStringBuilder();
for (let j = 0; j < arg.length; ++j) {
exports.appendChar(javaArg, arg.charCodeAt(j));
}
exports.setToStringArray(javaArgs, i, exports.buildString(javaArg));
exports.setToStringArray(javaArgs, i, stringToJava(args[i]));
}
try {
exports.main(javaArgs);

View File

@ -154,6 +154,7 @@ public class TestClockTick extends AbstractTest {
}
//-----------------------------------------------------------------------
@SkipPlatform(TestPlatform.WEBASSEMBLY_GC)
public void test_tickSeconds_ZoneId() throws Exception {
Clock test = Clock.tickSeconds(PARIS);
assertEquals(test.getZone(), PARIS);