Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dart2wasm] Simplify handling null receivers in gets, tear-offs, invocations #50508

Open
osa1 opened this issue Nov 18, 2022 · 3 comments
Open
Labels
area-dart2wasm Issues for the dart2wasm compiler. type-code-health Internal changes to our tools and workflows to make them cleaner, simpler, or more maintainable

Comments

@osa1
Copy link
Member

osa1 commented Nov 18, 2022

In dart2wasm we don't use the Null class for null values, instead we use Wasm's null value (ref.null).

This means we need special cases in the code generator when generating gets, tear-offs, and invocations (dynamic or instance) to check for null Wasm values and based on the member name generate code for null.toString tear-off or invocation, or null.hashCode instance get etc. Currently these look like this:

  • Instance get:
    late w.Label doneLabel;
    w.ValueType resultType =
    _virtualCall(node, target, _VirtualCallKind.Get, (signature) {
    doneLabel = b.block(const [], signature.outputs);
    w.Label nullLabel = b.block();
    wrap(node.receiver, translator.topInfo.nullableType);
    b.br_on_null(nullLabel);
    }, (_) {});
    b.br(doneLabel);
    b.end(); // nullLabel
    switch (target.name.text) {
    case "hashCode":
    b.i64_const(2011);
    break;
    case "runtimeType":
    case "_runtimeType":
    wrap(ConstantExpression(TypeLiteralConstant(NullType())), resultType);
    break;
    default:
    unimplemented(
    node, "Nullable get of ${target.name.text}", [resultType]);
    break;
    }
    b.end(); // doneLabel
    return resultType;
  • Instance tear-off:
    late w.Label doneLabel;
    w.ValueType resultType =
    _virtualCall(node, target, _VirtualCallKind.Get, (signature) {
    doneLabel = b.block(const [], signature.outputs);
    w.Label nullLabel = b.block();
    wrap(node.receiver, translator.topInfo.nullableType);
    b.br_on_null(nullLabel);
    translator.convertType(
    function, translator.topInfo.nullableType, signature.inputs[0]);
    }, (_) {});
    b.br(doneLabel);
    b.end(); // nullLabel
    switch (target.name.text) {
    case "toString":
    wrap(
    ConstantExpression(
    StaticTearOffConstant(translator.nullToString)),
    resultType);
    break;
    case "noSuchMethod":
    wrap(
    ConstantExpression(
    StaticTearOffConstant(translator.nullNoSuchMethod)),
    resultType);
    break;
    default:
    unimplemented(
    node, "Nullable tear-off of ${target.name.text}", [resultType]);
    break;
    }
    b.end(); // doneLabel
    return resultType;
  • Instance invocation:
    switch (target.name.text) {
    case "toString":
    late w.Label done;
    w.ValueType resultType =
    _virtualCall(node, target, _VirtualCallKind.Call, (signature) {
    done = b.block(const [], signature.outputs);
    w.Label nullString = b.block();
    wrap(node.receiver, translator.topInfo.nullableType);
    b.br_on_null(nullString);
    }, (_) {
    _visitArguments(node.arguments, node.interfaceTargetReference, 1);
    });
    b.br(done);
    b.end();
    wrap(StringLiteral("null"), resultType);
    b.end();
    return resultType;
    default:
    unimplemented(node, "Nullable invocation of ${target.name.text}",
    [if (expectedType != voidMarker) expectedType]);
    return expectedType;
    }
  • There should also be a few lines in dynamic invocation code generator

Since we don't use an actual Dart object for null we can't completely avoid these special cases, but we can simplify them a little bit by having a class representing the null object (say, WasmNull), and when a receiver is null in a get, tear-off etc. we replace the receiver with the singleton instance of WasmNull. This class can be defined in Dart with the right toString, hashCode etc. implementations.

I tried this in a branch but it caused various crashes and it wasn't immediately obvious how can my changes break that much. This won't fix any tests but it may be a worthwhile simplification. We should probably revisit this at some point.

Related CLs:

@osa1 osa1 added the area-dart2wasm Issues for the dart2wasm compiler. label Nov 18, 2022
@osa1 osa1 changed the title [dart2wasm] Simplify handling null receivers in gets, tear-offs, invocationsCreate an issue [dart2wasm] Simplify handling null receivers in gets, tear-offs, invocations Nov 18, 2022
@osa1
Copy link
Member Author

osa1 commented Jan 16, 2023

Without a null object, the special cases in instance invocations above need to also handle noSuchMethod.

@osa1
Copy link
Member Author

osa1 commented Mar 3, 2023

The test tests/language/closure/tearoff_dynamic_test fails because of missing null.noSuchMethod handling.

@mkustermann
Copy link
Member

The test tests/language/closure/tearoff_dynamic_test fails because of missing null.noSuchMethod handling.

This test seems to be passing now.

@mkustermann mkustermann added the type-code-health Internal changes to our tools and workflows to make them cleaner, simpler, or more maintainable label May 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-dart2wasm Issues for the dart2wasm compiler. type-code-health Internal changes to our tools and workflows to make them cleaner, simpler, or more maintainable
Projects
None yet
Development

No branches or pull requests

2 participants