Skip to content

Commit

Permalink
Merge pull request #39568 from chiranSachintha/anonymous-tuple-annota…
Browse files Browse the repository at this point in the history
…tion

Allow access field annotations in tuple type descriptors without a type definition at the package level
  • Loading branch information
chiranSachintha authored Feb 13, 2023
2 parents e519fde + b35bc0a commit 6d175fd
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@
import org.wso2.ballerinalang.compiler.util.ClosureVarSymbol;
import org.wso2.ballerinalang.compiler.util.CompilerContext;
import org.wso2.ballerinalang.compiler.util.Name;
import org.wso2.ballerinalang.compiler.util.Names;
import org.wso2.ballerinalang.util.Flags;

import java.util.ArrayList;
Expand All @@ -197,7 +198,7 @@
public class ClosureGenerator extends BLangNodeVisitor {
private static final CompilerContext.Key<ClosureGenerator> CLOSURE_GENERATOR_KEY = new CompilerContext.Key<>();
private Queue<BLangSimpleVariableDef> queue;
private List<BLangSimpleVariableDef> annotationClosureReferences;
private Queue<BLangSimpleVariableDef> annotationClosureReferences;
private SymbolTable symTable;
private SymbolEnv env;
private BLangNode result;
Expand All @@ -217,7 +218,7 @@ private ClosureGenerator(CompilerContext context) {
context.put(CLOSURE_GENERATOR_KEY, this);
this.symTable = SymbolTable.getInstance(context);
this.queue = new LinkedList<>();
this.annotationClosureReferences = new ArrayList<>();
this.annotationClosureReferences = new LinkedList<>();
this.symResolver = SymbolResolver.getInstance(context);
this.annotationDesugar = AnnotationDesugar.getInstance(context);
}
Expand All @@ -240,7 +241,7 @@ public void visit(BLangPackage pkgNode) {
pkgNode.annotations.forEach(annotation -> rewrite(annotation, pkgEnv));
pkgNode.initFunction = rewrite(pkgNode.initFunction, pkgEnv);
pkgNode.classDefinitions = rewrite(pkgNode.classDefinitions, pkgEnv);
pkgNode.globalVars.forEach(globalVar -> rewrite(globalVar, pkgEnv));
rewrite(pkgNode.globalVars, pkgEnv);
addClosuresToGlobalVariableList(pkgEnv);
for (int i = 0; i < pkgNode.functions.size(); i++) {
BLangFunction bLangFunction = pkgNode.functions.get(i);
Expand Down Expand Up @@ -410,7 +411,7 @@ public void visit(BLangTupleTypeNode tupleTypeNode) {

private void desugarFieldAnnotations(BSymbol owner, BTypeSymbol typeSymbol, List<BLangSimpleVariable> fields,
Location pos) {
if (owner.getKind() != SymbolKind.PACKAGE) {
if (owner.getKind() != SymbolKind.PACKAGE || typeSymbol.name == Names.EMPTY) {
owner = getOwner(env);
BLangLambdaFunction lambdaFunction = annotationDesugar.defineFieldAnnotations(fields, pos, env.enclPkg, env,
typeSymbol.pkgID, owner);
Expand Down Expand Up @@ -1598,9 +1599,18 @@ private <E extends BLangNode> E rewrite(E node, SymbolEnv env) {
}

private <E extends BLangNode> List<E> rewrite(List<E> nodeList, SymbolEnv env) {
for (int i = 0; i < nodeList.size(); i++) {
nodeList.set(i, rewrite(nodeList.get(i), env));
Queue<BLangSimpleVariableDef> previousQueue = this.annotationClosureReferences;
this.annotationClosureReferences = new LinkedList<>();
int size = nodeList.size();
for (int i = 0; i < size; i++) {
E node = rewrite(nodeList.remove(0), env);
Iterator<BLangSimpleVariableDef> iterator = annotationClosureReferences.iterator();
while (iterator.hasNext()) {
nodeList.add(rewrite((E) annotationClosureReferences.poll().var, env));
}
nodeList.add(node);
}
this.annotationClosureReferences = previousQueue;
return nodeList;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@
import org.testng.annotations.Test;

/**
* Class to test annotation evaluation within local tuple types.
* Class to test annotation evaluation within anonymous tuple types.
*
* @since 2201.4.0
*/
public class LocalTupleAnnotationTest {
public class AnonymousTupleAnnotationTest {
private CompileResult result;

@BeforeClass
public void setup() {
result = BCompileUtil.compile("test-src/annotations/local_tuple_annotation_test.bal");
result = BCompileUtil.compile("test-src/annotations/anonymous_tuple_annotation_test.bal");
Assert.assertEquals(result.getErrorCount(), 0);
}

Expand Down Expand Up @@ -85,7 +85,7 @@ public Object[] dataToTestAnnotationsOfLocalTuple() {
};
}

public static BMap getLocalTupleAnnotations(TypedescValue typedescValue, BString annotName) {
public static BMap getAnonymousTupleAnnotations(TypedescValue typedescValue, BString annotName) {
return (BMap) TypeChecker.getAnnotValue(typedescValue, annotName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,53 +29,78 @@ annotation AnnotTupleOne annotOne on type, field;
annotation AnnotTupleOne annotTwo on type, field;
annotation Details details on field;

[@annotOne {value: "10"} int, @annotOne {value: "k"} int, string...] g1 = [1, 2, "hello", "world"];

function testAnnotationOnTupleFields() {
string k = "chiranS";
[@annotOne {value: "10"} int, @annotOne {value: k} int, string...] x1 = [1, 2, "hello", "world"];
map<any> m1 = getLocalTupleAnnotations(typeof x1, "$field$.0");
map<any> m2 = getLocalTupleAnnotations(typeof x1, "$field$.1");
map<any> m1 = getAnonymousTupleAnnotations(typeof x1, "$field$.0");
map<any> m2 = getAnonymousTupleAnnotations(typeof x1, "$field$.1");
map<any> m3 = getAnonymousTupleAnnotations(typeof g1, "$field$.0");
map<any> m4 = getAnonymousTupleAnnotations(typeof g1, "$field$.1");
assertEquality({value: "10"}, <map<anydata>>m1["annotOne"]);
assertEquality({value: "chiranS"}, <map<anydata>>m2["annotOne"]);
assertEquality({value: "10"}, <map<anydata>>m3["annotOne"]);
assertEquality({value: "k"}, <map<anydata>>m4["annotOne"]);
}

string gVar0 = "foo";

// This test evaluates the reordering of global variables
[@details {name: gVar0, age: gVar4} int, @details {name: "name", age: 0} int, string...] g4 = [1, 2, "hello", "world"];

function testAnnotationOnTupleFields2() {
string name = "chiranS";
int age = 26;
[@details {name: name, age: age} int, @details {name: "name", age: 0} int, string...] x1 = [1, 2, "hello", "world"];
map<any> m1 = getLocalTupleAnnotations(typeof x1, "$field$.0");
map<any> m2 = getLocalTupleAnnotations(typeof x1, "$field$.1");
map<any> m1 = getAnonymousTupleAnnotations(typeof x1, "$field$.0");
map<any> m2 = getAnonymousTupleAnnotations(typeof x1, "$field$.1");
map<any> m3 = getAnonymousTupleAnnotations(typeof g4, "$field$.0");
map<any> m4 = getAnonymousTupleAnnotations(typeof g4, "$field$.1");
assertEquality({name: "chiranS", age: 26}, <map<anydata>>m1["details"]);
assertEquality({name: "name", age: 0}, <map<anydata>>m2["details"]);
assertEquality({name: "foo", age: 15}, <map<anydata>>m3["details"]);
assertEquality({name: "name", age: 0}, <map<anydata>>m4["details"]);
}

string gVar = "foo";
string gVar1 = "bar";
[@annotOne {value: gVar} int, int, string...] g2 = [1, 2, "hello", "world"];

function testAnnotationOnTupleWithGlobalVariable() {
[@annotOne {value: gVar} int, int, string...] x1 = [1, 2, "hello", "world"];
map<any> m = getLocalTupleAnnotations(typeof x1, "$field$.0");
assertEquality({value: "foo"}, <map<anydata>>m["annotOne"]);
map<any> m1 = getAnonymousTupleAnnotations(typeof x1, "$field$.0");
map<any> m2 = getAnonymousTupleAnnotations(typeof g2, "$field$.0");
assertEquality({value: "foo"}, <map<anydata>>m1["annotOne"]);
assertEquality({value: "foo"}, <map<anydata>>m2["annotOne"]);
}

[@annotOne {value: "foo"} @annotTwo {value: "bar"} int, @details {name: gVar2, age: 0} int, string...] g3 = [1, 2, "hello", "world"];

function testMultipleAnnotationsOnLocalTuple() {
string k = "chiranS";
[@annotOne {value: "foo"} @annotTwo {value: "bar"} int, @details {name: k, age: 0} int, string...] x1 = [1, 2, "hello", "world"];
map<any> m1 = getLocalTupleAnnotations(typeof x1, "$field$.0");
map<any> m2 = getLocalTupleAnnotations(typeof x1, "$field$.1");
map<any> m1 = getAnonymousTupleAnnotations(typeof x1, "$field$.0");
map<any> m2 = getAnonymousTupleAnnotations(typeof x1, "$field$.1");
map<any> m3 = getAnonymousTupleAnnotations(typeof g3, "$field$.0");
map<any> m4 = getAnonymousTupleAnnotations(typeof g3, "$field$.1");
assertEquality({value: "foo"}, <map<anydata>>m1["annotOne"]);
assertEquality({value: "bar"}, <map<anydata>>m1["annotTwo"]);
assertEquality({name: "chiranS", age: 0}, <map<anydata>>m2["details"]);
assertEquality({value: "foo"}, <map<anydata>>m3["annotOne"]);
assertEquality({value: "bar"}, <map<anydata>>m3["annotTwo"]);
assertEquality({name: "baz", age: 0}, <map<anydata>>m4["details"]);
}

function() returns [int] x = function() returns [@annotOne {value: "foo"} int] {return [1];};
function() returns [int] x2 = function() returns [@annotOne {value: gVar1} @annotTwo {value: gVar2} int] {return [1];};
function() returns [int, int] x3 = function() returns [@annotOne {value: gVar1} int, @details {name: "name", age: gVar3} int] {return [1, 1];};

function testGlobalAnnotationsOnFunctionPointerReturnType() {
map<any> m1 = getLocalTupleAnnotations(typeof x(), "$field$.0");
map<any> m2 = getLocalTupleAnnotations(typeof x2(), "$field$.0");
map<any> m3 = getLocalTupleAnnotations(typeof x3(), "$field$.0");
map<any> m4 = getLocalTupleAnnotations(typeof x3(), "$field$.1");
map<any> m1 = getAnonymousTupleAnnotations(typeof x(), "$field$.0");
map<any> m2 = getAnonymousTupleAnnotations(typeof x2(), "$field$.0");
map<any> m3 = getAnonymousTupleAnnotations(typeof x3(), "$field$.0");
map<any> m4 = getAnonymousTupleAnnotations(typeof x3(), "$field$.1");
assertEquality({value: "foo"}, <map<anydata>>m1["annotOne"]);
assertEquality({value: "bar"}, <map<anydata>>m2["annotOne"]);
assertEquality({value: "baz"}, <map<anydata>>m2["annotTwo"]);
Expand Down Expand Up @@ -107,10 +132,10 @@ function func2() returns [@details {name: "name", age: gVar4} int, @annotTwo {va
}

function testGlobalAnnotationsOnFunctionReturnType() {
map<any> m1 = getLocalTupleAnnotations(typeof func(), "$field$.0");
map<any> m2 = getLocalTupleAnnotations(typeof func1(), "$field$.0");
map<any> m3 = getLocalTupleAnnotations(typeof func2(), "$field$.0");
map<any> m4 = getLocalTupleAnnotations(typeof func2(), "$field$.1");
map<any> m1 = getAnonymousTupleAnnotations(typeof func(), "$field$.0");
map<any> m2 = getAnonymousTupleAnnotations(typeof func1(), "$field$.0");
map<any> m3 = getAnonymousTupleAnnotations(typeof func2(), "$field$.0");
map<any> m4 = getAnonymousTupleAnnotations(typeof func2(), "$field$.1");
assertEquality({value: "foo"}, <map<anydata>>m1["annotOne"]);
assertEquality({value: "foo"}, <map<anydata>>m2["annotOne"]);
assertEquality({value: "baz"}, <map<anydata>>m2["annotTwo"]);
Expand All @@ -123,13 +148,13 @@ function testGlobalAnnotationsOnFunctionReturnType() {
int gVar4 = 15;

function func3([@annotOne {value: "foo"} int] a) {
map<any> m1 = getLocalTupleAnnotations(typeof a, "$field$.0");
map<any> m1 = getAnonymousTupleAnnotations(typeof a, "$field$.0");
assertEquality({value: "foo"}, <map<anydata>>m1["annotOne"]);
}

function func4([@annotOne {value: "foo"} int, @annotTwo {value: "foo"} @details {name: "name", age: gVar4} int] a) {
map<any> m1 = getLocalTupleAnnotations(typeof a, "$field$.0");
map<any> m2 = getLocalTupleAnnotations(typeof a, "$field$.1");
map<any> m1 = getAnonymousTupleAnnotations(typeof a, "$field$.0");
map<any> m2 = getAnonymousTupleAnnotations(typeof a, "$field$.1");

assertEquality({value: "foo"}, <map<anydata>>m1["annotOne"]);
assertEquality({value: "foo"}, <map<anydata>>m2["annotTwo"]);
Expand All @@ -141,10 +166,10 @@ function testGlobalAnnotationsOnFunctionParameterType() {
func4([10, 10]);
}

function getLocalTupleAnnotations(typedesc<any> obj, string annotName) returns map<any> =
function getAnonymousTupleAnnotations(typedesc<any> obj, string annotName) returns map<any> =
@java:Method {
'class: "org/ballerinalang/test/annotations/LocalTupleAnnotationTest",
name: "getLocalTupleAnnotations"
'class: "org/ballerinalang/test/annotations/AnonymousTupleAnnotationTest",
name: "getAnonymousTupleAnnotations"
} external;

function assertEquality(anydata expected, anydata actual) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,14 @@ function testAnnotOnRecordFields() {
}

function testAnnotOnTupleFields() {
map<any> m = getLocalTupleAnnotations(typeof foo:testAnnotationsOnLocalTupleFields(), "$field$.0");
map<any> m = getAnonymousTupleAnnotations(typeof foo:testAnnotationsOnLocalTupleFields(), "$field$.0");
assertEquality({value : "10"} , <map<anydata>>m["testorg/foo:1:annotOne"]);
m = getLocalTupleAnnotations(typeof foo:testTupleFieldAnnotationsOnReturnType(), "$field$.0");
m = getAnonymousTupleAnnotations(typeof foo:testTupleFieldAnnotationsOnReturnType(), "$field$.0");
assertEquality({value : "100"} , <map<anydata>>m["testorg/foo:1:annotOne"]);
m = getAnonymousTupleAnnotations(typeof foo:g1, "$field$.0");
assertEquality({value : "chiranS"} , <map<anydata>>m["testorg/foo:1:annotOne"]);
m = getAnonymousTupleAnnotations(typeof foo:g1, "$field$.1");
assertEquality({value : "k"} , <map<anydata>>m["testorg/foo:1:annotOne"]);
}

function getLocalRecordAnnotations(typedesc<any> obj, string annotName) returns map<any> =
Expand All @@ -49,10 +53,10 @@ function getLocalRecordAnnotations(typedesc<any> obj, string annotName) returns
name: "getLocalRecordAnnotations"
} external;

function getLocalTupleAnnotations(typedesc<any> obj, string annotName) returns map<any> =
function getAnonymousTupleAnnotations(typedesc<any> obj, string annotName) returns map<any> =
@java:Method {
'class: "org/ballerinalang/test/annotations/LocalTupleAnnotationTest",
name: "getLocalTupleAnnotations"
'class: "org/ballerinalang/test/annotations/AnonymousTupleAnnotationTest",
name: "getAnonymousTupleAnnotations"
} external;

function assertEquality(anydata expected, anydata actual) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,7 @@ public function testAnnotationsOnLocalTupleFields() returns [@annotOne {value: "
[@annotOne {value: "10"} string] r = [""];
return r;
}

public string gVar = "chiranS";

public [@annotOne {value: gVar} int, @annotOne {value: "k"} int, string...] g1 = [1, 2, "hello", "world"];

0 comments on commit 6d175fd

Please sign in to comment.