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

Improve runtime anonymous type resolution #29792

Merged
merged 17 commits into from
Apr 16, 2021

Conversation

grainier
Copy link
Contributor

@grainier grainier commented Apr 2, 2021

Purpose

The purpose of this PR is to $title.

Fixes #28262

Approach

In this PR, it'll code-gen public Type getAnonType(int hash, String shapeInfo) method into module init, and that will allow type resolution like shown below.

generated method:

public Type getAnonType(int var1, String var2) {
    switch(var1) {
    case -1449513272:
        return $type$$anonType$_9;
    case -1413292641:
        return $type$$anonType$_14;
    case -899684319:
        return $type$$anonType$_8;
    case -270018685:
        return $type$$anonType$_0;
    case 18898473:
        return $type$$anonType$_15;
    case 124652424:
        return $type$$anonType$_4;
    case 1937814141:
        return $type$$anonType$_11;
    default:
        throw new ErrorValue(StringUtils.fromString("No such type: " + var2));
    }
}

generated usage:

BObjectType var10006 = (BObjectType)(new $_init()).getAnonType(124652424, "Package: ballerina/lang.__internal:0.1.0, Shape: object { public isolated function next () returns ((record {| (any|error) value; |}|error?)); }");

In case, if the type couldn't be resolved, a runtime error similar to the following will be thrown.

error("No such type: Package: ballerina/lang.__internal:0.1.0, Shape: object { public isolated function next () returns ((record {| (any|error) value; |}|error?)); })

Samples

n/a

Remarks

Check List

  • Read the Contributing Guide
  • Updated Change Log
  • Checked Tooling Support (#)
  • Added necessary tests
    • Unit Tests
    • Spec Conformance Tests
    • Integration Tests
    • Ballerina By Example Tests
  • Increased Test Coverage
  • Added necessary documentation
    • API documentation
    • Module documentation in Module.md files
    • Ballerina By Examples

@grainier grainier added Team/CompilerFE All issues related to Language implementation and Compiler, this exclude run times. Team/jBallerina All the issues related to BIR, JVM backend code generation and runtime labels Apr 2, 2021
@rdhananjaya
Copy link
Member

I think we need to add the string equals test too to avoid hash collusions. Similar to get method of record value classes.

type T record {|
    int i;
    string j;
|};

Generated (decompiled to java) get method

        String var2 = ((BString)var1).getValue();
        switch(var2.hashCode()) {
        case 105:
            if (var2.equals("i")) {
                return this.i;
            }
            break;
        case 106:
            if (var2.equals("j")) {
                return this.j;
            }
        }

        return super.get(var1);
    }

@manuranga
Copy link
Contributor

+1 for @rdhananjaya 's suggestion, but since current hash calculator (UniqueTypeVisitor<Integer>) returns an Integer, I am not sure this is possible to do without changing it.

@grainier
Copy link
Contributor Author

grainier commented Apr 8, 2021

+1 for @rdhananjaya 's suggestion, but since current hash calculator (UniqueTypeVisitor<Integer>) returns an Integer, I am not sure this is possible to do without changing it.

@manuranga, @rdhananjaya I'll add the hash validation in a separate PR. (issue to track this: #29928)

Copy link
Contributor

@KRVPerera KRVPerera left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is before the last commit to avoid generating signatures for the same module.

This means for the similar anonymous types we have the same signature. Is this expected?

public type Address object {
    public string city;
    public string country;

    public function value() returns string;
};

public type Address3 object {
    public string city;
    public string country;

    public function value() returns string;
};

public function main() {

var address = object Address {
                     // In object constructor expressions, the `init` method does not take any parameters.
                     public function init() {
                         self.city = "Colombo";
                         self.country = "Sri Lanka";
                     }

                     public function value() returns string {
                         return self.city + ", " + self.country;
                     }
                 };


var address2 = object Address3 {
                     // In object constructor expressions, the `init` method does not take any parameters.
                     public function init() {
                         self.city = "Colombo";
                         self.country = "Sri Lanka";
                     }

                     public function value() returns string {
                         return self.city + ", " + self.country;
                     }
                 };
}

Here for both $type$$anonType$_1 and $type$$anonType$_0 we have same signature.

final BObjectType duplicate = ((BObjectType)new $_init().getAnonType(-203469200, "Package: $_init, TypeName: $type$$anonType$_0, Shape: object { public string city; public string country; public function value () returns (string); }")).duplicate();


final BObjectType duplicate2 = ((BObjectType)new $_init().getAnonType(-203469200, "Package: $_init, TypeName: $type$$anonType$_1, Shape: object { public string city; public string country; public function value () returns (string); }")).duplicate();
@Override
    public Type getAnonType(final int n, final String str) {
        switch (n) {
            case -203469200: {
                return $_init.$type$$anonType$_0;
            }
            default: {
                throw new ErrorValue(StringUtils.fromString("No such type: " + str));
            }
        }
    }

@grainier
Copy link
Contributor Author

grainier commented Apr 8, 2021

@KRVPerera yes. We have only considered the shape. So, such situations can happen. Do you think this can lead to errors?

@KRVPerera
Copy link
Contributor

Think it is okay. We are taking type ids also to the hash. Sorry for the noise.

@grainier
Copy link
Contributor Author

grainier commented Apr 8, 2021

Think it is okay. We are taking type ids also to the hash. Sorry for the noise.

I really wasn't aware whether there can be any issues by treating similar shapes as one. That's why. :)

Anyway, we didn't use the type name initially, because if there's an anon type name and if we capture that as part of the shape, there's a possibility for that anon name to change later on (since we are using incremental anon type name generator, like in the root issue), and cause the hash to change as well.

Copy link
Contributor

@KRVPerera KRVPerera left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

grainier and others added 2 commits April 9, 2021 12:11
…ompiler/bir/codegen/internal/TypeHashComparator.java

Co-authored-by: Maryam Ziyad <maryamziyadm@gmail.com>
…ompiler/semantics/analyzer/TypeHashVisitor.java

Co-authored-by: Maryam Ziyad <maryamziyadm@gmail.com>
Copy link
Contributor

@hasithaa hasithaa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving since review suggestions are added.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Team/CompilerFE All issues related to Language implementation and Compiler, this exclude run times. Team/jBallerina All the issues related to BIR, JVM backend code generation and runtime
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Dependencies can become incompatible due to anonymous types
7 participants