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() { public String toString() {
if (PlatformDetector.isC()) { if (PlatformDetector.isC()) {
return toStringC(value); return toStringC(value);
} else if (PlatformDetector.isWebAssembly()) { } else if (PlatformDetector.isWebAssembly() || PlatformDetector.isWebAssemblyGC()) {
return toStringWebAssembly(value); return toStringWebAssembly(value);
} else { } else {
return JSDate.create(value).stringValue(); return JSDate.create(value).stringValue();

View File

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

View File

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

View File

@ -222,7 +222,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
} }
return new WasmNullConstant(type instanceof WasmType.Reference return new WasmNullConstant(type instanceof WasmType.Reference
? (WasmType.Reference) type ? (WasmType.Reference) type
: WasmType.Reference.STRUCT); : context.classInfoProvider().getClassInfo("java.lang.Object").getType());
} }
@Override @Override
@ -362,6 +362,39 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
return new WasmCast(value, (WasmType.Reference) targetType); 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 @Override
protected boolean needsClassInitializer(String className) { protected boolean needsClassInitializer(String className) {
return context.classInfoProvider().getClassInfo(className).getInitializerPointer() != null; return context.classInfoProvider().getClassInfo(className).getInitializerPointer() != null;
@ -516,6 +549,11 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
target.acceptVisitor(typeInference); target.acceptVisitor(typeInference);
var type = (WasmType.CompositeReference) typeInference.getResult(); var type = (WasmType.CompositeReference) typeInference.getResult();
if (type == null) {
result = new WasmUnreachable();
result.setLocation(expr.getLocation());
return;
}
var struct = (WasmStructure) type.composite; var struct = (WasmStructure) type.composite;
var fieldIndex = context.classInfoProvider().getFieldIndex(expr.getField()); var fieldIndex = context.classInfoProvider().getFieldIndex(expr.getField());
if (fieldIndex >= 0) { if (fieldIndex >= 0) {

View File

@ -15,6 +15,8 @@
*/ */
package org.teavm.backend.wasm.generate.gc.methods; package org.teavm.backend.wasm.generate.gc.methods;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -209,7 +211,11 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
generateNativeMethodBody(method, function); generateNativeMethodBody(method, function);
} }
} catch (RuntimeException e) { } 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; private final boolean nullable;
public Reference(boolean nullable) { Reference(boolean nullable) {
this.nullable = nullable; this.nullable = nullable;
} }

View File

@ -19,6 +19,7 @@ TeaVM.wasm = function() {
function defaults(imports) { function defaults(imports) {
let stderr = ""; let stderr = "";
let stdout = ""; let stdout = "";
let exports;
imports.teavm = { imports.teavm = {
putcharStderr(c) { putcharStderr(c) {
if (c === 10) { if (c === 10) {
@ -38,6 +39,9 @@ TeaVM.wasm = function() {
}, },
currentTimeMillis() { currentTimeMillis() {
return new Date().getTime(); return new Date().getTime();
},
dateToString(timestamp, controller) {
return stringToJava(new Date(timestamp).toString());
} }
}; };
imports.teavmMath = Math; 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) { function createMain(instance) {
return args => { return args => {
if (typeof args === "undefined") { if (typeof args === "undefined") {
args = []; args = [];
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let exports = instance.exports; exports = instance.exports;
let javaArgs = exports.createStringArray(args.length); let javaArgs = exports.createStringArray(args.length);
for (let i = 0; i < args.length; ++i) { for (let i = 0; i < args.length; ++i) {
let arg = args[i]; let arg = args[i];
let javaArg = exports.createStringBuilder(); exports.setToStringArray(javaArgs, i, stringToJava(args[i]));
for (let j = 0; j < arg.length; ++j) {
exports.appendChar(javaArg, arg.charCodeAt(j));
}
exports.setToStringArray(javaArgs, i, exports.buildString(javaArg));
} }
try { try {
exports.main(javaArgs); 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 { public void test_tickSeconds_ZoneId() throws Exception {
Clock test = Clock.tickSeconds(PARIS); Clock test = Clock.tickSeconds(PARIS);
assertEquals(test.getZone(), PARIS); assertEquals(test.getZone(), PARIS);