diff --git a/.github/workflows/pull_request_full_build.yml b/.github/workflows/pull_request_full_build.yml index 4a208fac63f2..c2c7fa49d41a 100644 --- a/.github/workflows/pull_request_full_build.yml +++ b/.github/workflows/pull_request_full_build.yml @@ -4,7 +4,8 @@ on: pull_request: branches: - master - + - java21 + - nutcracker jobs: build-lang: name: Build Ballerina Lang diff --git a/.github/workflows/pull_request_ubuntu_build.yml b/.github/workflows/pull_request_ubuntu_build.yml index 9dc7752bc97b..8d5e95ff9859 100644 --- a/.github/workflows/pull_request_ubuntu_build.yml +++ b/.github/workflows/pull_request_ubuntu_build.yml @@ -15,6 +15,8 @@ on: - native-build - revert-client-decl-master - query-grouping-aggregation + - java21 + - nutcracker jobs: ubuntu_build: diff --git a/.github/workflows/pull_request_windows_build.yml b/.github/workflows/pull_request_windows_build.yml index 9fb94c9a107e..25efdbe99d15 100644 --- a/.github/workflows/pull_request_windows_build.yml +++ b/.github/workflows/pull_request_windows_build.yml @@ -15,6 +15,9 @@ on: - native-build - revert-client-decl-master - query-grouping-aggregation + - java21 + - nutcracker + jobs: windows_build: name: Build with some tests on Windows diff --git a/ballerina-shell/modules/shell-cli/src/test/resources/testcases/operations.cast.json b/ballerina-shell/modules/shell-cli/src/test/resources/testcases/operations.cast.json index a687fab378e1..bcfb9d35a014 100644 --- a/ballerina-shell/modules/shell-cli/src/test/resources/testcases/operations.cast.json +++ b/ballerina-shell/modules/shell-cli/src/test/resources/testcases/operations.cast.json @@ -1,15 +1,15 @@ [ { "description": "Define types.", - "code": "type Person record { string name; int age; }; type Employee record { string name; int age; int empNo; }; type Department record { string code; };" + "code": "type PersonOP record { string name; int age; }; type EmployeeOP record { string name; int age; int empNo; }; type DepartmentOP record { string code; };" }, { "description": "Define employee.", - "code": "Employee employee = {name: \"Jane Doe\", age: 25, empNo: 1};" + "code": "EmployeeOP employee = {name: \"Jane Doe\", age: 25, empNo: 1};" }, { "description": "Cas employee to person.", - "code": "Person person = employee;" + "code": "PersonOP person = employee;" }, { "description": "Cas employee to person - get value.", @@ -18,7 +18,7 @@ }, { "description": "Recast back to employee.", - "code": "Employee employeeTwo = person;" + "code": "EmployeeOP employeeTwo = person;" }, { "description": "Recast back to employee - get value.", diff --git a/ballerina-shell/modules/shell-cli/src/test/resources/testcases/operations.equality.json b/ballerina-shell/modules/shell-cli/src/test/resources/testcases/operations.equality.json index 75c3e634644d..fe87ea575eef 100644 --- a/ballerina-shell/modules/shell-cli/src/test/resources/testcases/operations.equality.json +++ b/ballerina-shell/modules/shell-cli/src/test/resources/testcases/operations.equality.json @@ -1,15 +1,15 @@ [ { "description": "Define types.", - "code": "type Employee record { string name; int id; }; type Person record { string name; };" + "code": "type EmployeeEQ record { string name; int id; }; type PersonEQ record { string name; };" }, { "description": "Define employee.", - "code": "final Employee moduleEmployee = {name: \"John\", id: 2102};" + "code": "final EmployeeEQ moduleEmployee = {name: \"John\", id: 2102};" }, { "description": "Define module ref getter.", - "code": "function getModuleEmployee() returns Employee { return moduleEmployee; }" + "code": "function getModuleEmployee() returns EmployeeEQ { return moduleEmployee; }" }, { "description": "Equality ==.", @@ -49,7 +49,7 @@ }, { "description": "Deep inequality in records.", - "code": "Employee e1 = {name: \"Jane\", id: 1100}; Employee e2 = {name: \"Jane\", id: 1100};" + "code": "EmployeeEQ e1 = {name: \"Jane\", id: 1100}; EmployeeEQ e2 = {name: \"Jane\", id: 1100};" }, { "description": "Deep inequality in records. - get value", @@ -58,7 +58,7 @@ }, { "description": "Deep equality in records.", - "code": "Employee e3 = {name: \"Anne\", id: 1100};" + "code": "EmployeeEQ e3 = {name: \"Anne\", id: 1100};" }, { "description": "Deep equality in records. - get value", @@ -67,7 +67,7 @@ }, { "description": "Reference equality ===.", - "code": "Employee e4 = getModuleEmployee(); Person e5 = getModuleEmployee();" + "code": "EmployeeEQ e4 = getModuleEmployee(); PersonEQ e5 = getModuleEmployee();" }, { "description": "Reference equality ===. - get value", diff --git a/ballerina-shell/modules/shell-core/src/test/resources/testcases/evaluator/operations.clone.json b/ballerina-shell/modules/shell-core/src/test/resources/testcases/evaluator/operations.clone.json index 9c13311bedf4..2c6d6586bd66 100644 --- a/ballerina-shell/modules/shell-core/src/test/resources/testcases/evaluator/operations.clone.json +++ b/ballerina-shell/modules/shell-core/src/test/resources/testcases/evaluator/operations.clone.json @@ -1,19 +1,19 @@ [ { "description": "Define types.", - "code": "type Address record { string country; string state; string city; string street; }; type Person record { string name; int age; boolean married; float salary; Address address; };" + "code": "type AddressCloneTest record { string country; string state; string city; string street; }; type PersonCloneTest record { string name; int age; boolean married; float salary; AddressCloneTest address; };" }, { "description": "Define address.", - "code": "Address address = { country: \"USA\", state: \"NC\", city: \"Raleigh\", street: \"Daniels St\" };" + "code": "AddressCloneTest address = { country: \"USA\", state: \"NC\", city: \"Raleigh\", street: \"Daniels St\" };" }, { "description": "Define person.", - "code": "Person person = { name: \"Alex\", age: 24, married: false, salary: 8000.0, address: address };" + "code": "PersonCloneTest person = { name: \"Alex\", age: 24, married: false, salary: 8000.0, address: address };" }, { "description": "Clone operation.", - "code": "Person result = person.clone();" + "code": "PersonCloneTest result = person.clone();" }, { "description": "Check reference equality.", diff --git a/ballerina-shell/modules/shell-core/src/test/resources/testcases/evaluator/operations.immutable.json b/ballerina-shell/modules/shell-core/src/test/resources/testcases/evaluator/operations.immutable.json index 5e1fbfa5298b..870e6107df20 100644 --- a/ballerina-shell/modules/shell-core/src/test/resources/testcases/evaluator/operations.immutable.json +++ b/ballerina-shell/modules/shell-core/src/test/resources/testcases/evaluator/operations.immutable.json @@ -1,11 +1,11 @@ [ { "description": "Define Details.", - "code": "type Details record {| string name; int id; |};" + "code": "type DetailsImmutableTest record {| string name; int id; |};" }, { "description": "Define Student.", - "code": "type Student record {| int 'class; Details details; map marks; |};" + "code": "type StudentImmutableTest record {| int 'class; DetailsImmutableTest details; map marks; |};" }, { "description": "Define addEntryToMap.", @@ -13,11 +13,11 @@ }, { "description": "Define immutable Details", - "code": "Details & readonly immutableDetails = { name: \"May\", id: 112233 };" + "code": "DetailsImmutableTest & readonly immutableDetails = { name: \"May\", id: 112233 };" }, { "description": "Define immutable Student &", - "code": "Student & readonly student = { 'class: 12, details: immutableDetails, marks: { math: 80, physics: 85, chemistry: 75 } };" + "code": "StudentImmutableTest & readonly student = { 'class: 12, details: immutableDetails, marks: { math: 80, physics: 85, chemistry: 75 } };" }, { "description": "Readonly status of student.", diff --git a/bvm/ballerina-rt/build.gradle b/bvm/ballerina-rt/build.gradle index 5d0623d9c4f6..2cd3db732bbe 100644 --- a/bvm/ballerina-rt/build.gradle +++ b/bvm/ballerina-rt/build.gradle @@ -80,6 +80,7 @@ dependencies { dist project(':ballerina-lang:regexp') dist project(':ballerina-lang:jballerina.java') dist project(':ballerina-shell:shell-rt') + dist project(':semtypes') // Third party jars // config diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/constants/RuntimeConstants.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/constants/RuntimeConstants.java index 9dd918efebe1..7b8b06a95715 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/constants/RuntimeConstants.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/constants/RuntimeConstants.java @@ -90,6 +90,8 @@ public final class RuntimeConstants { // Empty value for string public static final BString STRING_EMPTY_VALUE = StringUtils.fromString(""); + public static final Long INT_MAX_VALUE = 9223372036854775807L; + public static final Long INT_MIN_VALUE = -9223372036854775807L - 1L; public static final Integer BBYTE_MIN_VALUE = 0; public static final Integer BBYTE_MAX_VALUE = 255; public static final Integer SIGNED32_MAX_VALUE = 2147483647; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/creators/TypeCreator.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/creators/TypeCreator.java index b041914300b6..e1a627bbf879 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/creators/TypeCreator.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/creators/TypeCreator.java @@ -50,6 +50,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * Class @{@link TypeCreator} provides APIs to create ballerina type instances. @@ -58,6 +59,7 @@ */ public final class TypeCreator { + private static final RecordTypeCache registeredRecordTypes = new RecordTypeCache(); /** * Creates a new array type with given element type. * @@ -147,7 +149,7 @@ public static TupleType createTupleType(List typeList, Type restType, int * @return the new tuple type */ public static TupleType createTupleType(List typeList, Type restType, - int typeFlags, boolean isCyclic, boolean readonly) { + int typeFlags, boolean isCyclic, boolean readonly) { return new BTupleType(typeList, restType, typeFlags, isCyclic, readonly); } @@ -162,16 +164,16 @@ public static TupleType createTupleType(List typeList, Type restType, * @return the new tuple type */ public static TupleType createTupleType(String name, Module pkg, - int typeFlags, boolean isCyclic, boolean readonly) { + int typeFlags, boolean isCyclic, boolean readonly) { return new BTupleType(name, pkg, typeFlags, isCyclic, readonly); } /** - * Create a {@code MapType} which represents the map type. - * - * @param constraint constraint type which particular map is bound to. - * @return the new map type - */ + * Create a {@code MapType} which represents the map type. + * + * @param constraint constraint type which particular map is bound to. + * @return the new map type + */ public static MapType createMapType(Type constraint) { return new BMapType(constraint); } @@ -224,6 +226,10 @@ public static MapType createMapType(String typeName, Type constraint, Module mod */ public static RecordType createRecordType(String typeName, Module module, long flags, boolean sealed, int typeFlags) { + BRecordType memo = registeredRecordType(typeName, module); + if (memo != null) { + return memo; + } return new BRecordType(typeName, typeName, module, flags, sealed, typeFlags); } @@ -240,8 +246,11 @@ public static RecordType createRecordType(String typeName, Module module, long f * @return the new record type */ public static RecordType createRecordType(String typeName, Module module, long flags, Map fields, - Type restFieldType, - boolean sealed, int typeFlags) { + Type restFieldType, boolean sealed, int typeFlags) { + BRecordType memo = registeredRecordType(typeName, module); + if (memo != null) { + return memo; + } return new BRecordType(typeName, module, flags, fields, restFieldType, sealed, typeFlags); } @@ -520,4 +529,45 @@ public static FiniteType createFiniteType(String typeName, Set values, i private TypeCreator() { } + + private static BRecordType registeredRecordType(String typeName, Module pkg) { + if (typeName == null || pkg == null) { + return null; + } + return registeredRecordTypes.get(new TypeIdentifier(typeName, pkg)); + } + + public static void registerRecordType(BRecordType recordType) { + String name = recordType.getName(); + Module pkg = recordType.getPackage(); + if (name == null || pkg == null) { + return; + } + if (name.contains("$anon")) { + return; + } + TypeIdentifier typeIdentifier = new TypeIdentifier(name, pkg); + registeredRecordTypes.put(typeIdentifier, recordType); + } + + private static final class RecordTypeCache { + + private static final Map cache = new ConcurrentHashMap<>(); + + BRecordType get(TypeIdentifier key) { + return cache.get(key); + } + + void put(TypeIdentifier identifier, BRecordType value) { + cache.put(identifier, value); + } + } + + public record TypeIdentifier(String typeName, Module pkg) { + + public TypeIdentifier { + assert typeName != null; + assert pkg != null; + } + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/PredefinedTypes.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/PredefinedTypes.java index 6541d6aa9811..00cee703124c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/PredefinedTypes.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/PredefinedTypes.java @@ -64,7 +64,7 @@ */ public final class PredefinedTypes { - private static final Module EMPTY_MODULE = new Module(null, null, null); + public static final Module EMPTY_MODULE = new Module(null, null, null); public static final IntegerType TYPE_INT = new BIntegerType(TypeConstants.INT_TNAME, EMPTY_MODULE); public static final IntegerType TYPE_INT_SIGNED_8 = diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/TypeTags.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/TypeTags.java index ebd78439ded5..daf201901bd6 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/TypeTags.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/TypeTags.java @@ -92,10 +92,9 @@ private TypeTags() { } public static boolean isIntegerTypeTag(int tag) { - - // TODO : Fix byte type. Ideally, byte belongs to here. But we have modeled it differently. return switch (tag) { - case INT_TAG, + case BYTE_TAG, + INT_TAG, SIGNED32_INT_TAG, SIGNED16_INT_TAG, SIGNED8_INT_TAG, diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Atom.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Atom.java new file mode 100644 index 000000000000..0ddea623de6c --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Atom.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Represent the BDD atom. + * + * @since 2201.11.0 + */ +public sealed interface Atom permits RecAtom, TypeAtom { + + /** + * Get the index of the atom. For {@code TypeAtoms} this is a unique index within the {@code Env}. Each + * {@code RecAtom} that points to the same {@code TypeAtom} will have the same index. + * + * @return index of the atom + */ + int index(); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/AtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/AtomicType.java new file mode 100644 index 000000000000..7e5163fa8a78 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/AtomicType.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.FunctionAtomicType; +import io.ballerina.runtime.internal.types.semtype.ListAtomicType; +import io.ballerina.runtime.internal.types.semtype.MappingAtomicType; + +/** + * Marker type representing AtomicType. + * + * @since 2201.11.0 + */ +public sealed interface AtomicType permits CellAtomicType, FunctionAtomicType, ListAtomicType, MappingAtomicType { + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeBitSet.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeBitSet.java new file mode 100644 index 000000000000..f3fd6f57048b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeBitSet.java @@ -0,0 +1,19 @@ +package io.ballerina.runtime.api.types.semtype; + +abstract sealed class BasicTypeBitSet permits SemType { + + private int all; + + protected BasicTypeBitSet(int all) { + this.all = all; + } + + protected void setAll(int all) { + this.all = all; + } + + public final int all() { + assert all != -1 : "SemType created by no arg constructor must be initialized with setAll"; + return all; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeCode.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeCode.java new file mode 100644 index 000000000000..c9158bce4bf5 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeCode.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Represent bit field that indicate which basic type a semType belongs to. + * + * @since 2201.11.0 + */ +public final class BasicTypeCode { + + public static final int CODE_NIL = 0x00; + public static final int CODE_BOOLEAN = 0x01; + public static final int CODE_INT = 0x02; + public static final int CODE_FLOAT = 0x03; + public static final int CODE_DECIMAL = 0x04; + public static final int CODE_STRING = 0x05; + public static final int CODE_ERROR = 0x06; + public static final int CODE_TYPEDESC = 0x07; + public static final int CODE_HANDLE = 0x08; + public static final int CODE_FUNCTION = 0x09; + public static final int CODE_REGEXP = 0x0A; + public static final int CODE_FUTURE = 0x0B; + public static final int CODE_STREAM = 0x0C; + public static final int CODE_LIST = 0x0D; + public static final int CODE_MAPPING = 0x0E; + public static final int CODE_TABLE = 0x0F; + public static final int CODE_XML = 0x10; + public static final int CODE_OBJECT = 0x11; + public static final int CODE_CELL = 0x12; + public static final int CODE_UNDEF = 0x13; + + // Inherently immutable + public static final BasicTypeCode BT_NIL = get(CODE_NIL); + public static final BasicTypeCode BT_BOOLEAN = get(CODE_BOOLEAN); + public static final BasicTypeCode BT_INT = get(CODE_INT); + public static final BasicTypeCode BT_FLOAT = get(CODE_FLOAT); + public static final BasicTypeCode BT_DECIMAL = get(CODE_DECIMAL); + public static final BasicTypeCode BT_STRING = get(CODE_STRING); + public static final BasicTypeCode BT_ERROR = get(CODE_ERROR); + public static final BasicTypeCode BT_TYPEDESC = get(CODE_TYPEDESC); + public static final BasicTypeCode BT_HANDLE = get(CODE_HANDLE); + public static final BasicTypeCode BT_FUNCTION = get(CODE_FUNCTION); + public static final BasicTypeCode BT_REGEXP = get(CODE_REGEXP); + + // Inherently mutable + public static final BasicTypeCode BT_FUTURE = get(CODE_FUTURE); + public static final BasicTypeCode BT_STREAM = get(CODE_STREAM); + + // Selectively immutable + public static final BasicTypeCode BT_LIST = get(CODE_LIST); + public static final BasicTypeCode BT_MAPPING = get(CODE_MAPPING); + public static final BasicTypeCode BT_TABLE = get(CODE_TABLE); + public static final BasicTypeCode BT_XML = get(CODE_XML); + public static final BasicTypeCode BT_OBJECT = get(CODE_OBJECT); + + // Non-val + public static final BasicTypeCode BT_CELL = get(CODE_CELL); + public static final BasicTypeCode BT_UNDEF = get(CODE_UNDEF); + + // Helper bit fields (does not represent basic type tag) + static final int VT_COUNT = CODE_OBJECT + 1; + public static final int BASIC_TYPE_MASK = (1 << (CODE_STRING + 1)) - 1; + public static final int VT_MASK = (1 << VT_COUNT) - 1; + + static final int VT_COUNT_INHERENTLY_IMMUTABLE = CODE_FUTURE; + public static final int VT_INHERENTLY_IMMUTABLE = (1 << VT_COUNT_INHERENTLY_IMMUTABLE) - 1; + + private final int code; + + private BasicTypeCode(int code) { + this.code = code; + } + + public static BasicTypeCode get(int code) { + if (BasicTypeCodeCache.isCached(code)) { + return BasicTypeCodeCache.cache[code]; + } + return new BasicTypeCode(code); + } + + public int code() { + return code; + } + + @Override + public int hashCode() { + return code; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof BasicTypeCode other) { + return code == other.code; + } + return false; + } + + private static final class BasicTypeCodeCache { + + private static final BasicTypeCode[] cache; + static { + cache = new BasicTypeCode[CODE_UNDEF + 2]; + for (int i = CODE_NIL; i < CODE_UNDEF + 1; i++) { + cache[i] = new BasicTypeCode(i); + } + } + + private static boolean isCached(int code) { + return 0 < code && code < VT_COUNT; + } + + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Bdd.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Bdd.java new file mode 100644 index 000000000000..2573230f05a8 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Bdd.java @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.SubTypeData; + +import static io.ballerina.runtime.api.types.semtype.Conjunction.and; + +/** + * Represent BDD node. Subtypes that uses BDDs to represent subtypes such as + * list, mapping and cell should implement + * their own {@code SubType} implementation that wraps an implementation of this + * class. + * + * @since 2201.11.0 + */ +public abstract sealed class Bdd extends SubType implements SubTypeData permits BddAllOrNothing, BddNode { + + Bdd(boolean all, boolean nothing) { + super(all, nothing); + } + + @Override + public SubType union(SubType other) { + return bddUnion((Bdd) other); + } + + private Bdd bddUnion(Bdd other) { + if (other == this) { + return this; + } else if (this == BddAllOrNothing.ALL || other == BddAllOrNothing.ALL) { + return BddAllOrNothing.ALL; + } else if (other == BddAllOrNothing.NOTHING) { + return this; + } else if (this == BddAllOrNothing.NOTHING) { + return other; + } + BddNode b1Bdd = (BddNode) this; + BddNode b2Bdd = (BddNode) other; + int cmp = atomCmp(b1Bdd.atom(), b2Bdd.atom()); + if (cmp < 0) { + return bddCreate(b1Bdd.atom(), + b1Bdd.left(), + b1Bdd.middle().bddUnion(other), + b1Bdd.right()); + } else if (cmp > 0) { + return bddCreate(b2Bdd.atom(), + b2Bdd.left(), + this.bddUnion(b2Bdd.middle()), + b2Bdd.right()); + } else { + return bddCreate(b1Bdd.atom(), + b1Bdd.left().bddUnion(b2Bdd.left()), + b1Bdd.middle().bddUnion(b2Bdd.middle()), + b1Bdd.right().bddUnion(b2Bdd.right())); + } + } + + private int atomCmp(Atom a1, Atom a2) { + if (a1 instanceof RecAtom r1) { + if (a2 instanceof RecAtom r2) { + return r1.index() - r2.index(); + } else { + return -1; + } + } else if (a2 instanceof RecAtom) { + return 1; + } else { + return a1.index() - a2.index(); + } + } + + @Override + public SubType intersect(SubType other) { + return bddIntersect((Bdd) other); + } + + private Bdd bddIntersect(Bdd other) { + if (other == this) { + return this; + } else if (this == BddAllOrNothing.NOTHING || other == BddAllOrNothing.NOTHING) { + return BddAllOrNothing.NOTHING; + } else if (other == BddAllOrNothing.ALL) { + return this; + } else if (this == BddAllOrNothing.ALL) { + return other; + } + BddNode b1Bdd = (BddNode) this; + BddNode b2Bdd = (BddNode) other; + int cmp = atomCmp(b1Bdd.atom(), b2Bdd.atom()); + if (cmp < 0) { + return bddCreate(b1Bdd.atom(), + b1Bdd.left().bddIntersect(other), + b1Bdd.middle().bddIntersect(other), + b1Bdd.right().bddIntersect(other)); + } else if (cmp > 0) { + return bddCreate(b2Bdd.atom(), + this.bddIntersect(b2Bdd.left()), + this.bddIntersect(b2Bdd.middle()), + this.bddIntersect(b2Bdd.right())); + } else { + return bddCreate(b1Bdd.atom(), + b1Bdd.left().bddUnion(b1Bdd.middle()).bddIntersect(b2Bdd.left().bddUnion(b2Bdd.middle())), + BddAllOrNothing.NOTHING, + b1Bdd.right().bddUnion(b1Bdd.middle()).bddIntersect(b2Bdd.right().bddUnion(b2Bdd.middle()))); + } + } + + @Override + public SubType diff(SubType other) { + return bddDiff((Bdd) other); + } + + private Bdd bddDiff(Bdd other) { + if (this == other || other == BddAllOrNothing.ALL || this == BddAllOrNothing.NOTHING) { + return BddAllOrNothing.NOTHING; + } else if (other == BddAllOrNothing.NOTHING) { + return this; + } else if (this == BddAllOrNothing.ALL) { + return other.bddComplement(); + } + BddNode b1Bdd = (BddNode) this; + BddNode b2Bdd = (BddNode) other; + int cmp = atomCmp(b1Bdd.atom(), b2Bdd.atom()); + if (cmp < 0L) { + return bddCreate(b1Bdd.atom(), + b1Bdd.left().bddUnion(b1Bdd.middle()).bddDiff(other), + BddAllOrNothing.NOTHING, + b1Bdd.right().bddUnion(b1Bdd.middle()).bddDiff(other)); + } else if (cmp > 0L) { + return bddCreate(b2Bdd.atom(), + this.bddDiff(b2Bdd.left().bddUnion(b2Bdd.middle())), + BddAllOrNothing.NOTHING, + this.bddDiff(b2Bdd.right().bddUnion(b2Bdd.middle()))); + } else { + // There is an error in the Castagna paper for this formula. + // The union needs to be materialized here. + // The original formula does not work in a case like (a0|a1) - a0. + // Castagna confirms that the following formula is the correct one. + return bddCreate(b1Bdd.atom(), + b1Bdd.left().bddUnion(b1Bdd.middle()).bddDiff(b2Bdd.left().bddUnion(b2Bdd.middle())), + BddAllOrNothing.NOTHING, + b1Bdd.right().bddUnion(b1Bdd.middle()).bddDiff(b2Bdd.right().bddUnion(b2Bdd.middle()))); + } + } + + @Override + public SubType complement() { + return bddComplement(); + } + + private Bdd bddComplement() { + if (this == BddAllOrNothing.ALL) { + return BddAllOrNothing.NOTHING; + } else if (this == BddAllOrNothing.NOTHING) { + return BddAllOrNothing.ALL; + } + Bdd nothing = BddAllOrNothing.NOTHING; + BddNode b = (BddNode) this; + if (b.right() == nothing) { + return bddCreate(b.atom(), + nothing, + b.left().bddUnion(b.middle()).bddComplement(), + b.middle().bddComplement()); + } else if (b.left() == nothing) { + return bddCreate(b.atom(), + b.middle().bddComplement(), + b.right().bddUnion(b.middle()).bddComplement(), + nothing); + } else if (b.middle() == nothing) { + return bddCreate(b.atom(), + b.left().bddComplement(), + b.left().bddUnion(b.right()).bddComplement(), + b.right().bddComplement()); + } else { + // There is a typo in the Frisch PhD thesis for this formula. + // (It has left and right swapped.) + // Castagna (the PhD supervisor) confirms that this is the correct formula. + return bddCreate(b.atom(), + b.left().bddUnion(b.middle()).bddComplement(), + nothing, + b.right().bddUnion(b.middle()).bddComplement()); + } + } + + private Bdd bddCreate(Atom atom, Bdd left, Bdd middle, Bdd right) { + if (middle == BddAllOrNothing.ALL) { + return middle; + } + if (left.equals(right)) { + return left.bddUnion(right); + } + + return new BddNodeImpl(atom, left, middle, right); + } + + @Override + public boolean isEmpty(Context cx) { + // Basic types that uses Bdd as a delegate should implement isEmpty instead. + throw new IllegalStateException("Bdd don't support isEmpty"); + } + + @Override + public SubTypeData data() { + // Basic types that uses Bdd (and has a meaningful data part) as a delegate should implement data instead. + throw new IllegalStateException("Bdd don't support data"); + } + + public static boolean bddEvery(Context cx, Bdd b, BddPredicate predicate) { + return bddEvery(cx, b, null, null, predicate); + } + + private static boolean bddEvery(Context cx, Bdd b, Conjunction pos, Conjunction neg, BddPredicate predicate) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing == BddAllOrNothing.NOTHING || predicate.apply(cx, pos, neg); + } + BddNode bn = (BddNode) b; + return bddEvery(cx, bn.left(), and(bn.atom(), pos), neg, predicate) + && bddEvery(cx, bn.middle(), pos, neg, predicate) + && bddEvery(cx, bn.right(), pos, and(bn.atom(), neg), predicate); + } + + public static boolean bddEveryPositive(Context cx, Bdd b, Conjunction pos, Conjunction neg, + BddPredicate predicate) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing == BddAllOrNothing.NOTHING || predicate.apply(cx, pos, neg); + } else { + BddNode bn = (BddNode) b; + return bddEveryPositive(cx, bn.left(), andIfPositive(bn.atom(), pos), neg, predicate) + && bddEveryPositive(cx, bn.middle(), pos, neg, predicate) + && bddEveryPositive(cx, bn.right(), pos, andIfPositive(bn.atom(), neg), predicate); + } + } + + private static Conjunction andIfPositive(Atom atom, Conjunction next) { + if (atom instanceof RecAtom recAtom && recAtom.index() < 0) { + return next; + } + return and(atom, next); + } + + public abstract boolean posMaybeEmpty(); + + // This is for debugging purposes. + // It uses the Frisch/Castagna notation. + public static String bddToString(Bdd b, boolean inner) { + if (b instanceof BddAllOrNothing) { + return b.isAll() ? "1" : "0"; + } else { + String str; + BddNode bdd = (BddNode) b; + Atom a = bdd.atom(); + + if (a instanceof RecAtom) { + str = "r"; + } else { + str = "a"; + } + str += a.index(); + str += "?" + bddToString(bdd.left(), true) + ":" + bddToString(bdd.middle(), true) + + ":" + bddToString(bdd.right(), true); + if (inner) { + str = "(" + str + ")"; + } + return str; + } + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddAllOrNothing.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddAllOrNothing.java new file mode 100644 index 000000000000..e5b6c67ebad5 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddAllOrNothing.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Represent the leaf node of a Bdd. + * + * @since 2201.11.0 + */ +public final class BddAllOrNothing extends Bdd { + + public static final BddAllOrNothing ALL = new BddAllOrNothing(true); + public static final BddAllOrNothing NOTHING = new BddAllOrNothing(false); + + private BddAllOrNothing(boolean all) { + super(all, !all); + } + + @Override + public int hashCode() { + return this == ALL ? 1 : 0; + } + + @Override + public boolean equals(Object o) { + return this == o; + } + + @Override + public boolean posMaybeEmpty() { + return this == ALL; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddIsEmptyPredicate.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddIsEmptyPredicate.java new file mode 100644 index 000000000000..d08297a81b06 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddIsEmptyPredicate.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Predicate to check if a BDD is empty. + * + * @since 2201.11.0 + */ +@FunctionalInterface +public interface BddIsEmptyPredicate { + + /** + * Check if the given BDD is empty. + * + * @param cx Type check context + * @param bdd BDD to check + * @return true if the BDD is empty, false otherwise + */ + boolean apply(Context cx, Bdd bdd); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNode.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNode.java new file mode 100644 index 000000000000..001f154701ee --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNode.java @@ -0,0 +1,66 @@ +package io.ballerina.runtime.api.types.semtype; + +/** + * Internal node of a BDD, which represents a disjunction of conjunctions of atoms. + * + * @since 2201.11.0 + */ +public abstract sealed class BddNode extends Bdd permits BddNodeImpl, BddNodeSimple { + + private volatile Integer hashCode = null; + + protected BddNode(boolean all, boolean nothing) { + super(all, nothing); + } + + public static BddNode bddAtom(Atom atom) { + return new BddNodeSimple(atom); + } + + public boolean isSimple() { + return this instanceof BddNodeSimple; + } + + public abstract Atom atom(); + + public abstract Bdd left(); + + public abstract Bdd middle(); + + public abstract Bdd right(); + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof BddNode other)) { + return false; + } + return atom().equals(other.atom()) && left().equals(other.left()) && middle().equals(other.middle()) && + right().equals(other.right()); + } + + @Override + public int hashCode() { + Integer result = hashCode; + if (result == null) { + // No need to synchronize this since {@code computeHashCode} is idempotent + hashCode = result = computeHashCode(); + } + return result; + } + + private int computeHashCode() { + int result = atom().hashCode(); + result = 31 * result + left().hashCode(); + result = 31 * result + middle().hashCode(); + result = 31 * result + right().hashCode(); + return result; + } + + @Override + public boolean posMaybeEmpty() { + return middle().posMaybeEmpty() || right().posMaybeEmpty(); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNodeImpl.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNodeImpl.java new file mode 100644 index 000000000000..71b1a9528220 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNodeImpl.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Generalized implementation of {@code BddNode}. + * + * @since 2201.11.0 + */ +final class BddNodeImpl extends BddNode { + + private final Atom atom; + private final Bdd left; + private final Bdd middle; + private final Bdd right; + + BddNodeImpl(Atom atom, Bdd left, Bdd middle, Bdd right) { + super(false, false); + this.atom = atom; + this.left = left; + this.middle = middle; + this.right = right; + } + + @Override + public Atom atom() { + return atom; + } + + @Override + public Bdd left() { + return left; + } + + @Override + public Bdd middle() { + return middle; + } + + @Override + public Bdd right() { + return right; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNodeSimple.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNodeSimple.java new file mode 100644 index 000000000000..bd64cdcf9af5 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNodeSimple.java @@ -0,0 +1,37 @@ +package io.ballerina.runtime.api.types.semtype; + +/** + * Represent a Bdd node that contains a single atom as positive. This is used to reduce the memory overhead of + * BddNodeImpl in representing such nodes + * + * @since 2201.11.0 + */ +final class BddNodeSimple extends BddNode { + + private final Atom atom; + + BddNodeSimple(Atom atom) { + super(false, false); + this.atom = atom; + } + + @Override + public Atom atom() { + return atom; + } + + @Override + public Bdd left() { + return BddAllOrNothing.ALL; + } + + @Override + public Bdd middle() { + return BddAllOrNothing.NOTHING; + } + + @Override + public Bdd right() { + return BddAllOrNothing.NOTHING; + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/FloatSubtype.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java similarity index 59% rename from semtypes/src/main/java/io/ballerina/semtype/subtypedata/FloatSubtype.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java index 8f28d7bc9025..4a6e47f3c870 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/FloatSubtype.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -14,16 +14,18 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * */ -package io.ballerina.semtype.subtypedata; -import io.ballerina.semtype.ProperSubtypeData; +package io.ballerina.runtime.api.types.semtype; /** - * Represent FloatSubtype. + * Represents a predicate that can be applied to a BDD conjunction. * - * @since 2.0.0 + * @since 2201.11.0 */ -public class FloatSubtype implements ProperSubtypeData { +@FunctionalInterface +public interface BddPredicate { + boolean apply(Context cx, Conjunction posList, Conjunction negList); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Builder.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Builder.java new file mode 100644 index 000000000000..4c4f1149387b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Builder.java @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.BBooleanSubType; +import io.ballerina.runtime.internal.types.semtype.BCellSubType; +import io.ballerina.runtime.internal.types.semtype.BDecimalSubType; +import io.ballerina.runtime.internal.types.semtype.BFloatSubType; +import io.ballerina.runtime.internal.types.semtype.BIntSubType; +import io.ballerina.runtime.internal.types.semtype.BListSubType; +import io.ballerina.runtime.internal.types.semtype.BMappingSubType; +import io.ballerina.runtime.internal.types.semtype.BStringSubType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; +import io.ballerina.runtime.internal.types.semtype.ListAtomicType; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import io.ballerina.runtime.internal.types.semtype.MappingAtomicType; +import io.ballerina.runtime.internal.types.semtype.MappingDefinition; +import io.ballerina.runtime.internal.types.semtype.TableUtils; +import io.ballerina.runtime.internal.types.semtype.XmlUtils; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_CELL; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_ERROR; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_FUNCTION; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_FUTURE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_HANDLE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_LIST; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_MAPPING; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_OBJECT; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_REGEXP; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_TYPEDESC; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_XML; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_UNDEF; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; +import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; +import static io.ballerina.runtime.api.types.semtype.Core.union; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; + +/** + * Utility class for creating semtypes. + * + * @since 2201.11.0 + */ +public final class Builder { + + private static final String[] EMPTY_STRING_ARR = new String[0]; + private static final SemType VAL = SemType.from(VT_MASK); + private static final SemType OBJECT = from(BT_OBJECT); + + private static final SemType INNER = getBasicTypeUnion(VAL.all() | from(BasicTypeCode.BT_UNDEF).all()); + private static final SemType ANY = getBasicTypeUnion(BasicTypeCode.VT_MASK & ~(1 << BasicTypeCode.BT_ERROR.code())); + private static final SemType SIMPLE_OR_STRING = + getBasicTypeUnion((1 << BasicTypeCode.BT_NIL.code()) + | (1 << BasicTypeCode.BT_BOOLEAN.code()) + | (1 << BasicTypeCode.BT_INT.code()) + | (1 << BasicTypeCode.BT_FLOAT.code()) + | (1 << BasicTypeCode.BT_DECIMAL.code()) + | (1 << BT_REGEXP.code()) + | (1 << BasicTypeCode.BT_STRING.code())); + + private static final SemType[] EMPTY_TYPES_ARR = new SemType[0]; + + private static final ConcurrentLazySupplier MAPPING_RO = new ConcurrentLazySupplier<>(() -> + basicSubType(BT_MAPPING, BMappingSubType.createDelegate(getBddSubtypeRo())) + ); + private static final ConcurrentLazySupplier ANYDATA = new ConcurrentLazySupplier<>( + () -> { + Env env = Env.getInstance(); + ListDefinition listDef = new ListDefinition(); + MappingDefinition mapDef = new MappingDefinition(); + SemType tableTy = TableUtils.tableContaining(env, mapDef.getSemType(env)); + SemType accum = Stream.of(getSimpleOrStringType(), getXmlType(), listDef.getSemType(env), + mapDef.getSemType(env), + tableTy).reduce(Builder.getNeverType(), Core::union); + listDef.defineListTypeWrapped(env, EMPTY_TYPES_ARR, 0, accum, CELL_MUT_LIMITED); + mapDef.defineMappingTypeWrapped(env, new MappingDefinition.Field[0], accum, CELL_MUT_LIMITED); + return accum; + } + ); + private static final ConcurrentLazySupplier INNER_RO = + new ConcurrentLazySupplier<>(() -> union(getReadonlyType(), getInnerType())); + + private static final ConcurrentLazySupplier LIST_ATOMIC_INNER = + new ConcurrentLazySupplier<>(() -> new ListAtomicType( + FixedLengthArray.empty(), PredefinedTypeEnv.getInstance().cellSemTypeInner())); + private static final ConcurrentLazySupplier MAPPING_ATOMIC_INNER = + new ConcurrentLazySupplier<>(() -> new MappingAtomicType( + EMPTY_STRING_ARR, EMPTY_TYPES_ARR, PredefinedTypeEnv.getInstance().cellSemTypeInner())); + + private static final ConcurrentLazySupplier XML_ELEMENT = new ConcurrentLazySupplier<>(() -> + XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_ELEMENT_RO | XmlUtils.XML_PRIMITIVE_ELEMENT_RW)); + private static final ConcurrentLazySupplier XML_COMMENT = new ConcurrentLazySupplier<>(() -> + XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_COMMENT_RO | XmlUtils.XML_PRIMITIVE_COMMENT_RW)); + private static final ConcurrentLazySupplier XML_TEXT = new ConcurrentLazySupplier<>(() -> + XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_TEXT)); + private static final ConcurrentLazySupplier XML_PI = new ConcurrentLazySupplier<>(() -> + XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_PI_RO | XmlUtils.XML_PRIMITIVE_PI_RW)); + private static final ConcurrentLazySupplier XML_NEVER = new ConcurrentLazySupplier<>(() -> + XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_NEVER)); + private static final PredefinedTypeEnv PREDEFINED_TYPE_ENV = PredefinedTypeEnv.getInstance(); + private static final BddNode LIST_SUBTYPE_THREE_ELEMENT = bddAtom(PREDEFINED_TYPE_ENV.atomListThreeElement()); + private static final BddNode LIST_SUBTYPE_THREE_ELEMENT_RO = bddAtom(PREDEFINED_TYPE_ENV.atomListThreeElementRO()); + private static final BddNode LIST_SUBTYPE_TWO_ELEMENT = bddAtom(PREDEFINED_TYPE_ENV.atomListTwoElement()); + + private Builder() { + } + + public static SemType from(BasicTypeCode typeCode) { + if (BasicTypeCache.isCached(typeCode)) { + return BasicTypeCache.cache[typeCode.code()]; + } + return SemType.from(1 << typeCode.code()); + } + + public static SemType getNeverType() { + return SemType.from(0); + } + + public static SemType getNilType() { + return from(BasicTypeCode.BT_NIL); + } + + public static SemType getUndefType() { + return from(BasicTypeCode.BT_UNDEF); + } + + public static SemType getCellType() { + return from(BT_CELL); + } + + public static SemType getInnerType() { + return INNER; + } + + public static SemType getIntType() { + return from(BasicTypeCode.BT_INT); + } + + public static SemType getDecimalType() { + return from(BasicTypeCode.BT_DECIMAL); + } + + public static SemType getFloatType() { + return from(BasicTypeCode.BT_FLOAT); + } + + public static SemType getBooleanType() { + return from(BasicTypeCode.BT_BOOLEAN); + } + + public static SemType getStringType() { + return from(BasicTypeCode.BT_STRING); + } + + public static SemType getCharType() { + return StringTypeCache.charType; + } + + public static SemType getListType() { + return from(BT_LIST); + } + + public static SemType getReadonlyType() { + return PREDEFINED_TYPE_ENV.readonlyType(); + } + + static SemType getBasicTypeUnion(int bitset) { + return switch (bitset) { + case 0 -> getNeverType(); + case VT_MASK -> VAL; + default -> { + if (Integer.bitCount(bitset) == 1) { + int code = Integer.numberOfTrailingZeros(bitset); + // TODO: what are the others? + if (BasicTypeCache.isCached(code)) { + yield BasicTypeCache.cache[code]; + } + } + yield SemType.from(bitset); + } + }; + } + + public static SemType basicSubType(BasicTypeCode basicTypeCode, SubType subType) { + assert !(subType instanceof Bdd) : "BDD should always be wrapped with a delegate"; + assert checkDelegate(basicTypeCode, subType) : "BDd is wrapped in wrong delegate"; + int some = 1 << basicTypeCode.code(); + SubType[] subTypes = initializeSubtypeArray(some); + subTypes[0] = subType; + return SemType.from(0, some, subTypes); + } + + private static boolean checkDelegate(BasicTypeCode basicTypeCode, SubType subType) { + return (basicTypeCode != BT_MAPPING || subType instanceof BMappingSubType) + && (basicTypeCode != BT_LIST || subType instanceof BListSubType) + && (basicTypeCode != BT_CELL || subType instanceof BCellSubType); + } + + public static SemType getIntConst(long value) { + if (value >= IntTypeCache.CACHE_MIN_VALUE && value <= IntTypeCache.CACHE_MAX_VALUE) { + return IntTypeCache.cache[(int) value - IntTypeCache.CACHE_MIN_VALUE]; + } + return createIntSingletonType(value); + } + + private static SemType createIntSingletonType(long value) { + List values = new ArrayList<>(1); + values.add(value); + return basicSubType(BasicTypeCode.BT_INT, BIntSubType.createIntSubType(values)); + } + + public static SemType getBooleanConst(boolean value) { + return value ? BooleanTypeCache.TRUE : BooleanTypeCache.FALSE; + } + + public static SemType createIntRange(long min, long max) { + return basicSubType(BasicTypeCode.BT_INT, BIntSubType.createIntSubType(min, max)); + } + + public static SemType getDecimalConst(BigDecimal value) { + BigDecimal[] values = {value}; + return basicSubType(BasicTypeCode.BT_DECIMAL, BDecimalSubType.createDecimalSubType(true, values)); + } + + public static SemType getFloatConst(double value) { + Double[] values = {value}; + return basicSubType(BasicTypeCode.BT_FLOAT, BFloatSubType.createFloatSubType(true, values)); + } + + // TODO: consider caching small strings + public static SemType getStringConst(String value) { + BStringSubType subType; + String[] values = {value}; + String[] empty = EMPTY_STRING_ARR; + if (value.length() == 1 || value.codePointCount(0, value.length()) == 1) { + subType = BStringSubType.createStringSubType(true, values, true, empty); + } else { + subType = BStringSubType.createStringSubType(true, empty, true, values); + } + return basicSubType(BasicTypeCode.BT_STRING, subType); + } + + static SubType[] initializeSubtypeArray(int some) { + return new SubType[Integer.bitCount(some)]; + } + + public static SemType getRoCellContaining(Env env, SemType ty) { + return getCellContaining(env, ty, CELL_MUT_NONE); + } + + public static SemType getRwCellContaining(Env env, SemType ty) { + return getCellContaining(env, ty, CellAtomicType.CellMutability.CELL_MUT_LIMITED); + } + + public static SemType getCellContaining(Env env, SemType ty, CellAtomicType.CellMutability mut) { + return env.getCachedCellType(ty, mut, () -> createCellSemType(env, ty, mut)); + } + + private static SemType createCellSemType(Env env, SemType ty, CellAtomicType.CellMutability mut) { + CellAtomicType atomicCell = CellAtomicType.from(ty, mut); + TypeAtom atom = env.cellAtom(atomicCell); + BddNode bdd = bddAtom(atom); + return basicSubType(BT_CELL, BCellSubType.createDelegate(bdd)); + } + + public static SemType getValType() { + return getBasicTypeUnion(VT_MASK); + } + + public static SemType getAnyType() { + return ANY; + } + + public static SemType getMappingType() { + return from(BT_MAPPING); + } + + public static SemType getFunctionType() { + return from(BT_FUNCTION); + } + + public static SemType getErrorType() { + return from(BT_ERROR); + } + + public static SemType getXmlType() { + return from(BT_XML); + } + + public static SemType getXmlElementType() { + return XML_ELEMENT.get(); + } + + public static SemType getXmlCommentType() { + return XML_COMMENT.get(); + } + + public static SemType getXmlTextType() { + return XML_TEXT.get(); + } + + public static SemType getXmlNeverType() { + return XML_NEVER.get(); + } + + public static SemType getXmlPIType() { + return XML_PI.get(); + } + + public static SemType getHandleType() { + return from(BT_HANDLE); + } + + public static SemType getFutureType() { + return from(BT_FUTURE); + } + + public static SemType getRegexType() { + return from(BT_REGEXP); + } + + public static SemType getTypeDescType() { + return from(BT_TYPEDESC); + } + + public static SemType getStreamType() { + return from(BasicTypeCode.BT_STREAM); + } + + public static SemType getAnyDataType() { + return ANYDATA.get(); + } + + public static SemType getObjectType() { + return OBJECT; + } + + static SemType mappingRO() { + return MAPPING_RO.get(); + } + + static SemType innerReadOnly() { + return INNER_RO.get(); + } + + static CellAtomicType cellAtomicVal() { + return PREDEFINED_TYPE_ENV.cellAtomicVal(); + } + + public static BddNode getBddSubtypeRo() { + return bddAtom(RecAtom.createRecAtom(0)); + } + + public static ListAtomicType getListAtomicInner() { + return LIST_ATOMIC_INNER.get(); + } + + public static MappingAtomicType getMappingAtomicInner() { + return MAPPING_ATOMIC_INNER.get(); + } + + public static BddNode getListSubtypeThreeElement() { + return LIST_SUBTYPE_THREE_ELEMENT; + } + + public static BddNode getListSubtypeThreeElementRO() { + return LIST_SUBTYPE_THREE_ELEMENT_RO; + } + + public static BddNode getListSubtypeTwoElement() { + return LIST_SUBTYPE_TWO_ELEMENT; + } + + public static SemType getSimpleOrStringType() { + return SIMPLE_OR_STRING; + } + + public static SemType getTableType() { + return from(BasicTypeCode.BT_TABLE); + } + + private static final class IntTypeCache { + + private static final int CACHE_MAX_VALUE = 127; + private static final int CACHE_MIN_VALUE = -128; + private static final SemType[] cache; + static { + cache = new SemType[CACHE_MAX_VALUE - CACHE_MIN_VALUE + 1]; + for (int i = CACHE_MIN_VALUE; i <= CACHE_MAX_VALUE; i++) { + cache[i - CACHE_MIN_VALUE] = createIntSingletonType(i); + } + } + } + + private static final class BooleanTypeCache { + + private static final SemType TRUE = createBooleanSingletonType(true); + private static final SemType FALSE = createBooleanSingletonType(false); + + private static SemType createBooleanSingletonType(boolean value) { + return basicSubType(BasicTypeCode.BT_BOOLEAN, BBooleanSubType.from(value)); + } + } + + private static final class StringTypeCache { + + private static final SemType charType; + static { + BStringSubType subTypeData = BStringSubType.createStringSubType(false, Builder.EMPTY_STRING_ARR, true, + Builder.EMPTY_STRING_ARR); + charType = basicSubType(BasicTypeCode.BT_STRING, subTypeData); + } + } + + private static final class BasicTypeCache { + + private static final SemType[] cache; + static { + cache = new SemType[CODE_UNDEF + 2]; + for (int i = 0; i < CODE_UNDEF + 1; i++) { + cache[i] = SemType.from(1 << i); + } + } + + private static boolean isCached(BasicTypeCode code) { + int i = code.code(); + return 0 < i && i <= CODE_UNDEF; + } + + private static boolean isCached(int code) { + return 0 < code && code <= CODE_UNDEF; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CacheableTypeDescriptor.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CacheableTypeDescriptor.java new file mode 100644 index 000000000000..bad069886c65 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CacheableTypeDescriptor.java @@ -0,0 +1,41 @@ +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.api.types.Type; + +import java.util.Optional; + +/** + * Represent TypeDescriptors whose type check results can be cached. + * + * @since 2201.11.0 + */ +public interface CacheableTypeDescriptor extends Type { + + /** + * Check whether the type check result of this type descriptor should be cached. Can be used to avoid caching in + * cases where either directly doing the type check is cheaper or we can't determine if two instances of a type + * descriptor are equal without doing a type check. + * + * @return true if the type check result should be cached, false otherwise + */ + boolean shouldCache(); + + /** + * Check whether the type check result of this type descriptor is cached for the given type descriptor. + * + * @param cx Context in which the type check is performed + * @param other Type descriptor to check the cached result for + * @return Optional containing the cached result if it is cached, empty otherwise + */ + Optional cachedTypeCheckResult(Context cx, CacheableTypeDescriptor other); + + /** + * Cache the type check result of this type descriptor for the given type descriptor. Note that implementations of + * this method could choose to not cache the result if {@link #shouldCache()} returns false. In such cases, even + * after calling this method, {@link #cachedTypeCheckResult(Context, CacheableTypeDescriptor)} could return empty. + * + * @param other Type descriptor to cache the result for + * @param result Result of the type check + */ + void cacheTypeCheckResult(CacheableTypeDescriptor other, boolean result); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplier.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplier.java new file mode 100644 index 000000000000..2ea49c4906f1 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplier.java @@ -0,0 +1,36 @@ +package io.ballerina.runtime.api.types.semtype; + +import java.util.function.Supplier; + +/** + * A thread-safe single lazy supplier that initialize the value only once. + * + * @param type of the value + * @since 2201.11.0 + */ +public class ConcurrentLazySupplier implements Supplier { + + private Supplier initializer; + private volatile E value = null; + + public ConcurrentLazySupplier(Supplier initializer) { + this.initializer = initializer; + } + + @Override + public E get() { + E result = value; + if (result == null) { + synchronized (this) { + result = value; + if (result == null) { + result = initializer.get(); + assert result != null; + value = result; + initializer = null; + } + } + } + return result; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplierWithCallback.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplierWithCallback.java new file mode 100644 index 000000000000..94646141bb5b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplierWithCallback.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * A thread-safe lazy supplier that initializes the value on the first call to {@link #get()} and calls the callback. + * + * @param the type of the value + * @since 2201.11.0 + */ +public class ConcurrentLazySupplierWithCallback implements Supplier { + + private volatile E value = null; + private Supplier initializer; + private Consumer callback; + + ConcurrentLazySupplierWithCallback(Supplier initializer, Consumer callback) { + this.initializer = initializer; + this.callback = callback; + } + + @Override + public E get() { + E result = value; + if (result == null) { + synchronized (this) { + result = value; + if (result == null) { + result = initializer.get(); + assert result != null; + value = result; + initializer = null; + callback.accept(result); + callback = null; + } + } + } + return result; + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/typeops/MappingRoOps.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java similarity index 51% rename from semtypes/src/main/java/io/ballerina/semtype/typeops/MappingRoOps.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java index 88b3ed961a9f..2faf2c559857 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/typeops/MappingRoOps.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -14,20 +14,22 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * */ -package io.ballerina.semtype.typeops; -import io.ballerina.semtype.SubtypeData; -import io.ballerina.semtype.TypeCheckContext; +package io.ballerina.runtime.api.types.semtype; /** - * Mapping readonly specific methods operate on SubtypeData. + * Represents the Conjunction in the BDD. * - * @since 2.0.0 + * @param atom Atom of this node + * @param next Next node in the conjunction, will be {@code null} if this is the + * last node + * @since 2201.11.0 */ -public class MappingRoOps extends MappingCommonOps { - @Override - public boolean isEmpty(TypeCheckContext tc, SubtypeData t) { - throw new AssertionError(); +public record Conjunction(Atom atom, Conjunction next) { + + public static Conjunction and(Atom atom, Conjunction next) { + return new Conjunction(atom, next); } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Context.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Context.java new file mode 100644 index 000000000000..4d6634f89f76 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Context.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.BddMemo; +import io.ballerina.runtime.internal.types.semtype.FunctionAtomicType; +import io.ballerina.runtime.internal.types.semtype.ListAtomicType; +import io.ballerina.runtime.internal.types.semtype.MappingAtomicType; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.WeakHashMap; + +/** + * Context in which type checking operations are performed. Note context is not + * thread safe, and multiple type check operations should not use the same + * context concurrently. Multiple contexts may share same environment without + * issue. + * + * @since 2201.11.0 + */ +public final class Context { + + // Contains all BddMemo entries with isEmpty == PROVISIONAL + private final List memoStack = new ArrayList<>(); + public final Env env; + public final Map listMemo = new WeakHashMap<>(); + public final Map mappingMemo = new WeakHashMap<>(); + public final Map functionMemo = new WeakHashMap<>(); + private static final int MAX_CACHE_SIZE = 100; + private final Map> typeCheckCacheMemo; + + private Context(Env env) { + this.env = env; + this.typeCheckCacheMemo = createTypeCheckCacheMemo(); + } + + private static Map> createTypeCheckCacheMemo() { + // This is fine since this map is not going to get leaked out of the context and + // context is unique to a thread. So there will be no concurrent modifications + return new LinkedHashMap<>(MAX_CACHE_SIZE, 1f, true) { + @Override + protected boolean removeEldestEntry( + Map.Entry> eldest) { + return size() > MAX_CACHE_SIZE; + } + }; + } + + public static Context from(Env env) { + return new Context(env); + } + + /** + * Memoization logic + * Castagna's paper does not deal with this fully. Although he calls it memoization, it is not, strictly speaking, + * just memoization, since it is not just an optimization, but required for correct handling of recursive types. + * The handling of recursive types depends on our types being defined inductively, rather than coinductively. + * This means that each shape that is a member of the set denoted by the type is finite. There is a tricky problem + * here with memoizing results that rely on assumptions that subsequently turn out to be false. Memoization/caching + * is discussed in section 7.1.2 of the Frisch thesis. This follows Frisch's approach of undoing memoizations that + * turn out to be wrong. (I did not succeed in fully understanding his approach, so I am not completely sure if we + * are doing the same.) + * @param memoTable corresponding memo table for the Bdd + * @param isEmptyPredicate predicate to be applied on the Bdd + * @param bdd Bdd to be checked + * @return result of applying predicate on the bdd + */ + public boolean memoSubtypeIsEmpty(Map memoTable, BddIsEmptyPredicate isEmptyPredicate, Bdd bdd) { + BddMemo m = memoTable.computeIfAbsent(bdd, ignored -> new BddMemo()); + return m.isEmpty().orElseGet(() -> memoSubTypeIsEmptyInner(isEmptyPredicate, bdd, m)); + } + + private boolean memoSubTypeIsEmptyInner(BddIsEmptyPredicate isEmptyPredicate, Bdd bdd, BddMemo m) { + // We are staring the type check with the assumption our type is empty (see: inductive type) + m.isEmpty = BddMemo.Status.PROVISIONAL; + int initStackDepth = memoStack.size(); + memoStack.add(m); + boolean isEmpty = isEmptyPredicate.apply(this, bdd); + boolean isLoop = m.isEmpty == BddMemo.Status.LOOP; + // if not empty our assumption is wrong so we need to reset the memoized values, otherwise we cleanup the stack + // at the end + if (!isEmpty || initStackDepth == 0) { + resetMemoizedValues(initStackDepth, isEmpty, isLoop, m); + } + return isEmpty; + } + + private void resetMemoizedValues(int initStackDepth, boolean isEmpty, boolean isLoop, BddMemo m) { + for (int i = initStackDepth + 1; i < memoStack.size(); i++) { + BddMemo.Status memoStatus = memoStack.get(i).isEmpty; + if (Objects.requireNonNull(memoStatus) == BddMemo.Status.PROVISIONAL || + memoStatus == BddMemo.Status.LOOP || memoStatus == BddMemo.Status.CYCLIC) { + // We started with the assumption our type is empty. Now we know for sure if we are empty or not + // if we are empty all of these who don't have anything except us should be empty as well. + // Otherwise, we don't know if they are empty or not + memoStack.get(i).isEmpty = isEmpty ? BddMemo.Status.TRUE : BddMemo.Status.NULL; + } + } + if (memoStack.size() > initStackDepth) { + memoStack.subList(initStackDepth, memoStack.size()).clear(); + } + if (isLoop && isEmpty) { + // The only way that we have found that this can be empty is by going through a loop. + // This means that the shapes in the type would all be infinite. + // But we define types inductively, which means we only consider finite shapes. + m.isEmpty = BddMemo.Status.CYCLIC; + } else { + m.isEmpty = isEmpty ? BddMemo.Status.TRUE : BddMemo.Status.FALSE; + } + } + + public ListAtomicType listAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return this.env.getRecListAtomType(recAtom); + } else { + return (ListAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public MappingAtomicType mappingAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return this.env.getRecMappingAtomType(recAtom); + } else { + return (MappingAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public FunctionAtomicType functionAtomicType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return this.env.getRecFunctionAtomType(recAtom); + } else { + return (FunctionAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public TypeCheckCache getTypeCheckCache(CacheableTypeDescriptor typeDescriptor) { + return typeCheckCacheMemo.computeIfAbsent(typeDescriptor, TypeCheckCache::new); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Core.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Core.java new file mode 100644 index 000000000000..3eefea1b58e4 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Core.java @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.AllOrNothing; +import io.ballerina.runtime.internal.types.semtype.BFutureSubType; +import io.ballerina.runtime.internal.types.semtype.BIntSubType; +import io.ballerina.runtime.internal.types.semtype.BObjectSubType; +import io.ballerina.runtime.internal.types.semtype.BStreamSubType; +import io.ballerina.runtime.internal.types.semtype.BTableSubType; +import io.ballerina.runtime.internal.types.semtype.BTypedescSubType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.DelegatedSubType; +import io.ballerina.runtime.internal.types.semtype.EnumerableSubtypeData; +import io.ballerina.runtime.internal.types.semtype.ListAtomicType; +import io.ballerina.runtime.internal.types.semtype.SubTypeData; +import io.ballerina.runtime.internal.types.semtype.SubtypePair; +import io.ballerina.runtime.internal.types.semtype.SubtypePairs; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.Optional; +import java.util.function.Function; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_CELL; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_DECIMAL; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_FLOAT; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_INT; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_LIST; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_STRING; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_FUTURE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_OBJECT; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_STREAM; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_TABLE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_TYPEDESC; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; +import static io.ballerina.runtime.api.types.semtype.Builder.getListType; +import static io.ballerina.runtime.api.types.semtype.Builder.getUndefType; +import static io.ballerina.runtime.internal.types.semtype.BListSubType.bddListMemberTypeInnerVal; +import static io.ballerina.runtime.internal.types.semtype.BMappingProj.mappingMemberTypeInner; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.cellAtomType; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.intersectCellAtomicType; + +/** + * Contain functions defined in `core.bal` file. + * + * @since 2201.11.0 + */ +public final class Core { + + private Core() { + } + + public static SemType diff(SemType t1, SemType t2) { + int all1 = t1.all(); + int all2 = t2.all(); + int some1 = t1.some(); + int some2 = t2.some(); + if (some1 == 0) { + if (some2 == 0) { + return Builder.getBasicTypeUnion(all1 & ~all2); + } else { + if (all1 == 0) { + return t1; + } + } + } else { + if (some2 == 0) { + if (all2 == VT_MASK) { + return Builder.getBasicTypeUnion(0); + } + } + } + int all = all1 & ~(all2 | some2); + int some = (all1 | some1) & ~all2; + some = some & ~all; + if (some == 0) { + return SemType.from(all); + } + SubType[] subtypes = Builder.initializeSubtypeArray(some); + int i = 0; + boolean filterNulls = false; + for (SubtypePair pair : new SubtypePairs(t1, t2, some)) { + SubType data1 = pair.subType1(); + SubType data2 = pair.subType2(); + int code = pair.typeCode(); + SubType data; + if (data1 == null) { + data = data2.complement(); + } else if (data2 == null) { + data = data1; + } else { + data = data1.diff(data2); + } + if (data.isAll()) { + all |= 1 << code; + some &= ~(1 << code); + filterNulls = true; + } else if (data.isNothing()) { + some &= ~(1 << code); + filterNulls = true; + } else { + subtypes[i] = data; + i++; + } + } + return SemType.from(all, some, filterNulls ? filterNulls(some, subtypes) : subtypes); + } + + // TODO: this should return SubTypeData not subtype + public static SubType getComplexSubtypeData(SemType t, BasicTypeCode code) { + assert (t.some() & (1 << code.code())) != 0; + SubType subType = t.subTypeByCode(code.code()); + if (subType instanceof DelegatedSubType wrapper) { + return wrapper.inner(); + } + return subType; + } + + // This computes the spec operation called "member type of K in T", + // for the case when T is a subtype of list, and K is either `int` or a singleton int. + // This is what Castagna calls projection. + // We will extend this to allow `key` to be a SemType, which will turn into an IntSubtype. + // If `t` is not a list, NEVER is returned + public static SemType listMemberTypeInnerVal(Context cx, SemType t, SemType k) { + if (t.some() == 0) { + return (t.all() & getListType().all()) != 0 ? Builder.getValType() : Builder.getNeverType(); + } else { + SubTypeData keyData = intSubtype(k); + if (isNothingSubtype(keyData)) { + return Builder.getNeverType(); + } + return bddListMemberTypeInnerVal(cx, (Bdd) getComplexSubtypeData(t, BT_LIST), keyData, + Builder.getValType()); + } + } + + public static SemType union(SemType t1, SemType t2) { + assert t1 != null && t2 != null; + int all1 = t1.all(); + int some1 = t1.some(); + int all2 = t2.all(); + int some2 = t2.some(); + if (some1 == 0) { + if (some2 == 0) { + return Builder.getBasicTypeUnion(all1 | all2); + } + } + + int all = all1 | all2; + int some = (some1 | some2) & ~all; + if (some == 0) { + return Builder.getBasicTypeUnion(all); + } + SubType[] subtypes = Builder.initializeSubtypeArray(some); + int i = 0; + boolean filterNulls = false; + for (SubtypePair pair : new SubtypePairs(t1, t2, some)) { + int code = pair.typeCode(); + SubType data1 = pair.subType1(); + SubType data2 = pair.subType2(); + SubType data; + if (data1 == null) { + data = data2; + } else if (data2 == null) { + data = data1; + } else { + data = data1.union(data2); + } + if (data.isAll()) { + filterNulls = true; + all |= 1 << code; + some &= ~(1 << code); + } else { + subtypes[i] = data; + i++; + } + } + if (some == 0) { + return SemType.from(all); + } + return SemType.from(all, some, filterNulls ? filterNulls(some, subtypes) : subtypes); + } + + private static SubType[] filterNulls(int some, SubType[] subtypes) { + int newSize = cardinality(some); + SubType[] filtered = new SubType[newSize]; + System.arraycopy(subtypes, 0, filtered, 0, newSize); + return filtered; + } + + public static SemType intersect(SemType t1, SemType t2) { + assert t1 != null && t2 != null; + int all1 = t1.all(); + int some1 = t1.some(); + int all2 = t2.all(); + int some2 = t2.some(); + if (some1 == 0) { + if (some2 == 0) { + return SemType.from(all1 & all2); + } else { + if (all1 == 0) { + return t1; + } + if (all1 == VT_MASK) { + return t2; + } + } + } else if (some2 == 0) { + if (all2 == 0) { + return t2; + } + if (all2 == VT_MASK) { + return t1; + } + } + + int all = all1 & all2; + int some = (some1 | all1) & (some2 | all2); + some = some & ~all; + if (some == 0) { + return SemType.from(all); + } + + SubType[] subtypes = Builder.initializeSubtypeArray(some); + int i = 0; + boolean filterNulls = false; + for (SubtypePair pair : new SubtypePairs(t1, t2, some)) { + int code = pair.typeCode(); + SubType data1 = pair.subType1(); + SubType data2 = pair.subType2(); + + SubType data; + if (data1 == null) { + data = data2; + } else if (data2 == null) { + data = data1; + } else { + data = data1.intersect(data2); + } + + if (!data.isNothing()) { + subtypes[i] = data; + i++; + } else { + some &= ~(1 << code); + filterNulls = true; + } + } + if (some == 0) { + return SemType.from(all); + } + return SemType.from(all, some, filterNulls ? filterNulls(some, subtypes) : subtypes); + } + + public static boolean isEmpty(Context cx, SemType t) { + if (t.some() == 0) { + return t.all() == 0; + } + if (t.all() != 0) { + return false; + } + for (SubType subType : t.subTypeData()) { + assert subType != null : "subtype array must not be sparse"; + if (!subType.isEmpty(cx)) { + return false; + } + } + return true; + } + + public static SemType complement(SemType t) { + return diff(Builder.getValType(), t); + } + + public static boolean isNever(SemType t) { + return t.all() == 0 && t.some() == 0; + } + + public static boolean isSubType(Context cx, SemType t1, SemType t2) { + return isEmpty(cx, diff(t1, t2)); + } + + public static boolean isSubtypeSimple(SemType t1, SemType t2) { + assert t1 != null && t2 != null; + int bits = t1.all() | t1.some(); + return (bits & ~t2.all()) == 0; + } + + public static boolean isNothingSubtype(SubTypeData data) { + return data == AllOrNothing.NOTHING; + } + + public static SubTypeData intSubtype(SemType t) { + return subTypeData(t, BT_INT); + } + + public static SubTypeData stringSubtype(SemType t) { + return subTypeData(t, BT_STRING); + } + + public static SubTypeData subTypeData(SemType s, BasicTypeCode code) { + if ((s.all() & (1 << code.code())) != 0) { + return AllOrNothing.ALL; + } + if (s.some() == 0) { + return AllOrNothing.NOTHING; + } + SubType subType = s.subTypeByCode(code.code()); + assert subType != null; + return subType.data(); + } + + public static boolean containsBasicType(SemType t1, SemType t2) { + int bits = t1.all() | t1.some(); + return (bits & t2.all()) != 0; + } + + public static boolean isSameType(Context cx, SemType t1, SemType t2) { + return isSubType(cx, t1, t2) && isSubType(cx, t2, t1); + } + + private static int cardinality(int bitset) { + return Integer.bitCount(bitset); + } + + public static SemType getCellContainingInnerVal(Env env, SemType t) { + CellAtomicType cat = + cellAtomicType(t).orElseThrow(() -> new IllegalArgumentException("t is not a cell semtype")); + return Builder.getCellContaining(env, diff(cat.ty(), getUndefType()), cat.mut()); + } + + public static SemType intersectCellMemberSemTypes(Env env, SemType t1, SemType t2) { + CellAtomicType c1 = + cellAtomicType(t1).orElseThrow(() -> new IllegalArgumentException("t1 is not a cell semtype")); + CellAtomicType c2 = + cellAtomicType(t2).orElseThrow(() -> new IllegalArgumentException("t2 is not a cell semtype")); + + CellAtomicType atomicType = intersectCellAtomicType(c1, c2); + return Builder.getCellContaining(env, atomicType.ty(), + getUndefType().equals(atomicType.ty()) ? CELL_MUT_NONE : atomicType.mut()); + } + + public static Optional cellAtomicType(SemType t) { + SemType cell = Builder.getCellType(); + if (t.some() == 0) { + return cell.equals(t) ? Optional.of(Builder.cellAtomicVal()) : Optional.empty(); + } else { + if (!isSubtypeSimple(t, cell)) { + return Optional.empty(); + } + return bddCellAtomicType((Bdd) getComplexSubtypeData(t, BT_CELL), Builder.cellAtomicVal()); + } + } + + private static Optional bddCellAtomicType(Bdd bdd, CellAtomicType top) { + if (bdd instanceof BddAllOrNothing allOrNothing) { + if (allOrNothing.isAll()) { + return Optional.of(top); + } + return Optional.empty(); + } + BddNode bddNode = (BddNode) bdd; + return bddNode.isSimple() ? Optional.of(cellAtomType(bddNode.atom())) : Optional.empty(); + } + + public static SemType cellInnerVal(SemType t) { + return diff(cellInner(t), getUndefType()); + } + + public static SemType cellInner(SemType t) { + CellAtomicType cat = + cellAtomicType(t).orElseThrow(() -> new IllegalArgumentException("t is not a cell semtype")); + return cat.ty(); + } + + public static SemType createBasicSemType(BasicTypeCode typeCode, Bdd bdd) { + if (bdd instanceof BddAllOrNothing) { + return bdd.isAll() ? Builder.from(typeCode) : Builder.getNeverType(); + } + SubType subType = switch (typeCode.code()) { + case CODE_OBJECT -> BObjectSubType.createDelegate(bdd); + case CODE_FUTURE -> BFutureSubType.createDelegate(bdd); + case CODE_TYPEDESC -> BTypedescSubType.createDelegate(bdd); + case CODE_TABLE -> BTableSubType.createDelegate(bdd); + case CODE_STREAM -> BStreamSubType.createDelegate(bdd); + default -> throw new IllegalArgumentException("Unexpected type code: " + typeCode); + }; + return SemType.from(0, 1 << typeCode.code(), new SubType[]{subType}); + } + + public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k) { + return diff(mappingMemberTypeInner(cx, t, k), Builder.getUndefType()); + } + + public static Optional listAtomicType(Context cx, SemType t) { + ListAtomicType listAtomicInner = Builder.getListAtomicInner(); + if (t.some() == 0) { + return Core.isSubtypeSimple(t, Builder.getListType()) ? Optional.ofNullable(listAtomicInner) : + Optional.empty(); + } + Env env = cx.env; + if (!isSubtypeSimple(t, Builder.getListType())) { + return Optional.empty(); + } + return bddListAtomicType(env, (Bdd) getComplexSubtypeData(t, BT_LIST), listAtomicInner); + } + + public static SemType floatToInt(SemType t) { + if (!containsBasicType(t, Builder.getFloatType())) { + return Builder.getNeverType(); + } + return convertEnumerableNumericType(t, BT_FLOAT, Builder.getIntType(), + (floatValue) -> ((Double) floatValue).longValue(), Builder::getIntConst); + } + + public static SemType floatToDecimal(SemType t) { + if (!containsBasicType(t, Builder.getFloatType())) { + return Builder.getNeverType(); + } + return convertEnumerableNumericType(t, BT_FLOAT, Builder.getDecimalType(), + (floatValue) -> BigDecimal.valueOf((Double) floatValue), Builder::getDecimalConst); + } + + public static SemType decimalToInt(SemType t) { + if (!containsBasicType(t, Builder.getDecimalType())) { + return Builder.getNeverType(); + } + return convertEnumerableNumericType(t, BT_DECIMAL, Builder.getIntType(), + (decimalVal) -> ((BigDecimal) decimalVal).longValue(), Builder::getIntConst); + } + + public static SemType decimalToFloat(SemType t) { + if (!containsBasicType(t, Builder.getDecimalType())) { + return Builder.getNeverType(); + } + return convertEnumerableNumericType(t, BT_DECIMAL, Builder.getFloatType(), + (decimalVal) -> ((BigDecimal) decimalVal).doubleValue(), Builder::getFloatConst); + } + + public static SemType intToFloat(SemType t) { + if (!containsBasicType(t, Builder.getIntType())) { + return Builder.getNeverType(); + } + SubTypeData subTypeData = subTypeData(t, BT_INT); + if (subTypeData == AllOrNothing.NOTHING) { + return Builder.getNeverType(); + } + if (subTypeData == AllOrNothing.ALL) { + return Builder.getFloatType(); + } + BIntSubType.IntSubTypeData intSubTypeData = (BIntSubType.IntSubTypeData) subTypeData; + return intSubTypeData.values().stream().map(Builder::getFloatConst).reduce(Builder.getNeverType(), Core::union); + } + + public static SemType intToDecimal(SemType t) { + if (!containsBasicType(t, Builder.getIntType())) { + return Builder.getNeverType(); + } + SubTypeData subTypeData = subTypeData(t, BT_INT); + if (subTypeData == AllOrNothing.NOTHING) { + return Builder.getNeverType(); + } + if (subTypeData == AllOrNothing.ALL) { + return Builder.getDecimalType(); + } + BIntSubType.IntSubTypeData intSubTypeData = (BIntSubType.IntSubTypeData) subTypeData; + return intSubTypeData.values().stream().map(BigDecimal::new).map(Builder::getDecimalConst) + .reduce(Builder.getNeverType(), Core::union); + } + + private static , T extends Comparable> SemType convertEnumerableNumericType( + SemType source, BasicTypeCode targetTypeCode, SemType topType, Function valueConverter, + Function semTypeCreator) { + SubTypeData subTypeData = subTypeData(source, targetTypeCode); + if (subTypeData == AllOrNothing.NOTHING) { + return Builder.getNeverType(); + } + if (subTypeData == AllOrNothing.ALL) { + return topType; + } + //noinspection unchecked - it's a enumerable type + EnumerableSubtypeData enumerableSubtypeData = (EnumerableSubtypeData) subTypeData; + SemType posType = + Arrays.stream(enumerableSubtypeData.values()).map(valueConverter).distinct().map(semTypeCreator) + .reduce(Builder.getNeverType(), Core::union); + if (enumerableSubtypeData.allowed()) { + return posType; + } + return diff(topType, posType); + } + + private static Optional bddListAtomicType(Env env, Bdd bdd, ListAtomicType top) { + if (!(bdd instanceof BddNode bddNode)) { + if (bdd.isAll()) { + return Optional.ofNullable(top); + } else { + return Optional.empty(); + } + } + return bddNode.isSimple() ? Optional.of(env.listAtomType(bddNode.atom())) : Optional.empty(); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Definition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Definition.java new file mode 100644 index 000000000000..0b0017f05841 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Definition.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; + +/** + * Represent a type definition which will act as a layer of indirection between {@code Env} and the type descriptor. + * + * @since 2201.11.0 + */ +public abstract class Definition { + + private DefinitionContainer container; + + /** + * Get the {@code SemType} of this definition in the given environment. + * + * @param env type environment + */ + public abstract SemType getSemType(Env env); + + /** + * Register the container as the holder of this definition. Used to maintain concurrency invariants. + * + * @param container holder of the definition + * @see io.ballerina.runtime.internal.types.semtype.DefinitionContainer + */ + public void registerContainer(DefinitionContainer container) { + this.container = container; + } + + protected void notifyContainer() { + if (container != null) { + container.definitionUpdated(); + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Env.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Env.java new file mode 100644 index 000000000000..10b00639a035 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Env.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.FunctionAtomicType; +import io.ballerina.runtime.internal.types.semtype.ListAtomicType; +import io.ballerina.runtime.internal.types.semtype.MappingAtomicType; + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Supplier; + +/** + * Represent the environment in which {@code SemTypes} are defined in. Type + * checking types defined in different + * environments with each other in undefined. This is safe to be shared between + * multiple threads. + * + * @since 2201.11.0 + */ +public final class Env { + // Currently there is no reason to worry about above restrictions since Env is a singleton, but strictly speaking + // there is not technical restriction preventing multiple instances of Env. + + private static final Env INSTANCE = new Env(); + + // Each atom is created once but will be accessed multiple times during type checking. Also in perfect world we + // will create atoms at the beginning of the execution and will eventually reach + // a steady state. + private final ReadWriteLock atomLock = new ReentrantReadWriteLock(); + private final Map> atomTable; + + private final ReadWriteLock recListLock = new ReentrantReadWriteLock(); + final List recListAtoms; + + private final ReadWriteLock recMapLock = new ReentrantReadWriteLock(); + final List recMappingAtoms; + + private final ReadWriteLock recFunctionLock = new ReentrantReadWriteLock(); + private final List recFunctionAtoms; + + private final Map cellTypeCache = new ConcurrentHashMap<>(); + + private final AtomicInteger distinctAtomCount = new AtomicInteger(0); + + private Env() { + this.atomTable = new WeakHashMap<>(); + this.recListAtoms = new ArrayList<>(); + this.recMappingAtoms = new ArrayList<>(); + this.recFunctionAtoms = new ArrayList<>(); + + PredefinedTypeEnv.getInstance().initializeEnv(this); + } + + public static Env getInstance() { + return INSTANCE; + } + + public TypeAtom cellAtom(CellAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + private TypeAtom typeAtom(AtomicType atomicType) { + atomLock.readLock().lock(); + try { + Reference ref = this.atomTable.get(atomicType); + if (ref != null) { + TypeAtom atom = ref.get(); + if (atom != null) { + return atom; + } + } + } finally { + atomLock.readLock().unlock(); + } + atomLock.writeLock().lock(); + try { + TypeAtom result = TypeAtom.createTypeAtom(this.atomTable.size(), atomicType); + this.atomTable.put(result.atomicType(), new WeakReference<>(result)); + return result; + } finally { + atomLock.writeLock().unlock(); + } + } + + // Ideally this cache should be in the builder as well. But technically we can't cache cells across environments. + // In practice this shouldn't be an issue since there should be only one environment, but I am doing this here + // just in case. + SemType getCachedCellType(SemType ty, CellAtomicType.CellMutability mut, Supplier semTypeCreator) { + if (ty.some() != 0) { + return semTypeCreator.get(); + } + return this.cellTypeCache.computeIfAbsent(new CellSemTypeCacheKey(ty, mut), k -> semTypeCreator.get()); + } + + public RecAtom recListAtom() { + recListLock.writeLock().lock(); + try { + int result = this.recListAtoms.size(); + // represents adding () in nballerina + this.recListAtoms.add(null); + return RecAtom.createRecAtom(result); + } finally { + recListLock.writeLock().unlock(); + } + } + + public void setRecListAtomType(RecAtom rec, ListAtomicType atomicType) { + // NOTE: this is fine since we are not actually changing the recList + recListLock.readLock().lock(); + try { + this.recListAtoms.set(rec.index(), atomicType); + } finally { + recListLock.readLock().unlock(); + } + + } + + public Atom listAtom(ListAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + public ListAtomicType getRecListAtomType(RecAtom ra) { + recListLock.readLock().lock(); + try { + return this.recListAtoms.get(ra.index()); + } finally { + recListLock.readLock().unlock(); + } + } + + public ListAtomicType listAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return getRecListAtomType(recAtom); + } else { + return (ListAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public RecAtom recMappingAtom() { + recMapLock.writeLock().lock(); + try { + int result = this.recMappingAtoms.size(); + // represents adding () in nballerina + this.recMappingAtoms.add(null); + return RecAtom.createRecAtom(result); + } finally { + recMapLock.writeLock().unlock(); + } + } + + public void setRecMappingAtomType(RecAtom rec, MappingAtomicType atomicType) { + recMapLock.readLock().lock(); + try { + this.recMappingAtoms.set(rec.index(), atomicType); + } finally { + recMapLock.readLock().unlock(); + } + } + + public TypeAtom mappingAtom(MappingAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + public MappingAtomicType getRecMappingAtomType(RecAtom recAtom) { + recMapLock.readLock().lock(); + try { + return this.recMappingAtoms.get(recAtom.index()); + } finally { + recMapLock.readLock().unlock(); + } + } + + public RecAtom recFunctionAtom() { + recFunctionLock.writeLock().lock(); + try { + int result = this.recFunctionAtoms.size(); + // represents adding () in nballerina + this.recFunctionAtoms.add(null); + return RecAtom.createRecAtom(result); + } finally { + recFunctionLock.writeLock().unlock(); + } + } + + public void setRecFunctionAtomType(RecAtom rec, FunctionAtomicType atomicType) { + recFunctionLock.readLock().lock(); + try { + this.recFunctionAtoms.set(rec.index(), atomicType); + } finally { + recFunctionLock.readLock().unlock(); + } + } + + public FunctionAtomicType getRecFunctionAtomType(RecAtom recAtom) { + recFunctionLock.readLock().lock(); + try { + return this.recFunctionAtoms.get(recAtom.index()); + } finally { + recFunctionLock.readLock().unlock(); + } + } + + public Atom functionAtom(FunctionAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + private record CellSemTypeCacheKey(SemType ty, CellAtomicType.CellMutability mut) { + + } + + public int distinctAtomCountGetAndIncrement() { + return this.distinctAtomCount.getAndIncrement(); + } + + // This is for debug purposes + public Optional atomicTypeByIndex(int index) { + atomLock.readLock().lock(); + try { + for (Map.Entry> entry : this.atomTable.entrySet()) { + TypeAtom typeAtom = entry.getValue().get(); + if (typeAtom == null) { + continue; + } + if (typeAtom.index() == index) { + return Optional.of(entry.getKey()); + } + } + return Optional.empty(); + } finally { + atomLock.readLock().unlock(); + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FieldPair.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FieldPair.java new file mode 100644 index 000000000000..29ce90a72be5 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FieldPair.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Represent the matching fields types of two mapping atomic types. + * + * @param name name of the field + * @param type1 type of the field in the first mapping + * @param type2 type of the field in teh second mapping + * @param index1 corresponding index of the field in the first mapping. If matching field is rest value is {@code null} + * @param index2 corresponding index of the field in the second mapping. If matching field is rest value is + * {@code null} + * @since 2201.11.0 + */ +public record FieldPair(String name, SemType type1, SemType type2, Integer index1, Integer index2) { + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FieldPairs.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FieldPairs.java new file mode 100644 index 000000000000..82618c4c93f6 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FieldPairs.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.Common; +import io.ballerina.runtime.internal.types.semtype.MappingAtomicType; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; + +/** + * {@code Iterable} over the matching fields of two mapping atomic types. + * + * @since 2201.11.0 + */ +public class FieldPairs implements Iterable { + + MappingAtomicType m1; + MappingAtomicType m2; + private final MappingPairIterator itr; + + public FieldPairs(MappingAtomicType m1, MappingAtomicType m2) { + this.m1 = m1; + this.m2 = m2; + itr = new MappingPairIterator(m1, m2); + } + + @Override + public Iterator iterator() { + return itr; + } + + private static final class MappingPairIterator implements Iterator { + + private final String[] names1; + private final String[] names2; + private final SemType[] types1; + private final SemType[] types2; + private final int len1; + private final int len2; + private int i1 = 0; + private int i2 = 0; + private final SemType rest1; + private final SemType rest2; + + private boolean doneIteration = false; + private boolean shouldCalculate = true; + private FieldPair cache = null; + + private MappingPairIterator(MappingAtomicType m1, MappingAtomicType m2) { + this.names1 = m1.names(); + this.len1 = this.names1.length; + this.types1 = m1.types(); + this.rest1 = m1.rest(); + this.names2 = m2.names(); + this.len2 = this.names2.length; + this.types2 = m2.types(); + this.rest2 = m2.rest(); + } + + @Override + public boolean hasNext() { + if (this.doneIteration) { + return false; + } + if (this.shouldCalculate) { + FieldPair cache = internalNext(); + if (cache == null) { + this.doneIteration = true; + } + this.cache = cache; + this.shouldCalculate = false; + } + return !this.doneIteration; + } + + @Override + public FieldPair next() { + if (this.doneIteration) { + throw new NoSuchElementException("Exhausted iterator"); + } + + if (this.shouldCalculate) { + FieldPair cache = internalNext(); + if (cache == null) { + // this.doneIteration = true; + throw new IllegalStateException(); + } + this.cache = cache; + } + this.shouldCalculate = true; + return this.cache; + } + + /* + * This method corresponds to `next` method of MappingPairing. + */ + private FieldPair internalNext() { + FieldPair p; + if (this.i1 >= this.len1) { + if (this.i2 >= this.len2) { + return null; + } + p = new FieldPair(curName2(), this.rest1, curType2(), null, this.i2); + this.i2 += 1; + } else if (this.i2 >= this.len2) { + p = new FieldPair(curName1(), curType1(), this.rest2, this.i1, null); + this.i1 += 1; + } else { + String name1 = curName1(); + String name2 = curName2(); + if (Common.codePointCompare(name1, name2)) { + p = new FieldPair(name1, curType1(), this.rest2, this.i1, null); + this.i1 += 1; + } else if (Common.codePointCompare(name2, name1)) { + p = new FieldPair(name2, this.rest1, curType2(), null, this.i2); + this.i2 += 1; + } else { + p = new FieldPair(name1, curType1(), curType2(), this.i1, this.i2); + this.i1 += 1; + this.i2 += 1; + } + } + return p; + } + + private SemType curType1() { + return this.types1[this.i1]; + } + + private String curName1() { + return this.names1[this.i1]; + } + + private SemType curType2() { + return this.types2[this.i2]; + } + + private String curName2() { + return this.names2[this.i2]; + } + + public void reset() { + this.i1 = 0; + this.i2 = 0; + } + + public Optional index1(String name) { + int i1Prev = this.i1 - 1; + return i1Prev >= 0 && Objects.equals(this.names1[i1Prev], name) ? Optional.of(i1Prev) : Optional.empty(); + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/RecAtom.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java similarity index 54% rename from semtypes/src/main/java/io/ballerina/semtype/RecAtom.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java index eaea5a9a50e5..e1d579e9b266 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/RecAtom.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -15,21 +15,23 @@ * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.BListProj; /** - * Represent a recursive type atom. + * Utility class for list type projection. * - * @since 2.0.0 + * @since 2201.11.0 */ -public class RecAtom implements Atom { - int index; +public final class ListProj { - public RecAtom(int index) { - this.index = index; + private ListProj() { } - public static RecAtom createRecAtom(int index) { - return new RecAtom(index); + public static SemType listProjInnerVal(Context cx, SemType t, SemType k) { + return BListProj.listProjInnerVal(cx, t, k); } + } diff --git a/semtypes/src/main/java/io/ballerina/semtype/typeops/MappingRWOps.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java similarity index 51% rename from semtypes/src/main/java/io/ballerina/semtype/typeops/MappingRWOps.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java index 0a6a69f9c51f..cd3c113054d3 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/typeops/MappingRWOps.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -14,20 +14,25 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * */ -package io.ballerina.semtype.typeops; -import io.ballerina.semtype.SubtypeData; -import io.ballerina.semtype.TypeCheckContext; +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.BMappingProj; /** - * Mapping read/write specific methods operate on SubtypeData. + * Utility class for mapping type projection. * - * @since 2.0.0 + * @since 2201.11.0 */ -public class MappingRWOps extends MappingCommonOps { - @Override - public boolean isEmpty(TypeCheckContext tc, SubtypeData t) { - throw new AssertionError(); +public final class MappingProj { + + private MappingProj() { } + + public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k) { + return BMappingProj.mappingMemberTypeInnerVal(cx, t, k); + } + } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Pair.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Pair.java new file mode 100644 index 000000000000..f8d4922c7f56 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Pair.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Data structure used to pass around pairs of values. + * + * @param type of first value + * @param type of second value + * @param first first values + * @param second second value + * @since 2201.11.0 + */ +public record Pair(E1 first, E2 second) { + + public static Pair from(E1 first, E2 second) { + return new Pair<>(first, second); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/PredefinedTypeEnv.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/PredefinedTypeEnv.java new file mode 100644 index 000000000000..1545a36e572e --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/PredefinedTypeEnv.java @@ -0,0 +1,609 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.BCellSubType; +import io.ballerina.runtime.internal.types.semtype.BListSubType; +import io.ballerina.runtime.internal.types.semtype.BMappingSubType; +import io.ballerina.runtime.internal.types.semtype.BObjectSubType; +import io.ballerina.runtime.internal.types.semtype.BTableSubType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; +import io.ballerina.runtime.internal.types.semtype.ListAtomicType; +import io.ballerina.runtime.internal.types.semtype.MappingAtomicType; +import io.ballerina.runtime.internal.types.semtype.XmlUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_CELL; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_LIST; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_MAPPING; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_OBJECT; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_TABLE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_XML; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_INHERENTLY_IMMUTABLE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; +import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; +import static io.ballerina.runtime.api.types.semtype.Builder.basicSubType; +import static io.ballerina.runtime.api.types.semtype.Builder.from; +import static io.ballerina.runtime.api.types.semtype.Builder.getBasicTypeUnion; +import static io.ballerina.runtime.api.types.semtype.Builder.getStringConst; +import static io.ballerina.runtime.api.types.semtype.Core.union; +import static io.ballerina.runtime.api.types.semtype.TypeAtom.createTypeAtom; + +final class PredefinedTypeEnv { + + private static PredefinedTypeEnv instance; + private final AtomicBoolean initialized = new AtomicBoolean(false); + private static final int BDD_REC_ATOM_OBJECT_READONLY = 1; + private static final RecAtom OBJECT_RO_REC_ATOM = RecAtom.createRecAtom(BDD_REC_ATOM_OBJECT_READONLY); + private static final BddNode MAPPING_SUBTYPE_OBJECT_RO = bddAtom(OBJECT_RO_REC_ATOM); + + private final List> initializedCellAtoms = new ArrayList<>(); + private final List> initializedListAtoms = new ArrayList<>(); + private final List> initializedMappingAtoms = new ArrayList<>(); + private final List initializedRecListAtoms = new ArrayList<>(); + private final List initializedRecMappingAtoms = new ArrayList<>(); + private final AtomicInteger nextAtomIndex = new AtomicInteger(0); + // This is to avoid passing down env argument when doing cell type operations. + // Please refer to the cellSubtypeDataEnsureProper() in cell.bal + private final Supplier cellAtomicVal = new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from(getBasicTypeUnion(VT_MASK), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); + private final Supplier atomCellVal = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicVal, this::cellAtomIndex); + private final Supplier cellSemTypeVal = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellVal())))); + private final Supplier cellAtomicNever = new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from(SemType.from(0), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); + private final Supplier atomCellNever = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicNever, this::cellAtomIndex); + // Represent the typeAtom required to construct equivalent subtypes of map and (any|error)[]. + + private final ConcurrentLazySupplier inner = + new ConcurrentLazySupplier<>(() -> SemType.from(VT_MASK | from(BasicTypeCode.BT_UNDEF).all())); + private final Supplier cellAtomicInner = new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from(inner.get(), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); + private final Supplier atomCellInner = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicInner, this::cellAtomIndex); + private final Supplier cellSemTypeInner = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInner())))); + // TypeAtoms related to (map)[]. This is to avoid passing down env argument when doing + // tableSubtypeComplement operation. + private final Supplier cellAtomicInnerMapping = new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from(union(Builder.getMappingType(), Builder.getUndefType()), + CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); + private final Supplier atomCellInnerMapping = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicInnerMapping, this::cellAtomIndex); + private final Supplier listAtomicMapping = new ConcurrentLazySupplierWithCallback<>( + () -> new ListAtomicType(FixedLengthArray.empty(), basicSubType( + BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerMapping())))), + this::addInitializedListAtom + ); + private final Supplier atomListMapping = + createTypeAtomSupplierFromCellAtomicSupplier(listAtomicMapping, this::listAtomIndex); + // TypeAtoms related to readonly type. This is to avoid requiring context when referring to readonly type. + // CELL_ATOMIC_INNER_MAPPING_RO & LIST_ATOMIC_MAPPING_RO are typeAtoms required to construct + // readonly & (map)[] which is then used for readonly table type when constructing VAL_READONLY + private final Supplier cellAtomicInnerMappingRO = new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from(union(Builder.mappingRO(), Builder.getUndefType()), + CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); + private final Supplier atomCellInnerMappingRO = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicInnerMappingRO, this::cellAtomIndex); + private final Supplier listAtomicMappingRO = new ConcurrentLazySupplierWithCallback<>( + () -> new ListAtomicType(FixedLengthArray.empty(), basicSubType( + BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerMappingRO())))), + this::addInitializedListAtom + ); + + private final Supplier listSubtypeMapping = new ConcurrentLazySupplier<>( + () -> bddAtom(atomListMapping.get())); + private final Supplier mappingArray = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_LIST, BListSubType.createDelegate(listSubtypeMapping.get()))); + private final Supplier cellAtomicMappingArray = new ConcurrentLazySupplierWithCallback<>(() -> + CellAtomicType.from(mappingArray.get(), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom); + private final Supplier atomCellMappingArray = new ConcurrentLazySupplier<>(() -> { + CellAtomicType cellAtom = cellAtomicMappingArray.get(); + return createTypeAtom(cellAtomIndex(cellAtom), cellAtom); + }); + private final Supplier cellSemTypeListSubtypeMapping = new ConcurrentLazySupplier<>(() -> + basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellMappingArray.get())))); + private final Supplier listAtomicThreeElement = new ConcurrentLazySupplierWithCallback<>( + () -> new ListAtomicType( + FixedLengthArray.from(new SemType[]{cellSemTypeListSubtypeMapping.get(), cellSemTypeVal.get()}, 3), + cellSemTypeVal.get()), + this::addInitializedListAtom + ); + private final Supplier atomListThreeElement = new ConcurrentLazySupplier<>(() -> { + ListAtomicType listAtomic = listAtomicThreeElement.get(); + return createTypeAtom(listAtomIndex(listAtomic), listAtomic); + }); + private final Supplier atomListMappingRO = + createTypeAtomSupplierFromCellAtomicSupplier(listAtomicMappingRO, this::listAtomIndex); + + private final Supplier cellAtomicUndef = new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from(Builder.getUndefType(), CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom + ); + private final Supplier atomCellUndef = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicUndef, this::cellAtomIndex); + private final Supplier cellSemTypeUndef = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellUndef.get())))); + + private final Supplier listSubtypeMappingRO = new ConcurrentLazySupplier<>(() -> bddAtom( + atomListMappingRO.get())); + private final Supplier mappingArrayRO = new ConcurrentLazySupplier<>(() -> basicSubType( + BT_LIST, BListSubType.createDelegate(listSubtypeMappingRO.get()))); + private final Supplier cellAtomicMappingArrayRO = new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from(mappingArrayRO.get(), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); + private final Supplier atomCellMappingArrayRO = new ConcurrentLazySupplier<>(() -> { + CellAtomicType cellAtom = cellAtomicMappingArrayRO.get(); + return createTypeAtom(cellAtomIndex(cellAtom), cellAtom); + }); + private final Supplier cellSemTypeListSubtypeMappingRO = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellMappingArrayRO.get())))); + private final Supplier listAtomicThreeElementRO = new ConcurrentLazySupplierWithCallback<>( + () -> new ListAtomicType( + FixedLengthArray.from(new SemType[]{cellSemTypeListSubtypeMappingRO.get(), cellSemTypeVal.get()}, + 3), + cellSemTypeUndef.get()), + this::addInitializedListAtom + ); + private final Supplier atomListThreeElementRO = new ConcurrentLazySupplier<>(() -> { + ListAtomicType listAtomic = listAtomicThreeElementRO.get(); + return createTypeAtom(listAtomIndex(listAtomic), listAtomic); + }); + + private final Supplier readonlyType = new ConcurrentLazySupplier<>(() -> unionOf( + SemType.from(VT_INHERENTLY_IMMUTABLE), + basicSubType(BT_LIST, BListSubType.createDelegate(bddSubtypeRo())), + basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddSubtypeRo())), + basicSubType(BT_OBJECT, BObjectSubType.createDelegate(MAPPING_SUBTYPE_OBJECT_RO)), + basicSubType(BT_TABLE, BTableSubType.createDelegate(bddAtom(atomListThreeElementRO.get()))), + basicSubType(BT_XML, XmlUtils.XML_SUBTYPE_RO) + )); + + private final ConcurrentLazySupplier innerReadOnly = + new ConcurrentLazySupplier<>(() -> union(readonlyType.get(), inner.get())); + private final Supplier cellAtomicInnerRO = new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from(innerReadOnly.get(), CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom + ); + private final Supplier atomCellInnerRO = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicInnerRO, this::cellAtomIndex); + private final Supplier cellSemTypeInnerRO = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerRO.get())))); + private final Supplier listAtomicRO = new ConcurrentLazySupplierWithCallback<>( + () -> new ListAtomicType(FixedLengthArray.empty(), cellSemTypeInnerRO.get()), + this.initializedRecListAtoms::add + ); + private final Supplier mappingAtomicRO = new ConcurrentLazySupplierWithCallback<>( + () -> new MappingAtomicType(new String[]{}, new SemType[]{}, cellSemTypeInnerRO.get()), + initializedRecMappingAtoms::add + ); + // TypeAtoms related to [any|error, any|error]. This is to avoid passing down env argument when doing + // streamSubtypeComplement operation. + private final Supplier cellAtomicObjectMemberKind = + new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from( + union(getStringConst("field"), getStringConst("method")), + CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom); + private final Supplier atomCellObjectMemberKind = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMemberKind, this::cellAtomIndex); + private final Supplier cellSemTypeObjectMemberKind = new ConcurrentLazySupplier<>( + () -> Builder.basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberKind())))); + private final Supplier cellAtomicObjectMemberVisibility = + new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from( + union(getStringConst("public"), getStringConst("private")), + CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom); + private final Supplier atomCellObjectMemberVisibility = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMemberVisibility, this::cellAtomIndex); + private final Supplier cellSemTypeObjectMemberVisibility = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberVisibility())))); + private final Supplier mappingAtomicObjectMember = new ConcurrentLazySupplierWithCallback<>( + () -> new MappingAtomicType( + new String[]{"kind", "value", "visibility"}, + new SemType[]{cellSemTypeObjectMemberKind.get(), cellSemTypeVal.get(), + cellSemTypeObjectMemberVisibility.get()}, + cellSemTypeUndef.get()), + this::addInitializedMapAtom + ); + private final Supplier atomMappingObjectMember = + createTypeAtomSupplierFromCellAtomicSupplier(mappingAtomicObjectMember, this::mappingAtomIndex); + private final Supplier mappingSemTypeObjectMember = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddAtom(atomMappingObjectMember())))); + private final Supplier cellAtomicObjectMember = + new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from( + mappingSemTypeObjectMember.get(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED), + this::addInitializedCellAtom); + private final Supplier atomCellObjectMember = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMember, this::cellAtomIndex); + private final Supplier cellSemTypeObjectMember = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMember())))); + private final Supplier mappingAtomicObject = new ConcurrentLazySupplierWithCallback<>( + () -> new MappingAtomicType( + new String[]{"$qualifiers"}, new SemType[]{cellSemTypeVal.get()}, + cellSemTypeObjectMember.get() + ), + this::addInitializedMapAtom + ); + private final Supplier atomMappingObject = + createTypeAtomSupplierFromCellAtomicSupplier(mappingAtomicObject, this::mappingAtomIndex); + private final Supplier cellAtomicValRO = + new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from( + readonlyType.get(), CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom); + private final Supplier atomCellValRO = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicValRO, this::cellAtomIndex); + private final Supplier cellSemTypeValRo = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellValRO())))); + private final Supplier mappingAtomicObjectMemberRO = new ConcurrentLazySupplierWithCallback<>( + () -> new MappingAtomicType( + new String[]{"kind", "value", "visibility"}, + new SemType[]{cellSemTypeObjectMemberKind.get(), cellSemTypeValRo.get(), + cellSemTypeObjectMemberVisibility.get()}, + cellSemTypeUndef.get()), + this::addInitializedMapAtom + ); + private final Supplier atomMappingObjectMemberRO = + createTypeAtomSupplierFromCellAtomicSupplier(mappingAtomicObjectMemberRO, this::mappingAtomIndex); + private final Supplier mappingSemTypeObjectMemberRO = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddAtom(atomMappingObjectMemberRO())))); + private final Supplier cellAtomicObjectMemberRO = + new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from( + mappingSemTypeObjectMemberRO.get(), CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom); + private final Supplier atomCellObjectMemberRO = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMemberRO, this::cellAtomIndex); + private final Supplier cellSemTypeObjectMemberRO = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberRO())))); + private final Supplier mappingAtomicObjectRO = new ConcurrentLazySupplierWithCallback<>( + () -> new MappingAtomicType( + new String[]{"$qualifiers"}, new SemType[]{cellSemTypeVal.get()}, + cellSemTypeObjectMemberRO.get() + ), + initializedRecMappingAtoms::add + ); + + private final Supplier listAtomicTwoElement = new ConcurrentLazySupplierWithCallback<>( + () -> new ListAtomicType( + FixedLengthArray.from(new SemType[]{cellSemTypeVal.get()}, 2), + cellSemTypeUndef.get()), + this::addInitializedListAtom + ); + private final Supplier atomListTwoElement = new ConcurrentLazySupplier<>(() -> { + ListAtomicType listAtomic = listAtomicTwoElement.get(); + return createTypeAtom(listAtomIndex(listAtomic), listAtomic); + }); + + private PredefinedTypeEnv() { + } + + private static SemType unionOf(SemType... types) { + SemType accum = types[0]; + for (int i = 1; i < types.length; i++) { + accum = union(accum, types[i]); + } + return accum; + } + + private static BddNode bddSubtypeRo() { + return bddAtom(RecAtom.createRecAtom(0)); + } + + + public static synchronized PredefinedTypeEnv getInstance() { + if (instance == null) { + instance = new PredefinedTypeEnv(); + instance.initialize(); + } + return instance; + } + + private static Supplier createTypeAtomSupplierFromCellAtomicSupplier( + Supplier atomicTypeSupplier, IndexSupplier indexSupplier) { + return new ConcurrentLazySupplier<>(() -> { + E atomicType = atomicTypeSupplier.get(); + int index = indexSupplier.get(atomicType); + return createTypeAtom(index, atomicType); + }); + } + + private void initialize() { + // Initialize RecAtoms + mappingAtomicRO(); + listAtomicRO(); + mappingAtomicObjectRO(); + + // initialize atomic types + cellAtomicVal(); + cellAtomicNever(); + cellAtomicInner(); + cellAtomicInnerMapping(); + listAtomicMapping(); + cellAtomicInner(); + listAtomicMappingRO(); + cellAtomicInnerRO(); + initialized.set(true); + } + + private void addInitializedCellAtom(CellAtomicType atom) { + addInitializedAtom(initializedCellAtoms, atom); + } + + private void addInitializedListAtom(ListAtomicType atom) { + addInitializedAtom(initializedListAtoms, atom); + } + + private void addInitializedMapAtom(MappingAtomicType atom) { + addInitializedAtom(initializedMappingAtoms, atom); + } + + private void addInitializedAtom(Collection> atoms, E atom) { + atoms.add(new InitializedTypeAtom<>(atom, nextAtomIndex.getAndIncrement())); + } + + private int cellAtomIndex(CellAtomicType atom) { + return atomIndex(initializedCellAtoms, atom); + } + + private int listAtomIndex(ListAtomicType atom) { + return atomIndex(initializedListAtoms, atom); + } + + private int mappingAtomIndex(MappingAtomicType atom) { + return atomIndex(initializedMappingAtoms, atom); + } + + private int atomIndex(List> initializedAtoms, E atom) { + for (InitializedTypeAtom initializedListAtom : initializedAtoms) { + if (initializedListAtom.atomicType() == atom) { + return initializedListAtom.index(); + } + } + throw new IndexOutOfBoundsException(); + } + + CellAtomicType cellAtomicVal() { + return cellAtomicVal.get(); + } + + TypeAtom atomCellVal() { + return atomCellVal.get(); + } + + CellAtomicType cellAtomicNever() { + return cellAtomicNever.get(); + } + + TypeAtom atomCellNever() { + return atomCellNever.get(); + } + + CellAtomicType cellAtomicInner() { + return cellAtomicInner.get(); + } + + TypeAtom atomCellInner() { + return atomCellInner.get(); + } + + CellAtomicType cellAtomicInnerMapping() { + return cellAtomicInnerMapping.get(); + } + + TypeAtom atomCellInnerMapping() { + return atomCellInnerMapping.get(); + } + + CellAtomicType cellAtomicInnerMappingRO() { + return cellAtomicInnerMappingRO.get(); + } + + TypeAtom atomCellInnerMappingRO() { + return atomCellInnerMappingRO.get(); + } + + ListAtomicType listAtomicMapping() { + return listAtomicMapping.get(); + } + + TypeAtom atomListMapping() { + return atomListMapping.get(); + } + + ListAtomicType listAtomicMappingRO() { + return listAtomicMappingRO.get(); + } + + TypeAtom atomListMappingRO() { + return atomListMappingRO.get(); + } + + CellAtomicType cellAtomicInnerRO() { + return cellAtomicInnerRO.get(); + } + + TypeAtom atomCellInnerRO() { + return atomCellInnerRO.get(); + } + + CellAtomicType cellAtomicUndef() { + return cellAtomicUndef.get(); + } + + TypeAtom atomCellUndef() { + return atomCellUndef.get(); + } + + CellAtomicType cellAtomicValRO() { + return cellAtomicValRO.get(); + } + + TypeAtom atomCellValRO() { + return atomCellValRO.get(); + } + + MappingAtomicType mappingAtomicObjectMemberRO() { + return mappingAtomicObjectMemberRO.get(); + } + + TypeAtom atomMappingObjectMemberRO() { + return atomMappingObjectMemberRO.get(); + } + + CellAtomicType cellAtomicObjectMemberRO() { + return cellAtomicObjectMemberRO.get(); + } + + TypeAtom atomCellObjectMemberRO() { + return atomCellObjectMemberRO.get(); + } + + CellAtomicType cellAtomicObjectMemberKind() { + return cellAtomicObjectMemberKind.get(); + } + + TypeAtom atomCellObjectMemberKind() { + return atomCellObjectMemberKind.get(); + } + + CellAtomicType cellAtomicObjectMemberVisibility() { + return cellAtomicObjectMemberVisibility.get(); + } + + TypeAtom atomCellObjectMemberVisibility() { + return atomCellObjectMemberVisibility.get(); + } + + MappingAtomicType mappingAtomicObjectMember() { + return mappingAtomicObjectMember.get(); + } + + TypeAtom atomMappingObjectMember() { + return atomMappingObjectMember.get(); + } + + CellAtomicType cellAtomicObjectMember() { + return cellAtomicObjectMember.get(); + } + + TypeAtom atomCellObjectMember() { + return atomCellObjectMember.get(); + } + + MappingAtomicType mappingAtomicObject() { + return mappingAtomicObject.get(); + } + + TypeAtom atomMappingObject() { + return atomMappingObject.get(); + } + + ListAtomicType listAtomicRO() { + return listAtomicRO.get(); + } + + MappingAtomicType mappingAtomicRO() { + return mappingAtomicRO.get(); + } + + MappingAtomicType mappingAtomicObjectRO() { + return mappingAtomicObjectRO.get(); + } + + TypeAtom atomListThreeElement() { + return atomListThreeElement.get(); + } + + TypeAtom atomListThreeElementRO() { + return atomListThreeElementRO.get(); + } + + SemType readonlyType() { + return readonlyType.get(); + } + + // Due to some reason SpotBug thinks this method is overrideable if we don't put final here as well. + final void initializeEnv(Env env) { + assert initialized.get() : "PredefinedTypeEnv has not fully initialized, check concurrency issues"; + fillRecAtoms(env.recListAtoms, initializedRecListAtoms); + fillRecAtoms(env.recMappingAtoms, initializedRecMappingAtoms); + initializedCellAtoms.forEach(each -> env.cellAtom(each.atomicType())); + initializedListAtoms.forEach(each -> env.listAtom(each.atomicType())); + } + + private void fillRecAtoms(List envRecAtomList, List initializedRecAtoms) { + int count = reservedRecAtomCount(); + for (int i = 0; i < count; i++) { + if (i < initializedRecAtoms.size()) { + envRecAtomList.add(initializedRecAtoms.get(i)); + } else { + // This is mainly to help with bir serialization/deserialization logic. Given the number of such atoms + // will be small this shouldn't be a problem. + envRecAtomList.add(null); + } + } + } + + private int reservedRecAtomCount() { + return Integer.max(initializedRecListAtoms.size(), initializedRecMappingAtoms.size()); + } + + SemType cellSemTypeInner() { + return cellSemTypeInner.get(); + } + + public Atom atomListTwoElement() { + return atomListTwoElement.get(); + } + + @FunctionalInterface + private interface IndexSupplier { + + int get(E atomicType); + } + + private record InitializedTypeAtom(E atomicType, int index) { + + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/RecAtom.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/RecAtom.java new file mode 100644 index 000000000000..f302a7dc3c45 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/RecAtom.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Represent a recursive type atom. + * + * @since 2201.11.0 + */ +public final class RecAtom implements Atom { + + private final int index; + private static final int BDD_REC_ATOM_READONLY = 0; + private static final RecAtom ZERO = new RecAtom(BDD_REC_ATOM_READONLY); + + private RecAtom(int index) { + this.index = index; + } + + public static RecAtom createRecAtom(int index) { + if (index == BDD_REC_ATOM_READONLY) { + return ZERO; + } + return new RecAtom(index); + } + + public static RecAtom createDistinctRecAtom(int index) { + return new RecAtom(index); + } + + @Override + public int index() { + return index; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o instanceof RecAtom recAtom) { + return recAtom.index == this.index; + } + return false; + } + + @Override + public int hashCode() { + return index; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemType.java new file mode 100644 index 000000000000..d185c96b6c7f --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemType.java @@ -0,0 +1,71 @@ +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.internal.types.semtype.ImmutableSemType; +import io.ballerina.runtime.internal.types.semtype.MutableSemType; +import io.ballerina.runtime.internal.types.semtype.SemTypeHelper; + +/** + * Represent a type in runtime. + * + * @since 2201.11.0 + */ +public sealed class SemType extends BasicTypeBitSet + permits io.ballerina.runtime.internal.types.BType, ImmutableSemType { + + private int some; + private SubType[] subTypeData; + + protected SemType(int all, int some, SubType[] subTypeData) { + super(all); + this.some = some; + this.subTypeData = subTypeData; + } + + protected SemType() { + this(-1, -1, null); + } + + public static SemType from(int all) { + return new SemType(all, 0, null); + } + + public static SemType from(int all, int some, SubType[] subTypes) { + return new SemType(all, some, subTypes); + } + + public final int some() { + assert some != -1 : "SemType created by no arg constructor must be initialized with setSome"; + return some; + } + + public final SubType[] subTypeData() { + return subTypeData; + } + + public final SubType subTypeByCode(int code) { + if ((some() & (1 << code)) == 0) { + return null; + } + int someMask = (1 << code) - 1; + int some = some() & someMask; + return subTypeData()[Integer.bitCount(some)]; + } + + protected void setSome(int some, SubType[] subTypeData) { + this.some = some; + this.subTypeData = subTypeData; + } + + public static SemType tryInto(Type type) { + if (type instanceof MutableSemType mutableSemType) { + mutableSemType.updateInnerSemTypeIfNeeded(); + } + return (SemType) type; + } + + @Override + public String toString() { + return SemTypeHelper.stringRepr(this); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java new file mode 100644 index 000000000000..99bc4c9a51f5 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java @@ -0,0 +1,72 @@ +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.api.values.BValue; +import io.ballerina.runtime.internal.types.TypeWithAcceptedType; +import io.ballerina.runtime.internal.types.TypeWithShape; +import io.ballerina.runtime.internal.values.DecimalValue; + +import java.util.Optional; + +/** + * Utility class for performing shape related operations. + * + * @since 2201.11.0 + */ +public class ShapeAnalyzer { + + private ShapeAnalyzer() { + } + + public static Optional acceptedTypeOf(Context cx, Type typeDesc) { + if (typeDesc instanceof TypeWithAcceptedType typeWithAcceptedType) { + return typeWithAcceptedType.acceptedTypeOf(cx); + } + return Optional.of(SemType.tryInto(typeDesc)); + } + + public static Optional shapeOf(Context cx, Object object) { + if (object == null) { + return Optional.of(Builder.getNilType()); + } else if (object instanceof DecimalValue decimalValue) { + return Optional.of(Builder.getDecimalConst(decimalValue.value())); + } else if (object instanceof Double doubleValue) { + return Optional.of(Builder.getFloatConst(doubleValue)); + } else if (object instanceof Number intValue) { + long value = + intValue instanceof Byte byteValue ? Byte.toUnsignedLong(byteValue) : intValue.longValue(); + return Optional.of(Builder.getIntConst(value)); + } else if (object instanceof Boolean booleanValue) { + return Optional.of(Builder.getBooleanConst(booleanValue)); + } else if (object instanceof BString stringValue) { + return Optional.of(Builder.getStringConst(stringValue.getValue())); + } else if (object instanceof BValue bValue) { + Type type = bValue.getType(); + if (type instanceof TypeWithShape typeWithShape) { + return typeWithShape.shapeOf(cx, ShapeAnalyzer::shapeOf, object); + } else { + return Optional.empty(); + } + } + return Optional.empty(); + } + + public static Optional inherentTypeOf(Context cx, Object object) { + if (object instanceof BValue bValue) { + return bValue.inherentTypeOf(cx); + } + if (object == null) { + return Optional.of(Builder.getNilType()); + } else if (object instanceof Double doubleValue) { + return Optional.of(Builder.getFloatConst(doubleValue)); + } else if (object instanceof Number intValue) { + long value = + intValue instanceof Byte byteValue ? Byte.toUnsignedLong(byteValue) : intValue.longValue(); + return Optional.of(Builder.getIntConst(value)); + } else if (object instanceof Boolean booleanValue) { + return Optional.of(Builder.getBooleanConst(booleanValue)); + } + return Optional.empty(); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SubType.java new file mode 100644 index 000000000000..85fabfa2303b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SubType.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.SubTypeData; + +import java.util.Objects; + +/** + * Describe set of operation supported by each basic Type. + * + * @since 2201.11.0 + */ +public abstract class SubType { + + private final boolean all; + private final boolean nothing; + + protected SubType(boolean all, boolean nothing) { + this.all = all; + this.nothing = nothing; + } + + public abstract SubType union(SubType other); + + public abstract SubType intersect(SubType other); + + public SubType diff(SubType other) { + return this.intersect(other.complement()); + } + + public abstract SubType complement(); + + public abstract boolean isEmpty(Context cx); + + public final boolean isAll() { + return all; + } + + public final boolean isNothing() { + return nothing; + } + + public abstract SubTypeData data(); + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SubType other = (SubType) o; + return Objects.equals(data(), other.data()); + } + + @Override + public int hashCode() { + return Objects.hashCode(data()); + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeAtom.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeAtom.java new file mode 100644 index 000000000000..02080171937e --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeAtom.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Represent a TypeAtom. Each operand of a type operation could be thought of as + * an atom + * + * @param index unique index within the {@code Env} + * @param atomicType atomic type representing the actual type represented by + * this atom. + * @since 2201.11.0 + */ +public record TypeAtom(int index, AtomicType atomicType) implements Atom { + + public TypeAtom { + assert atomicType != null; + } + + public static TypeAtom createTypeAtom(int index, AtomicType atomicType) { + return new TypeAtom(index, atomicType); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCache.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCache.java new file mode 100644 index 000000000000..cf9bc820da2a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCache.java @@ -0,0 +1,41 @@ +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.api.types.Type; + +import java.util.Map; +import java.util.Optional; +import java.util.WeakHashMap; + +/** + * Generalized implementation of type check result cache. It is okay to access + * this from multiple threads but makes no + * guarantee about the consistency of the cache under parallel access. Given + * result don't change due to race conditions + * this should eventually become consistent. + * + * @param Type of the type descriptor which owns this cache + * @since 2201.11.0 + */ +public class TypeCheckCache { + + // Not synchronizing this should be fine since race conditions don't lead to inconsistent results. (i.e. results + // of doing multiple type checks are agnostic to the order of execution). Data races shouldn't lead to tearing in + // 64-bit JVMs. + private final Map cachedResults = new WeakHashMap<>(); + private final T owner; + + public TypeCheckCache(T owner) { + this.owner = owner; + } + + public Optional cachedTypeCheckResult(T other) { + if (other.equals(owner)) { + return Optional.of(true); + } + return Optional.ofNullable(cachedResults.get(other)); + } + + public void cacheTypeCheckResult(T other, boolean result) { + cachedResults.put(other, result); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BCollection.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BCollection.java index a95834a70d6e..1c9d28de48da 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BCollection.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BCollection.java @@ -17,6 +17,8 @@ */ package io.ballerina.runtime.api.values; +import io.ballerina.runtime.api.types.semtype.SemType; + /** *

* {@link BCollection} represents a collection in Ballerina. @@ -32,4 +34,12 @@ public interface BCollection { * @return iterator created. */ BIterator getIterator(); + + default SemType shapeOf() { + return null; + } + + default void cacheShape(SemType semType) { + + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BError.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BError.java index 88b368d827f4..5dd81119cabd 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BError.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BError.java @@ -17,8 +17,14 @@ */ package io.ballerina.runtime.api.values; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; +import io.ballerina.runtime.internal.types.TypeWithShape; + import java.io.PrintWriter; import java.util.List; +import java.util.Optional; /** *

@@ -83,4 +89,9 @@ public void printStackTrace(PrintWriter printWriter) { */ public abstract List getCallStack(); + @Override + public Optional inherentTypeOf(Context cx) { + TypeWithShape type = (TypeWithShape) getType(); + return type.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BRegexpValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BRegexpValue.java index 7de5799b3f45..ed62af17014f 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BRegexpValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BRegexpValue.java @@ -17,8 +17,11 @@ */ package io.ballerina.runtime.api.values; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.values.RegExpDisjunction; +import java.util.Optional; + /** *

* Represents RegexpValue. @@ -33,4 +36,6 @@ public interface BRegexpValue extends BValue { public RegExpDisjunction getRegExpDisjunction(); BTypedesc getTypedesc(); + + Optional shapeOf(); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BString.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BString.java index b7308732d7bc..08d9a54c4e23 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BString.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BString.java @@ -17,6 +17,8 @@ */ package io.ballerina.runtime.api.values; +import io.ballerina.runtime.api.types.Type; + /** * Interface representing ballerina strings. * @@ -40,4 +42,5 @@ public interface BString { BIterator getIterator(); + Type getType(); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BValue.java index 82a8bdac0a59..c23f563ff6d1 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BValue.java @@ -18,8 +18,11 @@ package io.ballerina.runtime.api.values; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; import java.util.Map; +import java.util.Optional; /** *

@@ -58,4 +61,20 @@ default String informalStringValue(BLink parent) { String expressionStringValue(BLink parent); Type getType(); + + /** + * Basic type of the value. + * + * @return {@code SemType} representing the value's basic type + */ + default SemType widenedType() { + // This is wrong since we are actually returning the actual (narrowed) type of the value. But since this is + // used only as an optimization (to avoid recalculating singleton type) in the type checker this is better + // than caching the widened types as well. + return SemType.tryInto(getType()); + } + + default Optional inherentTypeOf(Context cx) { + return Optional.empty(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java index 4705c4ead712..2dedd7250a87 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/TypeChecker.java @@ -22,14 +22,19 @@ import io.ballerina.runtime.api.types.ArrayType.ArrayState; import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.FunctionType; -import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.MethodType; -import io.ballerina.runtime.api.types.PredefinedTypes; +import io.ballerina.runtime.api.types.ReadonlyType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; -import io.ballerina.runtime.api.types.UnionType; import io.ballerina.runtime.api.types.XmlNodeType; -import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.CacheableTypeDescriptor; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.values.BDecimal; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BObject; @@ -37,64 +42,39 @@ import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.api.values.BValue; import io.ballerina.runtime.api.values.BXml; -import io.ballerina.runtime.internal.commons.TypeValuePair; import io.ballerina.runtime.internal.types.BAnnotatableType; import io.ballerina.runtime.internal.types.BArrayType; -import io.ballerina.runtime.internal.types.BErrorType; -import io.ballerina.runtime.internal.types.BField; +import io.ballerina.runtime.internal.types.BBooleanType; import io.ballerina.runtime.internal.types.BFiniteType; -import io.ballerina.runtime.internal.types.BFunctionType; -import io.ballerina.runtime.internal.types.BFutureType; import io.ballerina.runtime.internal.types.BIntersectionType; -import io.ballerina.runtime.internal.types.BJsonType; -import io.ballerina.runtime.internal.types.BMapType; -import io.ballerina.runtime.internal.types.BNetworkObjectType; import io.ballerina.runtime.internal.types.BObjectType; -import io.ballerina.runtime.internal.types.BParameterizedType; import io.ballerina.runtime.internal.types.BRecordType; -import io.ballerina.runtime.internal.types.BResourceMethodType; -import io.ballerina.runtime.internal.types.BStreamType; import io.ballerina.runtime.internal.types.BTableType; import io.ballerina.runtime.internal.types.BTupleType; import io.ballerina.runtime.internal.types.BType; -import io.ballerina.runtime.internal.types.BTypeIdSet; import io.ballerina.runtime.internal.types.BTypeReferenceType; -import io.ballerina.runtime.internal.types.BTypedescType; import io.ballerina.runtime.internal.types.BUnionType; -import io.ballerina.runtime.internal.types.BXmlType; +import io.ballerina.runtime.internal.types.TypeWithShape; import io.ballerina.runtime.internal.utils.ErrorUtils; -import io.ballerina.runtime.internal.values.ArrayValue; import io.ballerina.runtime.internal.values.DecimalValue; import io.ballerina.runtime.internal.values.DecimalValueKind; -import io.ballerina.runtime.internal.values.ErrorValue; import io.ballerina.runtime.internal.values.HandleValue; -import io.ballerina.runtime.internal.values.MapValue; -import io.ballerina.runtime.internal.values.MapValueImpl; +import io.ballerina.runtime.internal.values.RefValue; import io.ballerina.runtime.internal.values.RegExpValue; -import io.ballerina.runtime.internal.values.StreamValue; -import io.ballerina.runtime.internal.values.TableValueImpl; -import io.ballerina.runtime.internal.values.TupleValueImpl; import io.ballerina.runtime.internal.values.TypedescValue; import io.ballerina.runtime.internal.values.TypedescValueImpl; import io.ballerina.runtime.internal.values.ValuePair; -import io.ballerina.runtime.internal.values.XmlComment; -import io.ballerina.runtime.internal.values.XmlItem; -import io.ballerina.runtime.internal.values.XmlPi; import io.ballerina.runtime.internal.values.XmlSequence; -import io.ballerina.runtime.internal.values.XmlText; import io.ballerina.runtime.internal.values.XmlValue; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Stream; import static io.ballerina.runtime.api.constants.RuntimeConstants.BALLERINA_BUILTIN_PKG_PREFIX; import static io.ballerina.runtime.api.constants.RuntimeConstants.BBYTE_MAX_VALUE; @@ -109,8 +89,6 @@ import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED16_MAX_VALUE; import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED32_MAX_VALUE; import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED8_MAX_VALUE; -import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_ANY; -import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_ANYDATA; import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_BOOLEAN; import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_BYTE; import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_DECIMAL; @@ -122,15 +100,8 @@ import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_INT_UNSIGNED_16; import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_INT_UNSIGNED_32; import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_INT_UNSIGNED_8; -import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_JSON; import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_NULL; -import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_READONLY_JSON; -import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_STRING; import static io.ballerina.runtime.api.utils.TypeUtils.getImpliedType; -import static io.ballerina.runtime.api.utils.TypeUtils.isValueType; -import static io.ballerina.runtime.internal.TypeConverter.ERROR_MESSAGE_UNION_END; -import static io.ballerina.runtime.internal.TypeConverter.ERROR_MESSAGE_UNION_SEPARATOR; -import static io.ballerina.runtime.internal.TypeConverter.ERROR_MESSAGE_UNION_START; import static io.ballerina.runtime.internal.utils.CloneUtils.getErrorMessage; /** @@ -143,34 +114,39 @@ public final class TypeChecker { private static final byte MAX_TYPECAST_ERROR_COUNT = 20; private static final String REG_EXP_TYPENAME = "RegExp"; + private static final ThreadLocal threadContext = + ThreadLocal.withInitial(() -> Context.from(Env.getInstance())); public static Object checkCast(Object sourceVal, Type targetType) { List errors = new ArrayList<>(); - Type sourceType = getImpliedType(getType(sourceVal)); - if (checkIsType(errors, sourceVal, sourceType, targetType)) { + if (checkIsType(sourceVal, targetType)) { return sourceVal; } - - if (sourceType.getTag() <= TypeTags.BOOLEAN_TAG && targetType.getTag() <= TypeTags.BOOLEAN_TAG) { - return TypeConverter.castValues(targetType, sourceVal); - } - - // if the source is a numeric value and the target type is a union, try to find a matching - // member. - if (sourceType.getTag() <= TypeTags.BOOLEAN_TAG && targetType.getTag() == TypeTags.UNION_TAG) { - for (Type memberType : ((BUnionType) targetType).getMemberTypes()) { - try { - return TypeConverter.castValues(memberType, sourceVal); - } catch (Exception e) { - //ignore and continue + Type sourceType = getType(sourceVal); + if (Core.containsBasicType(SemType.tryInto(sourceType), ConvertibleCastMaskHolder.CONVERTIBLE_CAST_MASK) && + Core.containsBasicType(SemType.tryInto(targetType), ConvertibleCastMaskHolder.CONVERTIBLE_CAST_MASK)) { + // We need to maintain order for these? + if (targetType instanceof BUnionType unionType) { + for (Type memberType : unionType.getMemberTypes()) { + try { + return TypeConverter.castValues(memberType, sourceVal); + } catch (Exception e) { + //ignore and continue + } } + } else { + return TypeConverter.castValues(targetType, sourceVal); } } - throw createTypeCastError(sourceVal, targetType, errors); } + public static Context context() { + // We are pinning each context to thread. We can't use the same context with multiple type checks concurrently + return threadContext.get(); + } + public static long anyToInt(Object sourceVal) { return TypeConverter.anyToIntCast(sourceVal, () -> ErrorUtils.createTypeCastError(sourceVal, TYPE_INT)); @@ -281,7 +257,14 @@ public static boolean anyToJBoolean(Object sourceVal) { * @return true if the value belongs to the given type, false otherwise */ public static boolean checkIsType(Object sourceVal, Type targetType) { - return checkIsType(null, sourceVal, getType(sourceVal), targetType); + Context cx = context(); + Type sourceType = getType(sourceVal); + if (isSubType(sourceType, targetType)) { + return true; + } + SemType sourceSemType = SemType.tryInto(sourceType); + return couldInherentTypeBeDifferent(sourceSemType) && + isSubTypeWithInherentType(cx, sourceVal, SemType.tryInto(targetType)); } /** @@ -294,22 +277,15 @@ public static boolean checkIsType(Object sourceVal, Type targetType) { * @return true if the value belongs to the given type, false otherwise */ public static boolean checkIsType(List errors, Object sourceVal, Type sourceType, Type targetType) { - if (checkIsType(sourceVal, sourceType, targetType, null)) { - return true; - } - - if (getImpliedType(sourceType).getTag() == TypeTags.XML_TAG && !targetType.isReadOnly()) { - XmlValue val = (XmlValue) sourceVal; - if (val.getNodeType() == XmlNodeType.SEQUENCE) { - return checkIsLikeOnValue(errors, sourceVal, sourceType, targetType, new ArrayList<>(), false, null); - } - } + return checkIsType(sourceVal, targetType); + } - if (isMutable(sourceVal, sourceType)) { - return false; + // This is just an optimization since shapes are not cached, when in doubt return false + private static boolean couldInherentTypeBeDifferent(SemType type) { + if (type instanceof TypeWithShape typeWithShape) { + return typeWithShape.couldInherentTypeBeDifferent(); } - - return checkIsLikeOnValue(errors, sourceVal, sourceType, targetType, new ArrayList<>(), false, null); + return true; } /** @@ -332,8 +308,27 @@ public static boolean checkIsLikeType(Object sourceValue, Type targetType) { * @return true if the value has the same shape as the given type; false otherwise */ public static boolean checkIsLikeType(Object sourceValue, Type targetType, boolean allowNumericConversion) { - return checkIsLikeType(null, sourceValue, targetType, new ArrayList<>(), allowNumericConversion, - null); + Context cx = context(); + SemType shape = ShapeAnalyzer.shapeOf(cx, sourceValue).orElseThrow(); + SemType targetSemType = ShapeAnalyzer.acceptedTypeOf(cx, targetType).orElseThrow(); + if (Core.isSubType(cx, shape, NumericTypeHolder.NUMERIC_TYPE) && allowNumericConversion) { + targetSemType = appendNumericConversionTypes(targetSemType); + } + return Core.isSubType(cx, shape, targetSemType); + } + + private static SemType appendNumericConversionTypes(SemType semType) { + SemType result = semType; + // We can represent any int value as a float or a decimal. This is to avoid the overhead of creating + // enumerable semtypes for them + if (Core.containsBasicType(semType, Builder.getIntType())) { + result = Core.union(Core.union(Builder.getDecimalType(), Builder.getFloatType()), result); + } + result = Core.union(result, Core.floatToInt(semType)); + result = Core.union(result, Core.floatToDecimal(semType)); + result = Core.union(result, Core.decimalToInt(semType)); + result = Core.union(result, Core.decimalToFloat(semType)); + return result; } /** @@ -344,29 +339,34 @@ public static boolean checkIsLikeType(Object sourceValue, Type targetType, boole * @return true if the two types are same; false otherwise */ public static boolean isSameType(Type sourceType, Type targetType) { - return sourceType == targetType || sourceType.equals(targetType); + return Core.isSameType(context(), SemType.tryInto(sourceType), SemType.tryInto(targetType)); } public static Type getType(Object value) { - if (value == null) { - return TYPE_NULL; - } else if (value instanceof Number) { - if (value instanceof Long) { - return TYPE_INT; - } else if (value instanceof Double) { - return TYPE_FLOAT; - } else if (value instanceof Integer || value instanceof Byte) { - return TYPE_BYTE; + if (value instanceof BValue bValue) { + if (!(value instanceof BObject bObject)) { + return bValue.getType(); } - } else if (value instanceof BString) { - return TYPE_STRING; - } else if (value instanceof Boolean) { - return TYPE_BOOLEAN; - } else if (value instanceof BObject bObject) { return bObject.getOriginalType(); } + if (value == null) { + return TYPE_NULL; + } else if (value instanceof Number number) { + return getNumberType(number); + } else if (value instanceof Boolean booleanValue) { + return BBooleanType.singletonType(booleanValue); + } + throw new IllegalArgumentException("unexpected value type"); + } - return ((BValue) value).getType(); + private static Type getNumberType(Number number) { + if (number instanceof Double) { + return TYPE_FLOAT; + } + if (number instanceof Integer || number instanceof Byte) { + return TYPE_BYTE; + } + return TYPE_INT; } /** @@ -380,18 +380,6 @@ public static boolean isEqual(Object lhsValue, Object rhsValue) { return isEqual(lhsValue, rhsValue, new HashSet<>()); } - /** - * Check if two decimal values are equal in value. - * - * @param lhsValue The value on the left hand side - * @param rhsValue The value of the right hand side - * @return True if values are equal, else false. - */ - public static boolean checkDecimalEqual(DecimalValue lhsValue, DecimalValue rhsValue) { - return isDecimalRealNumber(lhsValue) && isDecimalRealNumber(rhsValue) && - lhsValue.decimalValue().compareTo(rhsValue.decimalValue()) == 0; - } - /** * Check if two decimal values are exactly equal. * @@ -411,7 +399,7 @@ public static boolean checkDecimalExactEqual(DecimalValue lhsValue, DecimalValue * @param decimalValue The decimal value being checked * @return True if the decimal value is a real number. */ - private static boolean isDecimalRealNumber(DecimalValue decimalValue) { + static boolean isDecimalRealNumber(DecimalValue decimalValue) { return decimalValue.valueKind == DecimalValueKind.ZERO || decimalValue.valueKind == DecimalValueKind.OTHER; } @@ -434,54 +422,36 @@ public static boolean isReferenceEqual(Object lhsValue, Object rhsValue) { return false; } - Type lhsType = getImpliedType(getType(lhsValue)); - Type rhsType = getImpliedType(getType(rhsValue)); - - return switch (lhsType.getTag()) { - case TypeTags.FLOAT_TAG -> { - if (rhsType.getTag() != TypeTags.FLOAT_TAG) { - yield false; - } - yield lhsValue.equals(((Number) rhsValue).doubleValue()); - } - case TypeTags.DECIMAL_TAG -> { - if (rhsType.getTag() != TypeTags.DECIMAL_TAG) { - yield false; - } - yield checkDecimalExactEqual((DecimalValue) lhsValue, (DecimalValue) rhsValue); - } - case TypeTags.INT_TAG, - TypeTags.BYTE_TAG, - TypeTags.BOOLEAN_TAG, - TypeTags.STRING_TAG -> isEqual(lhsValue, rhsValue); - case TypeTags.XML_TAG, - TypeTags.XML_COMMENT_TAG, - TypeTags.XML_ELEMENT_TAG, - TypeTags.XML_PI_TAG, - TypeTags.XML_TEXT_TAG -> { - if (!TypeTags.isXMLTypeTag(rhsType.getTag())) { - yield false; - } - yield isXMLValueRefEqual((XmlValue) lhsValue, (XmlValue) rhsValue); - } - case TypeTags.HANDLE_TAG -> { - if (rhsType.getTag() != TypeTags.HANDLE_TAG) { - yield false; - } - yield isHandleValueRefEqual(lhsValue, rhsValue); - } - case TypeTags.FUNCTION_POINTER_TAG -> lhsType.getPackage().equals(rhsType.getPackage()) && - lhsType.getName().equals(rhsType.getName()) && rhsType.equals(lhsType); - default -> { - if (lhsValue instanceof RegExpValue lhsRegExpValue && rhsValue instanceof RegExpValue) { - yield lhsRegExpValue.equals(rhsValue, new HashSet<>()); - } - yield false; - } - }; + Context cx = context(); + SemType lhsType = widenedType(cx, lhsValue); + SemType rhsType = widenedType(cx, rhsValue); + if (isSimpleBasicSemType(lhsType)) { + return isSimpleBasicValuesEqual(lhsValue, rhsValue); + } + Predicate basicTypePredicate = + (basicType) -> Core.isSubType(cx, lhsType, basicType) && Core.isSubType(cx, rhsType, basicType); + if (basicTypePredicate.test(Builder.getStringType())) { + return isEqual(lhsValue, rhsValue); + } + if (basicTypePredicate.test(Builder.getXmlType())) { + return isXMLValueRefEqual((XmlValue) lhsValue, (XmlValue) rhsValue); + } + if (basicTypePredicate.test(Builder.getHandleType())) { + return isHandleValueRefEqual(lhsValue, rhsValue); + } + if (basicTypePredicate.test(Builder.getFunctionType())) { + return isFunctionPointerEqual(getImpliedType(getType(lhsValue)), getImpliedType(getType(rhsValue))); + } + if (basicTypePredicate.test(Builder.getRegexType())) { + RegExpValue lhsReg = (RegExpValue) lhsValue; + RegExpValue rhsReg = (RegExpValue) rhsValue; + return lhsReg.equals(rhsReg, new HashSet<>()); + } + // Other types have storage identity so == test should have passed + return false; } - private static boolean isXMLValueRefEqual(XmlValue lhsValue, XmlValue rhsValue) { + static boolean isXMLValueRefEqual(XmlValue lhsValue, XmlValue rhsValue) { boolean isLhsXmlSequence = lhsValue.getNodeType() == XmlNodeType.SEQUENCE; boolean isRhsXmlSequence = rhsValue.getNodeType() == XmlNodeType.SEQUENCE; @@ -519,6 +489,38 @@ private static boolean isXMLSequenceRefEqual(XmlSequence lhsValue, XmlSequence r return lhsIter.hasNext() == rhsIter.hasNext(); } + private static boolean isFunctionPointerEqual(Type lhsType, Type rhsType) { + return lhsType.getPackage().equals(rhsType.getPackage()) && + lhsType.getName().equals(rhsType.getName()) && rhsType.equals(lhsType); + } + + private static boolean isSimpleBasicValuesEqual(Object v1, Object v2) { + Context cx = context(); + SemType v1Ty = widenedType(cx, v1); + if (!isSimpleBasicSemType(v1Ty)) { + return false; + } + + SemType v2Ty = widenedType(cx, v2); + if (!isSimpleBasicSemType(v2Ty)) { + return false; + } + + if (!Core.isSameType(cx, v1Ty, v2Ty)) { + return false; + } + + if (Core.isSubType(cx, v1Ty, Builder.getDecimalType())) { + return checkDecimalExactEqual((DecimalValue) v1, (DecimalValue) v2); + } + if (Core.isSubType(cx, v1Ty, Builder.getIntType())) { + Number n1 = (Number) v1; + Number n2 = (Number) v2; + return n1.longValue() == n2.longValue(); + } + return v1.equals(v2); + } + /** * Get the typedesc of a value. * @@ -530,7 +532,7 @@ public static TypedescValue getTypedesc(Object value) { if (type == null) { return null; } - if (isSimpleBasicType(type)) { + if (belongToSingleBasicTypeOrString(type)) { return new TypedescValueImpl(new BFiniteType(value.toString(), Set.of(value), 0)); } if (value instanceof BRefValue bRefValue) { @@ -562,2207 +564,315 @@ public static Object getAnnotValue(TypedescValue typedescValue, BString annotTag * @return flag indicating the equivalence of the two types */ public static boolean checkIsType(Type sourceType, Type targetType) { - return checkIsType(sourceType, targetType, null); + return isSubType(sourceType, targetType); } @Deprecated public static boolean checkIsType(Type sourceType, Type targetType, List unresolvedTypes) { - // First check whether both types are the same. - if (sourceType == targetType || (sourceType.getTag() == targetType.getTag() && sourceType.equals(targetType))) { - return true; - } - - if (checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(sourceType)) { - return true; - } - - if (targetType.isReadOnly() && !sourceType.isReadOnly()) { - return false; - } - - int sourceTypeTag = sourceType.getTag(); - int targetTypeTag = targetType.getTag(); - - switch (sourceTypeTag) { - case TypeTags.INTERSECTION_TAG: - return checkIsType(((BIntersectionType) sourceType).getEffectiveType(), - targetTypeTag != TypeTags.INTERSECTION_TAG ? targetType : - ((BIntersectionType) targetType).getEffectiveType(), unresolvedTypes); - case TypeTags.TYPE_REFERENCED_TYPE_TAG: - return checkIsType(((BTypeReferenceType) sourceType).getReferredType(), - targetTypeTag != TypeTags.TYPE_REFERENCED_TYPE_TAG ? targetType : - ((BTypeReferenceType) targetType).getReferredType(), unresolvedTypes); - case TypeTags.PARAMETERIZED_TYPE_TAG: - if (targetTypeTag != TypeTags.PARAMETERIZED_TYPE_TAG) { - return checkIsType(((BParameterizedType) sourceType).getParamValueType(), targetType, - unresolvedTypes); - } - return checkIsType(((BParameterizedType) sourceType).getParamValueType(), - ((BParameterizedType) targetType).getParamValueType(), unresolvedTypes); - case TypeTags.READONLY_TAG: - return checkIsType(PredefinedTypes.ANY_AND_READONLY_OR_ERROR_TYPE, - targetType, unresolvedTypes); - case TypeTags.UNION_TAG: - return isUnionTypeMatch((BUnionType) sourceType, targetType, unresolvedTypes); - case TypeTags.FINITE_TYPE_TAG: - if ((targetTypeTag == TypeTags.FINITE_TYPE_TAG || targetTypeTag <= TypeTags.NULL_TAG || - targetTypeTag == TypeTags.XML_TEXT_TAG)) { - return isFiniteTypeMatch((BFiniteType) sourceType, targetType); - } - break; - default: - break; - } - - return switch (targetTypeTag) { - case TypeTags.BYTE_TAG, - TypeTags.SIGNED8_INT_TAG, - TypeTags.FLOAT_TAG, - TypeTags.DECIMAL_TAG, - TypeTags.CHAR_STRING_TAG, - TypeTags.BOOLEAN_TAG, - TypeTags.NULL_TAG -> sourceTypeTag == targetTypeTag; - case TypeTags.STRING_TAG -> TypeTags.isStringTypeTag(sourceTypeTag); - case TypeTags.XML_TEXT_TAG -> { - if (sourceTypeTag == TypeTags.XML_TAG) { - yield ((BXmlType) sourceType).constraint.getTag() == TypeTags.NEVER_TAG; - } - yield sourceTypeTag == targetTypeTag; - } - case TypeTags.INT_TAG -> sourceTypeTag == TypeTags.INT_TAG || sourceTypeTag == TypeTags.BYTE_TAG || - (sourceTypeTag >= TypeTags.SIGNED8_INT_TAG && sourceTypeTag <= TypeTags.UNSIGNED32_INT_TAG); - case TypeTags.SIGNED16_INT_TAG -> sourceTypeTag == TypeTags.BYTE_TAG || - (sourceTypeTag >= TypeTags.SIGNED8_INT_TAG && sourceTypeTag <= TypeTags.SIGNED16_INT_TAG); - case TypeTags.SIGNED32_INT_TAG -> sourceTypeTag == TypeTags.BYTE_TAG || - (sourceTypeTag >= TypeTags.SIGNED8_INT_TAG && sourceTypeTag <= TypeTags.SIGNED32_INT_TAG); - case TypeTags.UNSIGNED8_INT_TAG -> - sourceTypeTag == TypeTags.BYTE_TAG || sourceTypeTag == TypeTags.UNSIGNED8_INT_TAG; - case TypeTags.UNSIGNED16_INT_TAG -> - sourceTypeTag == TypeTags.BYTE_TAG || sourceTypeTag == TypeTags.UNSIGNED8_INT_TAG || - sourceTypeTag == TypeTags.UNSIGNED16_INT_TAG; - case TypeTags.UNSIGNED32_INT_TAG -> - sourceTypeTag == TypeTags.BYTE_TAG || sourceTypeTag == TypeTags.UNSIGNED8_INT_TAG || - sourceTypeTag == TypeTags.UNSIGNED16_INT_TAG || - sourceTypeTag == TypeTags.UNSIGNED32_INT_TAG; - case TypeTags.ANY_TAG -> checkIsAnyType(sourceType); - case TypeTags.ANYDATA_TAG -> sourceType.isAnydata(); - case TypeTags.SERVICE_TAG -> checkIsServiceType(sourceType, targetType, - unresolvedTypes == null ? new ArrayList<>() : unresolvedTypes); - case TypeTags.HANDLE_TAG -> sourceTypeTag == TypeTags.HANDLE_TAG; - case TypeTags.READONLY_TAG -> - checkIsType(sourceType, PredefinedTypes.ANY_AND_READONLY_OR_ERROR_TYPE, unresolvedTypes); - case TypeTags.XML_ELEMENT_TAG, - TypeTags.XML_COMMENT_TAG, - TypeTags.XML_PI_TAG -> targetTypeTag == sourceTypeTag; - case TypeTags.INTERSECTION_TAG -> - checkIsType(sourceType, ((BIntersectionType) targetType).getEffectiveType(), unresolvedTypes); - case TypeTags.TYPE_REFERENCED_TYPE_TAG -> - checkIsType(sourceType, ((BTypeReferenceType) targetType).getReferredType(), unresolvedTypes); - default -> checkIsRecursiveType(sourceType, targetType, - unresolvedTypes == null ? new ArrayList<>() : unresolvedTypes); - }; - } - - private static boolean checkIsType(Object sourceVal, Type sourceType, Type targetType, - List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - targetType = getImpliedType(targetType); - - int sourceTypeTag = sourceType.getTag(); - int targetTypeTag = targetType.getTag(); - - // If the source type is neither a record type nor an object type, check `is` type by looking only at the types. - // Else, since records and objects may have `readonly` or `final` fields, need to use the value also. - // e.g., - // const HUNDRED = 100; - // - // type Foo record { - // HUNDRED i; - // }; - // - // type Bar record { - // readonly string|int i; - // }; - // - // where `Bar b = {i: 100};`, `b is Foo` should evaluate to true. - if (sourceTypeTag != TypeTags.RECORD_TYPE_TAG && sourceTypeTag != TypeTags.OBJECT_TYPE_TAG) { - return checkIsType(sourceType, targetType); - } - - if (sourceType == targetType || (sourceType.getTag() == targetType.getTag() && sourceType.equals(targetType))) { - return true; - } - - if (targetType.isReadOnly() && !sourceType.isReadOnly()) { - return false; - } - - return switch (targetTypeTag) { - case TypeTags.ANY_TAG -> checkIsAnyType(sourceType); - case TypeTags.READONLY_TAG -> isInherentlyImmutableType(sourceType) || sourceType.isReadOnly(); - default -> checkIsRecursiveTypeOnValue(sourceVal, sourceType, targetType, sourceTypeTag, targetTypeTag, - unresolvedTypes == null ? new ArrayList<>() : unresolvedTypes); - }; - } - - // Private methods - - private static boolean checkTypeDescType(Type sourceType, BTypedescType targetType, - List unresolvedTypes) { - if (sourceType.getTag() != TypeTags.TYPEDESC_TAG) { - return false; - } - - BTypedescType sourceTypedesc = (BTypedescType) sourceType; - return checkIsType(sourceTypedesc.getConstraint(), targetType.getConstraint(), unresolvedTypes); - } - - private static boolean checkIsRecursiveType(Type sourceType, Type targetType, List unresolvedTypes) { - return switch (targetType.getTag()) { - case TypeTags.MAP_TAG -> checkIsMapType(sourceType, (BMapType) targetType, unresolvedTypes); - case TypeTags.STREAM_TAG -> checkIsStreamType(sourceType, (BStreamType) targetType, unresolvedTypes); - case TypeTags.TABLE_TAG -> checkIsTableType(sourceType, (BTableType) targetType, unresolvedTypes); - case TypeTags.JSON_TAG -> checkIsJSONType(sourceType, unresolvedTypes); - case TypeTags.RECORD_TYPE_TAG -> checkIsRecordType(sourceType, (BRecordType) targetType, unresolvedTypes); - case TypeTags.FUNCTION_POINTER_TAG -> checkIsFunctionType(sourceType, (BFunctionType) targetType); - case TypeTags.ARRAY_TAG -> checkIsArrayType(sourceType, (BArrayType) targetType, unresolvedTypes); - case TypeTags.TUPLE_TAG -> checkIsTupleType(sourceType, (BTupleType) targetType, unresolvedTypes); - case TypeTags.UNION_TAG -> checkIsUnionType(sourceType, (BUnionType) targetType, unresolvedTypes); - case TypeTags.OBJECT_TYPE_TAG -> - checkObjectEquivalency(sourceType, (BObjectType) targetType, unresolvedTypes); - case TypeTags.FINITE_TYPE_TAG -> checkIsFiniteType(sourceType, (BFiniteType) targetType); - case TypeTags.FUTURE_TAG -> checkIsFutureType(sourceType, (BFutureType) targetType, unresolvedTypes); - case TypeTags.ERROR_TAG -> checkIsErrorType(sourceType, (BErrorType) targetType, unresolvedTypes); - case TypeTags.TYPEDESC_TAG -> checkTypeDescType(sourceType, (BTypedescType) targetType, unresolvedTypes); - case TypeTags.XML_TAG -> checkIsXMLType(sourceType, targetType, unresolvedTypes); - // other non-recursive types shouldn't reach here - default -> false; - }; - } - - private static boolean checkIsRecursiveTypeOnValue(Object sourceVal, Type sourceType, Type targetType, - int sourceTypeTag, int targetTypeTag, - List unresolvedTypes) { - return switch (targetTypeTag) { - case TypeTags.ANYDATA_TAG -> { - if (sourceTypeTag == TypeTags.OBJECT_TYPE_TAG) { - yield false; - } - yield checkRecordBelongsToAnydataType((MapValue) sourceVal, (BRecordType) sourceType, unresolvedTypes); - } - case TypeTags.MAP_TAG -> checkIsMapType(sourceVal, sourceType, (BMapType) targetType, unresolvedTypes); - case TypeTags.JSON_TAG -> checkIsMapType(sourceVal, sourceType, - new BMapType(targetType.isReadOnly() ? TYPE_READONLY_JSON : - TYPE_JSON), unresolvedTypes); - case TypeTags.RECORD_TYPE_TAG -> - checkIsRecordType(sourceVal, sourceType, (BRecordType) targetType, unresolvedTypes); - case TypeTags.UNION_TAG -> { - for (Type type : ((BUnionType) targetType).getMemberTypes()) { - if (checkIsType(sourceVal, sourceType, type, unresolvedTypes)) { - yield true; - } - } - yield false; - } - case TypeTags.OBJECT_TYPE_TAG -> - checkObjectEquivalency(sourceVal, sourceType, (BObjectType) targetType, unresolvedTypes); - default -> false; - }; - } - - private static boolean isFiniteTypeMatch(BFiniteType sourceType, Type targetType) { - for (Object bValue : sourceType.valueSpace) { - if (!checkIsType(bValue, targetType)) { - return false; - } - } - return true; + return isSubType(sourceType, targetType); } - private static boolean isUnionTypeMatch(BUnionType sourceType, Type targetType, List unresolvedTypes) { - for (Type type : sourceType.getMemberTypes()) { - if (!checkIsType(type, targetType, unresolvedTypes)) { - return false; - } - } - return true; - } - - private static boolean checkIsUnionType(Type sourceType, BUnionType targetType, List unresolvedTypes) { - // If we encounter two types that we are still resolving, then skip it. - // This is done to avoid recursive checking of the same type. - sourceType = getImpliedType(sourceType); - TypePair pair = new TypePair(sourceType, targetType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - - return switch (sourceType.getTag()) { - case TypeTags.UNION_TAG, - TypeTags.JSON_TAG, - TypeTags.ANYDATA_TAG -> isUnionTypeMatch((BUnionType) sourceType, targetType, unresolvedTypes); - case TypeTags.FINITE_TYPE_TAG -> isFiniteTypeMatch((BFiniteType) sourceType, targetType); - default -> { - for (Type type : targetType.getMemberTypes()) { - if (checkIsType(sourceType, type, unresolvedTypes)) { - yield true; - } - } - yield false; - } - }; + /** + * Check if two decimal values are equal in value. + * + * @param lhsValue The value on the left hand side + * @param rhsValue The value of the right hand side + * @return True if values are equal, else false. + */ + public static boolean checkDecimalEqual(DecimalValue lhsValue, DecimalValue rhsValue) { + return isDecimalRealNumber(lhsValue) && isDecimalRealNumber(rhsValue) && + lhsValue.decimalValue().compareTo(rhsValue.decimalValue()) == 0; } - private static boolean checkIsMapType(Type sourceType, BMapType targetType, List unresolvedTypes) { - Type targetConstrainedType = targetType.getConstrainedType(); - sourceType = getImpliedType(sourceType); - switch (sourceType.getTag()) { - case TypeTags.MAP_TAG: - return checkConstraints(((BMapType) sourceType).getConstrainedType(), targetConstrainedType, - unresolvedTypes); - case TypeTags.RECORD_TYPE_TAG: - BRecordType recType = (BRecordType) sourceType; - BUnionType wideTypeUnion = new BUnionType(getWideTypeComponents(recType)); - return checkConstraints(wideTypeUnion, targetConstrainedType, unresolvedTypes); - default: - return false; - } + public static boolean isNumericType(Type type) { + return Core.isSubType(context(), SemType.tryInto(type), NumericTypeHolder.NUMERIC_TYPE); } - private static boolean checkIsMapType(Object sourceVal, Type sourceType, BMapType targetType, - List unresolvedTypes) { - Type targetConstrainedType = targetType.getConstrainedType(); - sourceType = getImpliedType(sourceType); - return switch (sourceType.getTag()) { - case TypeTags.MAP_TAG -> checkConstraints(((BMapType) sourceType).getConstrainedType(), - targetConstrainedType, unresolvedTypes); - case TypeTags.RECORD_TYPE_TAG -> checkIsMapType((MapValue) sourceVal, (BRecordType) sourceType, - unresolvedTypes, targetConstrainedType); - default -> false; - }; + public static boolean isByteLiteral(long longValue) { + return (longValue >= BBYTE_MIN_VALUE && longValue <= BBYTE_MAX_VALUE); } - private static boolean checkIsMapType(MapValue sourceVal, BRecordType sourceType, List unresolvedTypes, - Type targetConstrainedType) { - for (Field field : sourceType.getFields().values()) { - if (!SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY)) { - if (!checkIsType(field.getFieldType(), targetConstrainedType, unresolvedTypes)) { - return false; - } - continue; - } - - BString name = StringUtils.fromString(field.getFieldName()); - - if (SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL) && !sourceVal.containsKey(name)) { - continue; - } - - if (!checkIsLikeType(sourceVal.get(name), targetConstrainedType)) { - return false; - } - } - - if (sourceType.sealed) { - return true; - } + // Private methods - return checkIsType(sourceType.restFieldType, targetConstrainedType, unresolvedTypes); + private static boolean isSubTypeWithInherentType(Context cx, Object sourceValue, SemType target) { + return ShapeAnalyzer.inherentTypeOf(cx, sourceValue) + .map(source -> !Core.isEmpty(cx, source) && Core.isSubType(cx, source, target)) + // OR else do the normal type check by taking the shape of + .orElse(false); } - private static boolean checkIsXMLType(Type sourceType, Type targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - int sourceTag = sourceType.getTag(); - if (sourceTag == TypeTags.FINITE_TYPE_TAG) { - return isFiniteTypeMatch((BFiniteType) sourceType, targetType); - } - - BXmlType target = ((BXmlType) targetType); - if (sourceTag == TypeTags.XML_TAG) { - Type targetConstraint = getRecursiveTargetConstraintType(target); - BXmlType source = (BXmlType) sourceType; - if (source.constraint.getTag() == TypeTags.NEVER_TAG) { - if (targetConstraint.getTag() == TypeTags.UNION_TAG) { - return checkIsUnionType(sourceType, (BUnionType) targetConstraint, unresolvedTypes); - } - return targetConstraint.getTag() == TypeTags.XML_TEXT_TAG || - targetConstraint.getTag() == TypeTags.NEVER_TAG; - } - return checkIsType(source.constraint, targetConstraint, unresolvedTypes); - } - if (TypeTags.isXMLTypeTag(sourceTag)) { - return checkIsType(sourceType, target.constraint, unresolvedTypes); + private static boolean isSubType(Type source, Type target) { + if (source instanceof CacheableTypeDescriptor sourceCacheableType && + target instanceof CacheableTypeDescriptor targetCacheableType) { + return isSubTypeWithCache(sourceCacheableType, targetCacheableType); } - return false; + // This is really a workaround for Standard libraries that create record types that are not the "same". But + // with the same name and expect them to be same. + return isSubTypeInner(context(), source, target); } - private static Type getRecursiveTargetConstraintType(BXmlType target) { - Type targetConstraint = getImpliedType(target.constraint); - // TODO: Revisit and check why xml>> on chained iteration - while (targetConstraint.getTag() == TypeTags.XML_TAG) { - target = (BXmlType) targetConstraint; - targetConstraint = getImpliedType(target.constraint); - } - return targetConstraint; + private static boolean isSubTypeInner(Context cx, Type source, Type target) { + SemType sourceSemType = SemType.tryInto(source); + SemType targetSemType = SemType.tryInto(target); + return Core.isSubType(cx, sourceSemType, targetSemType); } - private static List getWideTypeComponents(BRecordType recType) { - List types = new ArrayList<>(); - for (Field f : recType.getFields().values()) { - types.add(f.getFieldType()); + private static boolean isSubTypeWithCache(CacheableTypeDescriptor source, CacheableTypeDescriptor target) { + Context cx = context(); + if (!source.shouldCache() || !target.shouldCache()) { + return isSubTypeInner(cx, source, target); } - if (!recType.sealed) { - types.add(recType.restFieldType); - } - return types; - } - - private static boolean checkIsStreamType(Type sourceType, BStreamType targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - if (sourceType.getTag() != TypeTags.STREAM_TAG) { - return false; + Optional cachedResult = source.cachedTypeCheckResult(cx, target); + if (cachedResult.isPresent()) { + assert cachedResult.get() == isSubTypeInner(cx, source, target); + return cachedResult.get(); } - return checkConstraints(((BStreamType) sourceType).getConstrainedType(), targetType.getConstrainedType(), - unresolvedTypes) - && checkConstraints(((BStreamType) sourceType).getCompletionType(), targetType.getCompletionType(), - unresolvedTypes); + boolean result = isSubTypeInner(cx, source, target); + source.cacheTypeCheckResult(target, result); + return result; } - private static boolean checkIsTableType(Type sourceType, BTableType targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - if (sourceType.getTag() != TypeTags.TABLE_TAG) { - return false; - } - - BTableType srcTableType = (BTableType) sourceType; - - if (!checkConstraints(srcTableType.getConstrainedType(), targetType.getConstrainedType(), - unresolvedTypes)) { - return false; - } - - if (targetType.getKeyType() == null && targetType.getFieldNames().length == 0) { - return true; + private static SemType widenedType(Context cx, Object value) { + if (value instanceof BValue bValue) { + return bValue.widenedType(); } - - if (targetType.getKeyType() != null) { - if (srcTableType.getKeyType() != null && - (checkConstraints(srcTableType.getKeyType(), targetType.getKeyType(), unresolvedTypes))) { - return true; - } - - if (srcTableType.getFieldNames().length == 0) { - return false; - } - - List fieldTypes = new ArrayList<>(); - Arrays.stream(srcTableType.getFieldNames()).forEach(field -> fieldTypes - .add(Objects.requireNonNull(getTableConstraintField(srcTableType.getConstrainedType(), field)) - .getFieldType())); - - if (fieldTypes.size() == 1) { - return checkConstraints(fieldTypes.get(0), targetType.getKeyType(), unresolvedTypes); - } - - BTupleType tupleType = new BTupleType(fieldTypes); - return checkConstraints(tupleType, targetType.getKeyType(), unresolvedTypes); + if (value == null) { + return Builder.getNilType(); + } else if (value instanceof Double) { + return Builder.getFloatType(); + } else if (value instanceof Number) { + return Builder.getIntType(); + } else if (value instanceof BString) { + return Builder.getStringType(); + } else if (value instanceof Boolean) { + return Builder.getBooleanType(); } - - return Arrays.equals(srcTableType.getFieldNames(), targetType.getFieldNames()); + throw new IllegalArgumentException("Unexpected object type"); } - static BField getTableConstraintField(Type constraintType, String fieldName) { - switch (constraintType.getTag()) { - case TypeTags.RECORD_TYPE_TAG: - Map fieldList = ((BRecordType) constraintType).getFields(); - return (BField) fieldList.get(fieldName); - case TypeTags.INTERSECTION_TAG: - Type effectiveType = ((BIntersectionType) constraintType).getEffectiveType(); - return getTableConstraintField(effectiveType, fieldName); - case TypeTags.TYPE_REFERENCED_TYPE_TAG: - Type referredType = ((BTypeReferenceType) constraintType).getReferredType(); - return getTableConstraintField(referredType, fieldName); - case TypeTags.UNION_TAG: - BUnionType unionType = (BUnionType) constraintType; - List memTypes = unionType.getMemberTypes(); - List fields = memTypes.stream().map(type -> getTableConstraintField(type, fieldName)) - .filter(Objects::nonNull).toList(); - - if (fields.size() != memTypes.size()) { - return null; - } - - if (fields.stream().allMatch(field -> isSameType(field.getFieldType(), fields.get(0).getFieldType()))) { - return fields.get(0); - } - return null; - default: - return null; - } + public static boolean isInherentlyImmutableType(Type sourceType) { + // readonly part is there to match to old API + return + Core.isSubType(context(), SemType.tryInto(sourceType), + InherentlyImmutableTypeHolder.INHERENTLY_IMMUTABLE_TYPE) || + sourceType instanceof ReadonlyType; } - private static boolean checkIsJSONType(Type sourceType, List unresolvedTypes) { - BJsonType jsonType = (BJsonType) TYPE_JSON; - sourceType = getImpliedType(sourceType); - // If we encounter two types that we are still resolving, then skip it. - // This is done to avoid recursive checking of the same type. - TypePair pair = new TypePair(sourceType, jsonType); - if (unresolvedTypes.contains(pair)) { + // NOTE: this is not the same as selectively immutable as it stated in the spec + public static boolean isSelectivelyImmutableType(Type type, Set unresolvedTypes) { + if (!unresolvedTypes.add(type)) { return true; } - unresolvedTypes.add(pair); - switch (sourceType.getTag()) { - case TypeTags.STRING_TAG: - case TypeTags.CHAR_STRING_TAG: - case TypeTags.INT_TAG: - case TypeTags.SIGNED32_INT_TAG: - case TypeTags.SIGNED16_INT_TAG: - case TypeTags.SIGNED8_INT_TAG: - case TypeTags.UNSIGNED32_INT_TAG: - case TypeTags.UNSIGNED16_INT_TAG: - case TypeTags.UNSIGNED8_INT_TAG: - case TypeTags.BYTE_TAG: - case TypeTags.FLOAT_TAG: - case TypeTags.DECIMAL_TAG: - case TypeTags.BOOLEAN_TAG: - case TypeTags.NULL_TAG: + switch (type.getTag()) { + case TypeTags.ANY_TAG: + case TypeTags.ANYDATA_TAG: case TypeTags.JSON_TAG: + case TypeTags.XML_TAG: + case TypeTags.XML_COMMENT_TAG: + case TypeTags.XML_ELEMENT_TAG: + case TypeTags.XML_PI_TAG: + case TypeTags.READONLY_TAG: return true; case TypeTags.ARRAY_TAG: - // Element type of the array should be 'is type' JSON - return checkIsType(((BArrayType) sourceType).getElementType(), jsonType, unresolvedTypes); - case TypeTags.FINITE_TYPE_TAG: - return isFiniteTypeMatch((BFiniteType) sourceType, jsonType); - case TypeTags.MAP_TAG: - return checkIsType(((BMapType) sourceType).getConstrainedType(), jsonType, unresolvedTypes); - case TypeTags.RECORD_TYPE_TAG: - BRecordType recordType = (BRecordType) sourceType; - for (Field field : recordType.getFields().values()) { - if (!checkIsJSONType(field.getFieldType(), unresolvedTypes)) { + Type elementType = ((BArrayType) type).getElementType(); + return isInherentlyImmutableType(elementType) || + isSelectivelyImmutableType(elementType, unresolvedTypes); + case TypeTags.TUPLE_TAG: + BTupleType tupleType = (BTupleType) type; + for (Type tupMemType : tupleType.getTupleTypes()) { + if (!isInherentlyImmutableType(tupMemType) && + !isSelectivelyImmutableType(tupMemType, unresolvedTypes)) { return false; } } - if (!recordType.sealed) { - return checkIsJSONType(recordType.restFieldType, unresolvedTypes); + Type tupRestType = tupleType.getRestType(); + if (tupRestType == null) { + return true; } - return true; - case TypeTags.TUPLE_TAG: - BTupleType sourceTupleType = (BTupleType) sourceType; - for (Type memberType : sourceTupleType.getTupleTypes()) { - if (!checkIsJSONType(memberType, unresolvedTypes)) { - return false; - } - } - Type tupleRestType = sourceTupleType.getRestType(); - if (tupleRestType != null) { - return checkIsJSONType(tupleRestType, unresolvedTypes); - } - return true; - case TypeTags.UNION_TAG: - for (Type memberType : ((BUnionType) sourceType).getMemberTypes()) { - if (!checkIsJSONType(memberType, unresolvedTypes)) { - return false; - } - } - return true; - default: - return false; - } - } - - private static boolean checkIsRecordType(Type sourceType, BRecordType targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - return switch (sourceType.getTag()) { - case TypeTags.RECORD_TYPE_TAG -> checkIsRecordType((BRecordType) sourceType, targetType, unresolvedTypes); - case TypeTags.MAP_TAG -> checkIsRecordType((BMapType) sourceType, targetType, unresolvedTypes); - default -> false; - }; - } - - private static boolean checkIsRecordType(BRecordType sourceRecordType, BRecordType targetType, - List unresolvedTypes) { - // If we encounter two types that we are still resolving, then skip it. - // This is done to avoid recursive checking of the same type. - TypePair pair = new TypePair(sourceRecordType, targetType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - - // Unsealed records are not equivalent to sealed records, unless their rest field type is 'never'. But - // vice-versa is allowed. - if (targetType.sealed && !sourceRecordType.sealed && (sourceRecordType.restFieldType == null || - getImpliedType(sourceRecordType.restFieldType).getTag() != TypeTags.NEVER_TAG)) { - return false; - } - - // If both are sealed check the rest field type - if (!sourceRecordType.sealed && !targetType.sealed && - !checkIsType(sourceRecordType.restFieldType, targetType.restFieldType, unresolvedTypes)) { - return false; - } - - Map sourceFields = sourceRecordType.getFields(); - Set targetFieldNames = targetType.getFields().keySet(); - - for (Map.Entry targetFieldEntry : targetType.getFields().entrySet()) { - Field targetField = targetFieldEntry.getValue(); - Field sourceField = sourceFields.get(targetFieldEntry.getKey()); - - if (sourceField == null) { - if (!SymbolFlags.isFlagOn(targetField.getFlags(), SymbolFlags.OPTIONAL)) { - return false; - } - - if (!sourceRecordType.sealed && !checkIsType(sourceRecordType.restFieldType, targetField.getFieldType(), - unresolvedTypes)) { - return false; - } - - continue; - } - - if (hasIncompatibleReadOnlyFlags(targetField, sourceField)) { - return false; - } - - // If the target field is required, the source field should be required as well. - if (!SymbolFlags.isFlagOn(targetField.getFlags(), SymbolFlags.OPTIONAL) - && SymbolFlags.isFlagOn(sourceField.getFlags(), SymbolFlags.OPTIONAL)) { - return false; - } - - if (!checkIsType(sourceField.getFieldType(), targetField.getFieldType(), unresolvedTypes)) { - return false; - } - } - - // If there are fields remaining in the source record, first check if it's a closed record. Closed records - // should only have the fields specified by its type. - if (targetType.sealed) { - return targetFieldNames.containsAll(sourceFields.keySet()); - } - - // If it's an open record, check if they are compatible with the rest field of the target type. - for (Map.Entry sourceFieldEntry : sourceFields.entrySet()) { - if (targetFieldNames.contains(sourceFieldEntry.getKey())) { - continue; - } - - if (!checkIsType(sourceFieldEntry.getValue().getFieldType(), targetType.restFieldType, unresolvedTypes)) { - return false; - } - } - return true; - } - - private static boolean checkIsRecordType(BMapType sourceType, BRecordType targetType, - List unresolvedTypes) { - // If we encounter two types that we are still resolving, then skip it. - // This is done to avoid recursive checking of the same type. - TypePair pair = new TypePair(sourceType, targetType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - - if (targetType.sealed) { - return false; - } - - Type constraintType = sourceType.getConstrainedType(); - - for (Field field : targetType.getFields().values()) { - long flags = field.getFlags(); - if (!SymbolFlags.isFlagOn(flags, SymbolFlags.OPTIONAL)) { - return false; - } - - if (SymbolFlags.isFlagOn(flags, SymbolFlags.READONLY) && !sourceType.isReadOnly()) { - return false; - } - - if (!checkIsType(constraintType, field.getFieldType(), unresolvedTypes)) { - return false; - } - } - - return checkIsType(constraintType, targetType.restFieldType, unresolvedTypes); - } - - private static boolean checkRecordBelongsToAnydataType(MapValue sourceVal, BRecordType recordType, - List unresolvedTypes) { - Type targetType = TYPE_ANYDATA; - TypePair pair = new TypePair(recordType, targetType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - - Map fields = recordType.getFields(); - - for (Map.Entry fieldEntry : fields.entrySet()) { - String fieldName = fieldEntry.getKey(); - Field field = fieldEntry.getValue(); - - if (SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY)) { - BString fieldNameBString = StringUtils.fromString(fieldName); - - if (SymbolFlags - .isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL) && !sourceVal.containsKey(fieldNameBString)) { - continue; - } - - if (!checkIsLikeType(sourceVal.get(fieldNameBString), targetType)) { - return false; - } - } else { - if (!checkIsType(field.getFieldType(), targetType, unresolvedTypes)) { - return false; - } - } - } - - if (recordType.sealed) { - return true; - } - - return checkIsType(recordType.restFieldType, targetType, unresolvedTypes); - } - - private static boolean checkIsRecordType(Object sourceVal, Type sourceType, BRecordType targetType, - List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - return switch (sourceType.getTag()) { - case TypeTags.RECORD_TYPE_TAG -> - checkIsRecordType((MapValue) sourceVal, (BRecordType) sourceType, targetType, unresolvedTypes); - case TypeTags.MAP_TAG -> checkIsRecordType((BMapType) sourceType, targetType, unresolvedTypes); - default -> false; - }; - } - - private static boolean checkIsRecordType(MapValue sourceRecordValue, BRecordType sourceRecordType, - BRecordType targetType, List unresolvedTypes) { - TypePair pair = new TypePair(sourceRecordType, targetType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - - // Unsealed records are not equivalent to sealed records, unless their rest field type is 'never'. But - // vice-versa is allowed. - if (targetType.sealed && !sourceRecordType.sealed && (sourceRecordType.restFieldType == null || - getImpliedType(sourceRecordType.restFieldType).getTag() != TypeTags.NEVER_TAG)) { - return false; - } - - // If both are sealed check the rest field type - if (!sourceRecordType.sealed && !targetType.sealed && - !checkIsType(sourceRecordType.restFieldType, targetType.restFieldType, unresolvedTypes)) { - return false; - } - - Map sourceFields = sourceRecordType.getFields(); - Set targetFieldNames = targetType.getFields().keySet(); - - for (Map.Entry targetFieldEntry : targetType.getFields().entrySet()) { - String fieldName = targetFieldEntry.getKey(); - Field targetField = targetFieldEntry.getValue(); - Field sourceField = sourceFields.get(fieldName); - - if (getImpliedType(targetField.getFieldType()).getTag() == TypeTags.NEVER_TAG && - containsInvalidNeverField(sourceField, sourceRecordType)) { - return false; - } - - if (sourceField == null) { - if (!SymbolFlags.isFlagOn(targetField.getFlags(), SymbolFlags.OPTIONAL)) { - return false; - } - - if (!sourceRecordType.sealed && !checkIsType(sourceRecordType.restFieldType, targetField.getFieldType(), - unresolvedTypes)) { - return false; - } - - continue; - } - - if (hasIncompatibleReadOnlyFlags(targetField, sourceField)) { - return false; - } - - boolean optionalTargetField = SymbolFlags.isFlagOn(targetField.getFlags(), SymbolFlags.OPTIONAL); - boolean optionalSourceField = SymbolFlags.isFlagOn(sourceField.getFlags(), SymbolFlags.OPTIONAL); - - if (SymbolFlags.isFlagOn(sourceField.getFlags(), SymbolFlags.READONLY)) { - BString fieldNameBString = StringUtils.fromString(fieldName); - - if (optionalSourceField && !sourceRecordValue.containsKey(fieldNameBString)) { - if (!optionalTargetField) { - return false; - } - continue; - } - - if (!checkIsLikeType(sourceRecordValue.get(fieldNameBString), targetField.getFieldType())) { - return false; - } - } else { - if (!optionalTargetField && optionalSourceField) { - return false; - } - - if (!checkIsType(sourceField.getFieldType(), targetField.getFieldType(), unresolvedTypes)) { - return false; - } - } - } - - if (targetType.sealed) { - for (String sourceFieldName : sourceFields.keySet()) { - if (targetFieldNames.contains(sourceFieldName)) { - continue; - } - - if (!checkIsNeverTypeOrStructureTypeWithARequiredNeverMember( - sourceFields.get(sourceFieldName).getFieldType())) { - return false; - } - } - return true; - } - - for (Map.Entry targetFieldEntry : sourceFields.entrySet()) { - String fieldName = targetFieldEntry.getKey(); - Field field = targetFieldEntry.getValue(); - if (targetFieldNames.contains(fieldName)) { - continue; - } - - if (SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY)) { - if (!checkIsLikeType(sourceRecordValue.get(StringUtils.fromString(fieldName)), - targetType.restFieldType)) { - return false; - } - } else if (!checkIsType(field.getFieldType(), targetType.restFieldType, unresolvedTypes)) { - return false; - } - } - return true; - } - - private static boolean containsInvalidNeverField(Field sourceField, BRecordType sourceRecordType) { - if (sourceField != null) { - return !containsNeverType(sourceField.getFieldType()); - } - if (sourceRecordType.isSealed()) { - return true; - } - return !containsNeverType(sourceRecordType.getRestFieldType()); - } - - private static boolean containsNeverType(Type fieldType) { - fieldType = getImpliedType(fieldType); - int fieldTag = fieldType.getTag(); - if (fieldTag == TypeTags.NEVER_TAG) { - return true; - } - if (fieldTag == TypeTags.UNION_TAG) { - List memberTypes = ((BUnionType) fieldType).getOriginalMemberTypes(); - for (Type member : memberTypes) { - if (getImpliedType(member).getTag() == TypeTags.NEVER_TAG) { - return true; - } - } - } - return false; - } - - private static boolean hasIncompatibleReadOnlyFlags(Field targetField, Field sourceField) { - return SymbolFlags.isFlagOn(targetField.getFlags(), SymbolFlags.READONLY) && !SymbolFlags - .isFlagOn(sourceField.getFlags(), - SymbolFlags.READONLY); - } - - private static boolean checkIsArrayType(BArrayType sourceType, BArrayType targetType, - List unresolvedTypes) { - switch (sourceType.getState()) { - case OPEN: - if (targetType.getState() != ArrayState.OPEN) { - return false; - } - break; - case CLOSED: - if (targetType.getState() == ArrayState.CLOSED && - sourceType.getSize() != targetType.getSize()) { - return false; - } - break; - default: - break; - } - return checkIsType(sourceType.getElementType(), targetType.getElementType(), unresolvedTypes); - } - - private static boolean checkIsArrayType(BTupleType sourceType, BArrayType targetType, - List unresolvedTypes) { - List tupleTypes = sourceType.getTupleTypes(); - Type sourceRestType = sourceType.getRestType(); - Type targetElementType = targetType.getElementType(); - - if (targetType.getState() == ArrayState.OPEN) { - for (Type sourceElementType : tupleTypes) { - if (!checkIsType(sourceElementType, targetElementType, unresolvedTypes)) { - return false; - } - } - if (sourceRestType != null) { - return checkIsType(sourceRestType, targetElementType, unresolvedTypes); - } - return true; - } - if (sourceRestType != null) { - return false; - } - if (tupleTypes.size() != targetType.getSize()) { - return false; - } - for (Type sourceElementType : tupleTypes) { - if (!checkIsType(sourceElementType, targetElementType, unresolvedTypes)) { - return false; - } - } - return true; - } - - private static boolean checkIsArrayType(Type sourceType, BArrayType targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - int sourceTypeTag = sourceType.getTag(); - - if (sourceTypeTag == TypeTags.UNION_TAG) { - for (Type memberType : ((BUnionType) sourceType).getMemberTypes()) { - if (!checkIsArrayType(memberType, targetType, unresolvedTypes)) { - return false; - } - } - return true; - } - - if (sourceTypeTag != TypeTags.ARRAY_TAG && sourceTypeTag != TypeTags.TUPLE_TAG) { - return false; - } - - if (sourceTypeTag == TypeTags.ARRAY_TAG) { - return checkIsArrayType((BArrayType) sourceType, targetType, unresolvedTypes); - } - return checkIsArrayType((BTupleType) sourceType, targetType, unresolvedTypes); - } - - private static boolean checkIsTupleType(BArrayType sourceType, BTupleType targetType, - List unresolvedTypes) { - Type sourceElementType = sourceType.getElementType(); - List targetTypes = targetType.getTupleTypes(); - Type targetRestType = targetType.getRestType(); - - switch (sourceType.getState()) { - case OPEN: - if (targetRestType == null) { - return false; - } - if (targetTypes.isEmpty()) { - return checkIsType(sourceElementType, targetRestType, unresolvedTypes); - } - return false; - case CLOSED: - if (sourceType.getSize() < targetTypes.size()) { - return false; - } - if (targetTypes.isEmpty()) { - if (targetRestType != null) { - return checkIsType(sourceElementType, targetRestType, unresolvedTypes); - } - return sourceType.getSize() == 0; - } - - for (Type targetElementType : targetTypes) { - if (!(checkIsType(sourceElementType, targetElementType, unresolvedTypes))) { - return false; - } - } - if (sourceType.getSize() == targetTypes.size()) { - return true; - } - if (targetRestType != null) { - return checkIsType(sourceElementType, targetRestType, unresolvedTypes); - } - return false; - default: - return false; - } - } - - private static boolean checkIsTupleType(BTupleType sourceType, BTupleType targetType, - List unresolvedTypes) { - List sourceTypes = sourceType.getTupleTypes(); - Type sourceRestType = sourceType.getRestType(); - List targetTypes = targetType.getTupleTypes(); - Type targetRestType = targetType.getRestType(); - - if (sourceRestType != null && targetRestType == null) { - return false; - } - int sourceTypeSize = sourceTypes.size(); - int targetTypeSize = targetTypes.size(); - - if (sourceRestType == null && targetRestType == null && sourceTypeSize != targetTypeSize) { - return false; - } - - if (sourceTypeSize < targetTypeSize) { - return false; - } - - for (int i = 0; i < targetTypeSize; i++) { - if (!checkIsType(sourceTypes.get(i), targetTypes.get(i), unresolvedTypes)) { - return false; - } - } - if (sourceTypeSize == targetTypeSize) { - if (sourceRestType != null) { - return checkIsType(sourceRestType, targetRestType, unresolvedTypes); - } - return true; - } - - for (int i = targetTypeSize; i < sourceTypeSize; i++) { - if (!checkIsType(sourceTypes.get(i), targetRestType, unresolvedTypes)) { - return false; - } - } - if (sourceRestType != null) { - return checkIsType(sourceRestType, targetRestType, unresolvedTypes); - } - return true; - } - - private static boolean checkIsTupleType(Type sourceType, BTupleType targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - int sourceTypeTag = sourceType.getTag(); - - if (sourceTypeTag == TypeTags.UNION_TAG) { - for (Type memberType : ((BUnionType) sourceType).getMemberTypes()) { - if (!checkIsTupleType(memberType, targetType, unresolvedTypes)) { - return false; - } - } - return true; - } - - if (sourceTypeTag != TypeTags.ARRAY_TAG && sourceTypeTag != TypeTags.TUPLE_TAG) { - return false; - } - - if (sourceTypeTag == TypeTags.ARRAY_TAG) { - return checkIsTupleType((BArrayType) sourceType, targetType, unresolvedTypes); - } - return checkIsTupleType((BTupleType) sourceType, targetType, unresolvedTypes); - } - - private static boolean checkIsAnyType(Type sourceType) { - sourceType = getImpliedType(sourceType); - return switch (sourceType.getTag()) { - case TypeTags.ERROR_TAG, - TypeTags.READONLY_TAG -> false; - case TypeTags.UNION_TAG, - TypeTags.ANYDATA_TAG, - TypeTags.JSON_TAG -> { - for (Type memberType : ((BUnionType) sourceType).getMemberTypes()) { - if (!checkIsAnyType(memberType)) { - yield false; - } - } - yield true; - } - default -> true; - }; - } - - private static boolean checkIsFiniteType(Type sourceType, BFiniteType targetType) { - sourceType = getImpliedType(sourceType); - if (sourceType.getTag() != TypeTags.FINITE_TYPE_TAG) { - return false; - } - - BFiniteType sourceFiniteType = (BFiniteType) sourceType; - if (sourceFiniteType.valueSpace.size() != targetType.valueSpace.size()) { - return false; - } - - return targetType.valueSpace.containsAll(sourceFiniteType.valueSpace); - } - - private static boolean checkIsFutureType(Type sourceType, BFutureType targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - if (sourceType.getTag() != TypeTags.FUTURE_TAG) { - return false; - } - return checkConstraints(((BFutureType) sourceType).getConstrainedType(), targetType.getConstrainedType(), - unresolvedTypes); - } - - private static boolean checkObjectEquivalency(Type sourceType, BObjectType targetType, - List unresolvedTypes) { - return checkObjectEquivalency(null, sourceType, targetType, unresolvedTypes); - } - - private static boolean checkObjectEquivalency(Object sourceVal, Type sourceType, BObjectType targetType, - List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - if (sourceType.getTag() != TypeTags.OBJECT_TYPE_TAG && sourceType.getTag() != TypeTags.SERVICE_TAG) { - return false; - } - // If we encounter two types that we are still resolving, then skip it. - // This is done to avoid recursive checking of the same type. - TypePair pair = new TypePair(sourceType, targetType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - - BObjectType sourceObjectType = (BObjectType) sourceType; - - if (SymbolFlags.isFlagOn(targetType.flags, SymbolFlags.ISOLATED) && - !SymbolFlags.isFlagOn(sourceObjectType.flags, SymbolFlags.ISOLATED)) { - return false; - } - - Map targetFields = targetType.getFields(); - Map sourceFields = sourceObjectType.getFields(); - List targetFuncs = getAllFunctionsList(targetType); - List sourceFuncs = getAllFunctionsList(sourceObjectType); - - if (targetType.getFields().values().stream().anyMatch(field -> SymbolFlags - .isFlagOn(field.getFlags(), SymbolFlags.PRIVATE)) - || targetFuncs.stream().anyMatch(func -> SymbolFlags.isFlagOn(func.getFlags(), - SymbolFlags.PRIVATE))) { - return false; - } - - if (targetFields.size() > sourceFields.size() || targetFuncs.size() > sourceFuncs.size()) { - return false; - } - - String targetTypeModule = Optional.ofNullable(targetType.getPackage()).map(Module::toString).orElse(""); - String sourceTypeModule = Optional.ofNullable(sourceObjectType.getPackage()).map(Module::toString).orElse(""); - - if (sourceVal == null) { - if (!checkObjectSubTypeForFields(targetFields, sourceFields, targetTypeModule, sourceTypeModule, - unresolvedTypes)) { - return false; - } - } else if (!checkObjectSubTypeForFieldsByValue(targetFields, sourceFields, targetTypeModule, sourceTypeModule, - (BObject) sourceVal, unresolvedTypes)) { - return false; - } - - return checkObjectSubTypeForMethods(unresolvedTypes, targetFuncs, sourceFuncs, targetTypeModule, - sourceTypeModule, sourceObjectType, targetType); - } - - private static List getAllFunctionsList(BObjectType objectType) { - List functionList = new ArrayList<>(Arrays.asList(objectType.getMethods())); - if (objectType.getTag() == TypeTags.SERVICE_TAG || - (objectType.flags & SymbolFlags.CLIENT) == SymbolFlags.CLIENT) { - Collections.addAll(functionList, ((BNetworkObjectType) objectType).getResourceMethods()); - } - - return functionList; - } - - private static boolean checkObjectSubTypeForFields(Map targetFields, - Map sourceFields, String targetTypeModule, - String sourceTypeModule, List unresolvedTypes) { - for (Field lhsField : targetFields.values()) { - Field rhsField = sourceFields.get(lhsField.getFieldName()); - if (rhsField == null || - !isInSameVisibilityRegion(targetTypeModule, sourceTypeModule, lhsField.getFlags(), - rhsField.getFlags()) || hasIncompatibleReadOnlyFlags(lhsField, - rhsField) || - !checkIsType(rhsField.getFieldType(), lhsField.getFieldType(), unresolvedTypes)) { - return false; - } - } - return true; - } - - private static boolean checkObjectSubTypeForFieldsByValue(Map targetFields, - Map sourceFields, String targetTypeModule, - String sourceTypeModule, BObject sourceObjVal, - List unresolvedTypes) { - for (Field lhsField : targetFields.values()) { - String name = lhsField.getFieldName(); - Field rhsField = sourceFields.get(name); - if (rhsField == null || - !isInSameVisibilityRegion(targetTypeModule, sourceTypeModule, lhsField.getFlags(), - rhsField.getFlags()) || hasIncompatibleReadOnlyFlags(lhsField, - rhsField)) { - return false; - } - - if (SymbolFlags.isFlagOn(rhsField.getFlags(), SymbolFlags.FINAL)) { - Object fieldValue = sourceObjVal.get(StringUtils.fromString(name)); - Type fieldValueType = getType(fieldValue); - - if (fieldValueType.isReadOnly()) { - if (!checkIsLikeType(fieldValue, lhsField.getFieldType())) { - return false; - } - continue; - } - - if (!checkIsType(fieldValueType, lhsField.getFieldType(), unresolvedTypes)) { - return false; - } - } else if (!checkIsType(rhsField.getFieldType(), lhsField.getFieldType(), unresolvedTypes)) { - return false; - } - } - return true; - } - - private static boolean checkObjectSubTypeForMethods(List unresolvedTypes, - List targetFuncs, - List sourceFuncs, - String targetTypeModule, String sourceTypeModule, - BObjectType sourceType, BObjectType targetType) { - for (MethodType lhsFunc : targetFuncs) { - Optional rhsFunction = getMatchingInvokableType(sourceFuncs, lhsFunc, unresolvedTypes); - if (rhsFunction.isEmpty()) { - return false; - } - - MethodType rhsFunc = rhsFunction.get(); - if (!isInSameVisibilityRegion(targetTypeModule, sourceTypeModule, lhsFunc.getFlags(), rhsFunc.getFlags())) { - return false; - } - if (SymbolFlags.isFlagOn(lhsFunc.getFlags(), SymbolFlags.REMOTE) != SymbolFlags - .isFlagOn(rhsFunc.getFlags(), SymbolFlags.REMOTE)) { - return false; - } - } - - // Target type is not a distinct type, no need to match type-ids - BTypeIdSet targetTypeIdSet = targetType.typeIdSet; - if (targetTypeIdSet == null) { - return true; - } - - BTypeIdSet sourceTypeIdSet = sourceType.typeIdSet; - if (sourceTypeIdSet == null) { - return false; - } - - return sourceTypeIdSet.containsAll(targetTypeIdSet); - } - - private static boolean isInSameVisibilityRegion(String lhsTypePkg, String rhsTypePkg, long lhsFlags, - long rhsFlags) { - if (SymbolFlags.isFlagOn(lhsFlags, SymbolFlags.PRIVATE)) { - return lhsTypePkg.equals(rhsTypePkg); - } else if (SymbolFlags.isFlagOn(lhsFlags, SymbolFlags.PUBLIC)) { - return SymbolFlags.isFlagOn(rhsFlags, SymbolFlags.PUBLIC); - } - return !SymbolFlags.isFlagOn(rhsFlags, SymbolFlags.PRIVATE) && !SymbolFlags - .isFlagOn(rhsFlags, SymbolFlags.PUBLIC) && - lhsTypePkg.equals(rhsTypePkg); - } - - private static Optional getMatchingInvokableType(List rhsFuncs, - MethodType lhsFunc, - List unresolvedTypes) { - Optional matchingFunction = rhsFuncs.stream() - .filter(rhsFunc -> lhsFunc.getName().equals(rhsFunc.getName())) - .filter(rhsFunc -> checkFunctionTypeEqualityForObjectType(rhsFunc.getType(), lhsFunc.getType(), - unresolvedTypes)) - .findFirst(); - - if (matchingFunction.isEmpty()) { - return matchingFunction; - } - // For resource function match, we need to check whether lhs function resource path type belongs to - // rhs function resource path type - MethodType matchingFunc = matchingFunction.get(); - boolean lhsFuncIsResource = SymbolFlags.isFlagOn(lhsFunc.getFlags(), SymbolFlags.RESOURCE); - boolean matchingFuncIsResource = SymbolFlags.isFlagOn(matchingFunc.getFlags(), SymbolFlags.RESOURCE); - - if (!lhsFuncIsResource && !matchingFuncIsResource) { - return matchingFunction; - } - - if (!lhsFuncIsResource || !matchingFuncIsResource) { - return Optional.empty(); - } - - Type[] lhsFuncResourcePathTypes = ((BResourceMethodType) lhsFunc).pathSegmentTypes; - Type[] rhsFuncResourcePathTypes = ((BResourceMethodType) matchingFunc).pathSegmentTypes; - - int lhsFuncResourcePathTypesSize = lhsFuncResourcePathTypes.length; - if (lhsFuncResourcePathTypesSize != rhsFuncResourcePathTypes.length) { - return Optional.empty(); - } - - for (int i = 0; i < lhsFuncResourcePathTypesSize; i++) { - if (!checkIsType(lhsFuncResourcePathTypes[i], rhsFuncResourcePathTypes[i])) { - return Optional.empty(); - } - } - - return matchingFunction; - } - - private static boolean checkFunctionTypeEqualityForObjectType(FunctionType source, FunctionType target, - List unresolvedTypes) { - if (hasIncompatibleIsolatedFlags(target, source)) { - return false; - } - - if (source.getParameters().length != target.getParameters().length) { - return false; - } - - for (int i = 0; i < source.getParameters().length; i++) { - if (!checkIsType(target.getParameters()[i].type, source.getParameters()[i].type, unresolvedTypes)) { - return false; - } - } - - if (source.getReturnType() == null && target.getReturnType() == null) { - return true; - } else if (source.getReturnType() == null || target.getReturnType() == null) { - return false; - } - - return checkIsType(source.getReturnType(), target.getReturnType(), unresolvedTypes); - } - - private static boolean checkIsFunctionType(Type sourceType, BFunctionType targetType) { - sourceType = getImpliedType(sourceType); - if (sourceType.getTag() != TypeTags.FUNCTION_POINTER_TAG) { - return false; - } - - BFunctionType source = (BFunctionType) sourceType; - if (hasIncompatibleIsolatedFlags(targetType, source) || hasIncompatibleTransactionalFlags(targetType, source)) { - return false; - } - - if (SymbolFlags.isFlagOn(targetType.getFlags(), SymbolFlags.ANY_FUNCTION)) { - return true; - } - - if (source.parameters.length != targetType.parameters.length) { - return false; - } - - for (int i = 0; i < source.parameters.length; i++) { - if (!checkIsType(targetType.parameters[i].type, source.parameters[i].type, new ArrayList<>())) { - return false; - } - } - - return checkIsType(source.retType, targetType.retType, new ArrayList<>()); - } - - private static boolean hasIncompatibleIsolatedFlags(FunctionType target, FunctionType source) { - return SymbolFlags.isFlagOn(target.getFlags(), SymbolFlags.ISOLATED) && !SymbolFlags - .isFlagOn(source.getFlags(), SymbolFlags.ISOLATED); - } - - private static boolean hasIncompatibleTransactionalFlags(FunctionType target, FunctionType source) { - return SymbolFlags.isFlagOn(source.getFlags(), SymbolFlags.TRANSACTIONAL) && !SymbolFlags - .isFlagOn(target.getFlags(), SymbolFlags.TRANSACTIONAL); - } - - private static boolean checkIsServiceType(Type sourceType, Type targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - if (sourceType.getTag() == TypeTags.SERVICE_TAG) { - return checkObjectEquivalency(sourceType, (BObjectType) targetType, unresolvedTypes); - } - - if (sourceType.getTag() == TypeTags.OBJECT_TYPE_TAG) { - long flags = ((BObjectType) sourceType).flags; - return (flags & SymbolFlags.SERVICE) == SymbolFlags.SERVICE; - } - - return false; - } - - public static boolean isInherentlyImmutableType(Type sourceType) { - sourceType = getImpliedType(sourceType); - if (isSimpleBasicType(sourceType)) { - return true; - } - - return switch (sourceType.getTag()) { - case TypeTags.XML_TEXT_TAG, - TypeTags.FINITE_TYPE_TAG, // Assuming a finite type will only have members from simple basic types. - TypeTags.READONLY_TAG, - TypeTags.NULL_TAG, - TypeTags.NEVER_TAG, - TypeTags.ERROR_TAG, - TypeTags.INVOKABLE_TAG, - TypeTags.SERVICE_TAG, - TypeTags.TYPEDESC_TAG, - TypeTags.FUNCTION_POINTER_TAG, - TypeTags.HANDLE_TAG, - TypeTags.REG_EXP_TYPE_TAG -> true; - case TypeTags.XML_TAG -> ((BXmlType) sourceType).constraint.getTag() == TypeTags.NEVER_TAG; - case TypeTags.TYPE_REFERENCED_TYPE_TAG -> - isInherentlyImmutableType(((BTypeReferenceType) sourceType).getReferredType()); - default -> false; - }; - } - - public static boolean isSelectivelyImmutableType(Type type, Set unresolvedTypes) { - if (!unresolvedTypes.add(type)) { - return true; - } - - switch (type.getTag()) { - case TypeTags.ANY_TAG: - case TypeTags.ANYDATA_TAG: - case TypeTags.JSON_TAG: - case TypeTags.XML_TAG: - case TypeTags.XML_COMMENT_TAG: - case TypeTags.XML_ELEMENT_TAG: - case TypeTags.XML_PI_TAG: - return true; - case TypeTags.ARRAY_TAG: - Type elementType = ((BArrayType) type).getElementType(); - return isInherentlyImmutableType(elementType) || - isSelectivelyImmutableType(elementType, unresolvedTypes); - case TypeTags.TUPLE_TAG: - BTupleType tupleType = (BTupleType) type; - for (Type tupMemType : tupleType.getTupleTypes()) { - if (!isInherentlyImmutableType(tupMemType) && - !isSelectivelyImmutableType(tupMemType, unresolvedTypes)) { - return false; - } - } - - Type tupRestType = tupleType.getRestType(); - if (tupRestType == null) { - return true; - } - - return isInherentlyImmutableType(tupRestType) || - isSelectivelyImmutableType(tupRestType, unresolvedTypes); - case TypeTags.RECORD_TYPE_TAG: - BRecordType recordType = (BRecordType) type; - for (Field field : recordType.getFields().values()) { - Type fieldType = field.getFieldType(); - if (!isInherentlyImmutableType(fieldType) && - !isSelectivelyImmutableType(fieldType, unresolvedTypes)) { - return false; - } - } - - Type recordRestType = recordType.restFieldType; - if (recordRestType == null) { - return true; - } - - return isInherentlyImmutableType(recordRestType) || - isSelectivelyImmutableType(recordRestType, unresolvedTypes); - case TypeTags.OBJECT_TYPE_TAG: - BObjectType objectType = (BObjectType) type; - - if (SymbolFlags.isFlagOn(objectType.flags, SymbolFlags.CLASS) && - !SymbolFlags.isFlagOn(objectType.flags, SymbolFlags.READONLY)) { - return false; - } - - for (Field field : objectType.getFields().values()) { - Type fieldType = field.getFieldType(); - if (!isInherentlyImmutableType(fieldType) && - !isSelectivelyImmutableType(fieldType, unresolvedTypes)) { - return false; - } - } - return true; - case TypeTags.MAP_TAG: - Type constraintType = ((BMapType) type).getConstrainedType(); - return isInherentlyImmutableType(constraintType) || - isSelectivelyImmutableType(constraintType, unresolvedTypes); - case TypeTags.TABLE_TAG: - Type tableConstraintType = ((BTableType) type).getConstrainedType(); - return isInherentlyImmutableType(tableConstraintType) || - isSelectivelyImmutableType(tableConstraintType, unresolvedTypes); - case TypeTags.UNION_TAG: - boolean readonlyIntersectionExists = false; - for (Type memberType : ((BUnionType) type).getMemberTypes()) { - if (isInherentlyImmutableType(memberType) || - isSelectivelyImmutableType(memberType, unresolvedTypes)) { - readonlyIntersectionExists = true; - break; - } - } - return readonlyIntersectionExists; - case TypeTags.INTERSECTION_TAG: - return isSelectivelyImmutableType(((BIntersectionType) type).getEffectiveType(), unresolvedTypes); - case TypeTags.TYPE_REFERENCED_TYPE_TAG: - return isSelectivelyImmutableType(((BTypeReferenceType) type).getReferredType(), unresolvedTypes); - default: - return false; - } - } - - private static boolean checkConstraints(Type sourceConstraint, Type targetConstraint, - List unresolvedTypes) { - if (sourceConstraint == null) { - sourceConstraint = TYPE_ANY; - } - - if (targetConstraint == null) { - targetConstraint = TYPE_ANY; - } - - return checkIsType(sourceConstraint, targetConstraint, unresolvedTypes); - } - - private static boolean isMutable(Object value, Type sourceType) { - // All the value types are immutable - sourceType = getImpliedType(sourceType); - if (value == null || sourceType.getTag() < TypeTags.NULL_TAG || - sourceType.getTag() == TypeTags.FINITE_TYPE_TAG) { - return false; - } - - return !((BRefValue) value).isFrozen(); - } - - private static boolean checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(Type type) { - Set visitedTypeSet = new HashSet<>(); - return checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(type, visitedTypeSet); - } - - private static boolean checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(Type type, - Set visitedTypeSet) { - switch (type.getTag()) { - case TypeTags.NEVER_TAG: - return true; - case TypeTags.RECORD_TYPE_TAG: - BRecordType recordType = (BRecordType) type; - visitedTypeSet.add(recordType.getName()); - for (Field field : recordType.getFields().values()) { - // skip check for fields with self referencing type and not required fields. - if ((SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.REQUIRED) || - !SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL)) && - !visitedTypeSet.contains(field.getFieldType()) && - checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(field.getFieldType(), - visitedTypeSet)) { - return true; - } - } - return false; - case TypeTags.TUPLE_TAG: - BTupleType tupleType = (BTupleType) type; - visitedTypeSet.add(tupleType.getName()); - List tupleTypes = tupleType.getTupleTypes(); - for (Type mem : tupleTypes) { - if (!visitedTypeSet.add(mem.getName())) { - continue; - } - if (checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(mem, visitedTypeSet)) { - return true; - } - } - return false; - case TypeTags.ARRAY_TAG: - BArrayType arrayType = (BArrayType) type; - visitedTypeSet.add(arrayType.getName()); - Type elemType = arrayType.getElementType(); - visitedTypeSet.add(elemType.getName()); - return arrayType.getState() != ArrayState.OPEN && - checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(elemType, visitedTypeSet); - case TypeTags.TYPE_REFERENCED_TYPE_TAG: - return checkIsNeverTypeOrStructureTypeWithARequiredNeverMember( - ((BTypeReferenceType) type).getReferredType(), visitedTypeSet); - case TypeTags.INTERSECTION_TAG: - return checkIsNeverTypeOrStructureTypeWithARequiredNeverMember( - ((BIntersectionType) type).getEffectiveType(), visitedTypeSet); - default: - return false; - } - } - - /** - * Check whether a given value confirms to a given type. First it checks if the type of the value, and - * if fails then falls back to checking the value. - * - * @param errors list to collect typecast errors - * @param sourceValue Value to check - * @param targetType Target type - * @param unresolvedValues Values that are unresolved so far - * @param allowNumericConversion Flag indicating whether to perform numeric conversions - * @param varName variable name to identify the parent of a record field - * @return True if the value confirms to the provided type. False, otherwise. - */ - private static boolean checkIsLikeType(List errors, Object sourceValue, Type targetType, - List unresolvedValues, - boolean allowNumericConversion, String varName) { - Type sourceType = getType(sourceValue); - if (checkIsType(sourceType, targetType, new ArrayList<>())) { - return true; - } - - return checkIsLikeOnValue(errors, sourceValue, sourceType, targetType, unresolvedValues, allowNumericConversion, - varName); - } - - /** - * Check whether a given value confirms to a given type. Strictly checks the value only, and does not consider the - * type of the value for consideration. - * - * @param errors list to collect typecast errors - * @param sourceValue Value to check - * @param sourceType Type of the value - * @param targetType Target type - * @param unresolvedValues Values that are unresolved so far - * @param allowNumericConversion Flag indicating whether to perform numeric conversions - * @param varName variable name to identify the parent of a record field - * @return True if the value confirms to the provided type. False, otherwise. - */ - private static boolean checkIsLikeOnValue(List errors, Object sourceValue, Type sourceType, Type targetType, - List unresolvedValues, boolean allowNumericConversion, - String varName) { - int sourceTypeTag = sourceType.getTag(); - int targetTypeTag = targetType.getTag(); - - switch (sourceTypeTag) { - case TypeTags.INTERSECTION_TAG: - return checkIsLikeOnValue(errors, sourceValue, ((BIntersectionType) sourceType).getEffectiveType(), - targetTypeTag != TypeTags.INTERSECTION_TAG ? targetType : - ((BIntersectionType) targetType).getEffectiveType(), - unresolvedValues, allowNumericConversion, varName); - case TypeTags.PARAMETERIZED_TYPE_TAG: - if (targetTypeTag != TypeTags.PARAMETERIZED_TYPE_TAG) { - return checkIsLikeOnValue(errors, sourceValue, - ((BParameterizedType) sourceType).getParamValueType(), targetType, unresolvedValues, - allowNumericConversion, varName); - } - return checkIsLikeOnValue(errors, sourceValue, ((BParameterizedType) sourceType).getParamValueType(), - ((BParameterizedType) targetType).getParamValueType(), unresolvedValues, - allowNumericConversion, varName); - default: - break; - } - - return switch (targetTypeTag) { - case TypeTags.READONLY_TAG -> true; - case TypeTags.BYTE_TAG -> { - if (TypeTags.isIntegerTypeTag(sourceTypeTag)) { - yield isByteLiteral((Long) sourceValue); - } - yield allowNumericConversion && TypeConverter.isConvertibleToByte(sourceValue); - } - case TypeTags.INT_TAG -> allowNumericConversion && TypeConverter.isConvertibleToInt(sourceValue); - case TypeTags.SIGNED32_INT_TAG, - TypeTags.SIGNED16_INT_TAG, - TypeTags.SIGNED8_INT_TAG, - TypeTags.UNSIGNED32_INT_TAG, - TypeTags.UNSIGNED16_INT_TAG, - TypeTags.UNSIGNED8_INT_TAG -> { - if (TypeTags.isIntegerTypeTag(sourceTypeTag)) { - yield TypeConverter.isConvertibleToIntSubType(sourceValue, targetType); - } - yield allowNumericConversion && TypeConverter.isConvertibleToIntSubType(sourceValue, targetType); - } - case TypeTags.FLOAT_TAG, - TypeTags.DECIMAL_TAG -> - allowNumericConversion && TypeConverter.isConvertibleToFloatingPointTypes(sourceValue); - case TypeTags.CHAR_STRING_TAG -> TypeConverter.isConvertibleToChar(sourceValue); - case TypeTags.RECORD_TYPE_TAG -> - checkIsLikeRecordType(sourceValue, (BRecordType) targetType, unresolvedValues, - allowNumericConversion, varName, errors); - case TypeTags.TABLE_TAG -> checkIsLikeTableType(sourceValue, (BTableType) targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.JSON_TAG -> - checkIsLikeJSONType(sourceValue, sourceType, (BJsonType) targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.MAP_TAG -> - checkIsLikeMapType(sourceValue, (BMapType) targetType, unresolvedValues, allowNumericConversion); - case TypeTags.STREAM_TAG -> checkIsLikeStreamType(sourceValue, (BStreamType) targetType); - case TypeTags.ARRAY_TAG -> checkIsLikeArrayType(sourceValue, (BArrayType) targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.TUPLE_TAG -> checkIsLikeTupleType(sourceValue, (BTupleType) targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.ERROR_TAG -> checkIsLikeErrorType(sourceValue, (BErrorType) targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.ANYDATA_TAG -> - checkIsLikeAnydataType(sourceValue, sourceType, unresolvedValues, allowNumericConversion); - case TypeTags.FINITE_TYPE_TAG -> - checkFiniteTypeAssignable(sourceValue, sourceType, (BFiniteType) targetType, - unresolvedValues, allowNumericConversion); - case TypeTags.XML_ELEMENT_TAG, - TypeTags.XML_COMMENT_TAG, - TypeTags.XML_PI_TAG, - TypeTags.XML_TEXT_TAG -> { - if (TypeTags.isXMLTypeTag(sourceTypeTag)) { - yield checkIsLikeXmlValueSingleton((XmlValue) sourceValue, targetType); - } - yield false; - } - case TypeTags.XML_TAG -> { - if (TypeTags.isXMLTypeTag(sourceTypeTag)) { - yield checkIsLikeXMLSequenceType((XmlValue) sourceValue, targetType); - } - yield false; - } - case TypeTags.UNION_TAG -> - checkIsLikeUnionType(errors, sourceValue, (BUnionType) targetType, unresolvedValues, - allowNumericConversion, varName); - case TypeTags.INTERSECTION_TAG -> checkIsLikeOnValue(errors, sourceValue, sourceType, - ((BIntersectionType) targetType).getEffectiveType(), unresolvedValues, allowNumericConversion, - varName); - case TypeTags.TYPE_REFERENCED_TYPE_TAG -> checkIsLikeOnValue(errors, sourceValue, sourceType, - ((BTypeReferenceType) targetType).getReferredType(), unresolvedValues, allowNumericConversion, - varName); - default -> false; - }; - } - - private static boolean checkIsLikeUnionType(List errors, Object sourceValue, BUnionType targetType, - List unresolvedValues, boolean allowNumericConversion, - String varName) { - if (allowNumericConversion) { - List compatibleTypesWithNumConversion = new ArrayList<>(); - List compatibleTypesWithoutNumConversion = new ArrayList<>(); - for (Type type : targetType.getMemberTypes()) { - List tempList = new ArrayList<>(unresolvedValues.size()); - tempList.addAll(unresolvedValues); - - if (checkIsLikeType(null, sourceValue, type, tempList, false, varName)) { - compatibleTypesWithoutNumConversion.add(type); - } - - if (checkIsLikeType(null, sourceValue, type, unresolvedValues, true, varName)) { - compatibleTypesWithNumConversion.add(type); - } - } - // Conversion should only be possible to one other numeric type. - return !compatibleTypesWithNumConversion.isEmpty() && - compatibleTypesWithNumConversion.size() - compatibleTypesWithoutNumConversion.size() <= 1; - } else { - return checkIsLikeUnionType(errors, sourceValue, targetType, unresolvedValues, varName); - } - } - - private static boolean checkIsLikeUnionType(List errors, Object sourceValue, BUnionType targetType, - List unresolvedValues, String varName) { - if (errors == null) { - for (Type type : targetType.getMemberTypes()) { - if (checkIsLikeType(null, sourceValue, type, unresolvedValues, false, varName)) { - return true; - } - } - } else { - int initialErrorCount; - errors.add(ERROR_MESSAGE_UNION_START); - int initialErrorListSize = errors.size(); - for (Type type : targetType.getMemberTypes()) { - initialErrorCount = errors.size(); - if (checkIsLikeType(errors, sourceValue, type, unresolvedValues, false, varName)) { - errors.subList(initialErrorListSize - 1, errors.size()).clear(); - return true; - } - if (initialErrorCount != errors.size()) { - errors.add(ERROR_MESSAGE_UNION_SEPARATOR); - } - } - int currentErrorListSize = errors.size(); - errors.remove(currentErrorListSize - 1); - if (initialErrorListSize != currentErrorListSize) { - errors.add(ERROR_MESSAGE_UNION_END); - } - } - return false; - } - private static XmlNodeType getXmlNodeType(Type type) { - return switch (getImpliedType(type).getTag()) { - case TypeTags.XML_ELEMENT_TAG -> XmlNodeType.ELEMENT; - case TypeTags.XML_COMMENT_TAG -> XmlNodeType.COMMENT; - case TypeTags.XML_PI_TAG -> XmlNodeType.PI; - default -> XmlNodeType.TEXT; - }; - } - - private static boolean checkIsLikeXmlValueSingleton(XmlValue xmlSource, Type targetType) { - XmlNodeType targetXmlNodeType = getXmlNodeType(targetType); - XmlNodeType xmlSourceNodeType = xmlSource.getNodeType(); - - if (xmlSourceNodeType == targetXmlNodeType) { - return true; - } - - if (xmlSourceNodeType == XmlNodeType.SEQUENCE) { - XmlSequence seq = (XmlSequence) xmlSource; - return seq.size() == 1 && seq.getChildrenList().get(0).getNodeType() == targetXmlNodeType || - (targetXmlNodeType == XmlNodeType.TEXT && seq.isEmpty()); - } - - return false; - } - - private static void populateTargetXmlNodeTypes(Set nodeTypes, Type targetType) { - // there are only 4 xml subtypes - if (nodeTypes.size() == 4) { - return; - } - - Type referredType = getImpliedType(targetType); - switch (referredType.getTag()) { - case TypeTags.UNION_TAG: - for (Type memberType : ((UnionType) referredType).getMemberTypes()) { - populateTargetXmlNodeTypes(nodeTypes, memberType); - } - break; - case TypeTags.INTERSECTION_TAG: - populateTargetXmlNodeTypes(nodeTypes, ((IntersectionType) referredType).getEffectiveType()); - break; - case TypeTags.XML_ELEMENT_TAG: - nodeTypes.add(XmlNodeType.ELEMENT); - break; - case TypeTags.XML_COMMENT_TAG: - nodeTypes.add(XmlNodeType.COMMENT); - break; - case TypeTags.XML_PI_TAG: - nodeTypes.add(XmlNodeType.PI); - break; - case TypeTags.XML_TEXT_TAG: - nodeTypes.add(XmlNodeType.TEXT); - break; - case TypeTags.XML_TAG: - populateTargetXmlNodeTypes(nodeTypes, ((BXmlType) referredType).constraint); - break; - default: - break; - - } - } - - private static boolean checkIsLikeXMLSequenceType(XmlValue xmlSource, Type targetType) { - Set acceptedNodeTypes = new HashSet<>(); - populateTargetXmlNodeTypes(acceptedNodeTypes, targetType); - - XmlNodeType xmlSourceNodeType = xmlSource.getNodeType(); - if (xmlSourceNodeType != XmlNodeType.SEQUENCE) { - return acceptedNodeTypes.contains(xmlSourceNodeType); - } - - XmlSequence seq = (XmlSequence) xmlSource; - for (BXml m : seq.getChildrenList()) { - if (!acceptedNodeTypes.contains(m.getNodeType())) { - return false; - } - } - return true; - } - - public static boolean isNumericType(Type type) { - type = getImpliedType(type); - return type.getTag() < TypeTags.STRING_TAG || TypeTags.isIntegerTypeTag(type.getTag()); - } - - private static boolean checkIsLikeAnydataType(Object sourceValue, Type sourceType, - List unresolvedValues, - boolean allowNumericConversion) { - sourceType = getImpliedType(sourceType); - switch (sourceType.getTag()) { + return isInherentlyImmutableType(tupRestType) || + isSelectivelyImmutableType(tupRestType, unresolvedTypes); case TypeTags.RECORD_TYPE_TAG: - case TypeTags.MAP_TAG: - return isLikeAnydataType(((MapValueImpl) sourceValue).values().toArray(), - unresolvedValues, allowNumericConversion); - case TypeTags.TABLE_TAG: - return isLikeAnydataType(((TableValueImpl) sourceValue).values().toArray(), - unresolvedValues, allowNumericConversion); - case TypeTags.ARRAY_TAG: - ArrayValue arr = (ArrayValue) sourceValue; - BArrayType arrayType = (BArrayType) getImpliedType(arr.getType()); - return switch (getImpliedType(arrayType.getElementType()).getTag()) { - case TypeTags.INT_TAG, - TypeTags.FLOAT_TAG, - TypeTags.DECIMAL_TAG, - TypeTags.STRING_TAG, - TypeTags.BOOLEAN_TAG, - TypeTags.BYTE_TAG -> true; - default -> isLikeAnydataType(arr.getValues(), unresolvedValues, allowNumericConversion); - }; - case TypeTags.TUPLE_TAG: - return isLikeAnydataType(((ArrayValue) sourceValue).getValues(), unresolvedValues, - allowNumericConversion); - default: - return sourceType.isAnydata(); - } - } - - private static boolean isLikeAnydataType(Object[] objects, List unresolvedValues, - boolean allowNumericConversion) { - for (Object value : objects) { - if (!checkIsLikeType(null, value, TYPE_ANYDATA, unresolvedValues, allowNumericConversion, - null)) { - return false; - } - } - return true; - } - - private static boolean checkIsLikeTupleType(Object sourceValue, BTupleType targetType, - List unresolvedValues, boolean allowNumericConversion) { - if (!(sourceValue instanceof ArrayValue source)) { - return false; - } - - List targetTypes = targetType.getTupleTypes(); - int sourceTypeSize = source.size(); - int targetTypeSize = targetTypes.size(); - Type targetRestType = targetType.getRestType(); - - if (sourceTypeSize < targetTypeSize) { - return false; - } - if (targetRestType == null && sourceTypeSize > targetTypeSize) { - return false; - } - - for (int i = 0; i < targetTypeSize; i++) { - if (!checkIsLikeType(null, source.getRefValue(i), targetTypes.get(i), unresolvedValues, - allowNumericConversion, null)) { - return false; - } - } - for (int i = targetTypeSize; i < sourceTypeSize; i++) { - if (!checkIsLikeType(null, source.getRefValue(i), targetRestType, unresolvedValues, - allowNumericConversion, null)) { - return false; - } - } - return true; - } - - public static boolean isByteLiteral(long longValue) { - return (longValue >= BBYTE_MIN_VALUE && longValue <= BBYTE_MAX_VALUE); - } - - static boolean isSigned32LiteralValue(Long longObject) { - - return (longObject >= SIGNED32_MIN_VALUE && longObject <= SIGNED32_MAX_VALUE); - } - - static boolean isSigned16LiteralValue(Long longObject) { - - return (longObject.intValue() >= SIGNED16_MIN_VALUE && longObject.intValue() <= SIGNED16_MAX_VALUE); - } - - static boolean isSigned8LiteralValue(Long longObject) { - - return (longObject.intValue() >= SIGNED8_MIN_VALUE && longObject.intValue() <= SIGNED8_MAX_VALUE); - } - - static boolean isUnsigned32LiteralValue(Long longObject) { - - return (longObject >= 0 && longObject <= UNSIGNED32_MAX_VALUE); - } - - static boolean isUnsigned16LiteralValue(Long longObject) { - - return (longObject.intValue() >= 0 && longObject.intValue() <= UNSIGNED16_MAX_VALUE); - } - - static boolean isUnsigned8LiteralValue(Long longObject) { - - return (longObject.intValue() >= 0 && longObject.intValue() <= UNSIGNED8_MAX_VALUE); - } - - static boolean isCharLiteralValue(Object object) { - String value; - if (object instanceof BString bString) { - value = bString.getValue(); - } else if (object instanceof String s) { - value = s; - } else { - return false; - } - return value.codePoints().count() == 1; - } - - private static boolean checkIsLikeArrayType(Object sourceValue, BArrayType targetType, - List unresolvedValues, boolean allowNumericConversion) { - if (!(sourceValue instanceof ArrayValue source)) { - return false; - } - - Type targetTypeElementType = targetType.getElementType(); - if (source.getType().getTag() == TypeTags.ARRAY_TAG) { - Type sourceElementType = ((BArrayType) source.getType()).getElementType(); - if (isValueType(sourceElementType)) { - - if (checkIsType(sourceElementType, targetTypeElementType, new ArrayList<>())) { - return true; - } - - if (allowNumericConversion && isNumericType(sourceElementType)) { - if (isNumericType(targetTypeElementType)) { - return true; - } - - if (targetTypeElementType.getTag() != TypeTags.UNION_TAG) { + BRecordType recordType = (BRecordType) type; + for (Field field : recordType.getFields().values()) { + Type fieldType = field.getFieldType(); + if (!isInherentlyImmutableType(fieldType) && + !isSelectivelyImmutableType(fieldType, unresolvedTypes)) { return false; } - - List targetNumericTypes = new ArrayList<>(); - for (Type memType : ((BUnionType) targetTypeElementType).getMemberTypes()) { - if (isNumericType(memType) && !targetNumericTypes.contains(memType)) { - targetNumericTypes.add(memType); - } - } - return targetNumericTypes.size() == 1; } - if (targetTypeElementType.getTag() == TypeTags.FLOAT_TAG || - targetTypeElementType.getTag() == TypeTags.DECIMAL_TAG) { - return false; + Type recordRestType = recordType.restFieldType; + if (recordRestType == null) { + return true; } - } - } - - int sourceSize = source.size(); - if ((targetType.getState() != ArrayState.OPEN) && (sourceSize != targetType.getSize())) { - return false; - } - for (int i = 0; i < sourceSize; i++) { - if (!checkIsLikeType(null, source.get(i), targetTypeElementType, unresolvedValues, - allowNumericConversion, null)) { - return false; - } - } - return true; - } - private static boolean checkIsLikeMapType(Object sourceValue, BMapType targetType, - List unresolvedValues, boolean allowNumericConversion) { - if (!(sourceValue instanceof MapValueImpl sourceMapValue)) { - return false; - } - - for (Object mapEntry : sourceMapValue.values()) { - if (!checkIsLikeType(null, mapEntry, targetType.getConstrainedType(), unresolvedValues, - allowNumericConversion, null)) { - return false; - } - } - return true; - } - - private static boolean checkIsLikeStreamType(Object sourceValue, BStreamType targetType) { - if (!(sourceValue instanceof StreamValue streamValue)) { - return false; - } - - BStreamType streamType = (BStreamType) streamValue.getType(); - - return streamType.getConstrainedType() == targetType.getConstrainedType(); - } + return isInherentlyImmutableType(recordRestType) || + isSelectivelyImmutableType(recordRestType, unresolvedTypes); + case TypeTags.OBJECT_TYPE_TAG: + BObjectType objectType = (BObjectType) type; - private static boolean checkIsLikeJSONType(Object sourceValue, Type sourceType, BJsonType targetType, - List unresolvedValues, boolean allowNumericConversion) { - Type referredSourceType = getImpliedType(sourceType); - switch (referredSourceType.getTag()) { - case TypeTags.ARRAY_TAG: - ArrayValue source = (ArrayValue) sourceValue; - Type elementType = ((BArrayType) referredSourceType).getElementType(); - if (checkIsType(elementType, targetType, new ArrayList<>())) { - return true; + if (SymbolFlags.isFlagOn(objectType.flags, SymbolFlags.CLASS) && + !SymbolFlags.isFlagOn(objectType.flags, SymbolFlags.READONLY)) { + return false; } - Object[] arrayValues = source.getValues(); - for (int i = 0; i < source.size(); i++) { - if (!checkIsLikeType(null, arrayValues[i], targetType, unresolvedValues, - allowNumericConversion, null)) { - return false; - } - } - return true; - case TypeTags.TUPLE_TAG: - for (Object obj : ((TupleValueImpl) sourceValue).getValues()) { - if (!checkIsLikeType(null, obj, targetType, unresolvedValues, allowNumericConversion, - null)) { + for (Field field : objectType.getFields().values()) { + Type fieldType = field.getFieldType(); + if (!isInherentlyImmutableType(fieldType) && + !isSelectivelyImmutableType(fieldType, unresolvedTypes)) { return false; } } return true; case TypeTags.MAP_TAG: - return checkIsMappingLikeJsonType((MapValueImpl) sourceValue, targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.RECORD_TYPE_TAG: - TypeValuePair typeValuePair = new TypeValuePair(sourceValue, targetType); - if (unresolvedValues.contains(typeValuePair)) { - return true; + Type constraintType = ((MapType) type).getConstrainedType(); + return isInherentlyImmutableType(constraintType) || + isSelectivelyImmutableType(constraintType, unresolvedTypes); + case TypeTags.TABLE_TAG: + Type tableConstraintType = ((BTableType) type).getConstrainedType(); + return isInherentlyImmutableType(tableConstraintType) || + isSelectivelyImmutableType(tableConstraintType, unresolvedTypes); + case TypeTags.UNION_TAG: + boolean readonlyIntersectionExists = false; + for (Type memberType : ((BUnionType) type).getMemberTypes()) { + if (isInherentlyImmutableType(memberType) || + isSelectivelyImmutableType(memberType, unresolvedTypes)) { + readonlyIntersectionExists = true; + break; + } } - unresolvedValues.add(typeValuePair); - return checkIsMappingLikeJsonType((MapValueImpl) sourceValue, targetType, unresolvedValues, - allowNumericConversion); + return readonlyIntersectionExists; + case TypeTags.INTERSECTION_TAG: + return isSelectivelyImmutableType(((BIntersectionType) type).getEffectiveType(), unresolvedTypes); + case TypeTags.TYPE_REFERENCED_TYPE_TAG: + return isSelectivelyImmutableType(((BTypeReferenceType) type).getReferredType(), unresolvedTypes); default: return false; } } - private static boolean checkIsMappingLikeJsonType(MapValueImpl sourceValue, BJsonType targetType, - List unresolvedValues, - boolean allowNumericConversion) { - for (Object value : sourceValue.values()) { - if (!checkIsLikeType(null, value, targetType, unresolvedValues, allowNumericConversion, - null)) { - return false; - } - } - return true; + static boolean isSigned32LiteralValue(Long longObject) { + + return (longObject >= SIGNED32_MIN_VALUE && longObject <= SIGNED32_MAX_VALUE); } - private static boolean checkIsLikeRecordType(Object sourceValue, BRecordType targetType, - List unresolvedValues, boolean allowNumericConversion, - String varName, List errors) { - if (!(sourceValue instanceof MapValueImpl sourceMapValue)) { - return false; - } + static boolean isSigned16LiteralValue(Long longObject) { - TypeValuePair typeValuePair = new TypeValuePair(sourceValue, targetType); - if (unresolvedValues.contains(typeValuePair)) { - return true; - } - unresolvedValues.add(typeValuePair); + return (longObject.intValue() >= SIGNED16_MIN_VALUE && longObject.intValue() <= SIGNED16_MAX_VALUE); + } - Map targetFieldTypes = new HashMap<>(); - Type restFieldType = targetType.restFieldType; - boolean returnVal = true; + static boolean isSigned8LiteralValue(Long longObject) { - for (Field field : targetType.getFields().values()) { - targetFieldTypes.put(field.getFieldName(), field.getFieldType()); - } + return (longObject.intValue() >= SIGNED8_MIN_VALUE && longObject.intValue() <= SIGNED8_MAX_VALUE); + } - for (Map.Entry targetTypeEntry : targetFieldTypes.entrySet()) { - String fieldName = targetTypeEntry.getKey().toString(); - String fieldNameLong = TypeConverter.getLongFieldName(varName, fieldName); - Field targetField = targetType.getFields().get(fieldName); + static boolean isUnsigned32LiteralValue(Long longObject) { - if (!(sourceMapValue.containsKey(StringUtils.fromString(fieldName))) && - !SymbolFlags.isFlagOn(targetField.getFlags(), SymbolFlags.OPTIONAL)) { - addErrorMessage((errors == null) ? 0 : errors.size(), "missing required field '" + fieldNameLong + - "' of type '" + targetField.getFieldType().toString() + "' in record '" + targetType + "'", - errors); - if ((errors == null) || (errors.size() >= MAX_TYPECAST_ERROR_COUNT + 1)) { - return false; - } - returnVal = false; - } - } + return (longObject >= 0 && longObject <= UNSIGNED32_MAX_VALUE); + } - for (Object object : sourceMapValue.entrySet()) { - Map.Entry valueEntry = (Map.Entry) object; - String fieldName = valueEntry.getKey().toString(); - String fieldNameLong = TypeConverter.getLongFieldName(varName, fieldName); - int initialErrorCount = (errors == null) ? 0 : errors.size(); + static boolean isUnsigned16LiteralValue(Long longObject) { - if (targetFieldTypes.containsKey(fieldName)) { - if (!checkIsLikeType(errors, (valueEntry.getValue()), targetFieldTypes.get(fieldName), - unresolvedValues, allowNumericConversion, fieldNameLong)) { - addErrorMessage(initialErrorCount, "field '" + fieldNameLong + "' in record '" + targetType + - "' should be of type '" + targetFieldTypes.get(fieldName) + "', found '" + - TypeConverter.getShortSourceValue(valueEntry.getValue()) + "'", errors); - returnVal = false; - } - } else { - if (!targetType.sealed) { - if (!checkIsLikeType(errors, (valueEntry.getValue()), restFieldType, unresolvedValues, - allowNumericConversion, fieldNameLong)) { - addErrorMessage(initialErrorCount, "value of field '" + valueEntry.getKey() + - "' adding to the record '" + targetType + "' should be of type '" + restFieldType + - "', found '" + TypeConverter.getShortSourceValue(valueEntry.getValue()) + "'", errors); - returnVal = false; - } - } else { - addErrorMessage(initialErrorCount, "field '" + fieldNameLong + - "' cannot be added to the closed record '" + targetType + "'", errors); - returnVal = false; - } - } - if ((!returnVal) && ((errors == null) || (errors.size() >= MAX_TYPECAST_ERROR_COUNT + 1))) { - return false; - } - } - return returnVal; + return (longObject.intValue() >= 0 && longObject.intValue() <= UNSIGNED16_MAX_VALUE); } - private static void addErrorMessage(int initialErrorCount, String errorMessage, List errors) { - if ((errors != null) && (errors.size() <= MAX_TYPECAST_ERROR_COUNT) && - ((errors.size() - initialErrorCount) == 0)) { - errors.add(errorMessage); - } - } + static boolean isUnsigned8LiteralValue(Long longObject) { - private static boolean checkIsLikeTableType(Object sourceValue, BTableType targetType, - List unresolvedValues, boolean allowNumericConversion) { - if (!(sourceValue instanceof TableValueImpl tableValue)) { - return false; - } - BTableType sourceType = (BTableType) getImpliedType(tableValue.getType()); - if (targetType.getKeyType() != null && sourceType.getFieldNames().length == 0) { - return false; - } + return (longObject.intValue() >= 0 && longObject.intValue() <= UNSIGNED8_MAX_VALUE); + } - if (sourceType.getKeyType() != null && !checkIsType(tableValue.getKeyType(), targetType.getKeyType())) { + static boolean isCharLiteralValue(Object object) { + String value; + if (object instanceof BString) { + value = ((BString) object).getValue(); + } else if (object instanceof String) { + value = (String) object; + } else { return false; } + return value.codePoints().count() == 1; + } - TypeValuePair typeValuePair = new TypeValuePair(sourceValue, targetType); - if (unresolvedValues.contains(typeValuePair)) { + /** + * Deep value equality check for anydata. + * + * @param lhsValue The value on the left hand side + * @param rhsValue The value on the right hand side + * @param checkedValues Structured value pairs already compared or being compared + * @return True if values are equal, else false. + */ + public static boolean isEqual(Object lhsValue, Object rhsValue, Set checkedValues) { + if (lhsValue == rhsValue) { return true; } - Object[] objects = tableValue.values().toArray(); - for (Object object : objects) { - if (!checkIsLikeType(object, targetType.getConstrainedType(), allowNumericConversion)) { - return false; - } + if (null == lhsValue || null == rhsValue) { + return false; } - return true; + + return checkValueEqual(lhsValue, rhsValue, new HashSet<>(checkedValues)); } - private static boolean checkFiniteTypeAssignable(Object sourceValue, Type sourceType, BFiniteType targetType, - List unresolvedValues, - boolean allowNumericConversion) { - if (targetType.valueSpace.size() == 1) { - Type valueType = getImpliedType(getType(targetType.valueSpace.iterator().next())); - if (!isSimpleBasicType(valueType) && valueType.getTag() != TypeTags.NULL_TAG) { - return checkIsLikeOnValue(null, sourceValue, sourceType, valueType, unresolvedValues, - allowNumericConversion, null); - } + private static boolean checkValueEqual(Object lhsValue, Object rhsValue, Set checkedValues) { + Context cx = context(); + SemType lhsShape = ShapeAnalyzer.inherentTypeOf(cx, lhsValue).orElseThrow(); + SemType rhsShape = ShapeAnalyzer.inherentTypeOf(cx, rhsValue).orElseThrow(); + Predicate belongToSameBasicType = (basicType) -> Core.containsBasicType(lhsShape, basicType) && + Core.containsBasicType(rhsShape, basicType); + if (belongToSameBasicType.test(Builder.getStringType()) || + belongToSameBasicType.test(Builder.getBooleanType())) { + return lhsValue.equals(rhsValue); + } + if (belongToSameBasicType.test(Builder.getIntType())) { + // TODO: is this correct if one of the values are bytes (shouldn't we check of unsigned etc) + return ((Number) lhsValue).longValue() == ((Number) rhsValue).longValue(); } + if (belongToSameBasicType.test(Builder.getFloatType())) { + Double lhs = (Double) lhsValue; + Double rhs = (Double) rhsValue; + // directly doing equals don't work with -0 and 0 + return (Double.isNaN(lhs) && Double.isNaN(rhs)) || lhs.doubleValue() == rhs.doubleValue(); + } + if (belongToSameBasicType.test(Builder.getDecimalType())) { + return checkDecimalEqual((DecimalValue) lhsValue, (DecimalValue) rhsValue); + } + if (belongToSameBasicType.test(RefValueTypeMaskHolder.REF_TYPE_MASK)) { + RefValue lhs = (RefValue) lhsValue; + return lhs.equals(rhsValue, checkedValues); + } + return false; + } - for (Object valueSpaceItem : targetType.valueSpace) { - // TODO: 8/13/19 Maryam fix for conversion - if (isFiniteTypeValue(sourceValue, sourceType, valueSpaceItem, allowNumericConversion)) { + public static boolean isRegExpType(Type targetType) { + if (targetType.getTag() == TypeTags.TYPE_REFERENCED_TYPE_TAG) { + Type referredType = ((BTypeReferenceType) targetType).getReferredType(); + Module referredTypePackage = referredType.getPackage(); + if ((referredTypePackage != null) && BALLERINA_BUILTIN_PKG_PREFIX.equals(referredTypePackage.getOrg()) + && REGEXP_LANG_LIB.equals(referredTypePackage.getName()) + && REG_EXP_TYPENAME.equals(referredType.getName())) { return true; } + return isRegExpType(referredType); } return false; } + static boolean isStructuredType(Type type) { + Type referredType = getImpliedType(type); + return switch (referredType.getTag()) { + case TypeTags.ARRAY_TAG, + TypeTags.TUPLE_TAG, + TypeTags.MAP_TAG, + TypeTags.RECORD_TYPE_TAG, + TypeTags.TABLE_TAG -> + true; + default -> false; + }; + } + static boolean isFiniteTypeValue(Object sourceValue, Type sourceType, Object valueSpaceItem, boolean allowNumericConversion) { Type valueSpaceItemType = getType(valueSpaceItem); @@ -2808,7 +918,8 @@ static boolean isFiniteTypeValue(Object sourceValue, Type sourceType, Object val DecimalValue.valueOf(((Number) valueSpaceItem).longValue())) && allowNumericConversion; case TypeTags.FLOAT_TAG: return checkDecimalEqual((DecimalValue) sourceValue, - DecimalValue.valueOf(((Number) valueSpaceItem).doubleValue())) && allowNumericConversion; + DecimalValue.valueOf(((Number) valueSpaceItem).doubleValue())) && + allowNumericConversion; case TypeTags.DECIMAL_TAG: return checkDecimalEqual((DecimalValue) sourceValue, (DecimalValue) valueSpaceItem); } @@ -2820,183 +931,8 @@ static boolean isFiniteTypeValue(Object sourceValue, Type sourceType, Object val } } - private static boolean checkIsErrorType(Type sourceType, BErrorType targetType, List unresolvedTypes) { - if (sourceType.getTag() != TypeTags.ERROR_TAG) { - return false; - } - // Handle recursive error types. - TypePair pair = new TypePair(sourceType, targetType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - BErrorType bErrorType = (BErrorType) sourceType; - - if (!checkIsType(bErrorType.detailType, targetType.detailType, unresolvedTypes)) { - return false; - } - - if (targetType.typeIdSet == null) { - return true; - } - - BTypeIdSet sourceTypeIdSet = bErrorType.typeIdSet; - if (sourceTypeIdSet == null) { - return false; - } - - return sourceTypeIdSet.containsAll(targetType.typeIdSet); - } - - private static boolean checkIsLikeErrorType(Object sourceValue, BErrorType targetType, - List unresolvedValues, boolean allowNumericConversion) { - Type sourceTypeReferredType = getImpliedType(getType(sourceValue)); - if (sourceValue == null || sourceTypeReferredType.getTag() != TypeTags.ERROR_TAG) { - return false; - } - if (!checkIsLikeType(null, ((ErrorValue) sourceValue).getDetails(), targetType.detailType, - unresolvedValues, allowNumericConversion, null)) { - return false; - } - if (targetType.typeIdSet == null) { - return true; - } - BTypeIdSet sourceIdSet = ((BErrorType) sourceTypeReferredType).typeIdSet; - if (sourceIdSet == null) { - return false; - } - return sourceIdSet.containsAll(targetType.typeIdSet); - } - - static boolean isSimpleBasicType(Type type) { - return getImpliedType(type).getTag() < TypeTags.NULL_TAG; - } - - /** - * Deep value equality check for anydata. - * - * @param lhsValue The value on the left hand side - * @param rhsValue The value on the right hand side - * @param checkedValues Structured value pairs already compared or being compared - * @return True if values are equal, else false. - */ - public static boolean isEqual(Object lhsValue, Object rhsValue, Set checkedValues) { - if (lhsValue == rhsValue) { - return true; - } - - if (null == lhsValue || null == rhsValue) { - return false; - } - - return checkValueEquals(lhsValue, rhsValue, checkedValues, getType(lhsValue), getType(rhsValue)); - } - - private static boolean checkValueEquals(Object lhsValue, Object rhsValue, Set checkedValues, - Type lhsValType, Type rhsValType) { - lhsValType = getImpliedType(lhsValType); - rhsValType = getImpliedType(rhsValType); - int lhsValTypeTag = lhsValType.getTag(); - int rhsValTypeTag = rhsValType.getTag(); - - switch (lhsValTypeTag) { - case TypeTags.STRING_TAG: - case TypeTags.BOOLEAN_TAG: - return lhsValue.equals(rhsValue); - case TypeTags.INT_TAG: - if (rhsValTypeTag != TypeTags.BYTE_TAG && rhsValTypeTag != TypeTags.INT_TAG) { - return false; - } - return lhsValue.equals(((Number) rhsValue).longValue()); - case TypeTags.BYTE_TAG: - if (rhsValTypeTag != TypeTags.BYTE_TAG && rhsValTypeTag != TypeTags.INT_TAG) { - return false; - } - return ((Number) lhsValue).byteValue() == ((Number) rhsValue).byteValue(); - case TypeTags.FLOAT_TAG: - if (rhsValTypeTag != TypeTags.FLOAT_TAG) { - return false; - } - if (Double.isNaN((Double) lhsValue) && Double.isNaN((Double) rhsValue)) { - return true; - } - return ((Number) lhsValue).doubleValue() == ((Number) rhsValue).doubleValue(); - case TypeTags.DECIMAL_TAG: - if (rhsValTypeTag != TypeTags.DECIMAL_TAG) { - return false; - } - return checkDecimalEqual((DecimalValue) lhsValue, (DecimalValue) rhsValue); - case TypeTags.XML_TAG: - // Instance of xml never - if (lhsValue instanceof XmlText xmlText) { - return TypeTags.isXMLTypeTag(rhsValTypeTag) && xmlText.equals(rhsValue, checkedValues); - } - return TypeTags.isXMLTypeTag(rhsValTypeTag) && ((XmlSequence) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.XML_ELEMENT_TAG: - return TypeTags.isXMLTypeTag(rhsValTypeTag) && ((XmlItem) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.XML_COMMENT_TAG: - return TypeTags.isXMLTypeTag(rhsValTypeTag) && ((XmlComment) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.XML_TEXT_TAG: - return TypeTags.isXMLTypeTag(rhsValTypeTag) && ((XmlText) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.XML_PI_TAG: - return TypeTags.isXMLTypeTag(rhsValTypeTag) && ((XmlPi) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.MAP_TAG: - case TypeTags.JSON_TAG: - case TypeTags.RECORD_TYPE_TAG: - return isMappingType(rhsValTypeTag) && ((MapValueImpl) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.TUPLE_TAG: - case TypeTags.ARRAY_TAG: - return isListType(rhsValTypeTag) && ((ArrayValue) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.ERROR_TAG: - return rhsValTypeTag == TypeTags.ERROR_TAG && ((ErrorValue) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.TABLE_TAG: - return rhsValTypeTag == TypeTags.TABLE_TAG && - ((TableValueImpl) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.TYPE_REFERENCED_TYPE_TAG: - return checkValueEquals(lhsValue, rhsValue, checkedValues, - ((BTypeReferenceType) lhsValType).getReferredType(), rhsValType); - case TypeTags.SERVICE_TAG: - default: - if (lhsValue instanceof RegExpValue lhsRegExpValue) { - return lhsRegExpValue.equals(rhsValue, checkedValues); - } - return false; - } - } - - private static boolean isListType(int typeTag) { - return typeTag == TypeTags.ARRAY_TAG || typeTag == TypeTags.TUPLE_TAG; - } - - private static boolean isMappingType(int typeTag) { - return typeTag == TypeTags.MAP_TAG || typeTag == TypeTags.RECORD_TYPE_TAG || typeTag == TypeTags.JSON_TAG; - } - - public static boolean isRegExpType(Type targetType) { - if (targetType.getTag() == TypeTags.TYPE_REFERENCED_TYPE_TAG) { - Type referredType = ((BTypeReferenceType) targetType).getReferredType(); - Module referredTypePackage = referredType.getPackage(); - if ((referredTypePackage != null) && BALLERINA_BUILTIN_PKG_PREFIX.equals(referredTypePackage.getOrg()) - && REGEXP_LANG_LIB.equals(referredTypePackage.getName()) - && REG_EXP_TYPENAME.equals(referredType.getName())) { - return true; - } - return isRegExpType(referredType); - } - return false; - } - - static boolean isStructuredType(Type type) { - Type referredType = getImpliedType(type); - return switch (referredType.getTag()) { - case TypeTags.ARRAY_TAG, - TypeTags.TUPLE_TAG, - TypeTags.MAP_TAG, - TypeTags.RECORD_TYPE_TAG, - TypeTags.TABLE_TAG -> - true; - default -> false; - }; + public static Env getEnv() { + return Env.getInstance(); } /** @@ -3004,7 +940,7 @@ static boolean isStructuredType(Type type) { * * @since 0.995.0 */ - private static class TypePair { + static class TypePair { Type sourceType; Type targetType; @@ -3044,11 +980,35 @@ public static boolean hasFillerValue(Type type) { return hasFillerValue(type, new ArrayList<>()); } + private enum FillerValueResult { + TRUE, FALSE, MAYBE + } + + private static FillerValueResult hasFillerValueSemType(Context cx, SemType type) { + if (Core.containsBasicType(type, Builder.getNilType())) { + return FillerValueResult.TRUE; + } + if (Integer.bitCount(type.all() | type.some()) > 1) { + return FillerValueResult.FALSE; + } + if (type.some() != 0) { + return FillerValueResult.MAYBE; + } + return Core.containsBasicType(type, TopTypesWithFillValueMaskHolder.TOP_TYPES_WITH_ALWAYS_FILLING) ? + FillerValueResult.TRUE : + FillerValueResult.FALSE; + } + private static boolean hasFillerValue(Type type, List unanalyzedTypes) { if (type == null) { return true; } + FillerValueResult fastResult = hasFillerValueSemType(context(), SemType.tryInto(type)); + if (fastResult != FillerValueResult.MAYBE) { + return fastResult == FillerValueResult.TRUE; + } + int typeTag = type.getTag(); if (TypeTags.isXMLTypeTag(typeTag)) { return typeTag == TypeTags.XML_TAG || typeTag == TypeTags.XML_TEXT_TAG; @@ -3077,7 +1037,7 @@ private static boolean hasFillerValue(Type type, List unanalyzedTypes) { }; } - private static boolean checkFillerValue(BTupleType tupleType, List unAnalyzedTypes) { + private static boolean checkFillerValue(BTupleType tupleType, List unAnalyzedTypes) { if (unAnalyzedTypes.contains(tupleType)) { return true; } @@ -3174,7 +1134,7 @@ private static boolean isSameBasicType(Type sourceType, Type targetType) { } private static boolean isIntegerSubTypeTag(int typeTag) { - return TypeTags.isIntegerTypeTag(typeTag) || typeTag == TypeTags.BYTE_TAG; + return TypeTags.isIntegerTypeTag(typeTag); } private static boolean isFillerValueOfFiniteTypeBasicType(Object value) { @@ -3253,7 +1213,8 @@ private static boolean hasFillerValueInValueSpace(Set finiteTypeValueSpa if (firstElement instanceof BString) { return containsElement(finiteTypeValueSpace, ""); - } else if ((firstElement instanceof Long) || (firstElement instanceof BDecimal)) { + } else if ((firstElement instanceof Integer) || (firstElement instanceof Long) || + (firstElement instanceof BDecimal)) { return containsElement(finiteTypeValueSpace, "0"); } else if (firstElement instanceof Double) { return containsElement(finiteTypeValueSpace, "0.0"); @@ -3289,6 +1250,96 @@ private static BError createTypeCastError(Object value, Type targetType, List + ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_BYTE)); + } + Predicate isIntSubType = (subType) -> Core.isSameType(cx, targetType, SemType.tryInto(subType)); + if (isIntSubType.test(PredefinedTypes.TYPE_INT_SIGNED_32)) { + return anyToSigned32(inputValue); + } + if (isIntSubType.test(PredefinedTypes.TYPE_INT_SIGNED_16)) { + return anyToSigned16(inputValue); + } + if (isIntSubType.test(PredefinedTypes.TYPE_INT_SIGNED_8)) { + return anyToSigned8(inputValue); + } + if (isIntSubType.test(PredefinedTypes.TYPE_INT_UNSIGNED_32)) { + return anyToUnsigned32(inputValue); + } + if (isIntSubType.test(PredefinedTypes.TYPE_INT_UNSIGNED_16)) { + return anyToUnsigned16(inputValue); + } + if (isIntSubType.test(PredefinedTypes.TYPE_INT_UNSIGNED_8)) { + return anyToUnsigned8(inputValue); + } + return anyToIntCast(inputValue, () -> + ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_INT)); + } + public static Object castValues(Type targetType, Object inputValue) { - return switch (targetType.getTag()) { - case TypeTags.SIGNED32_INT_TAG -> anyToSigned32(inputValue); - case TypeTags.SIGNED16_INT_TAG -> anyToSigned16(inputValue); - case TypeTags.SIGNED8_INT_TAG -> anyToSigned8(inputValue); - case TypeTags.UNSIGNED32_INT_TAG -> anyToUnsigned32(inputValue); - case TypeTags.UNSIGNED16_INT_TAG -> anyToUnsigned16(inputValue); - case TypeTags.UNSIGNED8_INT_TAG -> anyToUnsigned8(inputValue); - case TypeTags.INT_TAG -> anyToIntCast(inputValue, () -> - ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_INT)); - case TypeTags.DECIMAL_TAG -> anyToDecimalCast(inputValue, () -> + return castValuesInner(SemType.tryInto(targetType), inputValue, + () -> ErrorUtils.createTypeCastError(inputValue, targetType)); + } + + static Object castValuesInner(SemType targetType, Object inputValue, Supplier errorSupplier) { + Context cx = TypeChecker.context(); + if (Core.isSubType(cx, targetType, Builder.getIntType())) { + return castValueToInt(targetType, inputValue); + } + if (Core.isSubType(cx, targetType, Builder.getDecimalType())) { + return anyToDecimalCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_DECIMAL)); - case TypeTags.FLOAT_TAG -> anyToFloatCast(inputValue, () -> + } + if (Core.isSubType(cx, targetType, Builder.getFloatType())) { + return anyToFloatCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_FLOAT)); - case TypeTags.STRING_TAG -> anyToStringCast(inputValue, () -> + } + if (Core.isSubType(cx, targetType, Builder.getStringType())) { + return anyToStringCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_STRING)); - case TypeTags.BOOLEAN_TAG -> anyToBooleanCast(inputValue, () -> + } + if (Core.isSubType(cx, targetType, Builder.getBooleanType())) { + return anyToBooleanCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_BOOLEAN)); - case TypeTags.BYTE_TAG -> anyToByteCast(inputValue, () -> - ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_BYTE)); - default -> throw ErrorUtils.createTypeCastError(inputValue, targetType); - }; + } + throw errorSupplier.get(); } static boolean isConvertibleToByte(Object value) { @@ -266,7 +308,7 @@ public static Type getConvertibleType(Object inputValue, Type targetType, String } break; case TypeTags.MAP_TAG: - if (isConvertibleToMapType(inputValue, (BMapType) targetType, unresolvedValues, varName, errors, + if (isConvertibleToMapType(inputValue, (MapType) targetType, unresolvedValues, varName, errors, allowNumericConversion)) { return targetType; } @@ -385,7 +427,7 @@ public static Type getConvertibleFiniteType(Object inputValue, BFiniteType targe // only the first matching type is returned. if (targetFiniteType.valueSpace.size() == 1) { Type valueType = getType(targetFiniteType.valueSpace.iterator().next()); - if (!isSimpleBasicType(valueType) && valueType.getTag() != TypeTags.NULL_TAG) { + if (!belongToSingleBasicTypeOrString(valueType) && valueType.getTag() != TypeTags.NULL_TAG) { return getConvertibleType(inputValue, valueType, varName, unresolvedValues, errors, allowNumericConversion); } @@ -499,7 +541,7 @@ static String getShortSourceValue(Object sourceValue) { return "()"; } String sourceValueName = sourceValue.toString(); - if (TypeChecker.getType(sourceValue) == TYPE_STRING) { + if (TypeChecker.checkIsType(sourceValue, TYPE_STRING)) { sourceValueName = "\"" + sourceValueName + "\""; } if (sourceValueName.length() > MAX_DISPLAYED_SOURCE_VALUE_LENGTH) { @@ -577,7 +619,7 @@ private static boolean isConvertibleToTableType(Object sourceValue, BTableType t } } - private static boolean isConvertibleToMapType(Object sourceValue, BMapType targetType, + private static boolean isConvertibleToMapType(Object sourceValue, MapType targetType, Set unresolvedValues, String varName, List errors, boolean allowNumericConversion) { if (!(sourceValue instanceof MapValueImpl)) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/json/JsonInternalUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/json/JsonInternalUtils.java index 0fe77bbde9da..e77b4b4aa9fb 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/json/JsonInternalUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/json/JsonInternalUtils.java @@ -43,7 +43,6 @@ import io.ballerina.runtime.internal.types.BArrayType; import io.ballerina.runtime.internal.types.BFiniteType; import io.ballerina.runtime.internal.types.BJsonType; -import io.ballerina.runtime.internal.types.BMapType; import io.ballerina.runtime.internal.types.BStructureType; import io.ballerina.runtime.internal.types.BUnionType; import io.ballerina.runtime.internal.values.ArrayValue; @@ -62,6 +61,7 @@ import java.util.Map.Entry; import static io.ballerina.runtime.api.constants.RuntimeConstants.MAP_LANG_LIB; +import static io.ballerina.runtime.internal.TypeChecker.isByteLiteral; import static io.ballerina.runtime.internal.errors.ErrorReasons.INHERENT_TYPE_VIOLATION_ERROR_IDENTIFIER; import static io.ballerina.runtime.internal.errors.ErrorReasons.JSON_OPERATION_ERROR; import static io.ballerina.runtime.internal.errors.ErrorReasons.MAP_KEY_NOT_FOUND_ERROR; @@ -317,6 +317,8 @@ public static Object convertJSON(Object jsonValue, Type targetType) { targetType = TypeUtils.getImpliedType(targetType); Type matchingType; switch (targetType.getTag()) { + case TypeTags.BYTE_TAG: + return jsonNodeToByte(jsonValue); case TypeTags.INT_TAG: return jsonNodeToInt(jsonValue); case TypeTags.FLOAT_TAG: @@ -360,7 +362,7 @@ public static Object convertJSON(Object jsonValue, Type targetType) { case TypeTags.ARRAY_TAG: return convertJSONToBArray(jsonValue, (BArrayType) targetType); case TypeTags.MAP_TAG: - return jsonToMap(jsonValue, (BMapType) targetType); + return jsonToMap(jsonValue, (MapType) targetType); case TypeTags.NULL_TAG: if (jsonValue == null) { return null; @@ -563,12 +565,30 @@ public static BError createJsonConversionError(Throwable throwable, String prefi * @return BInteger value of the JSON, if its a integer or a long JSON node. Error, otherwise. */ private static long jsonNodeToInt(Object json) { - if (!(json instanceof Long l)) { + if (!(json instanceof Long || json instanceof Integer)) { throw ErrorHelper.getRuntimeException(ErrorCodes.INCOMPATIBLE_TYPE_FOR_CASTING_JSON, PredefinedTypes.TYPE_INT, getTypeName(json)); } - return l; + return ((Number) json).longValue(); + } + + /** + * Convert to byte. + * + * @param json node to be converted + * @return JSON value as a long, if the value is within byte range. Error, otherwise. + */ + private static long jsonNodeToByte(Object json) { + if (json instanceof Long || json instanceof Integer) { + long x = ((Number) json).longValue(); + if (isByteLiteral(x)) { + return x; + } + } + + throw ErrorHelper.getRuntimeException(ErrorCodes.INCOMPATIBLE_TYPE_FOR_CASTING_JSON, + PredefinedTypes.TYPE_BYTE, getTypeName(json)); } /** diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnyType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnyType.java index feb9d147bf7c..adb22bc0bdde 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnyType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnyType.java @@ -25,6 +25,10 @@ import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.values.RefValue; import java.util.Optional; @@ -34,11 +38,7 @@ * * @since 0.995.0 */ -public class BAnyType extends BType implements AnyType { - - private final boolean readonly; - private IntersectionType immutableType; - private IntersectionType intersectionType = null; +public final class BAnyType extends BSemTypeWrapper implements AnyType { /** * Create a {@code BAnyType} which represents the any type. @@ -46,58 +46,98 @@ public class BAnyType extends BType implements AnyType { * @param typeName string name of the type */ public BAnyType(String typeName, Module pkg, boolean readonly) { - super(typeName, pkg, RefValue.class); - this.readonly = readonly; - - if (!readonly) { - BAnyType immutableAnyType = new BAnyType(TypeConstants.READONLY_ANY_TNAME, pkg, true); - this.immutableType = new BIntersectionType(pkg, new Type[]{ this, PredefinedTypes.TYPE_READONLY}, - immutableAnyType, TypeFlags.asMask(TypeFlags.NILABLE), true); - } + super(new ConcurrentLazySupplier<>(() -> new BAnyTypeImpl(typeName, pkg, readonly)), + typeName, pkg, TypeTags.ANY_TAG, pickSemType(readonly)); } @Override - public V getZeroValue() { - return null; + public Optional getIntersectionType() { + return this.getbType().getIntersectionType(); } @Override - public V getEmptyValue() { - return null; + public void setIntersectionType(IntersectionType intersectionType) { + this.getbType().setIntersectionType(intersectionType); } @Override - public int getTag() { - return TypeTags.ANY_TAG; + public Type getReferredType() { + return this.getbType().getReferredType(); } @Override - public boolean isNilable() { - return true; + public IntersectionType getImmutableType() { + return this.getbType().getImmutableType(); } - @Override - public boolean isReadOnly() { - return this.readonly; - } + protected static final class BAnyTypeImpl extends BType implements AnyType { - @Override - public IntersectionType getImmutableType() { - return this.immutableType; - } + private final boolean readonly; + private IntersectionType immutableType; + private IntersectionType intersectionType = null; - @Override - public void setImmutableType(IntersectionType immutableType) { - this.immutableType = immutableType; - } + private BAnyTypeImpl(String typeName, Module pkg, boolean readonly) { + super(typeName, pkg, RefValue.class); + this.readonly = readonly; + + if (!readonly) { + BAnyType immutableAnyType = new BAnyType(TypeConstants.READONLY_ANY_TNAME, pkg, true); + this.immutableType = new BIntersectionType(pkg, new Type[]{this, PredefinedTypes.TYPE_READONLY}, + immutableAnyType, TypeFlags.asMask(TypeFlags.NILABLE), true); + } + } + + @Override + public V getZeroValue() { + return null; + } + + @Override + public V getEmptyValue() { + return null; + } + + @Override + public int getTag() { + return TypeTags.ANY_TAG; + } + + public boolean isNilable() { + return true; + } + + @Override + public boolean isReadOnly() { + return this.readonly; + } + + @Override + public IntersectionType getImmutableType() { + return this.immutableType; + } + + @Override + public void setImmutableType(IntersectionType immutableType) { + this.immutableType = immutableType; + } + + @Override + public Optional getIntersectionType() { + return this.intersectionType == null ? Optional.empty() : Optional.of(this.intersectionType); + } + + @Override + public void setIntersectionType(IntersectionType intersectionType) { + this.intersectionType = intersectionType; + } - @Override - public Optional getIntersectionType() { - return this.intersectionType == null ? Optional.empty() : Optional.of(this.intersectionType); } - @Override - public void setIntersectionType(IntersectionType intersectionType) { - this.intersectionType = intersectionType; + private static SemType pickSemType(boolean readonly) { + SemType semType = Builder.getAnyType(); + if (readonly) { + semType = Core.intersect(semType, Builder.getReadonlyType()); + } + return semType; } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnydataType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnydataType.java index 0bc38e99f4fb..71dcdc374f48 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnydataType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnydataType.java @@ -24,6 +24,9 @@ import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.values.RefValue; /** @@ -86,4 +89,16 @@ public String toString() { } return super.toString(); } + + // TODO: this type don't have mutable parts so this should be a immutable + // semtype. But some things could depend on this being a union type descriptor + // as well (which has to be mutable) + @Override + public SemType createSemType() { + SemType semType = Builder.getAnyDataType(); + if (isReadOnly()) { + semType = Core.intersect(semType, Builder.getReadonlyType()); + } + return semType; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BArrayType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BArrayType.java index a399bcddf71a..07834065c87d 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BArrayType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BArrayType.java @@ -22,12 +22,26 @@ import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import io.ballerina.runtime.internal.values.AbstractArrayValue; import io.ballerina.runtime.internal.values.ArrayValue; import io.ballerina.runtime.internal.values.ArrayValueImpl; import io.ballerina.runtime.internal.values.ReadOnlyUtils; import java.util.Optional; +import java.util.Set; + +import static io.ballerina.runtime.api.types.semtype.Builder.getNeverType; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_UNLIMITED; /** * {@code BArrayType} represents a type of an arrays in Ballerina. @@ -40,7 +54,9 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BArrayType extends BType implements ArrayType { +public class BArrayType extends BType implements ArrayType, TypeWithShape { + + private static final SemType[] EMPTY_SEMTYPE_ARR = new SemType[0]; private Type elementType; private int dimensions = 1; private int size = -1; @@ -51,6 +67,8 @@ public class BArrayType extends BType implements ArrayType { private IntersectionType immutableType; private IntersectionType intersectionType = null; private int typeFlags; + private final DefinitionContainer defn = new DefinitionContainer<>(); + private final DefinitionContainer acceptedTypeDefn = new DefinitionContainer<>(); public BArrayType(Type elementType) { this(elementType, false); } @@ -85,6 +103,9 @@ public BArrayType(int typeFlags, int size, boolean readonly, boolean hasFillerVa } public void setElementType(Type elementType, int dimensions, boolean elementRO) { + if (this.elementType != null) { + resetSemType(); + } this.elementType = readonly && !elementRO ? ReadOnlyUtils.getReadOnlyType(elementType) : elementType; this.dimensions = dimensions; } @@ -129,7 +150,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { if (obj instanceof BArrayType other) { - if (other.state == ArrayState.CLOSED && this.size != other.size) { + if ((other.state == ArrayState.CLOSED || this.state == ArrayState.CLOSED) && this.size != other.size) { return false; } return this.elementType.equals(other.elementType) && this.readonly == other.readonly; @@ -204,4 +225,101 @@ public Optional getIntersectionType() { public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + public SemType createSemType() { + Env env = Env.getInstance(); + if (defn.isDefinitionReady()) { + return defn.getSemType(env); + } + var result = defn.trySetDefinition(ListDefinition::new); + if (!result.updated()) { + return defn.getSemType(env); + } + ListDefinition ld = result.definition(); + CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + return getSemTypePart(env, ld, size, tryInto(getElementType()), mut); + } + + private SemType getSemTypePart(Env env, ListDefinition defn, int size, SemType elementType, + CellAtomicType.CellMutability mut) { + if (size == -1) { + return defn.defineListTypeWrapped(env, EMPTY_SEMTYPE_ARR, 0, elementType, mut); + } else { + SemType[] initial = {elementType}; + return defn.defineListTypeWrapped(env, initial, size, getNeverType(), mut); + } + } + + @Override + public void resetSemType() { + defn.clear(); + super.resetSemType(); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return elementType instanceof MayBeDependentType eType && eType.isDependentlyTyped(visited); + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } + AbstractArrayValue value = (AbstractArrayValue) object; + SemType cachedShape = value.shapeOf(); + if (cachedShape != null) { + return Optional.of(cachedShape); + } + SemType semType = shapeOfInner(cx, shapeSupplier, value); + value.cacheShape(semType); + return Optional.of(semType); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + return isReadOnly(); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + return Optional.of(shapeOfInner(cx, shapeSupplier, (AbstractArrayValue) object)); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + Env env = cx.env; + if (acceptedTypeDefn.isDefinitionReady()) { + return Optional.of(acceptedTypeDefn.getSemType(cx.env)); + } + var result = acceptedTypeDefn.trySetDefinition(ListDefinition::new); + if (!result.updated()) { + return Optional.of(acceptedTypeDefn.getSemType(env)); + } + ListDefinition ld = result.definition(); + SemType elementType = ShapeAnalyzer.acceptedTypeOf(cx, getElementType()).orElseThrow(); + return Optional.of(getSemTypePart(env, ld, size, elementType, CELL_MUT_UNLIMITED)); + } + + private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, AbstractArrayValue value) { + ListDefinition readonlyShapeDefinition = value.getReadonlyShapeDefinition(); + if (readonlyShapeDefinition != null) { + return readonlyShapeDefinition.getSemType(cx.env); + } + int size = value.size(); + SemType[] memberTypes = new SemType[size]; + ListDefinition ld = new ListDefinition(); + value.setReadonlyShapeDefinition(ld); + for (int i = 0; i < size; i++) { + Optional memberType = shapeSupplier.get(cx, value.get(i)); + assert memberType.isPresent(); + memberTypes[i] = memberType.get(); + } + CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CELL_MUT_LIMITED; + SemType semType = ld.defineListTypeWrapped(cx.env, memberTypes, memberTypes.length, getNeverType(), mut); + value.resetReadonlyShapeDefinition(); + return semType; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BBooleanType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BBooleanType.java index 6fd5b0afb07d..f5a6c275ccda 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BBooleanType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BBooleanType.java @@ -18,15 +18,29 @@ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.BooleanType; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.function.Supplier; /** * {@code BBooleanType} represents boolean type in Ballerina. * * @since 0.995.0 */ -public class BBooleanType extends BType implements BooleanType { +public final class BBooleanType extends BSemTypeWrapper implements BooleanType { + + private static final BBooleanType TRUE = + new BBooleanType(() -> new BBooleanTypeImpl(TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE), + TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE, Builder.getBooleanConst(true)); + private static final BBooleanType FALSE = + new BBooleanType(() -> new BBooleanTypeImpl(TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE), + TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE, Builder.getBooleanConst(false)); /** * Create a {@code BBooleanType} which represents the boolean type. @@ -34,28 +48,43 @@ public class BBooleanType extends BType implements BooleanType { * @param typeName string name of the type */ public BBooleanType(String typeName, Module pkg) { - super(typeName, pkg, Boolean.class); + this(() -> new BBooleanTypeImpl(typeName, pkg), typeName, pkg, Builder.getBooleanType()); } - @Override - @SuppressWarnings("unchecked") - public V getZeroValue() { - return (V) Boolean.FALSE; + public static BBooleanType singletonType(boolean value) { + return value ? TRUE : FALSE; } - @SuppressWarnings("unchecked") - @Override - public V getEmptyValue() { - return (V) Boolean.FALSE; + private BBooleanType(Supplier bTypeSupplier, String typeName, Module pkg, SemType semType) { + super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, pkg, TypeTags.BOOLEAN_TAG, semType); } - @Override - public int getTag() { - return TypeTags.BOOLEAN_TAG; - } + protected static final class BBooleanTypeImpl extends BType implements BooleanType { + + private BBooleanTypeImpl(String typeName, Module pkg) { + super(typeName, pkg, Boolean.class); + } + + @Override + @SuppressWarnings("unchecked") + public V getZeroValue() { + return (V) Boolean.FALSE; + } + + @SuppressWarnings("unchecked") + @Override + public V getEmptyValue() { + return (V) Boolean.FALSE; + } + + @Override + public int getTag() { + return TypeTags.BOOLEAN_TAG; + } - @Override - public boolean isReadOnly() { - return true; + @Override + public boolean isReadOnly() { + return true; + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BByteType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BByteType.java index 97aca3f82894..d69b43dc9b8d 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BByteType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BByteType.java @@ -19,15 +19,26 @@ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.ByteType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.function.Supplier; + +import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED8_MAX_VALUE; +import static io.ballerina.runtime.api.types.PredefinedTypes.EMPTY_MODULE; /** * {@code BByteType} represents byte type in Ballerina. * * @since 0.995.0 */ -public class BByteType extends BType implements ByteType { +public final class BByteType extends BSemTypeWrapper implements ByteType { + + private static final BByteTypeImpl DEFAULT_B_TYPE = new BByteTypeImpl(TypeConstants.BYTE_TNAME, EMPTY_MODULE); /** * Create a {@code BByteType} which represents the byte type. @@ -35,28 +46,50 @@ public class BByteType extends BType implements ByteType { * @param typeName string name of the type */ public BByteType(String typeName, Module pkg) { - super(typeName, pkg, Integer.class); + this(() -> new BByteTypeImpl(typeName, pkg), typeName, EMPTY_MODULE, + Builder.createIntRange(0, UNSIGNED8_MAX_VALUE)); } - @Override - @SuppressWarnings("unchecked") - public V getZeroValue() { - return (V) new Integer(0); + private BByteType(Supplier bTypeSupplier, String typeName, Module pkg, SemType semType) { + super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, pkg, TypeTags.BYTE_TAG, semType); } - @Override - @SuppressWarnings("unchecked") - public V getEmptyValue() { - return (V) new Integer(0); + public static BByteType singletonType(long value) { + return new BByteType(() -> (BByteTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.BYTE_TNAME, EMPTY_MODULE, + Builder.getIntConst(value)); } - @Override - public int getTag() { - return TypeTags.BYTE_TAG; - } + protected static final class BByteTypeImpl extends BType implements ByteType, Cloneable { + + private BByteTypeImpl(String typeName, Module pkg) { + super(typeName, pkg, Integer.class); + } + + @Override + @SuppressWarnings("unchecked") + public V getZeroValue() { + return (V) new Integer(0); + } + + @Override + @SuppressWarnings("unchecked") + public V getEmptyValue() { + return (V) new Integer(0); + } + + @Override + public int getTag() { + return TypeTags.BYTE_TAG; + } + + @Override + public boolean isReadOnly() { + return true; + } - @Override - public boolean isReadOnly() { - return true; + @Override + public BType clone() { + return super.clone(); + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BDecimalType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BDecimalType.java index d1bf252d0f0b..441da73583d2 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BDecimalType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BDecimalType.java @@ -19,11 +19,18 @@ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.DecimalType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.values.DecimalValue; import java.math.BigDecimal; +import java.util.function.Supplier; + +import static io.ballerina.runtime.api.types.PredefinedTypes.EMPTY_MODULE; /** * {@code BDecimalType} represents decimal type in Ballerina. @@ -31,7 +38,10 @@ * * @since 0.995.0 */ -public class BDecimalType extends BType implements DecimalType { +public final class BDecimalType extends BSemTypeWrapper implements DecimalType { + + private static final BDecimalTypeImpl DEFAULT_B_TYPE = + new BDecimalTypeImpl(TypeConstants.DECIMAL_TNAME, EMPTY_MODULE); /** * Create a {@code BDecimalType} which represents the decimal type. @@ -39,28 +49,49 @@ public class BDecimalType extends BType implements DecimalType { * @param typeName string name of the type */ public BDecimalType(String typeName, Module pkg) { - super(typeName, pkg, DecimalValue.class); + this(() -> new BDecimalTypeImpl(typeName, pkg), typeName, pkg, Builder.getDecimalType()); } - @Override - @SuppressWarnings("unchecked") - public V getZeroValue() { - return (V) new DecimalValue(BigDecimal.ZERO); + public static BDecimalType singletonType(BigDecimal value) { + return new BDecimalType(() -> (BDecimalTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.DECIMAL_TNAME, + EMPTY_MODULE, Builder.getDecimalConst(value)); } - @Override - @SuppressWarnings("unchecked") - public V getEmptyValue() { - return (V) new DecimalValue(BigDecimal.ZERO); + private BDecimalType(Supplier bType, String typeName, Module pkg, SemType semType) { + super(new ConcurrentLazySupplier<>(bType), typeName, pkg, TypeTags.DECIMAL_TAG, semType); } - @Override - public int getTag() { - return TypeTags.DECIMAL_TAG; - } + protected static final class BDecimalTypeImpl extends BType implements DecimalType, Cloneable { + + private BDecimalTypeImpl(String typeName, Module pkg) { + super(typeName, pkg, DecimalValue.class); + } + + @Override + @SuppressWarnings("unchecked") + public V getZeroValue() { + return (V) new DecimalValue(BigDecimal.ZERO); + } + + @Override + @SuppressWarnings("unchecked") + public V getEmptyValue() { + return (V) new DecimalValue(BigDecimal.ZERO); + } + + @Override + public int getTag() { + return TypeTags.DECIMAL_TAG; + } + + @Override + public boolean isReadOnly() { + return true; + } - @Override - public boolean isReadOnly() { - return true; + @Override + public BType clone() { + return super.clone(); + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BErrorType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BErrorType.java index 8c1002def149..44e8d987ccbf 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BErrorType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BErrorType.java @@ -24,20 +24,30 @@ import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.values.BError; +import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.ErrorUtils; import io.ballerina.runtime.internal.values.ErrorValue; +import io.ballerina.runtime.internal.values.MapValueImpl; import java.util.Optional; +import java.util.Set; /** * {@code BErrorType} represents error type in Ballerina. * * @since 0.995.0 */ -public class BErrorType extends BAnnotatableType implements ErrorType { +public class BErrorType extends BAnnotatableType implements ErrorType, TypeWithShape { - public Type detailType = PredefinedTypes.TYPE_ERROR_DETAIL; + public Type detailType = PredefinedTypes.TYPE_DETAIL; public BTypeIdSet typeIdSet; private IntersectionType intersectionType = null; + private volatile DistinctIdSupplier distinctIdSupplier; public BErrorType(String typeName, Module pkg, Type detailType) { super(typeName, pkg, ErrorValue.class); @@ -50,6 +60,9 @@ public BErrorType(String typeName, Module pkg) { public void setTypeIdSet(BTypeIdSet typeIdSet) { this.typeIdSet = typeIdSet; + synchronized (this) { + this.distinctIdSupplier = null; + } } @Override @@ -113,4 +126,76 @@ public Optional getIntersectionType() { public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + public SemType createSemType() { + SemType err; + if (detailType == null || isTopType()) { + err = Builder.getErrorType(); + } else { + err = ErrorUtils.errorDetail(tryInto(getDetailType())); + } + + initializeDistinctIdSupplierIfNeeded(); + return distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct).reduce(err, Core::intersect); + } + + private void initializeDistinctIdSupplierIfNeeded() { + if (distinctIdSupplier == null) { + synchronized (this) { + if (distinctIdSupplier == null) { + distinctIdSupplier = new DistinctIdSupplier(TypeChecker.context().env, getTypeIdSet()); + } + } + } + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return detailType instanceof MayBeDependentType mayBeDependentType && + mayBeDependentType.isDependentlyTyped(visited); + } + + private boolean isTopType() { + return detailType == PredefinedTypes.TYPE_DETAIL; + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } + BError errorValue = (BError) object; + Object details = errorValue.getDetails(); + if (!(details instanceof MapValueImpl errorDetails)) { + return Optional.empty(); + } + initializeDistinctIdSupplierIfNeeded(); + // Should we actually pass the readonly shape supplier here? + return BMapType.shapeOfInner(cx, shapeSupplier, errorDetails) + .map(ErrorUtils::errorDetail) + .map(err -> distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct) + .reduce(err, Core::intersect)); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + BError errorValue = (BError) object; + Object details = errorValue.getDetails(); + if (!(details instanceof MapValueImpl errorDetails)) { + return Optional.empty(); + } + return BMapType.shapeOfInner(cx, shapeSupplierFn, errorDetails).map(ErrorUtils::errorDetail); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + return Optional.of(getSemType()); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + // TODO: consider properly handling this + return true; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFiniteType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFiniteType.java index de03a587aa95..bcef259ed050 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFiniteType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFiniteType.java @@ -21,11 +21,17 @@ import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.FiniteType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.values.RefValue; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.Optional; import java.util.Set; import java.util.StringJoiner; @@ -39,8 +45,8 @@ public class BFiniteType extends BType implements FiniteType { public Set valueSpace; - private final int typeFlags; - private final String originalName; + private int typeFlags; + private String originalName; public BFiniteType(String typeName) { this(typeName, new LinkedHashSet<>(), 0); @@ -187,6 +193,27 @@ public boolean equals(Object o) { if (!(o instanceof BFiniteType that)) { return false; } - return this.valueSpace.size() == that.valueSpace.size() && this.valueSpace.containsAll(that.valueSpace); + if (this.valueSpace.size() != that.valueSpace.size()) { + return false; + } + for (var each : this.valueSpace) { + try { + if (!that.valueSpace.contains(each)) { + return false; + } + } catch (NullPointerException ex) { + // If one of the sets is an immutable collection this can happen + return false; + } + } + return true; + } + + @Override + public SemType createSemType() { + Context cx = TypeChecker.context(); + return this.valueSpace.stream().map(each -> ShapeAnalyzer.inherentTypeOf(cx, each)) + .map(Optional::orElseThrow) + .reduce(Builder.getNeverType(), Core::union); } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFloatType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFloatType.java index 50450df52beb..7d5aab69a070 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFloatType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFloatType.java @@ -18,8 +18,16 @@ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.FloatType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.function.Supplier; + +import static io.ballerina.runtime.api.types.PredefinedTypes.EMPTY_MODULE; /** * {@code BFloatType} represents a integer which is a 32-bit floating-point number according to the @@ -28,7 +36,7 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BFloatType extends BType implements FloatType { +public final class BFloatType extends BSemTypeWrapper implements FloatType { /** * Create a {@code BFloatType} which represents the boolean type. @@ -36,26 +44,42 @@ public class BFloatType extends BType implements FloatType { * @param typeName string name of the type */ public BFloatType(String typeName, Module pkg) { - super(typeName, pkg, Double.class); + this(() -> new BFloatTypeImpl(typeName, pkg), typeName, pkg, Builder.getFloatType()); } - @Override - public V getZeroValue() { - return (V) new Double(0); - } - - @Override - public V getEmptyValue() { - return (V) new Double(0); + private BFloatType(Supplier bType, String typeName, Module pkg, SemType semType) { + super(new ConcurrentLazySupplier<>(bType), typeName, pkg, TypeTags.FLOAT_TAG, semType); } - @Override - public int getTag() { - return TypeTags.FLOAT_TAG; + public static BFloatType singletonType(Double value) { + return new BFloatType(() -> new BFloatTypeImpl(TypeConstants.FLOAT_TNAME, EMPTY_MODULE), + TypeConstants.FLOAT_TNAME, EMPTY_MODULE, Builder.getFloatConst(value)); } - @Override - public boolean isReadOnly() { - return true; + protected static final class BFloatTypeImpl extends BType implements FloatType { + + private BFloatTypeImpl(String typeName, Module pkg) { + super(typeName, pkg, Double.class); + } + + @Override + public V getZeroValue() { + return (V) new Double(0); + } + + @Override + public V getEmptyValue() { + return (V) new Double(0); + } + + @Override + public int getTag() { + return TypeTags.FLOAT_TAG; + } + + @Override + public boolean isReadOnly() { + return true; + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFunctionType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFunctionType.java index dbef43081922..ecc2a44c1f98 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFunctionType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFunctionType.java @@ -25,8 +25,18 @@ import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; +import io.ballerina.runtime.internal.types.semtype.FunctionDefinition; +import io.ballerina.runtime.internal.types.semtype.FunctionQualifiers; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; import java.util.Arrays; +import java.util.Objects; +import java.util.Set; /** * {@code {@link BFunctionType }} represents a function type in ballerina. @@ -39,6 +49,10 @@ public class BFunctionType extends BAnnotatableType implements FunctionType { public Type retType; public long flags; public Parameter[] parameters; + private static final Env env = Env.getInstance(); + private static final SemType ISOLATED_TOP = createIsolatedTop(env); + + private final DefinitionContainer defn = new DefinitionContainer<>(); public BFunctionType(Module pkg) { super("function ()", pkg, Object.class); @@ -120,31 +134,14 @@ public boolean equals(Object o) { return false; } - boolean isSourceAnyFunction = SymbolFlags.isFlagOn(this.flags, SymbolFlags.ANY_FUNCTION); - boolean isTargetAnyFunction = SymbolFlags.isFlagOn(that.flags, SymbolFlags.ANY_FUNCTION); - - if (isSourceAnyFunction && isTargetAnyFunction) { - return true; - } - - if (isSourceAnyFunction != isTargetAnyFunction) { - return false; - } - - if (SymbolFlags.isFlagOn(that.flags, SymbolFlags.ISOLATED) != SymbolFlags - .isFlagOn(this.flags, SymbolFlags.ISOLATED)) { - return false; - } - - if (SymbolFlags.isFlagOn(that.flags, SymbolFlags.TRANSACTIONAL) != SymbolFlags - .isFlagOn(this.flags, SymbolFlags.TRANSACTIONAL)) { + if (this.flags != that.flags) { return false; } if (!Arrays.equals(parameters, that.parameters)) { return false; } - return retType.equals(that.retType); + return Objects.equals(retType, that.retType) && Objects.equals(restType, that.restType); } @Override @@ -218,4 +215,96 @@ public Type getReturnType() { public long getFlags() { return flags; } + + private static SemType createIsolatedTop(Env env) { + FunctionDefinition fd = new FunctionDefinition(); + SemType ret = Builder.getValType(); + return fd.define(env, Builder.getNeverType(), ret, FunctionQualifiers.create(true, false)); + } + + @Override + public SemType createSemType() { + if (isFunctionTop()) { + return getTopType(); + } + if (defn.isDefinitionReady()) { + return defn.getSemType(env); + } + var result = defn.trySetDefinition(FunctionDefinition::new); + if (!result.updated()) { + return defn.getSemType(env); + } + FunctionDefinition fd = result.definition(); + SemType[] params = new SemType[parameters.length]; + for (int i = 0; i < parameters.length; i++) { + params[i] = getSemType(parameters[i].type); + } + SemType rest; + if (restType instanceof BArrayType arrayType) { + rest = getSemType(arrayType.getElementType()); + } else { + rest = Builder.getNeverType(); + } + + SemType returnType = resolveReturnType(); + ListDefinition paramListDefinition = new ListDefinition(); + SemType paramType = paramListDefinition.defineListTypeWrapped(env, params, params.length, rest, + CellAtomicType.CellMutability.CELL_MUT_NONE); + return fd.define(env, paramType, returnType, getQualifiers()); + } + + private SemType getTopType() { + if (SymbolFlags.isFlagOn(flags, SymbolFlags.ISOLATED)) { + return ISOLATED_TOP; + } + return Builder.getFunctionType(); + } + + FunctionQualifiers getQualifiers() { + return FunctionQualifiers.create(SymbolFlags.isFlagOn(flags, SymbolFlags.ISOLATED), + SymbolFlags.isFlagOn(flags, SymbolFlags.TRANSACTIONAL)); + } + + private SemType getSemType(Type type) { + return tryInto(type); + } + + private boolean isFunctionTop() { + return parameters == null && restType == null && retType == null; + } + + @Override + public synchronized void resetSemType() { + defn.clear(); + super.resetSemType(); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return (restType instanceof BType rest && rest.isDependentlyTyped(visited)) || + (retType instanceof BType ret && ret.isDependentlyTyped(visited)) || + isDependentlyTypeParameters(visited); + } + + private boolean isDependentlyTypeParameters(Set visited) { + if (parameters == null) { + return false; + } + return Arrays.stream(parameters).map(each -> each.type).filter(each -> each instanceof MayBeDependentType) + .anyMatch(each -> ((MayBeDependentType) each).isDependentlyTyped(visited)); + } + + private SemType resolveReturnType() { + if (retType == null) { + return Builder.getNilType(); + } + MayBeDependentType retBType = (MayBeDependentType) retType; + SemType returnType = getSemType(retType); + ListDefinition ld = new ListDefinition(); + SemType dependentlyTypedBit = + retBType.isDependentlyTyped() ? Builder.getBooleanConst(true) : Builder.getBooleanType(); + SemType[] innerType = new SemType[]{dependentlyTypedBit, returnType}; + return ld.defineListTypeWrapped(env, innerType, 2, Builder.getNeverType(), + CellAtomicType.CellMutability.CELL_MUT_NONE); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFutureType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFutureType.java index 3eb490648562..8839a92922fe 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFutureType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFutureType.java @@ -22,7 +22,13 @@ import io.ballerina.runtime.api.types.FutureType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.FutureUtils; + +import java.util.Objects; +import java.util.Set; /** * {@code BFutureType} represents a future value in Ballerina. @@ -31,7 +37,7 @@ */ public class BFutureType extends BType implements FutureType { - private Type constraint; + private final Type constraint; /** * Create a {@code {@link BFutureType}} which represents the future value. @@ -41,6 +47,7 @@ public class BFutureType extends BType implements FutureType { */ public BFutureType(String typeName, Module pkg) { super(typeName, pkg, Object.class); + constraint = null; } public BFutureType(Type constraint) { @@ -81,8 +88,7 @@ public boolean equals(Object obj) { if (constraint == other.constraint) { return true; } - - return TypeChecker.checkIsType(constraint, other.constraint); + return Objects.equals(constraint, other.constraint); } @Override @@ -93,4 +99,17 @@ public String toString() { private String getConstraintString() { return constraint != null ? "<" + constraint + ">" : ""; } + + @Override + public SemType createSemType() { + if (constraint == null) { + return Builder.getFutureType(); + } + return FutureUtils.futureContaining(TypeChecker.context().env, tryInto(constraint)); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return constraint instanceof MayBeDependentType constraintType && constraintType.isDependentlyTyped(visited); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BHandleType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BHandleType.java index ed1065c1df9e..9bd32efe9577 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BHandleType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BHandleType.java @@ -18,44 +18,68 @@ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.HandleType; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; import io.ballerina.runtime.internal.values.RefValue; /** - * {@code BHandleType} represents a handle type in Ballerina. - * A handle value is a reference to a storage managed externally by a Ballerina program. + * {@code BHandleType} represents a handle type in Ballerina. A handle value is a reference to a storage managed + * externally by a Ballerina program. * * @since 1.0.0 */ -public class BHandleType extends BType implements HandleType { +public final class BHandleType extends BSemTypeWrapper implements HandleType { /** - * Create a {@code BAnyType} which represents the any type. + * Create a {@code BHandleType} which represents the handle type. * * @param typeName string name of the type */ public BHandleType(String typeName, Module pkg) { - super(typeName, pkg, RefValue.class); + super(new ConcurrentLazySupplier<> + (() -> BHandleTypeImpl.create(typeName, pkg)), typeName, pkg, TypeTags.HANDLE_TAG, + Builder.getHandleType()); } - @Override - public V getZeroValue() { - return null; - } + protected static final class BHandleTypeImpl extends BType implements HandleType { - @Override - public V getEmptyValue() { - return null; - } + private static final BHandleTypeImpl DEFAULT = + new BHandleTypeImpl(TypeConstants.HANDLE_TNAME, PredefinedTypes.EMPTY_MODULE); - @Override - public int getTag() { - return TypeTags.HANDLE_TAG; - } + private static BHandleTypeImpl create(String typeName, Module pkg) { + if (typeName.equals(TypeConstants.HANDLE_TNAME) && pkg == PredefinedTypes.EMPTY_MODULE) { + return DEFAULT; + } + return new BHandleTypeImpl(typeName, pkg); + } + + private BHandleTypeImpl(String typeName, Module pkg) { + super(typeName, pkg, RefValue.class); + } + + @Override + public V getZeroValue() { + return null; + } + + @Override + public V getEmptyValue() { + return null; + } + + @Override + public int getTag() { + return TypeTags.HANDLE_TAG; + } + + @Override + public boolean isReadOnly() { + return true; + } - @Override - public boolean isReadOnly() { - return true; } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntegerType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntegerType.java index d8ec02af768b..0b7053f3c0d0 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntegerType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntegerType.java @@ -1,25 +1,42 @@ /* -* Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -* -* WSO2 Inc. licenses this file to you under the Apache License, -* Version 2.0 (the "License"); you may not use this file except -* in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ + * Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.IntegerType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.function.Supplier; + +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED16_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED16_MIN_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED32_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED32_MIN_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED8_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED8_MIN_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED16_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED32_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED8_MAX_VALUE; +import static io.ballerina.runtime.api.types.PredefinedTypes.EMPTY_MODULE; /** * {@code BIntegerType} represents an integer which is a 32-bit signed number. @@ -27,9 +44,10 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BIntegerType extends BType implements IntegerType { +public final class BIntegerType extends BSemTypeWrapper implements IntegerType { - private final int tag; + private static final BIntegerTypeImpl DEFAULT_B_TYPE = + new BIntegerTypeImpl(TypeConstants.INT_TNAME, EMPTY_MODULE, TypeTags.INT_TAG); /** * Create a {@code BIntegerType} which represents the boolean type. @@ -37,32 +55,89 @@ public class BIntegerType extends BType implements IntegerType { * @param typeName string name of the type */ public BIntegerType(String typeName, Module pkg) { - super(typeName, pkg, Long.class); - tag = TypeTags.INT_TAG; + this(() -> new BIntegerTypeImpl(typeName, pkg, TypeTags.INT_TAG), typeName, pkg, TypeTags.INT_TAG, + Builder.getIntType()); } public BIntegerType(String typeName, Module pkg, int tag) { - super(typeName, pkg, Long.class); - this.tag = tag; + this(() -> new BIntegerTypeImpl(typeName, pkg, tag), typeName, pkg, tag, pickSemType(tag)); + } + + private BIntegerType(Supplier bIntegerTypeSupplier, String typeName, Module pkg, int tag, + SemType semType) { + super(new ConcurrentLazySupplier<>(bIntegerTypeSupplier), typeName, pkg, tag, semType); + } + + private static SemType pickSemType(int tag) { + return switch (tag) { + case TypeTags.INT_TAG -> Builder.getIntType(); + case TypeTags.SIGNED8_INT_TAG -> Builder.createIntRange(SIGNED8_MIN_VALUE, SIGNED8_MAX_VALUE); + case TypeTags.SIGNED16_INT_TAG -> Builder.createIntRange(SIGNED16_MIN_VALUE, SIGNED16_MAX_VALUE); + case TypeTags.SIGNED32_INT_TAG -> Builder.createIntRange(SIGNED32_MIN_VALUE, SIGNED32_MAX_VALUE); + case TypeTags.UNSIGNED8_INT_TAG, TypeTags.BYTE_TAG -> Builder.createIntRange(0, UNSIGNED8_MAX_VALUE); + case TypeTags.UNSIGNED16_INT_TAG -> Builder.createIntRange(0, UNSIGNED16_MAX_VALUE); + case TypeTags.UNSIGNED32_INT_TAG -> Builder.createIntRange(0, UNSIGNED32_MAX_VALUE); + default -> throw new UnsupportedOperationException("Unexpected int tag"); + }; } - @Override - public V getZeroValue() { - return (V) new Long(0); + public static BIntegerType singletonType(long value) { + if (value >= IntegerTypeCache.CACHE_MIN_VALUE && value <= IntegerTypeCache.CACHE_MAX_VALUE) { + return IntegerTypeCache.cache[(int) value - IntegerTypeCache.CACHE_MIN_VALUE]; + } + return createSingletonType(value); } - @Override - public V getEmptyValue() { - return (V) new Long(0); + private static BIntegerType createSingletonType(long value) { + return new BIntegerType(() -> (BIntegerTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.INT_TNAME, EMPTY_MODULE, + TypeTags.INT_TAG, Builder.getIntConst(value)); } - @Override - public int getTag() { - return tag; + protected static final class BIntegerTypeImpl extends BType implements IntegerType, Cloneable { + + private final int tag; + + private BIntegerTypeImpl(String typeName, Module pkg, int tag) { + super(typeName, pkg, Long.class); + this.tag = tag; + } + + @Override + public V getZeroValue() { + return (V) new Long(0); + } + + @Override + public V getEmptyValue() { + return (V) new Long(0); + } + + @Override + public int getTag() { + return tag; + } + + @Override + public boolean isReadOnly() { + return true; + } + + @Override + public BType clone() { + return super.clone(); + } } - @Override - public boolean isReadOnly() { - return true; + private static final class IntegerTypeCache { + + private static final BIntegerType[] cache; + private static final int CACHE_MAX_VALUE = 127; + private static final int CACHE_MIN_VALUE = -128; + static { + cache = new BIntegerType[CACHE_MAX_VALUE - CACHE_MIN_VALUE + 1]; + for (int i = CACHE_MIN_VALUE; i <= CACHE_MAX_VALUE; i++) { + cache[i - CACHE_MIN_VALUE] = createSingletonType(i); + } + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntersectionType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntersectionType.java index f9cf98a079df..dc1991feb42c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntersectionType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntersectionType.java @@ -24,20 +24,32 @@ import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; +import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.ErrorUtils; +import io.ballerina.runtime.internal.types.semtype.ObjectDefinition; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.StringJoiner; +import java.util.function.Function; + +import static io.ballerina.runtime.api.utils.TypeUtils.getImpliedType; /** * {@code BIntersectionType} represents an intersection type in Ballerina. * * @since 2.0.0 */ -public class BIntersectionType extends BType implements IntersectionType { +public class BIntersectionType extends BType implements IntersectionType, TypeWithShape { private static final String PADDED_AMPERSAND = " & "; private static final String OPENING_PARENTHESIS = "("; @@ -215,4 +227,71 @@ public Optional getIntersectionType() { public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + public SemType createSemType() { + return createSemTypeInner(SemType::tryInto); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return constituentTypes.stream().filter(each -> each instanceof MayBeDependentType) + .anyMatch(type -> ((MayBeDependentType) type).isDependentlyTyped(visited)); + } + + private SemType createSemTypeInner(Function semTypeFunction) { + if (constituentTypes.isEmpty()) { + return Builder.getNeverType(); + } + SemType result = constituentTypes.stream().map(semTypeFunction).reduce(Core::intersect).orElseThrow(); + Optional distinctPart = distinctTypePart(result); + if (distinctPart.isPresent()) { + result = Core.intersect(result, distinctPart.get()); + } + return result; + } + + private Optional distinctTypePart(SemType result) { + if (Core.isSubtypeSimple(result, Builder.getErrorType())) { + BErrorType effectiveErrorType = (BErrorType) getImpliedType(effectiveType); + DistinctIdSupplier distinctIdSupplier = + new DistinctIdSupplier(TypeChecker.context().env, effectiveErrorType.getTypeIdSet()); + return distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct).reduce(Core::intersect); + } else if (Core.isSubtypeSimple(result, Builder.getObjectType())) { + BObjectType effectiveObjectType = (BObjectType) getImpliedType(effectiveType); + DistinctIdSupplier distinctIdSupplier = + new DistinctIdSupplier(TypeChecker.context().env, effectiveObjectType.getTypeIdSet()); + return distinctIdSupplier.get().stream().map(ObjectDefinition::distinct).reduce(Core::intersect); + } + return Optional.empty(); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + Type effectiveType = getEffectiveType(); + if (effectiveType instanceof TypeWithShape typeWithShape) { + return typeWithShape.shapeOf(cx, shapeSupplierFn, object); + } + return Optional.empty(); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + return Optional.of(createSemTypeInner(type -> ShapeAnalyzer.acceptedTypeOf(cx, type).orElseThrow())); + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + Type effectiveType = getEffectiveType(); + if (effectiveType instanceof TypeWithShape typeWithShape) { + return typeWithShape.inherentTypeOf(cx, shapeSupplier, object); + } + return Optional.empty(); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + return true; + } + } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMapType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMapType.java index fa47a26e06fc..43d2058cd4d9 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMapType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMapType.java @@ -24,11 +24,24 @@ import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; +import io.ballerina.runtime.internal.types.semtype.MappingDefinition; import io.ballerina.runtime.internal.values.MapValueImpl; import io.ballerina.runtime.internal.values.ReadOnlyUtils; +import java.util.Map; import java.util.Optional; +import java.util.Set; + +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_UNLIMITED; /** * {@code BMapType} represents a type of a map in Ballerina. @@ -41,12 +54,15 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BMapType extends BType implements MapType { +public class BMapType extends BType implements MapType, TypeWithShape, Cloneable { + public static final MappingDefinition.Field[] EMPTY_FIELD_ARR = new MappingDefinition.Field[0]; private final Type constraint; private final boolean readonly; private IntersectionType immutableType; private IntersectionType intersectionType = null; + private final DefinitionContainer defn = new DefinitionContainer<>(); + private final DefinitionContainer acceptedTypeDefn = new DefinitionContainer<>(); public BMapType(Type constraint) { this(constraint, false); @@ -169,4 +185,104 @@ public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + @Override + public SemType createSemType() { + Env env = Env.getInstance(); + if (defn.isDefinitionReady()) { + return defn.getSemType(env); + } + var result = defn.trySetDefinition(MappingDefinition::new); + if (!result.updated()) { + return defn.getSemType(env); + } + MappingDefinition md = result.definition(); + CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + return createSemTypeInner(env, md, tryInto(getConstrainedType()), mut); + } + + @Override + public void resetSemType() { + defn.clear(); + super.resetSemType(); + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } + MapValueImpl value = (MapValueImpl) object; + SemType cachedShape = value.shapeOf(); + if (cachedShape != null) { + return Optional.of(cachedShape); + } + + return shapeOfInner(cx, shapeSupplier, value); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + return isReadOnly(); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + return shapeOfInner(cx, shapeSupplierFn, (MapValueImpl) object); + } + + @Override + public synchronized Optional acceptedTypeOf(Context cx) { + Env env = cx.env; + if (acceptedTypeDefn.isDefinitionReady()) { + return Optional.of(acceptedTypeDefn.getSemType(env)); + } + var result = acceptedTypeDefn.trySetDefinition(MappingDefinition::new); + if (!result.updated()) { + return Optional.of(acceptedTypeDefn.getSemType(env)); + } + MappingDefinition md = result.definition(); + SemType elementType = ShapeAnalyzer.acceptedTypeOf(cx, getConstrainedType()).orElseThrow(); + return Optional.of(createSemTypeInner(env, md, elementType, CELL_MUT_UNLIMITED)); + } + + static Optional shapeOfInner(Context cx, ShapeSupplier shapeSupplier, MapValueImpl value) { + MappingDefinition readonlyShapeDefinition = value.getReadonlyShapeDefinition(); + if (readonlyShapeDefinition != null) { + return Optional.of(readonlyShapeDefinition.getSemType(cx.env)); + } + int nFields = value.size(); + MappingDefinition md = new MappingDefinition(); + value.setReadonlyShapeDefinition(md); + MappingDefinition.Field[] fields = new MappingDefinition.Field[nFields]; + Map.Entry[] entries = value.entrySet().toArray(Map.Entry[]::new); + for (int i = 0; i < nFields; i++) { + Optional valueType = shapeSupplier.get(cx, entries[i].getValue()); + SemType fieldType = valueType.orElseThrow(); + fields[i] = new MappingDefinition.Field(entries[i].getKey().toString(), fieldType, true, false); + } + CellAtomicType.CellMutability mut = value.getType().isReadOnly() ? CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + SemType semType = md.defineMappingTypeWrapped(cx.env, fields, Builder.getNeverType(), mut); + value.cacheShape(semType); + value.resetReadonlyShapeDefinition(); + return Optional.of(semType); + } + + private SemType createSemTypeInner(Env env, MappingDefinition defn, SemType restType, + CellAtomicType.CellMutability mut) { + return defn.defineMappingTypeWrapped(env, EMPTY_FIELD_ARR, restType, mut); + } + + @Override + public BMapType clone() { + BMapType clone = (BMapType) super.clone(); + clone.defn.clear(); + return clone; + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return constraint instanceof MayBeDependentType constraintType && constraintType.isDependentlyTyped(visited); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMethodType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMethodType.java index 46d25842338c..4332e1a2f49b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMethodType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMethodType.java @@ -84,4 +84,8 @@ public MethodType duplicate() { public boolean isIsolated() { return SymbolFlags.isFlagOn(flags, SymbolFlags.ISOLATED); } + + public String name() { + return funcName; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNetworkObjectType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNetworkObjectType.java index c289e0bffd88..5478813f2dc3 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNetworkObjectType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNetworkObjectType.java @@ -24,6 +24,9 @@ import io.ballerina.runtime.api.types.ResourceMethodType; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.stream.Stream; /** * {@code BNetworkObjectType} represents a network object in Ballerina. @@ -79,4 +82,20 @@ private RemoteMethodType[] getRemoteMethods(MethodType[] methodTypes) { public ResourceMethodType[] getResourceMethods() { return resourceMethods; } + + @Override + protected Collection allMethods() { + Stream methodStream = Arrays.stream(getMethods()) + .filter(methodType -> !(SymbolFlags.isFlagOn(methodType.getFlags(), SymbolFlags.REMOTE) || + SymbolFlags.isFlagOn(methodType.getFlags(), SymbolFlags.RESOURCE))) + .map(MethodData::fromMethod); + Stream remoteMethodStream = + Arrays.stream(getRemoteMethods()) + .map(MethodData::fromRemoteMethod); + Stream resourceMethodStream = + Arrays.stream(getResourceMethods()) + .map(method -> MethodData.fromResourceMethod( + (BResourceMethodType) method)); + return Stream.concat(methodStream, Stream.concat(remoteMethodStream, resourceMethodStream)).toList(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNeverType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNeverType.java index f57eedcc6dcf..299b860ca917 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNeverType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNeverType.java @@ -21,29 +21,25 @@ import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.NeverType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; /** * {@code BNeverType} represents the type of a {@code Never}. * * @since 2.0.0-preview1 */ -public class BNeverType extends BNullType implements NeverType { +public final class BNeverType extends BNullType implements NeverType { /** * Create a {@code BNeverType} represents the type of a {@code Never}. * * @param pkg package path */ public BNeverType(Module pkg) { - super(TypeConstants.NEVER_TNAME, pkg); + super(TypeConstants.NEVER_TNAME, pkg, Builder.getNeverType(), TypeTags.NEVER_TAG); } @Override public boolean isAnydata() { return true; } - - @Override - public int getTag() { - return TypeTags.NEVER_TAG; - } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNullType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNullType.java index 9070784980fc..02e846517c71 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNullType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNullType.java @@ -20,13 +20,18 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.types.NullType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.function.Supplier; /** * {@code BNullType} represents the type of a {@code NullLiteral}. * * @since 0.995.0 */ -public class BNullType extends BType implements NullType { +public sealed class BNullType extends BSemTypeWrapper implements NullType permits BNeverType { /** * Create a {@code BNullType} represents the type of a {@code NullLiteral}. @@ -35,31 +40,47 @@ public class BNullType extends BType implements NullType { * @param pkg package path */ public BNullType(String typeName, Module pkg) { - super(typeName, pkg, null); + this(() -> new BNullTypeImpl(typeName, pkg), typeName, pkg, TypeTags.NULL_TAG, Builder.getNilType()); } - @Override - public V getZeroValue() { - return null; + protected BNullType(String typeName, Module pkg, SemType semType, int tag) { + this(() -> new BNullTypeImpl(typeName, pkg), typeName, pkg, tag, semType); } - @Override - public V getEmptyValue() { - return null; + private BNullType(Supplier bNullTypeSupplier, String typeName, Module pkg, int tag, + SemType semType) { + super(new ConcurrentLazySupplier<>(bNullTypeSupplier), typeName, pkg, tag, semType); } - @Override - public int getTag() { - return TypeTags.NULL_TAG; - } + protected static final class BNullTypeImpl extends BType implements NullType { - @Override - public boolean isNilable() { - return true; - } + private BNullTypeImpl(String typeName, Module pkg) { + super(typeName, pkg, null); + } + + @Override + public V getZeroValue() { + return null; + } + + @Override + public V getEmptyValue() { + return null; + } + + @Override + public int getTag() { + return TypeTags.NULL_TAG; + } + + @Override + public boolean isNilable() { + return true; + } - @Override - public boolean isReadOnly() { - return true; + @Override + public boolean isReadOnly() { + return true; + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BObjectType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BObjectType.java index 05cd15e570fc..713a96f0d469 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BObjectType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BObjectType.java @@ -22,25 +22,49 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.runtime.api.types.Field; +import io.ballerina.runtime.api.types.FunctionType; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.MethodType; import io.ballerina.runtime.api.types.ObjectType; +import io.ballerina.runtime.api.types.Parameter; import io.ballerina.runtime.api.types.ResourceMethodType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeIdSet; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BObject; +import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.scheduling.Scheduler; import io.ballerina.runtime.internal.scheduling.Strand; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; +import io.ballerina.runtime.internal.types.semtype.FunctionDefinition; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import io.ballerina.runtime.internal.types.semtype.Member; +import io.ballerina.runtime.internal.types.semtype.ObjectDefinition; +import io.ballerina.runtime.internal.types.semtype.ObjectQualifiers; import io.ballerina.runtime.internal.utils.ValueUtils; +import io.ballerina.runtime.internal.values.AbstractObjectValue; import java.lang.reflect.Array; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; +import java.util.Set; import java.util.StringJoiner; +import java.util.function.Function; import static io.ballerina.runtime.api.types.TypeTags.SERVICE_TAG; @@ -49,7 +73,7 @@ * * @since 0.995.0 */ -public class BObjectType extends BStructureType implements ObjectType { +public class BObjectType extends BStructureType implements ObjectType, TypeWithShape { private MethodType[] methodTypes; private MethodType initMethod; @@ -62,6 +86,9 @@ public class BObjectType extends BStructureType implements ObjectType { private String cachedToString; private boolean resolving; + private final DefinitionContainer defn = new DefinitionContainer<>(); + private final DefinitionContainer acceptedTypeDefn = new DefinitionContainer<>(); + private volatile DistinctIdSupplier distinctIdSupplier; /** * Create a {@code BObjectType} which represents the user defined struct type. @@ -215,6 +242,10 @@ public void setIntersectionType(IntersectionType intersectionType) { public void setTypeIdSet(BTypeIdSet typeIdSet) { this.typeIdSet = typeIdSet; + synchronized (this) { + this.distinctIdSupplier = null; + } + resetSemType(); } public BObjectType duplicate() { @@ -243,6 +274,270 @@ public boolean hasAnnotations() { @Override public TypeIdSet getTypeIdSet() { + if (typeIdSet == null) { + return new BTypeIdSet(); + } return new BTypeIdSet(new ArrayList<>(typeIdSet.ids)); } + + @Override + public final SemType createSemType() { + Env env = Env.getInstance(); + initializeDistinctIdSupplierIfNeeded(env); + CellAtomicType.CellMutability mut = + SymbolFlags.isFlagOn(getFlags(), SymbolFlags.READONLY) ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + SemType innerType; + if (defn.isDefinitionReady()) { + innerType = defn.getSemType(env); + } else { + var result = defn.trySetDefinition(ObjectDefinition::new); + if (!result.updated()) { + innerType = defn.getSemType(env); + } else { + ObjectDefinition od = result.definition(); + innerType = semTypeInner(od, mut, SemType::tryInto); + } + } + return distinctIdSupplier.get().stream().map(ObjectDefinition::distinct).reduce(innerType, Core::intersect); + } + + private static boolean skipField(Set seen, String name) { + if (name.startsWith("$")) { + return true; + } + return !seen.add(name); + } + + private SemType semTypeInner(ObjectDefinition od, CellAtomicType.CellMutability mut, + Function semTypeSupplier) { + Env env = Env.getInstance(); + ObjectQualifiers qualifiers = getObjectQualifiers(); + List members = new ArrayList<>(); + Set seen = new HashSet<>(); + for (Entry entry : fields.entrySet()) { + String name = entry.getKey(); + if (skipField(seen, name)) { + continue; + } + Field field = entry.getValue(); + boolean isPublic = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.PUBLIC); + boolean isImmutable = qualifiers.readonly() | SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); + members.add(new Member(name, semTypeSupplier.apply(field.getFieldType()), Member.Kind.Field, + isPublic ? Member.Visibility.Public : Member.Visibility.Private, isImmutable)); + } + for (MethodData method : allMethods()) { + String name = method.name(); + if (skipField(seen, name)) { + continue; + } + boolean isPublic = SymbolFlags.isFlagOn(method.flags(), SymbolFlags.PUBLIC); + members.add(new Member(name, method.semType(), Member.Kind.Method, + isPublic ? Member.Visibility.Public : Member.Visibility.Private, true)); + } + return od.define(env, qualifiers, members, mut); + } + + private ObjectQualifiers getObjectQualifiers() { + boolean isolated = SymbolFlags.isFlagOn(getFlags(), SymbolFlags.ISOLATED); + boolean readonly = SymbolFlags.isFlagOn(getFlags(), SymbolFlags.READONLY); + ObjectQualifiers.NetworkQualifier networkQualifier; + if (SymbolFlags.isFlagOn(getFlags(), SymbolFlags.SERVICE)) { + networkQualifier = ObjectQualifiers.NetworkQualifier.Service; + } else if (SymbolFlags.isFlagOn(getFlags(), SymbolFlags.CLIENT)) { + networkQualifier = ObjectQualifiers.NetworkQualifier.Client; + } else { + networkQualifier = ObjectQualifiers.NetworkQualifier.None; + } + return new ObjectQualifiers(isolated, readonly, networkQualifier); + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } + AbstractObjectValue abstractObjectValue = (AbstractObjectValue) object; + SemType cachedShape = abstractObjectValue.shapeOf(); + if (cachedShape != null) { + return Optional.of(cachedShape); + } + initializeDistinctIdSupplierIfNeeded(cx.env); + SemType shape = distinctIdSupplier.get().stream().map(ObjectDefinition::distinct) + .reduce(valueShape(cx, shapeSupplier, abstractObjectValue), Core::intersect); + abstractObjectValue.cacheShape(shape); + return Optional.of(shape); + } + + private void initializeDistinctIdSupplierIfNeeded(Env env) { + if (distinctIdSupplier == null) { + synchronized (this) { + if (distinctIdSupplier == null) { + distinctIdSupplier = new DistinctIdSupplier(env, typeIdSet); + } + } + } + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + return Optional.of(valueShape(cx, shapeSupplierFn, (AbstractObjectValue) object)); + } + + @Override + public final Optional acceptedTypeOf(Context cx) { + Env env = Env.getInstance(); + initializeDistinctIdSupplierIfNeeded(cx.env); + CellAtomicType.CellMutability mut = CellAtomicType.CellMutability.CELL_MUT_UNLIMITED; + SemType innerType; + if (acceptedTypeDefn.isDefinitionReady()) { + innerType = acceptedTypeDefn.getSemType(env); + } else { + var result = acceptedTypeDefn.trySetDefinition(ObjectDefinition::new); + if (!result.updated()) { + innerType = acceptedTypeDefn.getSemType(env); + } else { + ObjectDefinition od = result.definition(); + innerType = semTypeInner(od, mut, (type -> ShapeAnalyzer.acceptedTypeOf(cx, type).orElseThrow())); + } + } + return Optional.of( + distinctIdSupplier.get().stream().map(ObjectDefinition::distinct).reduce(innerType, Core::intersect)); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + if (SymbolFlags.isFlagOn(getFlags(), SymbolFlags.READONLY)) { + return true; + } + return fields.values().stream().anyMatch( + field -> SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY) || + SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.FINAL)); + } + + private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, AbstractObjectValue object) { + ObjectDefinition readonlyShapeDefinition = object.getReadonlyShapeDefinition(); + if (readonlyShapeDefinition != null) { + return readonlyShapeDefinition.getSemType(cx.env); + } + ObjectDefinition od = new ObjectDefinition(); + object.setReadonlyShapeDefinition(od); + List members = new ArrayList<>(); + Set seen = new HashSet<>(fields.size() + methodTypes.length); + ObjectQualifiers qualifiers = getObjectQualifiers(); + for (Entry entry : fields.entrySet()) { + String name = entry.getKey(); + if (skipField(seen, name)) { + continue; + } + Field field = entry.getValue(); + boolean isPublic = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.PUBLIC); + boolean isImmutable = qualifiers.readonly() | SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY) | + SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.FINAL); + members.add(new Member(name, fieldShape(cx, shapeSupplier, field, object, isImmutable), Member.Kind.Field, + isPublic ? Member.Visibility.Public : Member.Visibility.Private, isImmutable)); + } + for (MethodData method : allMethods()) { + String name = method.name(); + if (skipField(seen, name)) { + continue; + } + boolean isPublic = SymbolFlags.isFlagOn(method.flags(), SymbolFlags.PUBLIC); + members.add(new Member(name, method.semType(), Member.Kind.Method, + isPublic ? Member.Visibility.Public : Member.Visibility.Private, true)); + } + return od.define(cx.env, qualifiers, members, + qualifiers.readonly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + } + + private static SemType fieldShape(Context cx, ShapeSupplier shapeSupplier, Field field, + AbstractObjectValue objectValue, boolean isImmutable) { + if (!isImmutable) { + return SemType.tryInto(field.getFieldType()); + } + BString fieldName = StringUtils.fromString(field.getFieldName()); + Optional shape = shapeSupplier.get(cx, objectValue.get(fieldName)); + assert !shape.isEmpty(); + return shape.get(); + } + + @Override + public void resetSemType() { + defn.clear(); + super.resetSemType(); + } + + protected Collection allMethods() { + if (methodTypes == null) { + return List.of(); + } + return Arrays.stream(methodTypes) + .map(MethodData::fromMethod).toList(); + } + + protected record MethodData(String name, long flags, SemType semType) { + + static MethodData fromMethod(MethodType method) { + return new MethodData(method.getName(), method.getFlags(), + tryInto(method.getType())); + } + + static MethodData fromRemoteMethod(MethodType method) { + // Remote methods need to be distinct with remote methods only there can be instance methods with the same + // name + return new MethodData("@remote_" + method.getName(), method.getFlags(), + tryInto(method.getType())); + } + + static MethodData fromResourceMethod(BResourceMethodType method) { + StringBuilder sb = new StringBuilder(); + sb.append(method.getAccessor()); + for (var each : method.getResourcePath()) { + sb.append(each); + } + String methodName = sb.toString(); + + Type[] pathSegmentTypes = method.pathSegmentTypes; + FunctionType innerFn = method.getType(); + List paramTypes = new ArrayList<>(); + for (Type part : pathSegmentTypes) { + if (part == null) { + paramTypes.add(Builder.getAnyType()); + } else { + paramTypes.add(tryInto(part)); + } + } + for (Parameter paramType : innerFn.getParameters()) { + paramTypes.add(tryInto(paramType.type)); + } + SemType rest; + Type restType = innerFn.getRestType(); + if (restType instanceof BArrayType arrayType) { + rest = tryInto(arrayType.getElementType()); + } else { + rest = Builder.getNeverType(); + } + + SemType returnType; + if (innerFn.getReturnType() != null) { + returnType = tryInto(innerFn.getReturnType()); + } else { + returnType = Builder.getNilType(); + } + ListDefinition paramListDefinition = new ListDefinition(); + Env env = TypeChecker.context().env; + SemType paramType = paramListDefinition.defineListTypeWrapped(env, paramTypes.toArray(SemType[]::new), + paramTypes.size(), rest, CellAtomicType.CellMutability.CELL_MUT_NONE); + FunctionDefinition fd = new FunctionDefinition(); + SemType semType = fd.define(env, paramType, returnType, ((BFunctionType) innerFn).getQualifiers()); + return new MethodData(methodName, method.getFlags(), semType); + } + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return fields.values().stream().map(Field::getFieldType).filter(each -> each instanceof MayBeDependentType) + .anyMatch(each -> ((MayBeDependentType) each).isDependentlyTyped(visited)); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BParameterizedType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BParameterizedType.java index 9ab78cf7271c..05b9229d68ac 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BParameterizedType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BParameterizedType.java @@ -21,6 +21,9 @@ import io.ballerina.runtime.api.types.ParameterizedType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.Set; /** * {@code ParameterizedType} represents the parameterized type in dependently-typed functions. @@ -80,4 +83,14 @@ public Type getParamValueType() { public int getParamIndex() { return this.paramIndex; } + + @Override + public SemType createSemType() { + return SemType.tryInto(this.paramValueType); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return true; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BReadonlyType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BReadonlyType.java index c945f4941839..c4366334b3fa 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BReadonlyType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BReadonlyType.java @@ -20,6 +20,8 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.types.ReadonlyType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; import io.ballerina.runtime.internal.values.RefValue; /** @@ -27,34 +29,41 @@ * * @since 1.3.0 */ -public class BReadonlyType extends BType implements ReadonlyType { +public final class BReadonlyType extends BSemTypeWrapper implements ReadonlyType { public BReadonlyType(String typeName, Module pkg) { - super(typeName, pkg, RefValue.class); + super(new ConcurrentLazySupplier<>(() -> new BReadonlyTypeImpl(typeName, pkg)), typeName, pkg, + TypeTags.READONLY_TAG, Builder.getReadonlyType()); } - @Override - public V getZeroValue() { - return null; - } + protected static final class BReadonlyTypeImpl extends BType implements ReadonlyType { - @Override - public V getEmptyValue() { - return null; - } + private BReadonlyTypeImpl(String typeName, Module pkg) { + super(typeName, pkg, RefValue.class); + } - @Override - public int getTag() { - return TypeTags.READONLY_TAG; - } + @Override + public V getZeroValue() { + return null; + } - @Override - public boolean isNilable() { - return true; - } + @Override + public V getEmptyValue() { + return null; + } + + @Override + public int getTag() { + return TypeTags.READONLY_TAG; + } + + public boolean isNilable() { + return true; + } - @Override - public boolean isReadOnly() { - return true; + @Override + public boolean isReadOnly() { + return true; + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BRecordType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BRecordType.java index 2af42d5596d2..f64e13db2304 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BRecordType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BRecordType.java @@ -20,6 +20,7 @@ import io.ballerina.identifier.Utils; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.runtime.api.flags.TypeFlags; @@ -28,26 +29,44 @@ import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BFunctionPointer; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.scheduling.Scheduler; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability; +import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; +import io.ballerina.runtime.internal.types.semtype.MappingDefinition; import io.ballerina.runtime.internal.values.MapValue; import io.ballerina.runtime.internal.values.MapValueImpl; import io.ballerina.runtime.internal.values.ReadOnlyUtils; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.function.Function; + +import static io.ballerina.runtime.api.types.semtype.Builder.getNeverType; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_UNLIMITED; /** * {@code BRecordType} represents a user defined record type in Ballerina. * * @since 0.995.0 */ -public class BRecordType extends BStructureType implements RecordType { +public class BRecordType extends BStructureType implements RecordType, TypeWithShape { private final String internalName; public boolean sealed; public Type restFieldType; @@ -55,6 +74,9 @@ public class BRecordType extends BStructureType implements RecordType { private final boolean readonly; private IntersectionType immutableType; private IntersectionType intersectionType = null; + private final DefinitionContainer defn = new DefinitionContainer<>(); + private final DefinitionContainer acceptedTypeDefn = new DefinitionContainer<>(); + private byte couldInhereTypeBeDifferentCache = 0; private final Map defaultValues = new LinkedHashMap<>(); @@ -73,6 +95,7 @@ public BRecordType(String typeName, String internalName, Module pkg, long flags, this.sealed = sealed; this.typeFlags = typeFlags; this.readonly = SymbolFlags.isFlagOn(flags, SymbolFlags.READONLY); + TypeCreator.registerRecordType(this); } /** @@ -102,6 +125,7 @@ public BRecordType(String typeName, Module pkg, long flags, Map f this.fields = fields; } this.internalName = typeName; + TypeCreator.registerRecordType(this); } private Map getReadOnlyFields(Map fields) { @@ -218,4 +242,190 @@ public Map getDefaultValues() { return defaultValues; } + @Override + public SemType createSemType() { + Env env = Env.getInstance(); + if (defn.isDefinitionReady()) { + return defn.getSemType(env); + } + var result = defn.trySetDefinition(MappingDefinition::new); + if (!result.updated()) { + return defn.getSemType(env); + } + MappingDefinition md = result.definition(); + return createSemTypeInner(md, env, mut(), SemType::tryInto); + } + + private CellMutability mut() { + return isReadOnly() ? CELL_MUT_NONE : CellMutability.CELL_MUT_LIMITED; + } + + private SemType createSemTypeInner(MappingDefinition md, Env env, CellMutability mut, + Function semTypeFunction) { + Field[] fields = getFields().values().toArray(Field[]::new); + MappingDefinition.Field[] mappingFields = new MappingDefinition.Field[fields.length]; + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); + SemType fieldType = semTypeFunction.apply(field.getFieldType()); + if (!isOptional && Core.isNever(fieldType)) { + return getNeverType(); + } + boolean isReadonly = + SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY) || Core.isNever(fieldType); + mappingFields[i] = new MappingDefinition.Field(field.getFieldName(), fieldType, + isReadonly, isOptional); + } + SemType rest; + rest = restFieldType != null ? semTypeFunction.apply(restFieldType) : getNeverType(); + return md.defineMappingTypeWrapped(env, mappingFields, rest, mut); + } + + @Override + public void resetSemType() { + defn.clear(); + super.resetSemType(); + } + + @Override + public boolean isDependentlyTypedInner(Set visited) { + return fields.values().stream().map(Field::getFieldType).filter(each -> each instanceof MayBeDependentType) + .anyMatch(each -> ((MayBeDependentType) each).isDependentlyTyped(visited)); + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } + MapValueImpl value = (MapValueImpl) object; + SemType cachedSemType = value.shapeOf(); + if (cachedSemType != null) { + return Optional.of(cachedSemType); + } + SemType semTypePart = shapeOfInner(cx, shapeSupplier, value, isReadOnly()); + value.cacheShape(semTypePart); + return Optional.of(semTypePart); + } + + private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, MapValueImpl value, + boolean takeFieldShape) { + Env env = cx.env; + int nFields = value.size(); + Map.Entry[] entries = value.entrySet().toArray(Map.Entry[]::new); + Set handledFields = new HashSet<>(nFields); + MappingDefinition md; + if (takeFieldShape) { + MappingDefinition readonlyShapeDefinition = value.getReadonlyShapeDefinition(); + if (readonlyShapeDefinition != null) { + return readonlyShapeDefinition.getSemType(env); + } else { + md = new MappingDefinition(); + value.setReadonlyShapeDefinition(md); + } + } else { + md = new MappingDefinition(); + } + List fields = new ArrayList<>(nFields); + for (int i = 0; i < nFields; i++) { + String fieldName = entries[i].getKey().toString(); + Object fieldValue = entries[i].getValue(); + handledFields.add(fieldName); + fields.add(fieldShape(cx, shapeSupplier, fieldName, fieldValue, takeFieldShape)); + } + if (!takeFieldShape) { + getFields().values().stream() + .filter(field -> !handledFields.contains(field.getFieldName())) + .map(field -> fieldShapeWithoutValue(field, field.getFieldName())) + .forEach(fields::add); + } + MappingDefinition.Field[] fieldsArray = fields.toArray(MappingDefinition.Field[]::new); + SemType rest; + if (takeFieldShape) { + rest = Builder.getNeverType(); + } else { + rest = restFieldType != null ? SemType.tryInto(restFieldType) : getNeverType(); + } + SemType shape = md.defineMappingTypeWrapped(env, fieldsArray, rest, mut()); + value.resetReadonlyShapeDefinition(); + return shape; + } + + private MappingDefinition.Field fieldShapeWithoutValue(Field field, String fieldName) { + boolean isOptional = fieldIsOptional(fieldName); + boolean isReadonly = fieldIsReadonly(fieldName); + SemType fieldType = SemType.tryInto(field.getFieldType()); + if (isReadonly && isOptional) { + fieldType = Builder.getUndefType(); + } + MappingDefinition.Field field1 = new MappingDefinition.Field(field.getFieldName(), fieldType, + isReadonly, isOptional); + return field1; + } + + @Override + public boolean couldInherentTypeBeDifferent() { + if (couldInhereTypeBeDifferentCache != 0) { + return couldInhereTypeBeDifferentCache == 1; + } + boolean result = couldShapeBeDifferentInner(); + couldInhereTypeBeDifferentCache = (byte) (result ? 1 : 2); + return result; + } + + private boolean couldShapeBeDifferentInner() { + if (isReadOnly()) { + return true; + } + return fields.values().stream().anyMatch(field -> SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY)); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + return Optional.of(shapeOfInner(cx, shapeSupplier, (MapValueImpl) object, true)); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + Env env = cx.env; + if (acceptedTypeDefn.isDefinitionReady()) { + return Optional.of(acceptedTypeDefn.getSemType(env)); + } + var result = acceptedTypeDefn.trySetDefinition(MappingDefinition::new); + if (!result.updated()) { + return Optional.of(acceptedTypeDefn.getSemType(env)); + } + MappingDefinition md = result.definition(); + return Optional.of(createSemTypeInner(md, env, CELL_MUT_UNLIMITED, + (type) -> ShapeAnalyzer.acceptedTypeOf(cx, type).orElseThrow())); + } + + private Type fieldType(String fieldName) { + Field field = fields.get(fieldName); + return field == null ? restFieldType : field.getFieldType(); + } + + private boolean fieldIsReadonly(String fieldName) { + Field field = fields.get(fieldName); + return field != null && SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); + } + + private boolean fieldIsOptional(String fieldName) { + Field field = fields.get(fieldName); + return field != null && SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); + } + + private MappingDefinition.Field fieldShape(Context cx, ShapeSupplier shapeSupplier, String fieldName, + Object fieldValue, boolean alwaysTakeValueShape) { + boolean readonlyField = fieldIsReadonly(fieldName); + boolean optionalField = fieldIsOptional(fieldName); + SemType fieldType; + if (alwaysTakeValueShape || readonlyField) { + optionalField = false; + fieldType = shapeSupplier.get(cx, fieldValue).orElseThrow(); + } else { + fieldType = SemType.tryInto(fieldType(fieldName)); + } + return new MappingDefinition.Field(fieldName, fieldType, readonlyField, optionalField); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeWrapper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeWrapper.java new file mode 100644 index 000000000000..4c1f5ed85391 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeWrapper.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types; + +import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.ImmutableSemType; + +import java.util.Objects; +import java.util.Set; +import java.util.function.Supplier; + +/** + * Decorator on {@code BTypes} allowing them to behave as {@code SemType}. All + * {@code Types} that needs to behave as both a {@code BType} and a + * {@code SemType} should extend this class. + * + * @param The type of the {@code BType} that is being wrapped. + * @since 2201.11.0 + */ +public sealed class BSemTypeWrapper extends ImmutableSemType implements Type, MayBeDependentType + permits BAnyType, BBooleanType, BByteType, BDecimalType, BFloatType, BHandleType, BIntegerType, BNullType, + BReadonlyType, BStringType { + + private Type cachedReferredType = null; + private Type cachedImpliedType = null; + + private final Supplier bTypeSupplier; + private final int tag; + protected final String typeName; // Debugger uses this field to show the type name + private final Module pkg; + + protected BSemTypeWrapper(Supplier bTypeSupplier, String typeName, Module pkg, int tag, SemType semType) { + super(semType); + this.bTypeSupplier = bTypeSupplier; + this.typeName = typeName; + this.tag = tag; + this.pkg = pkg; + } + + public final Class getValueClass() { + return getbType().getValueClass(); + } + + @Override + public final V getZeroValue() { + return getbType().getZeroValue(); + } + + @Override + public final V getEmptyValue() { + return getbType().getEmptyValue(); + } + + @Override + public final int getTag() { + return tag; + } + + @Override + public final String toString() { + return getbType().toString(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BSemTypeWrapper other)) { + return false; + } + return Objects.equals(this.typeName, other.typeName) && Objects.equals(this.pkg, other.pkg); + } + + @Override + public final boolean isNilable() { + return Core.containsBasicType(this, Builder.getNilType()); + } + + @Override + public final int hashCode() { + return Objects.hash(this.typeName, this.pkg); + } + + @Override + public String getName() { + return typeName == null ? "" : typeName; + } + + @Override + public String getQualifiedName() { + String name = getName(); + if (name.isEmpty()) { + return ""; + } + + return pkg == null ? name : pkg + ":" + name; + } + + @Override + public Module getPackage() { + return pkg; + } + + @Override + public boolean isPublic() { + return getbType().isPublic(); + } + + @Override + public boolean isNative() { + return getbType().isNative(); + } + + @Override + public boolean isAnydata() { + Context cx = TypeChecker.context(); + return Core.isSubType(cx, this, Builder.getAnyDataType()); + } + + @Override + public boolean isPureType() { + Context cx = TypeChecker.context(); + return Core.isSubType(cx, this, Builder.getErrorType()) || isAnydata(); + } + + @Override + public boolean isReadOnly() { + Context cx = TypeChecker.context(); + return Core.isSubType(cx, this, Builder.getReadonlyType()); + } + + @Override + public Type getImmutableType() { + return getbType().getImmutableType(); + } + + @Override + public void setImmutableType(IntersectionType immutableType) { + getbType().setImmutableType(immutableType); + } + + @Override + public Module getPkg() { + return pkg; + } + + @Override + public long getFlags() { + return getbType().getFlags(); + } + + @Override + public void setCachedReferredType(Type type) { + cachedReferredType = type; + } + + @Override + public Type getCachedReferredType() { + return cachedReferredType; + } + + @Override + public void setCachedImpliedType(Type type) { + cachedImpliedType = type; + } + + @Override + public Type getCachedImpliedType() { + return cachedImpliedType; + } + + protected E getbType() { + return bTypeSupplier.get(); + } + + @Override + public boolean isDependentlyTyped() { + return false; + } + + @Override + public boolean isDependentlyTyped(Set visited) { + return isDependentlyTyped(); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStreamType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStreamType.java index 494dc1d54642..1382a3e29943 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStreamType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStreamType.java @@ -24,9 +24,16 @@ import io.ballerina.runtime.api.types.StreamType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; +import io.ballerina.runtime.internal.types.semtype.StreamDefinition; import io.ballerina.runtime.internal.values.StreamValue; import java.util.Objects; +import java.util.Set; /** * {@link BStreamType} represents streaming data in Ballerina. @@ -37,6 +44,7 @@ public class BStreamType extends BType implements StreamType { private final Type constraint; private final Type completionType; + private final DefinitionContainer definition = new DefinitionContainer<>(); /** * Creates a {@link BStreamType} which represents the stream type. @@ -135,4 +143,29 @@ public boolean equals(Object obj) { return Objects.equals(constraint, other.constraint) && Objects.equals(completionType, other.completionType); } + + @Override + public SemType createSemType() { + if (constraint == null) { + return Builder.getStreamType(); + } + Env env = TypeChecker.context().env; + if (definition.isDefinitionReady()) { + return definition.getSemType(env); + } + var result = definition.trySetDefinition(StreamDefinition::new); + if (!result.updated()) { + return definition.getSemType(env); + } + StreamDefinition sd = result.definition(); + return sd.define(env, tryInto(constraint), tryInto(completionType)); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return (constraint instanceof MayBeDependentType constrainedType && + constrainedType.isDependentlyTyped(visited)) || + (completionType instanceof MayBeDependentType completionType && + completionType.isDependentlyTyped(visited)); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStringType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStringType.java index 0211eef19b38..7ddbd3bf0f7c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStringType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStringType.java @@ -19,8 +19,14 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.constants.RuntimeConstants; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.StringType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.function.Supplier; /** * {@code BStringType} represents a String type in ballerina. @@ -28,9 +34,13 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BStringType extends BType implements StringType { +public final class BStringType extends BSemTypeWrapper implements StringType { - private final int tag; + // We are creating separate empty module instead of reusing PredefinedTypes.EMPTY_MODULE to avoid cyclic + // dependencies. + private static final Module DEFAULT_MODULE = new Module(null, null, null); + private static final BStringTypeImpl DEFAULT_B_TYPE = + new BStringTypeImpl(TypeConstants.STRING_TNAME, DEFAULT_MODULE, TypeTags.STRING_TAG); /** * Create a {@code BStringType} which represents the boolean type. @@ -38,32 +48,64 @@ public class BStringType extends BType implements StringType { * @param typeName string name of the type */ public BStringType(String typeName, Module pkg) { - super(typeName, pkg, String.class); - tag = TypeTags.STRING_TAG; + this(() -> new BStringTypeImpl(typeName, pkg, TypeTags.STRING_TAG), typeName, pkg, TypeTags.STRING_TAG, + Builder.getStringType()); } public BStringType(String typeName, Module pkg, int tag) { - super(typeName, pkg, String.class); - this.tag = tag; + this(() -> new BStringTypeImpl(typeName, pkg, tag), typeName, pkg, tag, pickSemtype(tag)); } - @Override - public V getZeroValue() { - return (V) RuntimeConstants.STRING_EMPTY_VALUE; + private BStringType(Supplier bTypeSupplier, String typeName, Module pkg, int tag, + SemType semType) { + super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, pkg, tag, semType); } - @Override - public V getEmptyValue() { - return (V) RuntimeConstants.STRING_EMPTY_VALUE; + public static BStringType singletonType(String value) { + return new BStringType(() -> (BStringTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.STRING_TNAME, + DEFAULT_MODULE, TypeTags.STRING_TAG, Builder.getStringConst(value)); } - @Override - public int getTag() { - return tag; + private static SemType pickSemtype(int tag) { + return switch (tag) { + case TypeTags.STRING_TAG -> Builder.getStringType(); + case TypeTags.CHAR_STRING_TAG -> Builder.getCharType(); + default -> throw new IllegalStateException("Unexpected string type tag: " + tag); + }; } - @Override - public boolean isReadOnly() { - return true; + protected static final class BStringTypeImpl extends BType implements StringType, Cloneable { + + private final int tag; + + private BStringTypeImpl(String typeName, Module pkg, int tag) { + super(typeName, pkg, String.class); + this.tag = tag; + } + + @Override + public V getZeroValue() { + return (V) RuntimeConstants.STRING_EMPTY_VALUE; + } + + @Override + public V getEmptyValue() { + return (V) RuntimeConstants.STRING_EMPTY_VALUE; + } + + @Override + public int getTag() { + return tag; + } + + @Override + public boolean isReadOnly() { + return true; + } + + @Override + public BType clone() { + return super.clone(); + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStructureType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStructureType.java index 64cf037ebde1..2acdcf181db7 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStructureType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStructureType.java @@ -72,6 +72,7 @@ public Map getFields() { @Override public void setFields(Map fields) { this.fields = fields; + resetSemType(); } @Override diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTableType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTableType.java index 14c7d2b803e2..df743c54c344 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTableType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTableType.java @@ -22,18 +22,27 @@ import io.ballerina.runtime.api.types.TableType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; +import io.ballerina.runtime.api.values.BTable; +import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.TableUtils; import io.ballerina.runtime.internal.values.ReadOnlyUtils; import io.ballerina.runtime.internal.values.TableValue; import io.ballerina.runtime.internal.values.TableValueImpl; import java.util.Optional; +import java.util.Set; /** * {@code BTableType} represents tabular data in Ballerina. * * @since 1.3.0 */ -public class BTableType extends BType implements TableType { +public class BTableType extends BType implements TableType, TypeWithShape { private final Type constraint; private Type keyType; @@ -162,4 +171,85 @@ public void setIntersectionType(IntersectionType intersectionType) { public boolean isAnydata() { return this.constraint.isAnydata(); } + + @Override + public SemType createSemType() { + return createSemTypeWithConstraint(tryInto(constraint)); + } + + private SemType createSemTypeWithConstraint(SemType constraintType) { + SemType semType; + Context cx = TypeChecker.context(); + if (fieldNames.length > 0) { + semType = TableUtils.tableContainingKeySpecifier(cx, constraintType, fieldNames); + } else if (keyType != null) { + semType = TableUtils.tableContainingKeyConstraint(cx, constraintType, tryInto(keyType)); + } else { + semType = TableUtils.tableContaining(cx.env, constraintType); + } + + if (isReadOnly()) { + semType = Core.intersect(semType, Builder.getReadonlyType()); + } + return semType; + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } + BTable table = (BTable) object; + SemType cachedShape = table.shapeOf(); + if (cachedShape != null) { + return Optional.of(cachedShape); + } + SemType semtype = valueShape(cx, shapeSupplier, table); + return Optional.of(semtype); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + return isReadOnly(); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + return Optional.of(valueShape(cx, shapeSupplierFn, (BTable) object)); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + SemType constraintType = ShapeAnalyzer.acceptedTypeOf(cx, this.constraint).orElseThrow(); + SemType semType; + if (fieldNames.length > 0) { + semType = TableUtils.acceptedTypeContainingKeySpecifier(cx, constraintType, fieldNames); + } else if (keyType != null) { + SemType keyAcceptedType = ShapeAnalyzer.acceptedTypeOf(cx, keyType).orElseThrow(); + semType = TableUtils.acceptedTypeContainingKeyConstraint(cx, constraintType, keyAcceptedType); + } else { + semType = TableUtils.acceptedType(cx.env, constraintType); + } + return Optional.of(semType); + } + + private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, BTable table) { + SemType constraintType = Builder.getNeverType(); + for (var value : table.values()) { + SemType valueShape = shapeSupplier.get(cx, value).orElse(SemType.tryInto(constraint)); + constraintType = Core.union(constraintType, valueShape); + } + return createSemTypeWithConstraint(constraintType); + } + + @Override + public boolean shouldCache() { + // TODO: remove this once we have fixed equals + return false; + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return constraint instanceof MayBeDependentType constraintType && constraintType.isDependentlyTyped(visited); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTupleType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTupleType.java index e4758fd5e8b8..ccda11d6f298 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTupleType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTupleType.java @@ -24,6 +24,15 @@ import io.ballerina.runtime.api.types.TupleType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import io.ballerina.runtime.internal.values.AbstractArrayValue; import io.ballerina.runtime.internal.values.ReadOnlyUtils; import io.ballerina.runtime.internal.values.TupleValueImpl; @@ -31,25 +40,36 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; +import static io.ballerina.runtime.api.types.semtype.Builder.getNeverType; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_UNLIMITED; + /** * {@code {@link BTupleType}} represents a tuple type in Ballerina. * * @since 0.995.0 */ -public class BTupleType extends BAnnotatableType implements TupleType { +public class BTupleType extends BAnnotatableType implements TupleType, TypeWithShape { private List tupleTypes; private Type restType; private int typeFlags; private final boolean readonly; + // This is used avoid unnecessary flag updates when we change the members. If this + // is set before accessing flags you must call {@code checkAllMembers}. + private volatile boolean flagsPoisoned = false; private IntersectionType immutableType; private IntersectionType intersectionType = null; public boolean isCyclic = false; private boolean resolving; private boolean resolvingReadonly; private String cachedToString; + private final DefinitionContainer defn = new DefinitionContainer<>(); + private final DefinitionContainer acceptedTypeDefn = new DefinitionContainer<>(); /** * Create a {@code BTupleType} which represents the tuple type. @@ -60,7 +80,7 @@ public BTupleType(List typeList) { super(null, null, Object.class); this.tupleTypes = typeList; this.restType = null; - checkAllMembers(); + this.flagsPoisoned = true; this.readonly = false; } @@ -154,6 +174,7 @@ public void setCyclic(boolean isCyclic) { } public void setMemberTypes(List members, Type restType) { + resetSemType(); if (members == null) { return; } @@ -166,7 +187,8 @@ public void setMemberTypes(List members, Type restType) { this.tupleTypes = members; this.restType = restType; } - checkAllMembers(); + flagsPoisoned = true; + defn.clear(); } @Override @@ -254,16 +276,24 @@ public boolean equals(Object o) { @Override public boolean isAnydata() { - return TypeFlags.isFlagOn(this.typeFlags, TypeFlags.ANYDATA); + return TypeFlags.isFlagOn(getTypeFlags(), TypeFlags.ANYDATA); } @Override public boolean isPureType() { - return TypeFlags.isFlagOn(this.typeFlags, TypeFlags.PURETYPE); + return TypeFlags.isFlagOn(getTypeFlags(), TypeFlags.PURETYPE); } @Override public int getTypeFlags() { + if (flagsPoisoned) { + synchronized (this) { + if (flagsPoisoned) { + checkAllMembers(); + flagsPoisoned = false; + } + } + } return this.typeFlags; } @@ -299,4 +329,108 @@ public void setIntersectionType(IntersectionType intersectionType) { public String getAnnotationKey() { return Utils.decodeIdentifier(this.typeName); } + + @Override + public SemType createSemType() { + Env env = Env.getInstance(); + if (defn.isDefinitionReady()) { + return defn.getSemType(env); + } + var result = defn.trySetDefinition(ListDefinition::new); + if (!result.updated()) { + return defn.getSemType(env); + } + ListDefinition ld = result.definition(); + return createSemTypeInner(env, ld, SemType::tryInto, mut()); + } + + private CellAtomicType.CellMutability mut() { + return isReadOnly() ? CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; + } + + private SemType createSemTypeInner(Env env, ListDefinition ld, Function semTypeFunction, + CellAtomicType.CellMutability mut) { + SemType[] memberTypes = new SemType[tupleTypes.size()]; + for (int i = 0; i < tupleTypes.size(); i++) { + SemType memberType = semTypeFunction.apply(tupleTypes.get(i)); + if (Core.isNever(memberType)) { + return getNeverType(); + } + memberTypes[i] = memberType; + } + SemType rest = restType != null ? semTypeFunction.apply(restType) : getNeverType(); + return ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, rest, mut); + } + + @Override + public void resetSemType() { + defn.clear(); + super.resetSemType(); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return tupleTypes.stream().filter(each -> each instanceof MayBeDependentType) + .anyMatch(each -> ((MayBeDependentType) each).isDependentlyTyped(visited)); + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } + AbstractArrayValue value = (AbstractArrayValue) object; + SemType cachedShape = value.shapeOf(); + if (cachedShape != null) { + return Optional.of(cachedShape); + } + SemType semType = shapeOfInner(cx, shapeSupplier, value); + value.cacheShape(semType); + return Optional.of(semType); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + return isReadOnly(); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + return Optional.of(shapeOfInner(cx, shapeSupplier, (AbstractArrayValue) object)); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + Env env = cx.env; + if (acceptedTypeDefn.isDefinitionReady()) { + return Optional.of(acceptedTypeDefn.getSemType(env)); + } + var result = acceptedTypeDefn.trySetDefinition(ListDefinition::new); + if (!result.updated()) { + return Optional.of(acceptedTypeDefn.getSemType(env)); + } + ListDefinition ld = result.definition(); + return Optional.of(createSemTypeInner(env, ld, (type) -> ShapeAnalyzer.acceptedTypeOf(cx, type).orElseThrow(), + CELL_MUT_UNLIMITED)); + } + + private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, AbstractArrayValue value) { + Env env = cx.env; + ListDefinition defn = value.getReadonlyShapeDefinition(); + if (defn != null) { + return defn.getSemType(env); + } + int size = value.size(); + SemType[] memberTypes = new SemType[size]; + ListDefinition ld = new ListDefinition(); + value.setReadonlyShapeDefinition(ld); + for (int i = 0; i < size; i++) { + Optional memberType = shapeSupplier.get(cx, value.get(i)); + assert memberType.isPresent(); + memberTypes[i] = memberType.get(); + } + SemType semType = ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, getNeverType(), mut()); + value.resetReadonlyShapeDefinition(); + return semType; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BType.java index d6cd9c996ba3..62c285dbe04d 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BType.java @@ -22,10 +22,20 @@ import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.CacheableTypeDescriptor; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.TypeCheckCache; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.MutableSemType; +import java.util.HashSet; import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** * {@code BType} represents a type in Ballerina. @@ -37,13 +47,18 @@ * * @since 0.995.0 */ -public abstract class BType implements Type { +public abstract non-sealed class BType extends SemType + implements Type, MutableSemType, Cloneable, CacheableTypeDescriptor, MayBeDependentType { + protected String typeName; protected Module pkg; protected Class valueClass; private int hashCode; private Type cachedReferredType = null; private Type cachedImpliedType = null; + private volatile SemType cachedSemType = null; + private volatile TypeCheckCache typeCheckCache; + private final ReadWriteLock typeCacheLock = new ReentrantReadWriteLock(); protected BType(String typeName, Module pkg, Class valueClass) { this.typeName = typeName; @@ -231,4 +246,97 @@ public void setCachedImpliedType(Type type) { public Type getCachedImpliedType() { return this.cachedImpliedType; } + + @Override + public SemType createSemType() { + throw new IllegalStateException("Child that are used for type checking must implement this method"); + } + + @Override + public void updateInnerSemTypeIfNeeded() { + if (cachedSemType == null) { + cachedSemType = createSemType(); + setAll(cachedSemType.all()); + setSome(cachedSemType.some(), cachedSemType.subTypeData()); + } + } + + protected SemType getSemType() { + updateInnerSemTypeIfNeeded(); + return cachedSemType; + } + + @Override + public void resetSemType() { + cachedSemType = null; + } + + @Override + public BType clone() { + try { + BType clone = (BType) super.clone(); + clone.cachedSemType = null; + clone.setCachedImpliedType(null); + clone.setCachedReferredType(null); + return clone; + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } + + @Override + public boolean shouldCache() { + return this.pkg != null && this.typeName != null && !this.typeName.contains("$anon"); + } + + @Override + public final Optional cachedTypeCheckResult(Context cx, CacheableTypeDescriptor other) { + initializeCacheIfNeeded(cx); + typeCacheLock.readLock().lock(); + try { + return typeCheckCache.cachedTypeCheckResult(other); + } finally { + typeCacheLock.readLock().unlock(); + } + } + + private void initializeCacheIfNeeded(Context cx) { + typeCacheLock.readLock().lock(); + boolean shouldInitialize = typeCheckCache == null; + typeCacheLock.readLock().unlock(); + if (!shouldInitialize) { + return; + } + try { + typeCacheLock.writeLock().lock(); + if (typeCheckCache == null) { + typeCheckCache = cx.getTypeCheckCache(this); + } + } finally { + typeCacheLock.writeLock().unlock(); + } + } + + @Override + public final void cacheTypeCheckResult(CacheableTypeDescriptor other, boolean result) { + // This happening after checking the cache so it must be initialized by now + typeCheckCache.cacheTypeCheckResult(other, result); + } + + @Override + public final boolean isDependentlyTyped() { + return isDependentlyTyped(new HashSet<>()); + } + + @Override + public final boolean isDependentlyTyped(Set visited) { + if (!visited.add(this)) { + return false; + } + return isDependentlyTypedInner(visited); + } + + protected boolean isDependentlyTypedInner(Set visited) { + return false; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeReferenceType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeReferenceType.java index cc2e78d6a319..2e6384699111 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeReferenceType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeReferenceType.java @@ -25,16 +25,20 @@ import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import java.util.Objects; import java.util.Optional; +import java.util.Set; /** * {@code TypeReferencedType} represents a type description which refers to another type. * * @since 2201.2.0 */ -public class BTypeReferenceType extends BAnnotatableType implements IntersectableReferenceType { +public class BTypeReferenceType extends BAnnotatableType implements IntersectableReferenceType, TypeWithShape { private final int typeFlags; private final boolean readOnly; @@ -126,4 +130,50 @@ public Optional getIntersectionType() { public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + public SemType createSemType() { + Type referredType = getReferredType(); + return tryInto(referredType); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return getReferredType() instanceof MayBeDependentType refType && refType.isDependentlyTyped(visited); + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } + Type referredType = getReferredType(); + if (referredType instanceof TypeWithShape typeWithShape) { + return typeWithShape.inherentTypeOf(cx, shapeSupplier, object); + } + return Optional.empty(); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + return referredType instanceof TypeWithShape typeWithShape && typeWithShape.couldInherentTypeBeDifferent(); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + Type referredType = getReferredType(); + if (referredType instanceof TypeWithShape typeWithShape) { + return typeWithShape.shapeOf(cx, shapeSupplierFn, object); + } + return ShapeAnalyzer.shapeOf(cx, referredType); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + Type referredType = getReferredType(); + if (referredType instanceof TypeWithShape typeWithShape) { + return typeWithShape.acceptedTypeOf(cx); + } + return ShapeAnalyzer.acceptedTypeOf(cx, referredType); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypedescType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypedescType.java index 411d75cf9662..da696b1bf335 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypedescType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypedescType.java @@ -24,19 +24,28 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.TypedescType; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.TypedescUtils; import io.ballerina.runtime.internal.values.TypedescValue; import io.ballerina.runtime.internal.values.TypedescValueImpl; +import java.util.Set; + /** * {@code BTypedescType} represents a type of a type in the Ballerina type system. * * @since 0.995.0 */ public class BTypedescType extends BType implements TypedescType { - private Type constraint; + + private final Type constraint; public BTypedescType(String typeName, Module pkg) { super(typeName, pkg, Object.class); + constraint = null; } public BTypedescType(Type constraint) { @@ -84,4 +93,20 @@ public boolean isReadOnly() { public String toString() { return "typedesc" + "<" + constraint.toString() + ">"; } + + @Override + public SemType createSemType() { + if (constraint == null) { + return Builder.getTypeDescType(); + } + SemType constraint = tryInto(getConstraint()); + Context cx = TypeChecker.context(); + return TypedescUtils.typedescContaining(cx.env, constraint); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return constraint instanceof MayBeDependentType constraintType && + constraintType.isDependentlyTyped(visited); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BUnionType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BUnionType.java index 4fe1f164c653..bdd97fe67536 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BUnionType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BUnionType.java @@ -21,10 +21,16 @@ import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.SelectivelyImmutableReferenceType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.UnionType; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.values.ReadOnlyUtils; @@ -44,7 +50,7 @@ * * @since 0.995.0 */ -public class BUnionType extends BType implements UnionType, SelectivelyImmutableReferenceType { +public class BUnionType extends BType implements UnionType, SelectivelyImmutableReferenceType, TypeWithAcceptedType { public boolean isCyclic = false; public static final String PIPE = "|"; @@ -167,6 +173,7 @@ public void setMemberTypes(Type[] members) { } this.memberTypes = readonly ? getReadOnlyTypes(members) : Arrays.asList(members); setFlagsBasedOnMembers(); + resetSemType(); } public void setOriginalMemberTypes(Type[] originalMemberTypes) { @@ -182,6 +189,9 @@ private void setMemberTypes(List members) { } private void setMemberTypes(List members, List originalMembers) { + if (memberTypes != null) { + resetSemType(); + } if (members == null) { return; } @@ -193,7 +203,6 @@ private void setMemberTypes(List members, List originalMembers) { this.memberTypes = readonly ? getReadOnlyTypes(members, new HashSet<>(members.size())) : members; this.resolvingReadonly = false; setFlagsBasedOnMembers(); - setOriginalMemberTypes(originalMembers); } @@ -231,12 +240,14 @@ private boolean checkNillable(List memberTypes) { } private void addMember(Type type) { + resetSemType(); this.memberTypes.add(type); setFlagsBasedOnMembers(); this.originalMemberTypes.add(type); } public void addMembers(Type... types) { + resetSemType(); this.memberTypes.addAll(Arrays.asList(types)); setFlagsBasedOnMembers(); this.originalMemberTypes.addAll(Arrays.asList(types)); @@ -446,7 +457,7 @@ public void mergeUnionType(BUnionType unionType) { this.addMember(newArrayType); continue; } - } else if (member instanceof BMapType mapType) { + } else if (member instanceof MapType mapType) { if (mapType.getConstrainedType() == unionType) { BMapType newMapType = new BMapType(this, this.readonly); this.addMember(newMapType); @@ -457,7 +468,7 @@ public void mergeUnionType(BUnionType unionType) { BTableType newTableType = new BTableType(this, tableType.isReadOnly()); this.addMember(newTableType); continue; - } else if (tableType.getConstrainedType() instanceof BMapType mapType) { + } else if (tableType.getConstrainedType() instanceof MapType mapType) { if (mapType.getConstrainedType() == unionType) { BMapType newMapType = new BMapType(this); BTableType newTableType = new BTableType(newMapType, @@ -540,4 +551,22 @@ public Optional getIntersectionType() { public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + public SemType createSemType() { + return memberTypes.stream().map(SemType::tryInto).reduce(Builder.getNeverType(), Core::union); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return memberTypes.stream() + .filter(each -> each instanceof MayBeDependentType) + .anyMatch(type -> ((MayBeDependentType) type).isDependentlyTyped(visited)); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + return Optional.of(memberTypes.stream().map(each -> ShapeAnalyzer.acceptedTypeOf(cx, each).orElseThrow()) + .reduce(Builder.getNeverType(), Core::union)); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlType.java index 05e1cde6985c..45dd60c0ad10 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlType.java @@ -20,11 +20,21 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.ParameterizedType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.XmlType; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.semtype.XmlUtils; import io.ballerina.runtime.internal.values.ReadOnlyUtils; +import io.ballerina.runtime.internal.values.XmlComment; +import io.ballerina.runtime.internal.values.XmlItem; +import io.ballerina.runtime.internal.values.XmlPi; import io.ballerina.runtime.internal.values.XmlSequence; +import io.ballerina.runtime.internal.values.XmlText; import io.ballerina.runtime.internal.values.XmlValue; import java.util.Optional; @@ -35,10 +45,10 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BXmlType extends BType implements XmlType { +public class BXmlType extends BType implements XmlType, TypeWithShape { private final int tag; - public Type constraint; + public final Type constraint; private final boolean readonly; private IntersectionType immutableType; private IntersectionType intersectionType = null; @@ -63,6 +73,13 @@ public BXmlType(String typeName, Module pkg, int tag, boolean readonly) { this.constraint = null; } + public BXmlType(String typeName, Type constraint, Module pkg, int tag, boolean readonly) { + super(typeName, pkg, XmlValue.class); + this.tag = tag; + this.readonly = readonly; + this.constraint = constraint; + } + public BXmlType(String typeName, Type constraint, Module pkg, boolean readonly) { super(typeName, pkg, XmlValue.class); this.tag = TypeTags.XML_TAG; @@ -138,8 +155,103 @@ public Optional getIntersectionType() { return this.intersectionType == null ? Optional.empty() : Optional.of(this.intersectionType); } + @Override + public SemType createSemType() { + SemType semType; + if (constraint == null) { + semType = pickTopType(); + } else { + SemType contraintSemtype; + if (constraint instanceof ParameterizedType parameterizedType) { + contraintSemtype = tryInto(parameterizedType.getParamValueType()); + } else { + contraintSemtype = tryInto(constraint); + } + semType = XmlUtils.xmlSequence(contraintSemtype); + } + return isReadOnly() ? Core.intersect(Builder.getReadonlyType(), semType) : semType; + } + + private SemType pickTopType() { + return switch (tag) { + case TypeTags.XML_TAG -> Builder.getXmlType(); + case TypeTags.XML_ELEMENT_TAG -> Builder.getXmlElementType(); + case TypeTags.XML_COMMENT_TAG -> Builder.getXmlCommentType(); + case TypeTags.XML_PI_TAG -> Builder.getXmlPIType(); + case TypeTags.XML_TEXT_TAG -> Builder.getXmlTextType(); + default -> throw new IllegalStateException("Unexpected value: " + tag); + }; + } + @Override public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + XmlValue xmlValue = (XmlValue) object; + if (!isReadOnly(xmlValue)) { + return Optional.of(getSemType()); + } + return readonlyShapeOf(object); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + return true; + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + return readonlyShapeOf(object).map(semType -> Core.intersect(semType, Builder.getReadonlyType())); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + return Optional.of(getSemType()); + } + + private Optional readonlyShapeOf(Object object) { + if (object instanceof XmlSequence xmlSequence) { + // We represent xml as an empty sequence + var children = xmlSequence.getChildrenList(); + if (children.isEmpty()) { + return Optional.of(XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_NEVER)); + } else if (children.size() == 1) { + // Not entirely sure if this is correct, but needed for passing tests + return readonlyShapeOf(children.get(0)); + } + return children.stream() + .map(this::readonlyShapeOf) + .filter(Optional::isPresent) + .map(Optional::get) + .reduce(Core::union) + .map(XmlUtils::xmlSequence); + } else if (object instanceof XmlText) { + // Text is inherently readonly + return Optional.of(Builder.getXmlTextType()); + } else if (object instanceof XmlItem xml) { + return getSemType(xml, Builder.getXmlElementType()); + } else if (object instanceof XmlComment xml) { + return getSemType(xml, Builder.getXmlCommentType()); + } else if (object instanceof XmlPi xml) { + return getSemType(xml, Builder.getXmlPIType()); + } + throw new IllegalArgumentException("Unexpected xml value: " + object); + } + + private static Optional getSemType(XmlValue xml, SemType baseType) { + if (isReadOnly(xml)) { + return Optional.of(Core.intersect(baseType, Builder.getReadonlyType())); + } + return Optional.of(baseType); + } + + private static boolean isReadOnly(XmlValue xmlValue) { + if (xmlValue instanceof XmlSequence || xmlValue instanceof XmlText) { + return true; + } + return xmlValue.getType().isReadOnly(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/DistinctIdSupplier.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/DistinctIdSupplier.java new file mode 100644 index 000000000000..771ea177cfa5 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/DistinctIdSupplier.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types; + +import io.ballerina.runtime.api.types.TypeId; +import io.ballerina.runtime.api.types.TypeIdSet; +import io.ballerina.runtime.api.types.semtype.Env; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +/** + * A supplier that provides a list of distinct ids for a given type id set. + * + * @since 2201.11.0 + */ +final class DistinctIdSupplier implements Supplier> { + + private List ids = null; + private static final Map allocatedIds = new ConcurrentHashMap<>(); + private final Env env; + private final TypeIdSet typeIdSet; + + DistinctIdSupplier(Env env, TypeIdSet typeIdSet) { + this.env = env; + this.typeIdSet = typeIdSet; + } + + public synchronized Collection get() { + if (ids != null) { + return ids; + } + if (typeIdSet == null) { + return List.of(); + } + ids = typeIdSet.getIds().stream().map(TypeIdWrapper::new).map(typeId -> allocatedIds.computeIfAbsent(typeId, + ignored -> env.distinctAtomCountGetAndIncrement())) + .toList(); + return ids; + } + + // This is to avoid whether id is primary or not affecting the hashcode. + private record TypeIdWrapper(TypeId typeId) { + + @Override + public boolean equals(Object obj) { + if (obj instanceof TypeIdWrapper other) { + return typeId.getName().equals(other.typeId().getName()) && + typeId.getPkg().equals(other.typeId().getPkg()); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(typeId.getPkg(), typeId.getName()); + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/BddNode.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/MayBeDependentType.java similarity index 58% rename from semtypes/src/main/java/io/ballerina/semtype/subtypedata/BddNode.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/MayBeDependentType.java index 9c657c468581..2a68601ce4bc 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/BddNode.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/MayBeDependentType.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -14,20 +14,21 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * */ -package io.ballerina.semtype.subtypedata; -import io.ballerina.semtype.Atom; -import io.ballerina.semtype.Bdd; +package io.ballerina.runtime.internal.types; + +import java.util.Set; /** - * Bdd node. + * Represents a type that may be dependently typed. * - * @since 2.0.0 + * @since 2201.11.0 */ -public class BddNode implements Bdd { - Atom atom; - BddNode left; - BddNode middle; - BddNode right; +public interface MayBeDependentType { + + boolean isDependentlyTyped(); + + boolean isDependentlyTyped(Set visited); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/ShapeSupplier.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/ShapeSupplier.java new file mode 100644 index 000000000000..999986483930 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/ShapeSupplier.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types; + +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.Optional; + +/** + * Function that can be used to get the shape of a value. + * + * @since 2201.11.0 + */ +@FunctionalInterface +public interface ShapeSupplier { + + Optional get(Context cx, Object object); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithAcceptedType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithAcceptedType.java new file mode 100644 index 000000000000..6dda26b4a8d6 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithAcceptedType.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types; + +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.Optional; + +/** + * Any {@code Type} that contains selectively immutable types must implement this interface. It represents the type + * against which {@code isLikeType} operation is performed. + * + * @since 2201.11.0 + */ +public interface TypeWithAcceptedType { + + Optional acceptedTypeOf(Context cx); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithShape.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithShape.java new file mode 100644 index 000000000000..259062fc6896 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithShape.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types; + +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.Optional; + +/** + * Types that are not basic types and have values whose shape could be different form the actual type (i.e. not handles) + * must implement this interface. Note that multiple values could share the same instance of TypeWithShape. Ideally + * different objects should be able to do their shape calculations in a non-blocking manner, even when they share the + * same instance of {@code TypeWithShape}. + * + * @since 2201.11.0 + */ +public interface TypeWithShape extends TypeWithAcceptedType { + + Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); + + Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); + + boolean couldInherentTypeBeDifferent(); +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/UniformSubtype.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/AllOrNothing.java similarity index 52% rename from semtypes/src/main/java/io/ballerina/semtype/UniformSubtype.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/AllOrNothing.java index bcd02fa91e34..104ed50d84ba 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/UniformSubtype.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/AllOrNothing.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -14,20 +14,20 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * */ -package io.ballerina.semtype; + +package io.ballerina.runtime.internal.types.semtype; /** - * UniformSubtype node. + * Represent cases where a subtype is either all or nothing of the basic type. + * For example if StringSubType has All as it's subtype data that means subtype + * is actually String basic type and nothing means it doesn't have any string + * subtype * - * @since 2.0.0 + * @since 2201.11.0 */ -public class UniformSubtype { - public final int uniformTypeCode; - public final SubtypeData subtypeData; - - public UniformSubtype(int uniformTypeCode, SubtypeData subtypeData) { - this.uniformTypeCode = uniformTypeCode; - this.subtypeData = subtypeData; - } +public enum AllOrNothing implements SubTypeData { + ALL, + NOTHING } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBooleanSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBooleanSubType.java new file mode 100644 index 000000000000..a93adceb286b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBooleanSubType.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +/** + * Runtime representation of Boolean Sub Type. + * + * @since 2201.11.0 + */ +public final class BBooleanSubType extends SubType { + + private final BBooleanSubTypeData data; + private static final BBooleanSubType ALL = new BBooleanSubType(BBooleanSubTypeData.ALL); + private static final BBooleanSubType NOTHING = new BBooleanSubType(BBooleanSubTypeData.NOTHING); + private static final BBooleanSubType TRUE = new BBooleanSubType(BBooleanSubTypeData.TRUE); + private static final BBooleanSubType FALSE = new BBooleanSubType(BBooleanSubTypeData.FALSE); + + private BBooleanSubType(BBooleanSubTypeData data) { + super(data.isAll(), data.isNothing()); + this.data = data; + } + + public static BBooleanSubType from(boolean value) { + return value ? TRUE : FALSE; + } + + @Override + public SubType union(SubType otherSubtype) { + if (!(otherSubtype instanceof BBooleanSubType other)) { + throw new IllegalArgumentException("union of different subtypes"); + } + if (this.isAll() || other.isAll()) { + return ALL; + } + if (this.isNothing()) { + return other; + } + if (other.isNothing()) { + return this; + } + if (this.data.value == other.data.value) { + return this; + } + return ALL; + } + + @Override + public SubType intersect(SubType otherSubtype) { + if (!(otherSubtype instanceof BBooleanSubType other)) { + throw new IllegalArgumentException("intersection of different subtypes"); + } + if (this.isAll()) { + return other; + } + if (other.isAll()) { + return this; + } + if (this.isNothing() || other.isNothing()) { + return NOTHING; + } + if (this.data.value == other.data.value) { + return this; + } + return NOTHING; + } + + @Override + public SubType diff(SubType otherSubtype) { + if (!(otherSubtype instanceof BBooleanSubType other)) { + throw new IllegalArgumentException("diff of different subtypes"); + } + if (this.isAll() && other.isAll()) { + return NOTHING; + } + if (this.isNothing() || other.isAll()) { + return NOTHING; + } + if (other.isNothing()) { + return this; + } + if (this.isAll()) { + return from(!other.data.value); + } + return this.data.value == other.data.value ? NOTHING : this; + } + + @Override + public SubType complement() { + if (isAll()) { + return NOTHING; + } + if (isNothing()) { + return ALL; + } + return from(!data.value); + } + + @Override + public boolean isEmpty(Context cx) { + return data.isNothing(); + } + + @Override + public SubTypeData data() { + return data.toData(); + } + + // This is instance controlled so only 4 possible instances exists. Default equals is therefore correct + @Override + public int hashCode() { + if (this == ALL) { + return 0; + } + if (this == NOTHING) { + return 1; + } + if (this == TRUE) { + return 2; + } + if (this == FALSE) { + return 3; + } + assert false : "unexpected BBooleanSubType instance"; + return -1; + } + + private record BBooleanSubTypeData(boolean isAll, boolean isNothing, boolean value) { + + private static final BBooleanSubTypeData ALL = new BBooleanSubTypeData(true, false, false); + private static final BBooleanSubTypeData NOTHING = new BBooleanSubTypeData(false, true, false); + private static final BBooleanSubTypeData TRUE = new BBooleanSubTypeData(false, false, true); + private static final BBooleanSubTypeData FALSE = new BBooleanSubTypeData(false, false, false); + + SubTypeData toData() { + if (isAll()) { + return AllOrNothing.ALL; + } else if (isNothing()) { + return AllOrNothing.NOTHING; + } + return new BooleanSubTypeData(value()); + } + } + + private record BooleanSubTypeData(boolean value) implements SubTypeData { + + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubType.java new file mode 100644 index 000000000000..2192eec3e530 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubType.java @@ -0,0 +1,52 @@ +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Atom; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; +import io.ballerina.runtime.api.types.semtype.TypeAtom; + +/** + * Represents a subtype of a Cell. + * + * @since 2201.11.0 + */ +public abstract sealed class BCellSubType extends SubType implements DelegatedSubType + permits BCellSubTypeImpl, BCellSubTypeSimple { + + public BCellSubType(boolean all, boolean nothing) { + super(all, nothing); + } + + public static BCellSubType createDelegate(SubType inner) { + Bdd bdd; + if (inner instanceof Bdd b) { + bdd = b; + } else if (inner instanceof BCellSubTypeImpl bCellImpl) { + bdd = bCellImpl.inner(); + } else if (inner instanceof BCellSubTypeSimple simple) { + return simple; + } else { + throw new IllegalArgumentException("Unexpected inner type"); + } + if (!(bdd instanceof BddNode bddNode && bddNode.isSimple())) { + return new BCellSubTypeImpl(bdd); + } + Atom atom = bddNode.atom(); + if (!(atom instanceof TypeAtom typeAtom)) { + return new BCellSubTypeImpl(bdd); + } + CellAtomicType atomicType = (CellAtomicType) typeAtom.atomicType(); + SemType ty = atomicType.ty(); + // We have special logic when it comes to handling undef that needs to be updated to deal with simple cell + // TODO: probably we can also handle immutable cells as well + if (Core.containsBasicType(ty, Builder.getUndefType()) || ty.some() != 0 || + atomicType.mut() != CellAtomicType.CellMutability.CELL_MUT_LIMITED) { + return new BCellSubTypeImpl(bdd); + } + return new BCellSubTypeSimple(ty, bddNode); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubTypeImpl.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubTypeImpl.java new file mode 100644 index 000000000000..0335d6513051 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubTypeImpl.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Conjunction; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Objects; +import java.util.function.Predicate; + +/** + * Runtime representation of CellSubType. + * + * @since 2201.11.0 + */ +final class BCellSubTypeImpl extends BCellSubType implements DelegatedSubType { + + private final Bdd inner; + + BCellSubTypeImpl(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + @Override + public SubType union(SubType other) { + if (other instanceof BCellSubType otherCell) { + return createDelegate(inner.union(otherCell.inner())); + } + throw new IllegalArgumentException("union of different subtypes"); + } + + @Override + public SubType intersect(SubType other) { + if (other instanceof BCellSubType otherCell) { + return createDelegate(inner.intersect(otherCell.inner())); + } + throw new IllegalArgumentException("intersect of different subtypes"); + } + + @Override + public SubType complement() { + return createDelegate(inner.complement()); + } + + @Override + public boolean isEmpty(Context cx) { + return Bdd.bddEvery(cx, inner, BCellSubTypeImpl::cellFormulaIsEmpty); + } + + @Override + public SubType diff(SubType other) { + if (other instanceof BCellSubType otherCell) { + return createDelegate(inner.diff(otherCell.inner())); + } + throw new IllegalArgumentException("diff of different subtypes"); + + } + + @Override + public SubTypeData data() { + throw new IllegalStateException("unimplemented"); + } + + private static boolean cellFormulaIsEmpty(Context cx, Conjunction posList, Conjunction negList) { + CellAtomicType combined; + if (posList == null) { + combined = CellAtomicType.from(Builder.getValType(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + } else { + combined = CellAtomicType.cellAtomType(posList.atom()); + Conjunction p = posList.next(); + while (p != null) { + combined = CellAtomicType.intersectCellAtomicType(combined, CellAtomicType.cellAtomType(p.atom())); + p = p.next(); + } + } + return !cellInhabited(cx, combined, negList); + } + + private static boolean cellInhabited(Context cx, CellAtomicType posCell, Conjunction negList) { + SemType pos = posCell.ty(); + if (Core.isEmpty(cx, pos)) { + return false; + } + return switch (posCell.mut()) { + case CELL_MUT_NONE -> cellMutNoneInhabited(cx, pos, negList); + case CELL_MUT_LIMITED -> cellMutLimitedInhabited(cx, pos, negList); + default -> cellMutUnlimitedInhabited(cx, pos, negList); + }; + } + + private static boolean cellMutUnlimitedInhabited(Context cx, SemType pos, Conjunction negList) { + Conjunction neg = negList; + while (neg != null) { + if (CellAtomicType.cellAtomType(neg.atom()).mut() == CellAtomicType.CellMutability.CELL_MUT_LIMITED && + Core.isSameType(cx, Builder.getValType(), CellAtomicType.cellAtomType(neg.atom()).ty())) { + return false; + } + neg = neg.next(); + } + SemType negListUnionResult = filteredCellListUnion(negList, + conjunction -> CellAtomicType.cellAtomType(conjunction.atom()).mut() == + CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + // We expect `isNever` condition to be `true` when there are no negative atoms with unlimited mutability. + // Otherwise, we do `isEmpty` to conclude on the inhabitance. + return Core.isNever(negListUnionResult) || !Core.isEmpty(cx, Core.diff(pos, negListUnionResult)); + } + + private static boolean cellMutLimitedInhabited(Context cx, SemType pos, Conjunction negList) { + if (negList == null) { + return true; + } + CellAtomicType negAtomicCell = CellAtomicType.cellAtomType(negList.atom()); + if ((negAtomicCell.mut().compareTo(CellAtomicType.CellMutability.CELL_MUT_LIMITED) >= 0) && + Core.isEmpty(cx, Core.diff(pos, negAtomicCell.ty()))) { + return false; + } + return cellMutLimitedInhabited(cx, pos, negList.next()); + } + + private static boolean cellMutNoneInhabited(Context cx, SemType pos, Conjunction negList) { + SemType negListUnionResult = cellListUnion(negList); + // We expect `isNever` condition to be `true` when there are no negative atoms. + // Otherwise, we do `isEmpty` to conclude on the inhabitance. + return Core.isNever(negListUnionResult) || !Core.isEmpty(cx, Core.diff(pos, negListUnionResult)); + } + + private static SemType cellListUnion(Conjunction negList) { + return filteredCellListUnion(negList, neg -> true); + } + + private static SemType filteredCellListUnion(Conjunction negList, Predicate predicate) { + SemType negUnion = Builder.getNeverType(); + Conjunction neg = negList; + while (neg != null) { + if (predicate.test(neg)) { + negUnion = Core.union(negUnion, CellAtomicType.cellAtomType(neg.atom()).ty()); + } + neg = neg.next(); + } + return negUnion; + } + + @Override + public Bdd inner() { + return inner; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BCellSubTypeImpl other)) { + return false; + } + return Objects.equals(inner, other.inner); + } + + @Override + public int hashCode() { + return Objects.hashCode(inner); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubTypeSimple.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubTypeSimple.java new file mode 100644 index 000000000000..8166bf862624 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubTypeSimple.java @@ -0,0 +1,137 @@ +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.BddAllOrNothing; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; +import io.ballerina.runtime.api.types.semtype.TypeAtom; +import io.ballerina.runtime.internal.TypeChecker; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; + +/** + * Simplified representation of cell if the type is only basic type union and mutability is limited. + * + * @since 2201.11.0 + */ +final class BCellSubTypeSimple extends BCellSubType implements DelegatedSubType { + + private final List pos; + private final List neg; + private BddNode inner; + + BCellSubTypeSimple(SemType type) { + super(type.all() == BasicTypeCode.VT_MASK, type.all() == 0); + assert type.some() == 0; + this.pos = List.of(type); + this.neg = List.of(); + } + + BCellSubTypeSimple(SemType type, BddNode bddNode) { + this(type); + inner = bddNode; + } + + private BCellSubTypeSimple(List pos, List neg) { + super(false, false); + this.pos = pos; + this.neg = neg; + } + + @Override + public SubType union(SubType other) { + if (other instanceof BCellSubTypeSimple simple) { + // P1\N1 U P2\N2 = (P1 U P2)\(N1 U N2) + List combinedPos = Stream.concat(pos.stream(), simple.pos.stream()).toList(); + List combinedNeg = Stream.concat(neg.stream(), simple.neg.stream()).toList(); + return new BCellSubTypeSimple(combinedPos, combinedNeg); + } else if (other instanceof BCellSubTypeImpl complex) { + return createDelegate(inner().union(complex.inner())); + } + throw new IllegalArgumentException("union of different subtypes"); + } + + @Override + public SubType intersect(SubType other) { + if (other instanceof BCellSubTypeSimple simple) { + // P1\N1 ∩ P2\N2 = (P1 ∩ P2)\(N1 U N2) + SemType pos = + Stream.concat(this.pos.stream(), simple.pos.stream()).reduce(Builder.getValType(), Core::intersect); + List neg = Stream.concat(this.neg.stream(), simple.neg.stream()).toList(); + return new BCellSubTypeSimple(List.of(pos), neg); + } else if (other instanceof BCellSubTypeImpl complex) { + return createDelegate(inner().intersect(complex.inner())); + } + throw new IllegalArgumentException("intersection of different subtypes"); + } + + @Override + public SubType complement() { + return new BCellSubTypeSimple(neg, pos); + } + + @Override + public boolean isEmpty(Context cx) { + if (pos.isEmpty()) { + return true; + } + SemType posUnion = pos.stream().reduce(Builder.getNeverType(), Core::union); + if (neg.isEmpty()) { + return Core.isEmpty(cx, posUnion); + } + return neg.stream().anyMatch(neg -> Core.isEmpty(cx, Core.diff(posUnion, neg))); + } + + @Override + public SubTypeData data() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public SubType inner() { + if (inner != null) { + return inner; + } + Env env = TypeChecker.getEnv(); + Optional posBdd = + pos.stream().map(semType -> fromSemType(env, semType)).reduce((acum, bdd) -> (Bdd) acum.union(bdd)); + if (posBdd.isEmpty()) { + return BddAllOrNothing.NOTHING; + } + Optional negBdd = + neg.stream().map(semType -> fromSemType(env, semType)).reduce((acum, bdd) -> (Bdd) acum.union(bdd)); + if (negBdd.isEmpty()) { + return posBdd.get(); + } + return posBdd.get().diff(negBdd.get()); + } + + private static Bdd fromSemType(Env env, SemType type) { + CellAtomicType atomicCell = CellAtomicType.from(type, CellAtomicType.CellMutability.CELL_MUT_LIMITED); + TypeAtom atom = env.cellAtom(atomicCell); + return bddAtom(atom); + } + + @Override + public int hashCode() { + return Stream.concat(pos.stream(), neg.stream()).map(SemType::hashCode).reduce(0, Integer::sum); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof BCellSubTypeSimple other)) { + return false; + } + return pos.equals(other.pos) && neg.equals(other.neg); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BDecimalSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BDecimalSubType.java new file mode 100644 index 000000000000..03d2902e17db --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BDecimalSubType.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Runtime representation of DecimalSubType. + * + * @since 2201.11.0 + */ +public final class BDecimalSubType extends SubType { + + final SubTypeData data; + + private BDecimalSubType(SubTypeData data) { + super(data == AllOrNothing.ALL, data == AllOrNothing.NOTHING); + this.data = data; + } + + private static final BDecimalSubType ALL = new BDecimalSubType(AllOrNothing.ALL); + private static final BDecimalSubType NOTHING = new BDecimalSubType(AllOrNothing.NOTHING); + + public static BDecimalSubType createDecimalSubType(boolean allowed, BigDecimal[] values) { + if (values.length == 0) { + if (!allowed) { + return ALL; + } else { + return NOTHING; + } + } + Arrays.sort(values); + return new BDecimalSubType(new DecimalSubTypeData(allowed, values)); + } + + @Override + public SubType union(SubType otherSubtype) { + BDecimalSubType other = (BDecimalSubType) otherSubtype; + if (data instanceof AllOrNothing) { + if (data == AllOrNothing.ALL) { + return this; + } else { + return other; + } + } else if (other.data instanceof AllOrNothing) { + if (other.data == AllOrNothing.ALL) { + return other; + } else { + return this; + } + } + List values = new ArrayList<>(); + DecimalSubTypeData data = (DecimalSubTypeData) this.data; + DecimalSubTypeData otherData = (DecimalSubTypeData) other.data; + boolean allowed = data.union(otherData, values); + return createDecimalSubType(allowed, values.toArray(BigDecimal[]::new)); + } + + @Override + public SubType intersect(SubType otherSubtype) { + BDecimalSubType other = (BDecimalSubType) otherSubtype; + if (data instanceof AllOrNothing) { + if (data == AllOrNothing.ALL) { + return other; + } else { + return NOTHING; + } + } else if (other.data instanceof AllOrNothing) { + if (other.data == AllOrNothing.ALL) { + return this; + } else { + return NOTHING; + } + } + List values = new ArrayList<>(); + DecimalSubTypeData data = (DecimalSubTypeData) this.data; + DecimalSubTypeData otherData = (DecimalSubTypeData) other.data; + boolean allowed = data.intersect(otherData, values); + return createDecimalSubType(allowed, values.toArray(BigDecimal[]::new)); + } + + @Override + public SubType complement() { + if (data == AllOrNothing.ALL) { + return NOTHING; + } else if (data == AllOrNothing.NOTHING) { + return ALL; + } + DecimalSubTypeData data = (DecimalSubTypeData) this.data; + return createDecimalSubType(!data.allowed, data.values); + } + + @Override + public boolean isEmpty(Context cx) { + return data == AllOrNothing.NOTHING; + } + + @Override + public SubTypeData data() { + return data; + } + + public BigDecimal defaultValue() { + if (data instanceof DecimalSubTypeData subTypeData && subTypeData.allowed && subTypeData.values.length == 1) { + return subTypeData.values[0]; + } + return null; + } + + @Override + public String toString() { + if (data instanceof DecimalSubTypeData subTypeData && subTypeData.allowed) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < subTypeData.values.length; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(subTypeData.values[i]); + } + return sb.toString(); + } + return "decimal"; + } + + private static final class DecimalSubTypeData extends EnumerableSubtypeData implements SubTypeData { + + private final boolean allowed; + private final BigDecimal[] values; + + private DecimalSubTypeData(boolean allowed, BigDecimal[] values) { + this.allowed = allowed; + this.values = values; + } + + @Override + public boolean allowed() { + return allowed; + } + + @Override + public BigDecimal[] values() { + return values; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BErrorSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BErrorSubType.java new file mode 100644 index 000000000000..5045122f9a2a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BErrorSubType.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEveryPositive; + +/** + * Runtime representation of a subtype of error type. + * + * @since 2201.11.0 + */ +public class BErrorSubType extends SubType implements DelegatedSubType { + + public final Bdd inner; + + private BErrorSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BErrorSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BErrorSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BErrorSubType bError) { + return new BErrorSubType(bError.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BErrorSubType otherError)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherError.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BErrorSubType otherError)) { + throw new IllegalArgumentException("intersect of different subtypes"); + } + return createDelegate(inner.intersect(otherError.inner)); + } + + @Override + public SubType complement() { + return createDelegate(Builder.getBddSubtypeRo().diff(inner)); + } + + @Override + public boolean isEmpty(Context cx) { + Bdd b = inner; + // The goal of this is to ensure that mappingFormulaIsEmpty call in errorBddIsEmpty beneath + // does not get an empty posList, because it will interpret that + // as `map` rather than `readonly & map`. + b = b.posMaybeEmpty() ? (Bdd) b.intersect(Builder.getBddSubtypeRo()) : b; + return cx.memoSubtypeIsEmpty(cx.mappingMemo, BErrorSubType::errorBddIsEmpty, b); + } + + private static boolean errorBddIsEmpty(Context cx, Bdd b) { + return bddEveryPositive(cx, b, null, null, BMappingSubType::mappingFormulaIsEmpty); + } + + @Override + public SubTypeData data() { + return inner(); + } + + @Override + public Bdd inner() { + return inner; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BErrorSubType that)) { + return false; + } + return Objects.equals(inner, that.inner); + } + + @Override + public int hashCode() { + return Objects.hashCode(inner); + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFloatSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFloatSubType.java new file mode 100644 index 000000000000..333e986ae9be --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFloatSubType.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Runtime representation of FloatSubType. + * + * @since 2201.11.0 + */ +public final class BFloatSubType extends SubType { + + final SubTypeData data; + + private BFloatSubType(SubTypeData data) { + super(data == AllOrNothing.ALL, data == AllOrNothing.NOTHING); + this.data = data; + } + + private static final BFloatSubType ALL = new BFloatSubType(AllOrNothing.ALL); + private static final BFloatSubType NOTHING = new BFloatSubType(AllOrNothing.NOTHING); + + public static BFloatSubType createFloatSubType(boolean allowed, Double[] values) { + if (values.length == 0) { + if (!allowed) { + return ALL; + } else { + return NOTHING; + } + } + return new BFloatSubType(new FloatSubTypeData(allowed, values)); + } + + @Override + public SubType union(SubType otherSubtype) { + BFloatSubType other = (BFloatSubType) otherSubtype; + if (data instanceof AllOrNothing) { + if (data == AllOrNothing.ALL) { + return this; + } else { + return other; + } + } else if (other.data instanceof AllOrNothing) { + if (other.data == AllOrNothing.ALL) { + return other; + } else { + return this; + } + } + List values = new ArrayList<>(); + FloatSubTypeData data = (FloatSubTypeData) this.data; + FloatSubTypeData otherData = (FloatSubTypeData) other.data; + boolean allowed = data.union(otherData, values); + return createFloatSubType(allowed, values.toArray(Double[]::new)); + } + + @Override + public SubType intersect(SubType otherSubtype) { + BFloatSubType other = (BFloatSubType) otherSubtype; + if (data instanceof AllOrNothing) { + if (data == AllOrNothing.ALL) { + return other; + } else { + return NOTHING; + } + } else if (other.data instanceof AllOrNothing) { + if (other.data == AllOrNothing.ALL) { + return this; + } else { + return NOTHING; + } + } + List values = new ArrayList<>(); + FloatSubTypeData data = (FloatSubTypeData) this.data; + FloatSubTypeData otherData = (FloatSubTypeData) other.data; + boolean allowed = data.intersect(otherData, values); + return createFloatSubType(allowed, values.toArray(Double[]::new)); + } + + @Override + public SubType complement() { + if (data == AllOrNothing.ALL) { + return NOTHING; + } else if (data == AllOrNothing.NOTHING) { + return ALL; + } + FloatSubTypeData data = (FloatSubTypeData) this.data; + return createFloatSubType(!data.allowed, data.values); + } + + @Override + public boolean isEmpty(Context cx) { + return data == AllOrNothing.NOTHING; + } + + @Override + public SubTypeData data() { + return data; + } + + static final class FloatSubTypeData extends EnumerableSubtypeData implements SubTypeData { + + private final boolean allowed; + private final Double[] values; + + private FloatSubTypeData(boolean allowed, Double[] values) { + this.allowed = allowed; + this.values = filteredValues(values); + } + + private static Double[] filteredValues(Double[] values) { + for (int i = 0; i < values.length; i++) { + values[i] = canon(values[i]); + } + if (values.length < 2) { + return values; + } + Arrays.sort(values); + Double[] buffer = new Double[values.length]; + buffer[0] = values[0]; + int bufferLen = 1; + for (int i = 1; i < values.length; i++) { + Double value = values[i]; + Double prevValue = values[i - 1]; + if (isSame(value, prevValue)) { + continue; + } + buffer[bufferLen++] = value; + } + return Arrays.copyOf(buffer, bufferLen); + } + + private static Double canon(Double d) { + if (d.equals(0.0) || d.equals(-0.0)) { + return 0.0; + } + return d; + } + + private static boolean isSame(double f1, double f2) { + if (Double.isNaN(f1)) { + return Double.isNaN(f2); + } + return f1 == f2; + } + + @Override + public boolean allowed() { + return allowed; + } + + @Override + public Double[] values() { + return values; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFunctionSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFunctionSubType.java new file mode 100644 index 000000000000..cad6312b7744 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFunctionSubType.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Conjunction; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; + +/** + * Runtime representation of a subtype of function type. + * + * @since 2201.11.0 + */ +public class BFunctionSubType extends SubType implements DelegatedSubType { + + public final Bdd inner; + + private BFunctionSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BFunctionSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BFunctionSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BFunctionSubType bFunction) { + return new BFunctionSubType(bFunction.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BFunctionSubType otherFn)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherFn.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BFunctionSubType otherList)) { + throw new IllegalArgumentException("intersect of different subtypes"); + } + return createDelegate(inner.intersect(otherList.inner)); + } + + @Override + public SubType complement() { + return createDelegate(inner.complement()); + } + + @Override + public boolean isEmpty(Context cx) { + return cx.memoSubtypeIsEmpty(cx.functionMemo, + (context, bdd) -> bddEvery(context, bdd, BFunctionSubType::functionFormulaIsEmpty), inner); + } + + private static boolean functionFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { + return functionPathIsEmpty(cx, functionUnionParams(cx, pos), functionUnionQualifiers(cx, pos), pos, neg); + } + + private static boolean functionPathIsEmpty(Context cx, SemType params, SemType qualifier, Conjunction pos, + Conjunction neg) { + if (neg == null) { + return false; + } + FunctionAtomicType t = cx.functionAtomicType(neg.atom()); + SemType t0 = t.paramType(); + SemType t1 = t.retType(); + SemType t2 = t.qualifiers(); + return (Core.isSubType(cx, qualifier, t2) && Core.isSubType(cx, t0, params) && + functionPhi(cx, t0, Core.complement(t1), pos)) + || functionPathIsEmpty(cx, params, qualifier, pos, neg.next()); + } + + private static boolean functionPhi(Context cx, SemType t0, SemType t1, Conjunction pos) { + if (pos == null) { + // t0 is NEVER only for function top types with qualifiers + return !Core.isNever(t0) && (Core.isEmpty(cx, t0) || Core.isEmpty(cx, t1)); + } + return functionPhiInner(cx, t0, t1, pos); + } + + private static boolean functionPhiInner(Context cx, SemType t0, SemType t1, Conjunction pos) { + if (pos == null) { + return Core.isEmpty(cx, t0) || Core.isEmpty(cx, t1); + } else { + FunctionAtomicType s = cx.functionAtomicType(pos.atom()); + SemType s0 = s.paramType(); + SemType s1 = s.retType(); + return (Core.isSubType(cx, t0, s0) + || Core.isSubType(cx, functionIntersectRet(cx, pos.next()), Core.complement(t1))) + && functionPhiInner(cx, t0, Core.intersect(t1, s1), pos.next()) + && functionPhiInner(cx, Core.diff(t0, s0), t1, pos.next()); + } + } + + private static SemType functionIntersectRet(Context cx, Conjunction pos) { + if (pos == null) { + return Builder.getValType(); + } + return Core.intersect(cx.functionAtomicType(pos.atom()).retType(), functionIntersectRet(cx, pos.next())); + } + + private static SemType functionUnionParams(Context cx, Conjunction pos) { + if (pos == null) { + return Builder.getNeverType(); + } + return Core.union(cx.functionAtomicType(pos.atom()).paramType(), functionUnionParams(cx, pos.next())); + } + + private static SemType functionUnionQualifiers(Context cx, Conjunction pos) { + if (pos == null) { + return Builder.getNeverType(); + } + return Core.union(cx.functionAtomicType(pos.atom()).qualifiers(), functionUnionQualifiers(cx, pos.next())); + } + + @Override + public SubTypeData data() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public Bdd inner() { + return inner; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BMappingSubType that)) { + return false; + } + return Objects.equals(inner, that.inner); + } + + @Override + public int hashCode() { + return Objects.hashCode(inner); + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFutureSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFutureSubType.java new file mode 100644 index 000000000000..c9389cff9671 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFutureSubType.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; + +/** + * Runtime representation of a subtype of future type. + * + * @since 2201.11.0 + */ +public final class BFutureSubType extends SubType implements DelegatedSubType { + + private final Bdd inner; + + private BFutureSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BFutureSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BFutureSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BFutureSubType other) { + return new BFutureSubType(other.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BFutureSubType otherFuture)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherFuture.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BFutureSubType otherFuture)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.intersect(otherFuture.inner)); + } + + @Override + public SubType complement() { + return createDelegate(inner.complement()); + } + + @Override + public boolean isEmpty(Context cx) { + return cx.memoSubtypeIsEmpty(cx.mappingMemo, + (context, bdd) -> bddEvery(context, bdd, BMappingSubType::mappingFormulaIsEmpty), inner); + } + + @Override + public SubTypeData data() { + throw new UnsupportedOperationException("Method not implemented"); + } + + @Override + public SubType inner() { + throw new UnsupportedOperationException("Method not implemented"); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof BFutureSubType other)) { + return false; + } + return Objects.equals(inner, other.inner); + } + + @Override + public int hashCode() { + return Objects.hash(inner); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BIntSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BIntSubType.java new file mode 100644 index 000000000000..30e123d338fe --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BIntSubType.java @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static io.ballerina.runtime.api.constants.RuntimeConstants.INT_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.INT_MIN_VALUE; + +/** + * Runtime representation of a int subtype. + * + * @since 2201.11.0 + */ +public final class BIntSubType extends SubType { + + final SubTypeData data; + + private BIntSubType(SubTypeData data) { + super(data == AllOrNothing.ALL, data == AllOrNothing.NOTHING); + this.data = data; + } + + private static final BIntSubType ALL = new BIntSubType(AllOrNothing.ALL); + private static final BIntSubType NOTHING = new BIntSubType(AllOrNothing.NOTHING); + + public static BIntSubType createIntSubType(List values) { + Collections.sort(values); + List ranges = new ArrayList<>(); + long start = values.get(0); + long end = start; + for (int i = 1; i < values.size(); i++) { + long value = values.get(i); + if (value == end + 1) { + end = value; + } else { + ranges.add(new Range(start, end)); + start = value; + end = value; + } + } + ranges.add(new Range(start, end)); + return new BIntSubType(new IntSubTypeData(ranges.toArray(Range[]::new))); + } + + public static BIntSubType createIntSubType(long min, long max) { + assert min < max : "Invalid range"; + Range range = new Range(min, max); + Range[] ranges = {range}; + return new BIntSubType(new IntSubTypeData(ranges)); + } + + @Override + public SubType union(SubType otherSubType) { + BIntSubType other = (BIntSubType) otherSubType; + if (data instanceof AllOrNothing) { + if (data == AllOrNothing.ALL) { + return this; + } else { + return other; + } + } else if (other.data instanceof AllOrNothing) { + if (other.data == AllOrNothing.ALL) { + return other; + } else { + return this; + } + } + IntSubTypeData thisData = (IntSubTypeData) data; + IntSubTypeData otherData = (IntSubTypeData) other.data; + IntSubTypeData v = thisData.union(otherData); + Range[] resultRanges = v.ranges; + if (resultRanges.length == 1 && resultRanges[0].min == INT_MAX_VALUE && resultRanges[0].max == INT_MAX_VALUE) { + return ALL; + } + return new BIntSubType(v); + } + + @Override + public SubType intersect(SubType otherSubType) { + BIntSubType other = (BIntSubType) otherSubType; + if (data instanceof AllOrNothing) { + if (data == AllOrNothing.ALL) { + return other; + } else { + return NOTHING; + } + } else if (other.data instanceof AllOrNothing) { + if (other.data == AllOrNothing.ALL) { + return this; + } else { + return NOTHING; + } + } + IntSubTypeData thisData = (IntSubTypeData) data; + IntSubTypeData otherData = (IntSubTypeData) other.data; + IntSubTypeData v = thisData.intersect(otherData); + Range[] resultRanges = v.ranges; + if (resultRanges.length == 0) { + return NOTHING; + } + return new BIntSubType(v); + } + + @Override + public SubType complement() { + if (this.data == AllOrNothing.ALL) { + return NOTHING; + } else if (this.data == AllOrNothing.NOTHING) { + return ALL; + } + IntSubTypeData intData = (IntSubTypeData) data; + return new BIntSubType(intData.complement()); + } + + @Override + public boolean isEmpty(Context cx) { + return data == AllOrNothing.NOTHING; + } + + @Override + public SubTypeData data() { + return data; + } + + public record Range(long min, long max) { + + } + + public static boolean intSubtypeContains(SubTypeData d, long n) { + if (!(d instanceof IntSubTypeData intSubTypeData)) { + return d == AllOrNothing.ALL; + } + return intSubTypeData.contains(n); + } + + public static final class IntSubTypeData implements SubTypeData { + + final Range[] ranges; + + private IntSubTypeData(Range range) { + this.ranges = new Range[]{range}; + } + + private IntSubTypeData(Range[] ranges) { + this.ranges = ranges; + } + + public List values() { + List values = new ArrayList<>(); + for (Range range : ranges) { + for (long i = range.min; i <= range.max; i++) { + values.add(i); + } + } + return values; + } + + public long max() { + return ranges[ranges.length - 1].max; + } + + boolean isRangeOverlap(Range range) { + IntSubTypeData subTypeData = intersect(new IntSubTypeData(range)); + return subTypeData.ranges.length != 0; + } + + private boolean contains(long n) { + for (Range r : ranges) { + if (r.min <= n && n <= r.max) { + return true; + } + } + return false; + } + + private IntSubTypeData union(IntSubTypeData other) { + List result = new ArrayList<>(); + int i1 = 0; + int i2 = 0; + Range[] v1 = this.ranges; + Range[] v2 = other.ranges; + int len1 = ranges.length; + int len2 = other.ranges.length; + while (true) { + if (i1 >= len1) { + if (i2 >= len2) { + break; + } + rangeUnionPush(result, v2[i2]); + i2++; + } else if (i2 >= len2) { + rangeUnionPush(result, v1[i1]); + i1++; + } else { + Range r1 = v1[i1]; + Range r2 = v2[i2]; + RangeOpResult combined = rangeUnion(r1, r2); + switch (combined.tag) { + case OVERLAP -> { + rangeUnionPush(result, combined.range); + i1++; + i2++; + } + case BEFORE -> { + rangeUnionPush(result, r1); + i1++; + } + case AFTER -> { + rangeUnionPush(result, r2); + i2++; + } + } + } + } + return new IntSubTypeData(result.toArray(Range[]::new)); + } + + IntSubTypeData intersect(IntSubTypeData other) { + List result = new ArrayList<>(); + int i1 = 0; + int i2 = 0; + Range[] v1 = this.ranges; + Range[] v2 = other.ranges; + int len1 = ranges.length; + int len2 = other.ranges.length; + while (true) { + if (i1 >= len1 || i2 >= len2) { + break; + } + Range r1 = v1[i1]; + Range r2 = v2[i2]; + RangeOpResult combined = rangeIntersect(r1, r2); + switch (combined.tag) { + case OVERLAP -> { + rangeUnionPush(result, combined.range); + i1++; + i2++; + } + case BEFORE -> i1++; + case AFTER -> i2++; + } + } + return new IntSubTypeData(result.toArray(Range[]::new)); + } + + IntSubTypeData complement() { + List result = new ArrayList<>(); + Range[] v = this.ranges; + int len = v.length; + long min = v[0].min; + if (min > INT_MIN_VALUE) { + result.add(new Range(INT_MIN_VALUE, min - 1)); + } + for (int i = 1; i < len; i++) { + result.add(new Range(v[i - 1].max + 1, v[i].min - 1)); + } + long max = v[v.length - 1].max; + if (max < INT_MAX_VALUE) { + result.add(new Range(max + 1, INT_MAX_VALUE)); + } + return new IntSubTypeData(result.toArray(Range[]::new)); + } + + private static void rangeUnionPush(List ranges, Range next) { + int lastIndex = ranges.size() - 1; + if (lastIndex < 0) { + ranges.add(next); + return; + } + RangeOpResult result = rangeUnion(ranges.get(lastIndex), next); + if (result.tag == RangeOpResultTag.OVERLAP) { + ranges.set(lastIndex, result.range); + } else { + ranges.add(next); + } + } + + private static RangeOpResult rangeIntersect(Range r1, Range r2) { + if (r1.max < r2.min) { + return new RangeOpResult(RangeOpResultTag.BEFORE, null); + } + if (r2.max < r1.min) { + return new RangeOpResult(RangeOpResultTag.AFTER, null); + } + return new RangeOpResult(RangeOpResultTag.OVERLAP, + new Range(Math.max(r1.min, r2.min), Math.min(r1.max, r2.max))); + } + + enum RangeOpResultTag { + BEFORE, + OVERLAP, + AFTER, + } + + record RangeOpResult(RangeOpResultTag tag, Range range) { + + } + + private static RangeOpResult rangeUnion(Range r1, Range r2) { + if (r1.max < r2.min) { + if (r1.max + 1 != r2.min) { + return new RangeOpResult(RangeOpResultTag.BEFORE, null); + } + } + if (r2.max < r1.min) { + if (r1.max + 1 != r2.min) { + return new RangeOpResult(RangeOpResultTag.AFTER, null); + } + } + return new RangeOpResult(RangeOpResultTag.OVERLAP, + new Range(Math.min(r1.min, r2.min), Math.max(r1.max, r2.max))); + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BListProj.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BListProj.java new file mode 100644 index 000000000000..adcd59d67c89 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BListProj.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Atom; +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.BddAllOrNothing; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Conjunction; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.Pair; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Builder.getRoCellContaining; +import static io.ballerina.runtime.api.types.semtype.Conjunction.and; +import static io.ballerina.runtime.api.types.semtype.Core.cellInnerVal; +import static io.ballerina.runtime.api.types.semtype.Core.diff; +import static io.ballerina.runtime.api.types.semtype.Core.getComplexSubtypeData; +import static io.ballerina.runtime.api.types.semtype.Core.isEmpty; +import static io.ballerina.runtime.api.types.semtype.Core.isNever; +import static io.ballerina.runtime.api.types.semtype.Core.isNothingSubtype; +import static io.ballerina.runtime.api.types.semtype.Core.union; +import static io.ballerina.runtime.internal.types.semtype.BIntSubType.intSubtypeContains; +import static io.ballerina.runtime.internal.types.semtype.BListSubType.fixedArrayAnyEmpty; +import static io.ballerina.runtime.internal.types.semtype.BListSubType.listIntersectWith; +import static io.ballerina.runtime.internal.types.semtype.BListSubType.listMemberAtInnerVal; +import static io.ballerina.runtime.internal.types.semtype.BListSubType.listSampleTypes; +import static io.ballerina.runtime.internal.types.semtype.BListSubType.listSamples; + +/** + * utility class for list type projection. + * + * @since 2201.11.0 + */ +public final class BListProj { + + private BListProj() { + } + + public static SemType listProjInnerVal(Context cx, SemType t, SemType k) { + if (t.some() == 0) { + return t == Builder.getListType() ? Builder.getValType() : Builder.getNeverType(); + } else { + SubTypeData keyData = Core.intSubtype(k); + if (isNothingSubtype(keyData)) { + return Builder.getNeverType(); + } + return listProjBddInnerVal(cx, keyData, (Bdd) getComplexSubtypeData(t, BasicTypeCode.BT_LIST), null, + null); + } + } + + private static SemType listProjBddInnerVal(Context cx, SubTypeData k, Bdd b, Conjunction pos, Conjunction neg) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? listProjPathInnerVal(cx, k, pos, neg) : Builder.getNeverType(); + } else { + BddNode bddNode = (BddNode) b; + return union(listProjBddInnerVal(cx, k, bddNode.left(), and(bddNode.atom(), pos), neg), + union(listProjBddInnerVal(cx, k, bddNode.middle(), pos, neg), + listProjBddInnerVal(cx, k, bddNode.right(), pos, and(bddNode.atom(), neg)))); + } + } + + private static SemType listProjPathInnerVal(Context cx, SubTypeData k, Conjunction pos, Conjunction neg) { + FixedLengthArray members; + SemType rest; + if (pos == null) { + members = FixedLengthArray.empty(); + rest = Builder.getRwCellContaining(cx.env, union(Builder.getValType(), Builder.getUndefType())); + } else { + // combine all the positive tuples using intersection + ListAtomicType lt = cx.listAtomType(pos.atom()); + members = lt.members(); + rest = lt.rest(); + Conjunction p = pos.next(); + // the neg case is in case we grow the array in listInhabited + if (p != null || neg != null) { + members = members.shallowCopy(); + } + + while (true) { + if (p == null) { + break; + } else { + Atom d = p.atom(); + p = p.next(); + lt = cx.listAtomType(d); + Pair + intersected = listIntersectWith(cx.env, members, rest, lt.members(), lt.rest()); + if (intersected == null) { + return Builder.getNeverType(); + } + members = intersected.first(); + rest = intersected.second(); + } + } + if (fixedArrayAnyEmpty(cx, members)) { + return Builder.getNeverType(); + } + // Ensure that we can use isNever on rest in listInhabited + if (!isNever(cellInnerVal(rest)) && isEmpty(cx, rest)) { + rest = getRoCellContaining(cx.env, Builder.getNeverType()); + } + } + Integer[] indices = listSamples(cx, members, rest, neg); + Pair projSamples = listProjSamples(indices, k); + indices = projSamples.first(); + Pair sampleTypes = listSampleTypes(cx, members, rest, indices); + return listProjExcludeInnerVal(cx, projSamples.first(), + projSamples.second(), + sampleTypes.first(), + sampleTypes.second(), neg); + } + + private static SemType listProjExcludeInnerVal(Context cx, Integer[] indices, Integer[] keyIndices, + SemType[] memberTypes, int nRequired, Conjunction neg) { + SemType p = Builder.getNeverType(); + if (neg == null) { + int len = memberTypes.length; + for (int k : keyIndices) { + if (k < len) { + p = union(p, cellInnerVal(memberTypes[k])); + } + } + } else { + final ListAtomicType nt = cx.listAtomType(neg.atom()); + if (nRequired > 0 && isNever(listMemberAtInnerVal(nt.members(), nt.rest(), indices[nRequired - 1]))) { + return listProjExcludeInnerVal(cx, indices, keyIndices, memberTypes, nRequired, neg.next()); + } + int negLen = nt.members().fixedLength(); + if (negLen > 0) { + int len = memberTypes.length; + if (len < indices.length && indices[len] < negLen) { + return listProjExcludeInnerVal(cx, indices, keyIndices, memberTypes, nRequired, neg.next()); + } + for (int i = nRequired; i < memberTypes.length; i++) { + if (indices[i] >= negLen) { + break; + } + SemType[] t = Arrays.copyOfRange(memberTypes, 0, i); + p = union(p, listProjExcludeInnerVal(cx, indices, keyIndices, t, nRequired, neg.next())); + } + } + for (int i = 0; i < memberTypes.length; i++) { + SemType d = + diff(cellInnerVal(memberTypes[i]), listMemberAtInnerVal(nt.members(), nt.rest(), indices[i])); + if (!Core.isEmpty(cx, d)) { + SemType[] t = memberTypes.clone(); + t[i] = Builder.getRwCellContaining(cx.env, d); + // We need to make index i be required + p = union(p, listProjExcludeInnerVal(cx, indices, keyIndices, t, Integer.max(nRequired, i + 1), + neg.next())); + } + } + } + return p; + } + + private static Pair listProjSamples(Integer[] indices, SubTypeData k) { + List> v = new ArrayList<>(); + for (int i : indices) { + v.add(Pair.from(i, intSubtypeContains(k, i))); + } + if (k instanceof BIntSubType.IntSubTypeData intSubtype) { + for (BIntSubType.Range range : intSubtype.ranges) { + long max = range.max(); + if (range.max() >= 0) { + v.add(Pair.from((int) max, true)); + int min = Integer.max(0, (int) range.min()); + if (min < max) { + v.add(Pair.from(min, true)); + } + } + } + } + v.sort(Comparator.comparingInt(Pair::first)); + List indices1 = new ArrayList<>(); + List keyIndices = new ArrayList<>(); + for (var ib : v) { + if (indices1.isEmpty() || !Objects.equals(ib.first(), indices1.get(indices1.size() - 1))) { + if (ib.second()) { + keyIndices.add(indices1.size()); + } + indices1.add(ib.first()); + } + } + return Pair.from(indices1.toArray(Integer[]::new), keyIndices.toArray(Integer[]::new)); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BListSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BListSubType.java new file mode 100644 index 000000000000..bb3bbec3769a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BListSubType.java @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Atom; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.BddAllOrNothing; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Conjunction; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.Pair; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; +import static io.ballerina.runtime.api.types.semtype.Core.cellInner; +import static io.ballerina.runtime.api.types.semtype.Core.cellInnerVal; +import static io.ballerina.runtime.api.types.semtype.Core.getCellContainingInnerVal; +import static io.ballerina.runtime.api.types.semtype.Core.intersectCellMemberSemTypes; +import static io.ballerina.runtime.internal.types.semtype.BIntSubType.intSubtypeContains; + +/** + * Runtime representation of a subtype of list type. + * + * @since 2201.11.0 + */ +public class BListSubType extends SubType implements DelegatedSubType { + + public final Bdd inner; + + private BListSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BListSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BListSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BListSubType bList) { + return new BListSubType(bList.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BListSubType otherList)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherList.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BListSubType otherList)) { + throw new IllegalArgumentException("intersect of different subtypes"); + } + return createDelegate(inner.intersect(otherList.inner)); + } + + @Override + public SubType complement() { + return createDelegate(inner.complement()); + } + + @Override + public SubType diff(SubType other) { + if (!(other instanceof BListSubType otherList)) { + throw new IllegalArgumentException("diff of different subtypes"); + } + return createDelegate(inner.diff(otherList.inner)); + } + + @Override + public boolean isEmpty(Context cx) { + return cx.memoSubtypeIsEmpty(cx.listMemo, + (context, bdd) -> bddEvery(context, bdd, BListSubType::listFormulaIsEmpty), inner); + } + + static boolean listFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { + FixedLengthArray members; + SemType rest; + if (pos == null) { + ListAtomicType atom = Builder.getListAtomicInner(); + members = atom.members(); + rest = atom.rest(); + } else { + // combine all the positive tuples using intersection + ListAtomicType lt = cx.listAtomType(pos.atom()); + members = lt.members(); + rest = lt.rest(); + Conjunction p = pos.next(); + // the neg case is in case we grow the array in listInhabited + if (p != null || neg != null) { + members = members.shallowCopy(); + } + while (true) { + if (p == null) { + break; + } else { + Atom d = p.atom(); + p = p.next(); + lt = cx.listAtomType(d); + Pair + intersected = listIntersectWith(cx.env, members, rest, lt.members(), lt.rest()); + if (intersected == null) { + return true; + } + members = intersected.first(); + rest = intersected.second(); + } + } + if (fixedArrayAnyEmpty(cx, members)) { + return true; + } + } + Integer[] indices = listSamples(cx, members, rest, neg); + Pair sampleTypes = listSampleTypes(cx, members, rest, indices); + return !listInhabited(cx, indices, sampleTypes.first(), sampleTypes.second(), neg); + } + + // This function determines whether a list type P & N is inhabited. + // where P is a positive list type and N is a list of negative list types. + // With just straightforward fixed-length tuples we can consider every index of the tuple. + // But we cannot do this in general because of rest types and fixed length array types e.g. `byte[10000000]`. + // So we consider instead a collection of indices that is sufficient for us to determine inhabitation, + // given the types of P and N. + // `indices` is this list of sample indices: these are indicies into members of the list type. + // We don't represent P directly. Instead P is represented by `memberTypes` and `nRequired`: + // `memberTypes[i]` is the type that P gives to `indices[i]`; + // `nRequired` is the number of members of `memberTypes` that are required by P. + // `neg` represents N. + private static boolean listInhabited(Context cx, Integer[] indices, SemType[] memberTypes, int nRequired, + Conjunction neg) { + if (neg == null) { + return true; + } else { + final ListAtomicType nt = cx.listAtomType(neg.atom()); + if (nRequired > 0 && Core.isNever(listMemberAtInnerVal(nt.members(), nt.rest(), indices[nRequired - 1]))) { + // Skip this negative if it is always shorter than the minimum required by the positive + return listInhabited(cx, indices, memberTypes, nRequired, neg.next()); + } + // Consider cases we can avoid this negative by having a sufficiently short list + int negLen = nt.members().fixedLength(); + if (negLen > 0) { + int len = memberTypes.length; + // If we have isEmpty(T1 & S1) or isEmpty(T2 & S2) then we have [T1, T2] / [S1, S2] = [T1, T2]. + // Therefore, we can skip the negative + for (int i = 0; i < len; i++) { + int index = indices[i]; + if (index >= negLen) { + break; + } + SemType negMemberType = listMemberAt(nt.members(), nt.rest(), index); + SemType common = Core.intersect(memberTypes[i], negMemberType); + if (Core.isEmpty(cx, common)) { + return listInhabited(cx, indices, memberTypes, nRequired, neg.next()); + } + } + // Consider cases we can avoid this negative by having a sufficiently short list + if (len < indices.length && indices[len] < negLen) { + return listInhabited(cx, indices, memberTypes, nRequired, neg.next()); + } + for (int i = nRequired; i < memberTypes.length; i++) { + if (indices[i] >= negLen) { + break; + } + // TODO: avoid creating new arrays here, maybe use an object pool for this + // -- Or use a copy on write array? + SemType[] t = Arrays.copyOfRange(memberTypes, 0, i); + if (listInhabited(cx, indices, t, nRequired, neg.next())) { + return true; + } + } + } + // Now we need to explore the possibility of shapes with length >= neglen + // This is the heart of the algorithm. + // For [v0, v1] not to be in [t0,t1], there are two possibilities + // (1) v0 is not in t0, or + // (2) v1 is not in t1 + // Case (1) + // For v0 to be in s0 but not t0, d0 must not be empty. + // We must then find a [v0,v1] satisfying the remaining negated tuples, + // such that v0 is in d0. + // SemType d0 = diff(s[0], t[0]); + // if !isEmpty(cx, d0) && tupleInhabited(cx, [d0, s[1]], neg.rest) { + // return true; + // } + // Case (2) + // For v1 to be in s1 but not t1, d1 must not be empty. + // We must then find a [v0,v1] satisfying the remaining negated tuples, + // such that v1 is in d1. + // SemType d1 = diff(s[1], t[1]); + // return !isEmpty(cx, d1) && tupleInhabited(cx, [s[0], d1], neg.rest); + // We can generalize this to tuples of arbitrary length. + for (int i = 0; i < memberTypes.length; i++) { + SemType d = Core.diff(memberTypes[i], listMemberAt(nt.members(), nt.rest(), indices[i])); + if (!Core.isEmpty(cx, d)) { + SemType[] t = memberTypes.clone(); + t[i] = d; + // We need to make index i be required + if (listInhabited(cx, indices, t, Integer.max(nRequired, i + 1), neg.next())) { + return true; + } + } + } + // This is correct for length 0, because we know that the length of the + // negative is 0, and [] - [] is empty. + return false; + } + } + + public static Pair listSampleTypes(Context cx, FixedLengthArray members, + SemType rest, Integer[] indices) { + List memberTypes = new ArrayList<>(indices.length); + int nRequired = 0; + for (int i = 0; i < indices.length; i++) { + int index = indices[i]; + SemType t = getCellContainingInnerVal(cx.env, listMemberAt(members, rest, index)); + if (Core.isEmpty(cx, t)) { + break; + } + memberTypes.add(t); + if (index < members.fixedLength()) { + nRequired = i + 1; + } + } + SemType[] buffer = new SemType[memberTypes.size()]; + return Pair.from(memberTypes.toArray(buffer), nRequired); + } + + // Return a list of sample indices for use as second argument of `listInhabited`. + // The positive list type P is represented by `members` and `rest`. + // The negative list types N are represented by `neg` + // The `indices` list (first member of return value) is constructed in two stages. + // First, the set of all non-negative integers is partitioned so that two integers are + // in different partitions if they get different types as an index in P or N. + // Second, we choose a number of samples from each partition. It doesn't matter + // which sample we choose, but (this is the key point) we need at least as many samples + // as there are negatives in N, so that for each negative we can freely choose a type for the sample + // to avoid being matched by that negative. + public static Integer[] listSamples(Context cx, FixedLengthArray members, SemType rest, Conjunction neg) { + int maxInitialLength = members.initial().length; + List fixedLengths = new ArrayList<>(); + fixedLengths.add(members.fixedLength()); + Conjunction tem = neg; + int nNeg = 0; + while (true) { + if (tem != null) { + ListAtomicType lt = cx.listAtomType(tem.atom()); + FixedLengthArray m = lt.members(); + maxInitialLength = Integer.max(maxInitialLength, m.initial().length); + if (m.fixedLength() > maxInitialLength) { + fixedLengths.add(m.fixedLength()); + } + nNeg += 1; + tem = tem.next(); + } else { + break; + } + } + Collections.sort(fixedLengths); + // `boundaries` partitions the non-negative integers + // Construct `boundaries` from `fixedLengths` and `maxInitialLength` + // An index b is a boundary point if indices < b are different from indices >= b + //int[] boundaries = from int i in 1 ... maxInitialLength select i; + List boundaries = new ArrayList<>(fixedLengths.size()); + for (int i = 1; i <= maxInitialLength; i++) { + boundaries.add(i); + } + for (int n : fixedLengths) { + // this also removes duplicates + if (boundaries.isEmpty() || n > boundaries.get(boundaries.size() - 1)) { + boundaries.add(n); + } + } + // Now construct the list of indices by taking nNeg samples from each partition. + List indices = new ArrayList<>(boundaries.size()); + int lastBoundary = 0; + if (nNeg == 0) { + // this is needed for when this is used in listProj + nNeg = 1; + } + for (int b : boundaries) { + int segmentLength = b - lastBoundary; + // Cannot have more samples than are in the parition. + int nSamples = Integer.min(segmentLength, nNeg); + for (int i = b - nSamples; i < b; i++) { + indices.add(i); + } + lastBoundary = b; + } + for (int i = 0; i < nNeg; i++) { + // Be careful to avoid integer overflow. + if (lastBoundary > Integer.MAX_VALUE - i) { + break; + } + indices.add(lastBoundary + i); + } + Integer[] arr = new Integer[indices.size()]; + return indices.toArray(arr); + } + + public static boolean fixedArrayAnyEmpty(Context cx, FixedLengthArray array) { + for (var t : array.initial()) { + if (Core.isEmpty(cx, t)) { + return true; + } + } + return false; + } + + public static Pair listIntersectWith(Env env, FixedLengthArray members1, SemType rest1, + FixedLengthArray members2, SemType rest2) { + + if (listLengthsDisjoint(members1, rest1, members2, rest2)) { + return null; + } + int max = Integer.max(members1.fixedLength(), members2.fixedLength()); + SemType[] initial = new SemType[max]; + for (int i = 0; i < max; i++) { + initial[i] = + intersectCellMemberSemTypes(env, listMemberAt(members1, rest1, i), + listMemberAt(members2, rest2, i)); + } + return Pair.from(FixedLengthArray.from(initial, + Integer.max(members1.fixedLength(), members2.fixedLength())), + intersectCellMemberSemTypes(env, rest1, rest2)); + } + + private static boolean listLengthsDisjoint(FixedLengthArray members1, SemType rest1, + FixedLengthArray members2, SemType rest2) { + int len1 = members1.fixedLength(); + int len2 = members2.fixedLength(); + if (len1 < len2) { + return Core.isNever(cellInnerVal(rest1)); + } + if (len2 < len1) { + return Core.isNever(cellInnerVal(rest2)); + } + return false; + } + + private static SemType listMemberAt(FixedLengthArray fixedArray, SemType rest, int index) { + if (index < fixedArray.fixedLength()) { + return fixedArrayGet(fixedArray, index); + } + return rest; + } + + private static SemType fixedArrayGet(FixedLengthArray members, int index) { + int memberLen = members.initial().length; + int i = Integer.min(index, memberLen - 1); + return members.initial()[i]; + } + + public static SemType listMemberAtInnerVal(FixedLengthArray fixedArray, SemType rest, int index) { + return cellInnerVal(listMemberAt(fixedArray, rest, index)); + } + + public static SemType bddListMemberTypeInnerVal(Context cx, Bdd b, SubTypeData key, SemType accum) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? accum : Builder.getNeverType(); + } else { + BddNode bddNode = (BddNode) b; + return Core.union(bddListMemberTypeInnerVal(cx, bddNode.left(), key, + Core.intersect(listAtomicMemberTypeInnerVal(cx.listAtomType(bddNode.atom()), key), accum)), + Core.union(bddListMemberTypeInnerVal(cx, bddNode.middle(), key, accum), + bddListMemberTypeInnerVal(cx, bddNode.right(), key, accum))); + } + } + + private static SemType listAtomicMemberTypeInnerVal(ListAtomicType atomic, SubTypeData key) { + return Core.diff(listAtomicMemberTypeInner(atomic, key), Builder.getUndefType()); + } + + private static SemType listAtomicMemberTypeInner(ListAtomicType atomic, SubTypeData key) { + return listAtomicMemberTypeAtInner(atomic.members(), atomic.rest(), key); + } + + static SemType listAtomicMemberTypeAtInner(FixedLengthArray fixedArray, SemType rest, SubTypeData key) { + if (key instanceof BIntSubType.IntSubTypeData intSubtype) { + SemType m = Builder.getNeverType(); + int initLen = fixedArray.initial().length; + int fixedLen = fixedArray.fixedLength(); + if (fixedLen != 0) { + for (int i = 0; i < initLen; i++) { + if (intSubtypeContains(key, i)) { + m = Core.union(m, cellInner(fixedArrayGet(fixedArray, i))); + } + } + if (intSubtype.isRangeOverlap(new BIntSubType.Range(initLen, fixedLen - 1))) { + m = Core.union(m, cellInner(fixedArrayGet(fixedArray, fixedLen - 1))); + } + } + if (fixedLen == 0 || intSubtype.max() > fixedLen - 1) { + m = Core.union(m, cellInner(rest)); + } + return m; + } + SemType m = cellInner(rest); + if (fixedArray.fixedLength() > 0) { + for (SemType ty : fixedArray.initial()) { + m = Core.union(m, cellInner(ty)); + } + } + return m; + } + + @Override + public SubTypeData data() { + return inner(); + } + + @Override + public Bdd inner() { + return inner; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BListSubType that)) { + return false; + } + return Objects.equals(inner, that.inner); + } + + @Override + public int hashCode() { + return Objects.hashCode(inner); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingProj.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingProj.java new file mode 100644 index 000000000000..94f344aa1e85 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingProj.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.BddAllOrNothing; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.ArrayList; +import java.util.List; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_MAPPING; +import static io.ballerina.runtime.api.types.semtype.Core.diff; +import static io.ballerina.runtime.api.types.semtype.Core.getComplexSubtypeData; +import static io.ballerina.runtime.api.types.semtype.Core.isNothingSubtype; +import static io.ballerina.runtime.api.types.semtype.Core.stringSubtype; + +/** + * Utility class for doing mapping type projection. + * + * @since 2201.11.0 + */ +public final class BMappingProj { + + private BMappingProj() { + } + + public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k) { + return diff(mappingMemberTypeInner(cx, t, k), Builder.getUndefType()); + } + + // This computes the spec operation called "member type of K in T", + // for when T is a subtype of mapping, and K is either `string` or a singleton string. + // This is what Castagna calls projection. + public static SemType mappingMemberTypeInner(Context cx, SemType t, SemType k) { + if (t.some() == 0) { + return (t.all() & Builder.getMappingType().all()) != 0 ? Builder.getValType() : Builder.getUndefType(); + } else { + SubTypeData keyData = stringSubtype(k); + if (isNothingSubtype(keyData)) { + return Builder.getUndefType(); + } + return bddMappingMemberTypeInner(cx, (Bdd) getComplexSubtypeData(t, BT_MAPPING), keyData, + Builder.getInnerType()); + } + } + + static SemType bddMappingMemberTypeInner(Context cx, Bdd b, SubTypeData key, SemType accum) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? accum : Builder.getNeverType(); + } else { + BddNode bdd = (BddNode) b; + return Core.union( + bddMappingMemberTypeInner(cx, bdd.left(), key, + Core.intersect(mappingAtomicMemberTypeInner(cx.mappingAtomType(bdd.atom()), key), + accum)), + Core.union(bddMappingMemberTypeInner(cx, bdd.middle(), key, accum), + bddMappingMemberTypeInner(cx, bdd.right(), key, accum))); + } + } + + static SemType mappingAtomicMemberTypeInner(MappingAtomicType atomic, SubTypeData key) { + SemType memberType = null; + for (SemType ty : mappingAtomicApplicableMemberTypesInner(atomic, key)) { + if (memberType == null) { + memberType = ty; + } else { + memberType = Core.union(memberType, ty); + } + } + return memberType == null ? Builder.getUndefType() : memberType; + } + + static List mappingAtomicApplicableMemberTypesInner(MappingAtomicType atomic, SubTypeData key) { + List types = new ArrayList<>(atomic.types().length); + for (SemType t : atomic.types()) { + types.add(Core.cellInner(t)); + } + + List memberTypes = new ArrayList<>(); + SemType rest = Core.cellInner(atomic.rest()); + if (isAllSubtype(key)) { + memberTypes.addAll(types); + memberTypes.add(rest); + } else { + BStringSubType.StringSubtypeListCoverage coverage = + ((BStringSubType.StringSubTypeData) key).stringSubtypeListCoverage(atomic.names()); + for (int index : coverage.indices()) { + memberTypes.add(types.get(index)); + } + if (!coverage.isSubType()) { + memberTypes.add(rest); + } + } + return memberTypes; + } + + static boolean isAllSubtype(SubTypeData d) { + return d == AllOrNothing.ALL; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingSubType.java new file mode 100644 index 000000000000..bc56809a3c66 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingSubType.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Conjunction; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.FieldPair; +import io.ballerina.runtime.api.types.semtype.FieldPairs; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Arrays; +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; + +/** + * Runtime representation of a subtype of mapping type. + * + * @since 2201.11.0 + */ +public class BMappingSubType extends SubType implements DelegatedSubType { + + public final Bdd inner; + + private BMappingSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BMappingSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BMappingSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BMappingSubType bMapping) { + return new BMappingSubType(bMapping.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public Bdd inner() { + return inner; + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BMappingSubType otherMapping)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherMapping.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BMappingSubType otherMapping)) { + throw new IllegalArgumentException("intersect of different subtypes"); + } + return createDelegate(inner.intersect(otherMapping.inner)); + } + + @Override + public SubType complement() { + return createDelegate(inner.complement()); + } + + @Override + public boolean isEmpty(Context cx) { + return cx.memoSubtypeIsEmpty(cx.mappingMemo, + (context, bdd) -> bddEvery(context, bdd, BMappingSubType::mappingFormulaIsEmpty), inner); + } + + static boolean mappingFormulaIsEmpty(Context cx, Conjunction posList, Conjunction negList) { + MappingAtomicType combined; + if (posList == null) { + combined = Builder.getMappingAtomicInner(); + } else { + // combine all the positive atoms using intersection + combined = cx.mappingAtomType(posList.atom()); + Conjunction p = posList.next(); + while (true) { + if (p == null) { + break; + } else { + MappingAtomicType m = + combined.intersectMapping(cx.env, cx.mappingAtomType(p.atom())); + if (m == null) { + return true; + } else { + combined = m; + } + p = p.next(); + } + } + for (SemType t : combined.types()) { + if (Core.isEmpty(cx, t)) { + return true; + } + } + + } + return !mappingInhabited(cx, combined, negList); + } + + private static boolean mappingInhabited(Context cx, MappingAtomicType pos, Conjunction negList) { + if (negList == null) { + return true; + } else { + MappingAtomicType neg = cx.mappingAtomType(negList.atom()); + + if (!Core.isEmpty(cx, Core.diff(pos.rest(), neg.rest()))) { + return mappingInhabited(cx, pos, negList.next()); + } + for (FieldPair fieldPair : new FieldPairs(pos, neg)) { + SemType intersect = Core.intersect(fieldPair.type1(), fieldPair.type2()); + // if types of at least one field are disjoint, the neg atom will not contribute to the next iteration. + // Therefore, we can skip the current neg atom. + // i.e. if we have isEmpty(T1 & S1) or isEmpty(T2 & S2) then, + // record { T1 f1; T2 f2; } / record { S1 f1; S2 f2; } = record { T1 f1; T2 f2; } + if (Core.isEmpty(cx, intersect)) { + return mappingInhabited(cx, pos, negList.next()); + } + + SemType d = Core.diff(fieldPair.type1(), fieldPair.type2()); + if (!Core.isEmpty(cx, d)) { + MappingAtomicType mt; + if (fieldPair.index1() == null) { + // the posType came from the rest type + mt = insertField(pos, fieldPair.name(), d); + } else { + SemType[] posTypes = pos.types().clone(); + posTypes[fieldPair.index1()] = d; + mt = new MappingAtomicType(pos.names(), posTypes, pos.rest()); + } + if (mappingInhabited(cx, mt, negList.next())) { + return true; + } + } + } + return false; + } + } + + private static MappingAtomicType insertField(MappingAtomicType m, String name, SemType t) { + String[] orgNames = m.names(); + String[] names = shallowCopyStrings(orgNames, orgNames.length + 1); + SemType[] orgTypes = m.types(); + SemType[] types = shallowCopySemTypes(orgTypes, orgTypes.length + 1); + int i = orgNames.length; + while (true) { + if (i == 0 || Common.codePointCompare(names[i - 1], name)) { + names[i] = name; + types[i] = t; + break; + } + names[i] = names[i - 1]; + types[i] = types[i - 1]; + i -= 1; + } + return new MappingAtomicType(names, types, m.rest()); + } + + static SemType[] shallowCopySemTypes(SemType[] v, int newLength) { + return Arrays.copyOf(v, newLength); + } + + private static String[] shallowCopyStrings(String[] v, int newLength) { + return Arrays.copyOf(v, newLength); + } + + @Override + public SubTypeData data() { + return inner(); + } + + @Override + public SubType diff(SubType other) { + if (!(other instanceof BMappingSubType otherList)) { + throw new IllegalArgumentException("diff of different subtypes"); + } + return createDelegate(inner.diff(otherList.inner)); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BMappingSubType that)) { + return false; + } + return Objects.equals(inner, that.inner); + } + + @Override + public int hashCode() { + return Objects.hashCode(inner); + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BObjectSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BObjectSubType.java new file mode 100644 index 000000000000..de6516a4086a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BObjectSubType.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEveryPositive; + +/** + * Runtime representation of a subtype of object type. + * + * @since 2201.11.0 + */ +public final class BObjectSubType extends SubType implements DelegatedSubType { + + public final Bdd inner; + + private BObjectSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BObjectSubType otherObject)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherObject.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BObjectSubType otherObject)) { + throw new IllegalArgumentException("intersect of different subtypes"); + } + return createDelegate(inner.intersect(otherObject.inner)); + } + + @Override + public SubType complement() { + return createDelegate(inner.complement()); + } + + @Override + public boolean isEmpty(Context cx) { + return cx.memoSubtypeIsEmpty(cx.mappingMemo, + (context, bdd) -> bddEveryPositive(context, bdd, null, null, BMappingSubType::mappingFormulaIsEmpty), + inner); + } + + @Override + public SubTypeData data() { + throw new UnsupportedOperationException("Method not implemented"); + } + + public static BObjectSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BObjectSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BObjectSubType bMapping) { + return new BObjectSubType(bMapping.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public Bdd inner() { + throw new UnsupportedOperationException("Method not implemented"); + } + + @Override + public SubType diff(SubType other) { + if (!(other instanceof BObjectSubType otherObject)) { + throw new IllegalArgumentException("diff of different subtypes"); + } + return createDelegate(inner.diff(otherObject.inner)); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof BObjectSubType that)) { + return false; + } + return Objects.equals(inner, that.inner); + } + + @Override + public int hashCode() { + return Objects.hashCode(inner); + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BStreamSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BStreamSubType.java new file mode 100644 index 000000000000..c666b54ce417 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BStreamSubType.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; + +/** + * Runtime representation of a subtype of stream type. + * + * @since 2201.11.0 + */ +public class BStreamSubType extends SubType implements DelegatedSubType { + + public final Bdd inner; + + private BStreamSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BStreamSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BStreamSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BStreamSubType bStreamSubType) { + return new BStreamSubType(bStreamSubType.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BStreamSubType otherStream)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherStream.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BStreamSubType otherStream)) { + throw new IllegalArgumentException("intersect of different subtypes"); + } + return createDelegate(inner.intersect(otherStream.inner)); + } + + @Override + public SubType complement() { + return createDelegate(Builder.getListSubtypeTwoElement().diff(inner)); + } + + @Override + public boolean isEmpty(Context cx) { + Bdd b = inner; + // The goal of this is to ensure that listSubtypeIsEmpty call beneath does + // not get an empty posList, because it will interpret that + // as `[any|error...]` rather than `[any|error, any|error]`. + b = b.posMaybeEmpty() ? (Bdd) b.intersect(Builder.getListSubtypeTwoElement()) : b; + return cx.memoSubtypeIsEmpty(cx.listMemo, + (context, bdd) -> bddEvery(context, bdd, BListSubType::listFormulaIsEmpty), b); + } + + @Override + public SubTypeData data() { + return inner(); + } + + @Override + public Bdd inner() { + return inner; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BStringSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BStringSubType.java new file mode 100644 index 000000000000..0527874f61f1 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BStringSubType.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * Runtime representation of subtype of string type. + * + * @since 2201.11.0 + */ +public final class BStringSubType extends SubType { + + final SubTypeData data; + private static final BStringSubType ALL = new BStringSubType(AllOrNothing.ALL); + private static final BStringSubType NOTHING = new BStringSubType(AllOrNothing.NOTHING); + + private BStringSubType(SubTypeData data) { + super(data == AllOrNothing.ALL, data == AllOrNothing.NOTHING); + this.data = data; + } + + public static BStringSubType createStringSubType(boolean charsAllowed, String[] chars, boolean nonCharsAllowed, + String[] nonChars) { + if (chars.length == 0 && nonChars.length == 0) { + if (!charsAllowed && !nonCharsAllowed) { + return ALL; + } else if (charsAllowed && nonCharsAllowed) { + return NOTHING; + } + } + Arrays.sort(chars); + Arrays.sort(nonChars); + ValueData charValues = new ValueData(charsAllowed, chars); + ValueData nonCharValues = new ValueData(nonCharsAllowed, nonChars); + StringSubTypeData data = new StringSubTypeData(charValues, nonCharValues); + return new BStringSubType(data); + } + + @Override + public String toString() { + if (data instanceof StringSubTypeData stringSubTypeData) { + var chars = stringSubTypeData.chars; + var nonChars = stringSubTypeData.nonChars; + if (chars.allowed && chars.values.length > 0 && nonChars.allowed && nonChars.values.length == 0) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < chars.values.length; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(chars.values[i]); + } + return sb.toString(); + } else if (nonChars.allowed && nonChars.values.length > 0 && chars.allowed && chars.values.length == 0) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < nonChars.values.length; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(nonChars.values[i]); + } + return sb.toString(); + } + } + return "string"; + } + + @Override + public SubType union(SubType otherSubType) { + BStringSubType other = (BStringSubType) otherSubType; + // TODO: refactor + if (this.data instanceof AllOrNothing || other.data instanceof AllOrNothing) { + if (this.data == AllOrNothing.ALL) { + return this; + } else if (other.data == AllOrNothing.ALL) { + return other; + } else if (this.data == AllOrNothing.NOTHING) { + return other; + } else if (other.data == AllOrNothing.NOTHING) { + return this; + } + throw new IllegalStateException("unreachable"); + } + StringSubTypeData data = (StringSubTypeData) this.data; + StringSubTypeData otherData = (StringSubTypeData) other.data; + List chars = new ArrayList<>(); + boolean charsAllowed = data.chars.union(otherData.chars, chars); + List nonChars = new ArrayList<>(); + boolean nonCharsAllowed = data.nonChars.union(otherData.nonChars, nonChars); + return createStringSubType(charsAllowed, chars.toArray(String[]::new), nonCharsAllowed, + nonChars.toArray(String[]::new)); + } + + @Override + public SubType intersect(SubType otherSubtype) { + BStringSubType other = (BStringSubType) otherSubtype; + if (this.data instanceof AllOrNothing) { + if (this.data == AllOrNothing.ALL) { + return other; + } else { + return NOTHING; + } + } else if (other.data instanceof AllOrNothing) { + if (other.data == AllOrNothing.ALL) { + return this; + } else { + return NOTHING; + } + } + StringSubTypeData data = (StringSubTypeData) this.data; + StringSubTypeData otherData = (StringSubTypeData) other.data; + List chars = new ArrayList<>(); + boolean charsAllowed = data.chars.intersect(otherData.chars, chars); + List nonChars = new ArrayList<>(); + boolean nonCharsAllowed = data.nonChars.intersect(otherData.nonChars, nonChars); + return createStringSubType(charsAllowed, chars.toArray(String[]::new), nonCharsAllowed, + nonChars.toArray(String[]::new)); + } + + @Override + public SubType complement() { + if (data instanceof AllOrNothing) { + if (data == AllOrNothing.ALL) { + return NOTHING; + } else { + return ALL; + } + } + StringSubTypeData stringData = (StringSubTypeData) data; + ValueData chars = stringData.chars; + ValueData nonChars = stringData.nonChars; + return createStringSubType(!chars.allowed, chars.values, !nonChars.allowed, nonChars.values); + } + + @Override + public boolean isEmpty(Context cx) { + return data == AllOrNothing.NOTHING; + } + + @Override + public SubTypeData data() { + return data; + } + + static void stringListIntersect(String[] values, String[] target, List indices) { + int i1 = 0; + int i2 = 0; + int len1 = values.length; + int len2 = target.length; + while (true) { + if (i1 >= len1 || i2 >= len2) { + break; + } else { + switch (compareStrings(values[i1], target[i2])) { + case EQ: + indices.add(i1); + i1 += 1; + i2 += 1; + break; + case LT: + i1 += 1; + break; + case GT: + i2 += 1; + break; + default: + throw new AssertionError("Invalid comparison value!"); + } + } + } + } + + private static ComparisonResult compareStrings(String s1, String s2) { + return Objects.equals(s1, s2) ? ComparisonResult.EQ : + (Common.codePointCompare(s1, s2) ? ComparisonResult.LT : ComparisonResult.GT); + } + + private enum ComparisonResult { + EQ, + LT, + GT + } + + record StringSubTypeData(ValueData chars, ValueData nonChars) implements SubTypeData { + + StringSubtypeListCoverage stringSubtypeListCoverage(String[] values) { + List indices = new ArrayList<>(); + ValueData ch = chars(); + ValueData nonChar = nonChars(); + int stringConsts = 0; + if (ch.allowed) { + stringListIntersect(values, ch.values, indices); + stringConsts = ch.values.length; + } else if (ch.values.length == 0) { + for (int i = 0; i < values.length; i++) { + if (values[i].length() == 1) { + indices.add(i); + } + } + } + if (nonChar.allowed) { + stringListIntersect(values, nonChar.values, indices); + stringConsts += nonChar.values.length; + } else if (nonChar.values.length == 0) { + for (int i = 0; i < values.length; i++) { + if (values[i].length() != 1) { + indices.add(i); + } + } + } + int[] inds = indices.stream().mapToInt(i -> i).toArray(); + return new StringSubtypeListCoverage(stringConsts == indices.size(), inds); + } + } + + record StringSubtypeListCoverage(boolean isSubType, int[] indices) { + + } + + static final class ValueData extends EnumerableSubtypeData { + + private final boolean allowed; + private final String[] values; + + // NOTE: this assumes values are sorted + private ValueData(boolean allowed, String[] values) { + this.allowed = allowed; + this.values = values; + } + + @Override + public boolean allowed() { + return allowed; + } + + @Override + public String[] values() { + return values; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTableSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTableSubType.java new file mode 100644 index 000000000000..91243d618228 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTableSubType.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; + +/** + * Represents the subtype of a table type. + * + * @since 2201.11.0 + */ +public final class BTableSubType extends SubType implements DelegatedSubType { + + private final Bdd inner; + + private BTableSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BTableSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BTableSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BTableSubType other) { + return new BTableSubType(other.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BTableSubType otherTable)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherTable.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BTableSubType otherTable)) { + throw new IllegalArgumentException("intersect of different subtypes"); + } + return createDelegate(inner.intersect(otherTable.inner)); + } + + @Override + public SubType complement() { + return createDelegate(Builder.getListSubtypeThreeElement().diff(inner)); + } + + @Override + public boolean isEmpty(Context cx) { + Bdd b = inner; + // The goal of this is to ensure that listSubtypeIsEmpty call beneath does + // not get an empty posList, because it will interpret that + // as `(any|error)[]` rather than `[(map)[], any|error, any|error]`. + b = b.posMaybeEmpty() ? (Bdd) b.intersect(Builder.getListSubtypeThreeElement()) : b; + return cx.memoSubtypeIsEmpty(cx.listMemo, + (context, bdd) -> bddEvery(context, bdd, BListSubType::listFormulaIsEmpty), b); + } + + @Override + public SubTypeData data() { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public SubType inner() { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof BTableSubType other)) { + return false; + } + return inner.equals(other.inner); + } + + @Override + public int hashCode() { + return Objects.hash(inner); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypedescSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypedescSubType.java new file mode 100644 index 000000000000..b8b0e91212a3 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypedescSubType.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEveryPositive; + +/** + * Represents the subtype of a typedesc type. + * + * @since 2201.11.0 + */ +public class BTypedescSubType extends SubType implements DelegatedSubType { + + private final Bdd inner; + + private BTypedescSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BTypedescSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BTypedescSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BTypedescSubType other) { + return new BTypedescSubType(other.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BTypedescSubType otherTypedesc)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherTypedesc.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BTypedescSubType otherTypedesc)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.intersect(otherTypedesc.inner)); + } + + @Override + public SubType complement() { + return createDelegate(Builder.getBddSubtypeRo().diff(inner)); + } + + @Override + public boolean isEmpty(Context cx) { + Bdd b = inner; + // The goal of this is to ensure that mappingFormulaIsEmpty call in errorBddIsEmpty beneath + // does not get an empty posList, because it will interpret that + // as `map` rather than `readonly & map`. + b = b.posMaybeEmpty() ? (Bdd) b.intersect(Builder.getBddSubtypeRo()) : b; + return cx.memoSubtypeIsEmpty(cx.mappingMemo, BTypedescSubType::typedescBddIsEmpty, b); + } + + private static boolean typedescBddIsEmpty(Context cx, Bdd b) { + return bddEveryPositive(cx, b, null, null, BMappingSubType::mappingFormulaIsEmpty); + } + + @Override + public SubTypeData data() { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public SubType inner() { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof BTypedescSubType other)) { + return false; + } + return Objects.equals(inner, other.inner); + } + + @Override + public int hashCode() { + return Objects.hash(inner); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BXmlSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BXmlSubType.java new file mode 100644 index 000000000000..35479680eb62 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BXmlSubType.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Conjunction; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.RecAtom; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Objects; + +/** + * Represents the subtype of an XML type. + * + * @since 2201.11.0 + */ +public class BXmlSubType extends SubType implements DelegatedSubType { + + public final Bdd inner; + private final int primitives; + + private BXmlSubType(Bdd inner, int primitives) { + super(false, false); + this.inner = inner; + this.primitives = primitives; + } + + public static BXmlSubType createDelegate(int primitives, SubType inner) { + if (inner instanceof Bdd bdd) { + return new BXmlSubType(bdd, primitives); + } else if (inner instanceof BXmlSubType bXml) { + return new BXmlSubType(bXml.inner, primitives); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + BXmlSubType otherXml = (BXmlSubType) other; + int primitives = this.primitives() | otherXml.primitives(); + return createDelegate(primitives, inner.union(otherXml.inner)); + } + + @Override + public SubType intersect(SubType other) { + BXmlSubType otherXml = (BXmlSubType) other; + int primitives = this.primitives() & otherXml.primitives(); + return createDelegate(primitives, inner.intersect(otherXml.inner)); + } + + @Override + public SubType diff(SubType other) { + BXmlSubType otherXml = (BXmlSubType) other; + return diff(this, otherXml); + } + + private static SubType diff(BXmlSubType st1, BXmlSubType st2) { + int primitives = st1.primitives() & ~st2.primitives(); + return createDelegate(primitives, st1.inner.diff(st2.inner)); + } + + @Override + public SubType complement() { + return diff((BXmlSubType) XmlUtils.XML_SUBTYPE_TOP, this); + } + + @Override + public boolean isEmpty(Context cx) { + if (primitives() != 0) { + return false; + } + return xmlBddEmpty(cx); + } + + private boolean xmlBddEmpty(Context cx) { + return Bdd.bddEvery(cx, inner, BXmlSubType::xmlFormulaIsEmpty); + } + + private static boolean xmlFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { + int allPosBits = collectAllPrimitives(pos) & XmlUtils.XML_PRIMITIVE_ALL_MASK; + return xmlHasTotalNegative(allPosBits, neg); + } + + private static boolean xmlHasTotalNegative(int allPosBits, Conjunction conjunction) { + if (allPosBits == 0) { + return true; + } + Conjunction n = conjunction; + while (n != null) { + if ((allPosBits & ~getIndex(n)) == 0) { + return true; + } + n = n.next(); + } + return false; + } + + private static int collectAllPrimitives(Conjunction conjunction) { + int bits = 0; + Conjunction current = conjunction; + while (current != null) { + bits &= getIndex(current); + current = current.next(); + } + return bits; + } + + private static int getIndex(Conjunction conjunction) { + var atom = conjunction.atom(); + assert atom instanceof RecAtom; + return atom.index(); + } + + @Override + public SubTypeData data() { + return this; + } + + @Override + public SubType inner() { + return this; + } + + int primitives() { + return primitives; + } + + Bdd bdd() { + return inner; + } + + @Override + public int hashCode() { + return Objects.hash(inner, primitives); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BXmlSubType other)) { + return false; + } + return Objects.equals(bdd(), other.bdd()) && primitives() == other.primitives(); + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BddMemo.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BddMemo.java new file mode 100644 index 000000000000..739117d179f5 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BddMemo.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import java.util.Objects; +import java.util.Optional; + +/** + * Represents the memoization emptiness of a BDD used in Context. + * + * @since 2201.11.0 + */ +public final class BddMemo { + + public Status isEmpty; + + public BddMemo() { + this.isEmpty = Status.NULL; + } + + public enum Status { + // We know where this BDD is empty or not + TRUE, + FALSE, + // There is some recursive part in this type + LOOP, + CYCLIC, + // We are in the process of determining if this BDD is empty or not + PROVISIONAL, + // We just initialized the node, treated to be same as not having a memo + NULL + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BddMemo bddMemo)) { + return false; + } + return isEmpty == bddMemo.isEmpty; + } + + @Override + public int hashCode() { + return Objects.hashCode(isEmpty); + } + + public Optional isEmpty() { + return switch (isEmpty) { + // Cyclic types are empty because we define types inductively + case TRUE, CYCLIC -> Optional.of(true); + case FALSE -> Optional.of(false); + case LOOP, PROVISIONAL -> { + // If it was provisional we came from a back edge + // Again we treat the loop part as empty + isEmpty = Status.LOOP; + yield Optional.of(true); + } + case NULL -> Optional.empty(); + }; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/CellAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/CellAtomicType.java new file mode 100644 index 000000000000..8e75f75af7b6 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/CellAtomicType.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Atom; +import io.ballerina.runtime.api.types.semtype.AtomicType; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.TypeAtom; + +import java.util.HashMap; +import java.util.Map; + +/** + * CellAtomicType node. + * + * @param ty Type "wrapped" by this cell + * @param mut Mutability of the cell + * @since 2201.11.0 + */ +public record CellAtomicType(SemType ty, CellMutability mut) implements AtomicType { + + public CellAtomicType { + assert ty != null; + } + + public static CellAtomicType from(SemType ty, CellMutability mut) { + return CellAtomCache.get(ty, mut); + } + + public static CellAtomicType intersectCellAtomicType(CellAtomicType c1, CellAtomicType c2) { + SemType ty = Core.intersect(c1.ty(), c2.ty()); + CellMutability mut = min(c1.mut(), c2.mut()); + return CellAtomicType.from(ty, mut); + } + + private static CellMutability min(CellMutability m1, + CellMutability m2) { + return m1.compareTo(m2) <= 0 ? m1 : m2; + } + + public static CellAtomicType cellAtomType(Atom atom) { + return (CellAtomicType) ((TypeAtom) atom).atomicType(); + } + + public enum CellMutability { + CELL_MUT_NONE, + CELL_MUT_LIMITED, + CELL_MUT_UNLIMITED + } + + private static final class CellAtomCache { + + private static final Map NONE_CACHE = new HashMap<>(); + private static final Map LIMITED_CACHE = new HashMap<>(); + private static final Map UNLIMITED_CACHE = new HashMap<>(); + + private static CellAtomicType get(SemType semType, CellMutability mut) { + if (semType.some() != 0) { + return new CellAtomicType(semType, mut); + } + int key = semType.all(); + return switch (mut) { + case CELL_MUT_NONE -> NONE_CACHE.computeIfAbsent(key, (ignored) -> new CellAtomicType(semType, mut)); + case CELL_MUT_LIMITED -> + LIMITED_CACHE.computeIfAbsent(key, (ignored) -> new CellAtomicType(semType, mut)); + case CELL_MUT_UNLIMITED -> + UNLIMITED_CACHE.computeIfAbsent(key, (ignored) -> new CellAtomicType(semType, mut)); + }; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Common.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Common.java new file mode 100644 index 000000000000..8d057aa5e4ef --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Common.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +/** + * Utility class for various operations related to semantic types. + * + * @since 2201.11.0 + */ +public final class Common { + + private Common() { + } + + public static boolean codePointCompare(String s1, String s2) { + if (s1.equals(s2)) { + return false; + } + int len1 = s1.length(); + int len2 = s2.length(); + if (len1 < len2 && s2.substring(0, len1).equals(s1)) { + return true; + } + int cpCount1 = s1.codePointCount(0, len1); + int cpCount2 = s2.codePointCount(0, len2); + for (int cp = 0; cp < cpCount1 && cp < cpCount2; ) { + int codepoint1 = s1.codePointAt(cp); + int codepoint2 = s2.codePointAt(cp); + if (codepoint1 == codepoint2) { + cp++; + continue; + } + return codepoint1 < codepoint2; + } + return false; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DefinitionContainer.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DefinitionContainer.java new file mode 100644 index 000000000000..2b4373a2de5e --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DefinitionContainer.java @@ -0,0 +1,111 @@ +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Definition; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Supplier; + +/** + * Container used to maintain concurrency invariants when creating a potentially recursive semtype. + * + * It maintains fallowing invariants. + * 1. When the type is being defined only the thread that is defining the type may proceed + * 2. After definition is completed any number of threads may proceed concurrently In order to achieve this container + * has three phases (init, defining, defined). At init phase (that is no definition has been set) any number of threads + * may proceed concurrently. When a thread sets a definition {@code setDefinition} container enters the defining phase. + * In that phase only that thread may continue in {@code getSemType} method (this is to allow for recursive type + * definitions). Container registers with the {@code Definition} using {@code registerContainer} method. When the + * {@code Definition} has been defined (ie. {@code Env} has an atom corresponding to the definition) it must notify the + * container using {@code definitionUpdated} method. At this point container moves to defined phase allowing concurrent + * access to {@code getSemType}. + * + * @param type of the definition + * @since 2201.11.0 + */ +public class DefinitionContainer { + + private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); + private volatile E definition; + + private final ReentrantLock recTypeLock = new ReentrantLock(); + private volatile boolean isDefining = false; + + public boolean isDefinitionReady() { + try { + rwLock.readLock().lock(); + return definition != null; + } finally { + rwLock.readLock().unlock(); + } + } + + public SemType getSemType(Env env) { + try { + rwLock.readLock().lock(); + // We don't need this check to be synchronized since {@code trySetDefinition} will hold the write lock until + // it completes, So isDefining should always be at a consistent state + if (isDefining) { + // This should prevent threads other than the defining thread to access the rec atom. + recTypeLock.lock(); + } + return definition.getSemType(env); + } finally { + rwLock.readLock().unlock(); + } + } + + public DefinitionUpdateResult trySetDefinition(Supplier supplier) { + try { + rwLock.writeLock().lock(); + boolean updated; + E newDefinition; + if (this.definition != null) { + updated = false; + newDefinition = null; + } else { + updated = true; + newDefinition = supplier.get(); + newDefinition.registerContainer(this); + this.recTypeLock.lock(); + isDefining = true; + this.definition = newDefinition; + } + return new DefinitionUpdateResult<>(newDefinition, updated); + } finally { + rwLock.writeLock().unlock(); + } + } + + public void clear() { + try { + rwLock.writeLock().lock(); + // This shouldn't happen because defining thread should hold the lock. + assert !isDefining; + this.definition = null; + } finally { + rwLock.writeLock().unlock(); + } + } + + public void definitionUpdated() { + recTypeLock.unlock(); + isDefining = false; + } + + /** + * Result of trying to update the definition. + * + * @param Type of the definition + * @param updated If update was successful. If this failed you must get the semtype using the {@code getSemType} + * method of the container + * @param definition If update was successful this will be the new definition. Otherwise, this will be null + * @since 2201.11.0 + */ + public record DefinitionUpdateResult(E definition, boolean updated) { + + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/StringSubtype.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java similarity index 59% rename from semtypes/src/main/java/io/ballerina/semtype/subtypedata/StringSubtype.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java index e2d25feb5261..6f798ca4d07c 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/StringSubtype.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -15,15 +15,17 @@ * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype.subtypedata; -import io.ballerina.semtype.ProperSubtypeData; +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.SubType; /** - * Represent StringSubtype. + * Represents the subtype implemented by BDDs. * - * @since 2.0.0 + * @since 2201.11.0 */ -public class StringSubtype implements ProperSubtypeData { +public interface DelegatedSubType extends SubTypeData { + SubType inner(); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/EnumerableSubtypeData.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/EnumerableSubtypeData.java new file mode 100644 index 000000000000..4f8429d51f1f --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/EnumerableSubtypeData.java @@ -0,0 +1,181 @@ +/* + * + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * / + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import java.util.Arrays; +import java.util.List; + +/** + * All {@code SubTypeData} where we can enumerate individual values must extend + * this class. It will provide common operations such as {@code union}, + * {@code intersect} and {@code diff} for all such data. + * + * @param type individual value in the subset + * @since 2201.11.0 + */ +public abstract class EnumerableSubtypeData> { + + public abstract boolean allowed(); + + public abstract E[] values(); + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof EnumerableSubtypeData other)) { + return false; + } + return other.allowed() == this.allowed() && Arrays.equals(other.values(), this.values()); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + boolean union(EnumerableSubtypeData other, List results) { + boolean b1 = this.allowed(); + boolean b2 = other.allowed(); + if (b1 && b2) { + enumerableListUnion(this.values(), other.values(), results); + return true; + } else if (!b1 && !b2) { + enumerableListIntersection(this.values(), other.values(), results); + return false; + } else if (b1 && !b2) { + enumerableListDiff(other.values(), this.values(), results); + return false; + } else { + enumerableListDiff(this.values(), other.values(), results); + return false; + } + } + + boolean intersect(EnumerableSubtypeData other, List results) { + boolean b1 = this.allowed(); + boolean b2 = other.allowed(); + if (b1 && b2) { + enumerableListIntersection(this.values(), other.values(), results); + return true; + } else if (!b1 && !b2) { + enumerableListUnion(this.values(), other.values(), results); + return false; + } else if (b1 && !b2) { + enumerableListDiff(this.values(), other.values(), results); + return true; + } else { + enumerableListDiff(other.values(), this.values(), results); + return true; + } + } + + private static > void enumerableListUnion(E[] values1, E[] values2, List results) { + int i1, i2; + i1 = i2 = 0; + int len1 = values1.length; + int len2 = values2.length; + while (true) { + if (i1 >= len1) { + if (i2 >= len2) { + break; + } + results.add(values2[i2]); + i2 += 1; + } else if (i2 >= len2) { + results.add(values1[i1]); + i1 += 1; + } else { + E s1 = values1[i1]; + E s2 = values2[i2]; + int result = s1.compareTo(s2); + if (result == 0) { + results.add(s1); + i1 += 1; + i2 += 1; + } else if (result < 0) { + results.add(s1); + i1 += 1; + } else { + results.add(s2); + i2 += 1; + } + } + } + } + + private static > void enumerableListIntersection(E[] v1, E[] v2, List results) { + int i1, i2; + i1 = i2 = 0; + int len1 = v1.length; + int len2 = v2.length; + while (true) { + // TODO: refactor condition + if (i1 >= len1 || i2 >= len2) { + break; + } else { + E s1 = v1[i1]; + E s2 = v2[i2]; + int result = s1.compareTo(s2); + if (result == 0) { + results.add(s1); + i1 += 1; + i2 += 1; + } else if (result < 0) { + i1 += 1; + } else { + i2 += 1; + } + } + } + } + + private static > void enumerableListDiff(E[] t1, E[] t2, List results) { + int i1, i2; + i1 = i2 = 0; + int len1 = t1.length; + int len2 = t2.length; + while (true) { + if (i1 >= len1) { + break; + } + if (i2 >= len2) { + results.add(t1[i1]); + i1 += 1; + } else { + E s1 = t1[i1]; + E s2 = t2[i2]; + int result = s1.compareTo(s2); + if (result == 0) { + i1 += 1; + i2 += 1; + } else if (result < 0) { + results.add(s1); + i1 += 1; + } else { + i2 += 1; + } + } + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ErrorUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ErrorUtils.java new file mode 100644 index 000000000000..870fae6f4e7a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ErrorUtils.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_ERROR; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_MAPPING; +import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; +import static io.ballerina.runtime.api.types.semtype.Builder.basicSubType; +import static io.ballerina.runtime.api.types.semtype.RecAtom.createDistinctRecAtom; + +/** + * Utility methods for creating error types. + * + * @since 2201.11.0 + */ +public final class ErrorUtils { + + private ErrorUtils() { + } + + public static SemType errorDetail(SemType detail) { + SubTypeData data = Core.subTypeData(detail, BT_MAPPING); + if (data == AllOrNothing.ALL) { + return Builder.getErrorType(); + } else if (data == AllOrNothing.NOTHING) { + return Builder.getNeverType(); + } + + assert data instanceof Bdd; + SubType sd = ((Bdd) data).intersect(Builder.getBddSubtypeRo()); + if (sd.equals(Builder.getBddSubtypeRo())) { + return Builder.getErrorType(); + } + return basicSubType(BT_ERROR, BErrorSubType.createDelegate(sd)); + } + + public static SemType errorDistinct(int distinctId) { + assert distinctId >= 0; + Bdd bdd = bddAtom(createDistinctRecAtom(-distinctId - 1)); + return basicSubType(BT_ERROR, BErrorSubType.createDelegate(bdd)); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FixedLengthArray.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FixedLengthArray.java new file mode 100644 index 000000000000..8284a390a38b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FixedLengthArray.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Represents required member part of a list atom. + * + * @since 2201.11.0 + */ +public final class FixedLengthArray { + + // We have a separate fixedLength so types such as {@code byte[500]} don't need to store 500 elements + // in {@code initial}. + private final SemType[] initial; + private final int fixedLength; + private Integer hashCode; + + private FixedLengthArray(SemType[] initial, int fixedLength) { + for (SemType semType : initial) { + if (semType == null) { + throw new IllegalArgumentException("initial members can't be null"); + } + } + this.initial = initial; + this.fixedLength = fixedLength; + } + + public static FixedLengthArray from(SemType[] initial, int fixedLength) { + return new FixedLengthArray(initial, fixedLength); + } + + private static final FixedLengthArray EMPTY = new FixedLengthArray(new SemType[0], 0); + + static FixedLengthArray normalized(SemType[] initial, int fixedLength) { + if (initial.length < 2) { + return new FixedLengthArray(initial, fixedLength); + } + int i = initial.length - 1; + SemType last = initial[i]; + i -= 1; + while (i >= 0) { + if (last != initial[i]) { + break; + } + i -= 1; + } + int length = Integer.max(1, i + 2); + SemType[] buffer = new SemType[length]; + if (length == 1) { + buffer[0] = last; + } else { + System.arraycopy(initial, 0, buffer, 0, length); + } + return new FixedLengthArray(buffer, fixedLength); + } + + public static FixedLengthArray empty() { + return EMPTY; + } + + public FixedLengthArray shallowCopy() { + // TODO: may be create a copy of write array instead + return new FixedLengthArray(initial.clone(), fixedLength); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof FixedLengthArray that)) { + return false; + } + return fixedLength == that.fixedLength && Objects.deepEquals(initial, that.initial); + } + + @Override + public int hashCode() { + Integer result = hashCode; + if (result == null) { + synchronized (this) { + result = hashCode; + if (result == null) { + hashCode = result = computeHashCode(); + } + } + } + return result; + } + + private int computeHashCode() { + return Objects.hash(Arrays.hashCode(initial), fixedLength); + } + + public int fixedLength() { + return fixedLength; + } + + public SemType[] initial() { + return initial; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionAtomicType.java new file mode 100644 index 000000000000..349ca151de59 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionAtomicType.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.AtomicType; +import io.ballerina.runtime.api.types.semtype.SemType; + +/** + * Represents a function atomic type. + * + * @param paramType function parameters. This is a list type + * @param retType return type + * @param qualifiers function qualifiers. This is a list type + * @since 2201.11.0 + */ +public record FunctionAtomicType(SemType paramType, SemType retType, SemType qualifiers) implements AtomicType { + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionDefinition.java new file mode 100644 index 000000000000..4878cbe1f717 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionDefinition.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Atom; +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Definition; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.RecAtom; +import io.ballerina.runtime.api.types.semtype.SemType; + +/** + * {@code Definition} used to create function subtypes. + * + * @since 2201.11.0 + */ +public class FunctionDefinition extends Definition { + + private volatile RecAtom rec; + private volatile SemType semType; + + @Override + public SemType getSemType(Env env) { + if (this.semType != null) { + return this.semType; + } else { + RecAtom rec = env.recFunctionAtom(); + this.rec = rec; + return this.createSemType(rec); + } + } + + private SemType createSemType(Atom atom) { + BddNode bdd = BddNode.bddAtom(atom); + SemType semType = Builder.basicSubType(BasicTypeCode.BT_FUNCTION, BFunctionSubType.createDelegate(bdd)); + this.semType = semType; + return semType; + } + + public SemType define(Env env, SemType args, SemType ret, FunctionQualifiers qualifiers) { + FunctionAtomicType atomicType = new FunctionAtomicType(args, ret, qualifiers.toSemType(env)); + RecAtom rec = this.rec; + Atom atom; + if (rec != null) { + atom = rec; + env.setRecFunctionAtomType(rec, atomicType); + } else { + atom = env.functionAtom(atomicType); + } + SemType semType = this.createSemType(atom); + notifyContainer(); + return semType; + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionQualifiers.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionQualifiers.java new file mode 100644 index 000000000000..0b90e9bad41d --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionQualifiers.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +/** + * Represents the qualifiers of a function. + * + * @since 2201.11.0 + */ +public final class FunctionQualifiers { + + private static final FunctionQualifiers DEFAULT = new FunctionQualifiers(false, false); + private final boolean isolated; + private final boolean transactional; + private SemType semType; + + private FunctionQualifiers(boolean isolated, boolean transactional) { + this.isolated = isolated; + this.transactional = transactional; + } + + public static FunctionQualifiers create(boolean isolated, boolean transactional) { + if (!isolated && !transactional) { + return DEFAULT; + } + return new FunctionQualifiers(isolated, transactional); + } + + synchronized SemType toSemType(Env env) { + if (semType == null) { + ListDefinition ld = new ListDefinition(); + SemType[] members = { + isolated ? Builder.getBooleanConst(true) : Builder.getBooleanType(), + transactional ? Builder.getBooleanType() : Builder.getBooleanConst(false) + }; + semType = ld.defineListTypeWrapped(env, members, 2, Builder.getNeverType(), + CellAtomicType.CellMutability.CELL_MUT_NONE); + } + return semType; + } + + public boolean isolated() { + return isolated; + } + + public boolean transactional() { + return transactional; + } + + @Override + public String toString() { + return "FunctionQualifiers[" + + "isolated=" + isolated + ", " + + "transactional=" + transactional + ']'; + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FutureUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FutureUtils.java new file mode 100644 index 000000000000..8c927ac0b442 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FutureUtils.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +import static io.ballerina.runtime.api.types.semtype.Core.createBasicSemType; +import static io.ballerina.runtime.api.types.semtype.Core.subTypeData; + +/** + * Utility methods for creating future types. + * + * @since 2201.11.0 + */ +public final class FutureUtils { + + private static final MappingDefinition.Field[] EMPTY_FIELDS = new MappingDefinition.Field[0]; + + private FutureUtils() { + } + + public static SemType futureContaining(Env env, SemType constraint) { + if (constraint == Builder.getValType()) { + return Builder.getFutureType(); + } + MappingDefinition md = new MappingDefinition(); + SemType mappingType = md.defineMappingTypeWrapped(env, EMPTY_FIELDS, constraint, + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + Bdd bdd = (Bdd) subTypeData(mappingType, BasicTypeCode.BT_MAPPING); + return createBasicSemType(BasicTypeCode.BT_FUTURE, bdd); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ImmutableSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ImmutableSemType.java new file mode 100644 index 000000000000..d63513b1b31f --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ImmutableSemType.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; +import io.ballerina.runtime.internal.types.BSemTypeWrapper; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Runtime representation of an immutable semtype. + * + * @since 2201.11.0 + */ +public abstract sealed class ImmutableSemType extends SemType permits BSemTypeWrapper { + + private static final SubType[] EMPTY_SUBTYPE_DATA = new SubType[0]; + + private Integer hashCode; + + ImmutableSemType(int all, int some, SubType[] subTypeData) { + super(all, some, subTypeData); + } + + ImmutableSemType(int all) { + this(all, 0, EMPTY_SUBTYPE_DATA); + } + + protected ImmutableSemType(SemType semType) { + this(semType.all(), semType.some(), semType.subTypeData()); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ImmutableSemType semType)) { + return false; + } + return all() == semType.all() && some() == semType.some() && + Objects.deepEquals(this.subTypeData(), semType.subTypeData()); + } + + @Override + public int hashCode() { + Integer result = hashCode; + if (result == null) { + synchronized (this) { + result = hashCode; + if (result == null) { + hashCode = result = computeHashCode(); + } + } + } + return result; + } + + private int computeHashCode() { + return Objects.hash(all(), some(), Arrays.hashCode(subTypeData())); + } + + @Override + protected void setAll(int all) { + throw new UnsupportedOperationException("Immutable semtypes cannot be modified"); + } + + @Override + protected void setSome(int some, SubType[] subTypeData) { + throw new UnsupportedOperationException("Immutable semtypes cannot be modified"); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListAtomicType.java new file mode 100644 index 000000000000..5631bbc55036 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListAtomicType.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.AtomicType; +import io.ballerina.runtime.api.types.semtype.SemType; + +/** + * Represent list atomic type. + * + * @param members required member types of the list + * @param rest rest of member type of the list + * @since 2201.11.0 + */ +public record ListAtomicType(FixedLengthArray members, SemType rest) implements AtomicType { + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListDefinition.java new file mode 100644 index 000000000000..58bd4674453e --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListDefinition.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Atom; +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Definition; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.RecAtom; +import io.ballerina.runtime.api.types.semtype.SemType; + +import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; +import static io.ballerina.runtime.api.types.semtype.Builder.basicSubType; +import static io.ballerina.runtime.api.types.semtype.Builder.getUndefType; +import static io.ballerina.runtime.api.types.semtype.Core.isNever; +import static io.ballerina.runtime.api.types.semtype.Core.union; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; + +/** + * {@code Definition} used to create a list type. + * + * @since 2201.11.0 + */ +public class ListDefinition extends Definition { + + private volatile RecAtom rec = null; + private volatile SemType semType = null; + + @Override + public SemType getSemType(Env env) { + SemType s = this.semType; + if (s == null) { + RecAtom rec = env.recListAtom(); + this.rec = rec; + return this.createSemType(env, rec); + } + return s; + } + + public SemType defineListTypeWrapped(Env env, SemType[] initial, int fixedLength, SemType rest, + CellAtomicType.CellMutability mut) { + SemType[] initialCells = new SemType[initial.length]; + for (int i = 0; i < initial.length; i++) { + initialCells[i] = Builder.getCellContaining(env, initial[i], mut); + } + SemType restCell = + Builder.getCellContaining(env, union(rest, getUndefType()), isNever(rest) ? CELL_MUT_NONE : mut); + SemType semType = define(env, initialCells, fixedLength, restCell); + notifyContainer(); + return semType; + } + + private SemType define(Env env, SemType[] initial, int fixedLength, SemType rest) { + FixedLengthArray members = FixedLengthArray.normalized(initial, fixedLength); + ListAtomicType atomicType = new ListAtomicType(members, rest); + Atom atom; + RecAtom rec = this.rec; + if (rec != null) { + atom = rec; + env.setRecListAtomType(rec, atomicType); + } else { + atom = env.listAtom(atomicType); + } + return this.createSemType(env, atom); + } + + private SemType createSemType(Env env, Atom atom) { + BddNode bdd = bddAtom(atom); + this.semType = basicSubType(BasicTypeCode.BT_LIST, BListSubType.createDelegate(bdd)); + return this.semType; + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingAtomicType.java new file mode 100644 index 000000000000..9ad1916a6637 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingAtomicType.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.AtomicType; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.FieldPair; +import io.ballerina.runtime.api.types.semtype.FieldPairs; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.ArrayList; +import java.util.Collection; + +import static io.ballerina.runtime.api.types.semtype.Core.cellInner; +import static io.ballerina.runtime.api.types.semtype.Core.intersectCellMemberSemTypes; +import static io.ballerina.runtime.api.types.semtype.Core.isNever; + +/** + * Represent mapping atomic type. + * + * @param names required member names of the mapping + * @param types required member types of the mapping + * @param rest rest of member type of the mapping + * @since 2201.11.0 + */ +public record MappingAtomicType(String[] names, SemType[] types, SemType rest) implements AtomicType { + + public MappingAtomicType { + assert names.length == types.length; + } + + public MappingAtomicType intersectMapping(Env env, MappingAtomicType other) { + int expectedSize = Integer.min(types().length, other.types().length); + Collection names = new ArrayList<>(expectedSize); + Collection types = new ArrayList<>(expectedSize); + for (FieldPair fieldPair : new FieldPairs(this, other)) { + names.add(fieldPair.name()); + SemType t = intersectCellMemberSemTypes(env, fieldPair.type1(), fieldPair.type2()); + if (isNever(cellInner(fieldPair.type1()))) { + return null; + + } + types.add(t); + } + SemType rest = intersectCellMemberSemTypes(env, this.rest(), other.rest()); + return new MappingAtomicType(names.toArray(String[]::new), types.toArray(SemType[]::new), rest); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingDefinition.java new file mode 100644 index 000000000000..5e9ae332e55b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingDefinition.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Atom; +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Definition; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.RecAtom; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.Arrays; +import java.util.Comparator; + +import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; +import static io.ballerina.runtime.api.types.semtype.Builder.basicSubType; +import static io.ballerina.runtime.api.types.semtype.Builder.getUndefType; +import static io.ballerina.runtime.api.types.semtype.Core.isNever; +import static io.ballerina.runtime.api.types.semtype.Core.union; + +/** + * {@code Definition} used to create a mapping type. + * + * @since 2201.11.0 + */ +public class MappingDefinition extends Definition { + + private volatile RecAtom rec = null; + private volatile SemType semType = null; + + @Override + public SemType getSemType(Env env) { + SemType s = this.semType; + if (s == null) { + RecAtom rec = env.recMappingAtom(); + this.rec = rec; + return this.createSemType(env, rec); + } else { + return s; + } + } + + private SemType createSemType(Env env, Atom atom) { + BddNode bdd = bddAtom(atom); + this.semType = basicSubType(BasicTypeCode.BT_MAPPING, BMappingSubType.createDelegate(bdd)); + return this.semType; + } + + public SemType defineMappingTypeWrapped(Env env, Field[] fields, SemType rest, CellAtomicType.CellMutability mut) { + assert rest != null; + BCellField[] cellFields = new BCellField[fields.length]; + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + BCellField cellField = BCellField.from(env, field, mut); + cellFields[i] = cellField; + } + SemType restCell = Builder.getCellContaining(env, union(rest, getUndefType()), + isNever(rest) ? CellAtomicType.CellMutability.CELL_MUT_NONE : mut); + SemType semType = define(env, cellFields, restCell); + notifyContainer(); + return semType; + } + + SemType define(Env env, BCellField[] cellFields, SemType rest) { + String[] names = new String[cellFields.length]; + SemType[] types = new SemType[cellFields.length]; + sortAndSplitFields(cellFields, names, types); + MappingAtomicType atomicType = new MappingAtomicType(names, types, rest); + Atom atom; + RecAtom rec = this.rec; + if (rec != null) { + atom = rec; + env.setRecMappingAtomType(rec, atomicType); + } else { + atom = env.mappingAtom(atomicType); + } + return this.createSemType(env, atom); + } + + private void sortAndSplitFields(BCellField[] fields, String[] names, SemType[] types) { + assert fields.length == names.length && fields.length == types.length; + Arrays.sort(fields, Comparator.comparing((field) -> field.name)); + for (int i = 0; i < fields.length; i++) { + names[i] = fields[i].name; + types[i] = fields[i].type; + } + } + + public record Field(String name, SemType ty, boolean readonly, boolean optional) { + + } + + record BCellField(String name, SemType type) { + + static BCellField from(Env env, Field field, CellAtomicType.CellMutability mut) { + SemType type = field.ty; + SemType cellType = Builder.getCellContaining(env, field.optional ? union(type, getUndefType()) : type, + field.readonly ? CellAtomicType.CellMutability.CELL_MUT_NONE : mut); + BCellField cellField = new BCellField(field.name, cellType); + return cellField; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Member.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Member.java new file mode 100644 index 000000000000..19a6938b9d68 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Member.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; + +import static io.ballerina.runtime.api.types.semtype.Builder.getStringConst; + +/** + * Represents a member of an object type. + * + * @param name the name of the member. For methods, this is the method name, and for fields, this is the field + * name. + * @param valueTy the type of the member + * @param kind the kind of the member (either {@link Kind#Field} or {@link Kind#Method}) + * @param visibility the visibility of the member (either {@link Visibility#Public} or {@link Visibility#Private}) + * @param immutable whether the member is immutable + * @since 2201.11.0 + */ +public record Member(String name, SemType valueTy, Kind kind, Visibility visibility, boolean immutable) { + + public enum Kind { + Field, + Method; + + private static final MappingDefinition.Field FIELD = + new MappingDefinition.Field("kind", getStringConst("field"), true, false); + private static final MappingDefinition.Field METHOD = + new MappingDefinition.Field("kind", getStringConst("method"), true, false); + + public MappingDefinition.Field field() { + return switch (this) { + case Field -> FIELD; + case Method -> METHOD; + }; + } + } + + public enum Visibility { + Public, + Private; + + private static final SemType PUBLIC_TAG = getStringConst("public"); + private static final MappingDefinition.Field PUBLIC = + new MappingDefinition.Field("visibility", PUBLIC_TAG, true, false); + private static final SemType PRIVATE_TAG = getStringConst("private"); + private static final MappingDefinition.Field PRIVATE = + new MappingDefinition.Field("visibility", PRIVATE_TAG, true, false); + static final MappingDefinition.Field ALL = + new MappingDefinition.Field("visibility", Core.union(PRIVATE_TAG, PUBLIC_TAG), true, false); + + public MappingDefinition.Field field() { + return switch (this) { + case Public -> PUBLIC; + case Private -> PRIVATE; + }; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemType.java new file mode 100644 index 000000000000..2059cb237766 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemType.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.BType; + +/** + * Represents a mutable semantic type. Note in the current implementation we assume after type checking this is to be + * immutable. + * + * @since 2201.11.0 + */ +public sealed interface MutableSemType permits BType { + + SemType createSemType(); + + void resetSemType(); + + void updateInnerSemTypeIfNeeded(); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectDefinition.java new file mode 100644 index 000000000000..f843d04d43b2 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectDefinition.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.Definition; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.List; +import java.util.stream.Stream; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_OBJECT; +import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; +import static io.ballerina.runtime.api.types.semtype.Core.createBasicSemType; +import static io.ballerina.runtime.api.types.semtype.Core.union; +import static io.ballerina.runtime.api.types.semtype.RecAtom.createDistinctRecAtom; + +/** + * {@code Definition} used to create an object type. + *

+ * Each object type is represented as mapping type (with its basic type set to object) as fallows + * {@code { "$qualifiers": { boolean isolated, "client"|"service" network }, [field_name]: { "field"|"method" kind, + * "public"|"private" visibility, VAL value; } ...{ "field" kind, "public"|"private" visibility, VAL value; } | { + * "method" kind, "public"|"private" visibility, FUNCTION value; } }} + * + * @since 2201.11.0 + */ +public class ObjectDefinition extends Definition { + + private final MappingDefinition mappingDefinition = new MappingDefinition(); + + @Override + public SemType getSemType(Env env) { + return objectContaining(mappingDefinition.getSemType(env)); + } + + public SemType define(Env env, ObjectQualifiers qualifiers, List members, + CellAtomicType.CellMutability mut) { + Stream memberStream = + members.stream().map(member -> memberField(env, member, qualifiers.readonly())); + Stream qualifierStream = Stream.of(qualifiers.field(env)); + SemType mappingType = mappingDefinition.define(env, Stream.concat(memberStream, qualifierStream) + .map(field -> MappingDefinition.BCellField.from(env, field, mut)) + .toArray(MappingDefinition.BCellField[]::new), restMemberType(env, mut, qualifiers.readonly())); + SemType semType = objectContaining(mappingType); + notifyContainer(); + return semType; + } + + private SemType objectContaining(SemType mappingType) { + Bdd mappingSubTypeData = (Bdd) Core.subTypeData(mappingType, BasicTypeCode.BT_MAPPING); + return createBasicSemType(BT_OBJECT, mappingSubTypeData); + } + + private SemType restMemberType(Env env, CellAtomicType.CellMutability mut, boolean readonly) { + MappingDefinition fieldDefn = new MappingDefinition(); + SemType fieldMemberType = fieldDefn.defineMappingTypeWrapped(env, new MappingDefinition.Field[]{ + new MappingDefinition.Field( + "value", + readonly ? Builder.getReadonlyType() : Builder.getValType(), + readonly, + false), + Member.Kind.Field.field(), Member.Visibility.ALL}, Builder.getNeverType(), + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + MappingDefinition methodDefn = new MappingDefinition(); + + SemType methodMemberType = methodDefn.defineMappingTypeWrapped(env, new MappingDefinition.Field[]{ + new MappingDefinition.Field("value", Builder.getFunctionType(), true, false), + Member.Kind.Method.field(), Member.Visibility.ALL}, Builder.getNeverType(), + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + return Builder.getCellContaining(env, union(fieldMemberType, methodMemberType), mut); + } + + private static MappingDefinition.Field memberField(Env env, Member member, boolean immutableObject) { + MappingDefinition md = new MappingDefinition(); + SemType semtype = md.defineMappingTypeWrapped(env, new MappingDefinition.Field[]{ + new MappingDefinition.Field("value", member.valueTy(), member.immutable(), false), + member.kind().field(), member.visibility().field()}, Builder.getNeverType(), + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + return new MappingDefinition.Field(member.name(), semtype, immutableObject | member.immutable(), false); + } + + public static SemType distinct(int distinctId) { + assert distinctId >= 0; + BddNode bdd = bddAtom(createDistinctRecAtom(-distinctId - 1)); + return createBasicSemType(BT_OBJECT, bdd); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectQualifiers.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectQualifiers.java new file mode 100644 index 000000000000..33d52a1816a9 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectQualifiers.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +import static io.ballerina.runtime.api.types.semtype.Builder.getBooleanConst; +import static io.ballerina.runtime.api.types.semtype.Builder.getStringConst; +import static io.ballerina.runtime.api.types.semtype.Core.union; + +/** + * Represents the qualifiers of an object type. + * + * @param isolated whether the object is isolated + * @param readonly whether the object is readonly + * @param networkQualifier the network qualifier of the object (either {@link NetworkQualifier#Client}, + * {@link NetworkQualifier#Service}, or {@link NetworkQualifier#None}) + * @since 2201.11.0 + */ +public record ObjectQualifiers(boolean isolated, boolean readonly, NetworkQualifier networkQualifier) { + + public MappingDefinition.Field field(Env env) { + MappingDefinition md = new MappingDefinition(); + MappingDefinition.Field isolatedField = + new MappingDefinition.Field("isolated", isolated ? getBooleanConst(true) : Builder.getBooleanType(), + true, false); + MappingDefinition.Field networkField = networkQualifier.field(); + SemType ty = md.defineMappingTypeWrapped(env, new MappingDefinition.Field[]{isolatedField, networkField}, + Builder.getNeverType(), CellAtomicType.CellMutability.CELL_MUT_NONE); + return new MappingDefinition.Field("$qualifiers", ty, true, false); + } + + public enum NetworkQualifier { + Client, + Service, + None; + + private static final SemType CLIENT_TAG = getStringConst("client"); + private static final MappingDefinition.Field CLIENT = + new MappingDefinition.Field("network", CLIENT_TAG, true, false); + + private static final SemType SERVICE_TAG = getStringConst("service"); + private static final MappingDefinition.Field SERVICE = + new MappingDefinition.Field("network", SERVICE_TAG, true, false); + + // Object can't be both client and service, which is enforced by the enum. We are using a union here so that + // if this is none it matches both + private static final MappingDefinition.Field NONE = + new MappingDefinition.Field("network", union(CLIENT_TAG, SERVICE_TAG), true, false); + + private MappingDefinition.Field field() { + return switch (this) { + case Client -> CLIENT; + case Service -> SERVICE; + case None -> NONE; + }; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/RegexUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/RegexUtils.java new file mode 100644 index 000000000000..39c4264ba8cc --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/RegexUtils.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.SemType; + +/** + * Utility class for creating semtypes of regex tagged basic type. + * + * @since 2201.11.0 + */ +public final class RegexUtils { + + private RegexUtils() { + + } + + public static SemType regexShape(String value) { + SemType stringSubtype = Builder.getStringConst(value); + BStringSubType stringSubType = (BStringSubType) stringSubtype.subTypeByCode(BasicTypeCode.CODE_STRING); + return Builder.basicSubType(BasicTypeCode.BT_REGEXP, stringSubType); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SemTypeHelper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SemTypeHelper.java new file mode 100644 index 000000000000..7bad30e0127b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SemTypeHelper.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.SemType; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_BOOLEAN; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_CELL; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_DECIMAL; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_ERROR; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_FLOAT; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_FUNCTION; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_FUTURE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_HANDLE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_INT; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_LIST; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_MAPPING; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_NIL; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_OBJECT; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_REGEXP; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_STREAM; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_STRING; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_TABLE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_TYPEDESC; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_UNDEF; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_XML; + +/** + * Collection of utility function on {@code SemType}. + * + * @since 2201.11.0 + */ +public final class SemTypeHelper { + + private SemTypeHelper() { + } + + public static String stringRepr(SemType ty) { + return "all[" + bitSetRepr(ty.all()) + "] some [" + bitSetRepr(ty.some()) + "]"; + } + + public static String bitSetRepr(int bits) { + StringBuilder sb = new StringBuilder(); + appendBitSetRepr(sb, bits, CODE_NIL, "NIL"); + appendBitSetRepr(sb, bits, CODE_BOOLEAN, "BOOLEAN"); + appendBitSetRepr(sb, bits, CODE_INT, "INT"); + appendBitSetRepr(sb, bits, CODE_FLOAT, "FLOAT"); + appendBitSetRepr(sb, bits, CODE_DECIMAL, "DECIMAL"); + appendBitSetRepr(sb, bits, CODE_STRING, "STRING"); + appendBitSetRepr(sb, bits, CODE_ERROR, "ERROR"); + appendBitSetRepr(sb, bits, CODE_TYPEDESC, "TYPE_DESC"); + appendBitSetRepr(sb, bits, CODE_HANDLE, "HANDLE"); + appendBitSetRepr(sb, bits, CODE_REGEXP, "REGEXP"); + appendBitSetRepr(sb, bits, CODE_FUNCTION, "FUNCTION"); + appendBitSetRepr(sb, bits, CODE_FUTURE, "FUTURE"); + appendBitSetRepr(sb, bits, CODE_STREAM, "STREAM"); + appendBitSetRepr(sb, bits, CODE_LIST, "LIST"); + appendBitSetRepr(sb, bits, CODE_MAPPING, "MAPPING"); + appendBitSetRepr(sb, bits, CODE_TABLE, "TABLE"); + appendBitSetRepr(sb, bits, CODE_XML, "XML"); + appendBitSetRepr(sb, bits, CODE_OBJECT, "OBJECT"); + appendBitSetRepr(sb, bits, CODE_CELL, "CELL"); + appendBitSetRepr(sb, bits, CODE_UNDEF, "UNDEF"); + return sb.toString(); + } + + private static void appendBitSetRepr(StringBuilder sb, int bits, int index, String name) { + int mask = 1 << index; + if ((bits & mask) != 0) { + if (!sb.isEmpty()) { + sb.append(", "); + } + sb.append(name).append(" "); + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/StreamDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/StreamDefinition.java new file mode 100644 index 000000000000..2023672f5848 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/StreamDefinition.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Definition; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +import static io.ballerina.runtime.api.types.semtype.Core.createBasicSemType; +import static io.ballerina.runtime.api.types.semtype.Core.subTypeData; + +/** + * {@code Definition} used to create a stream type. Stream is represented as a + * tuple of {@code [valueType, completionType]} + * + * @since 2201.11.0 + */ +public class StreamDefinition extends Definition { + + private final ListDefinition listDefinition = new ListDefinition(); + + @Override + public SemType getSemType(Env env) { + return streamContaining(listDefinition.getSemType(env)); + } + + public SemType define(Env env, SemType valueType, SemType completionType) { + if (Builder.getValType() == completionType && Builder.getValType() == valueType) { + return Builder.getStreamType(); + } + SemType tuple = listDefinition.defineListTypeWrapped(env, new SemType[]{valueType, completionType}, 2, + Builder.getNeverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); + SemType semType = streamContaining(tuple); + notifyContainer(); + return semType; + } + + private SemType streamContaining(SemType tupleType) { + SubTypeData bdd = subTypeData(tupleType, BasicTypeCode.BT_LIST); + assert bdd instanceof Bdd; + return createBasicSemType(BasicTypeCode.BT_STREAM, (Bdd) bdd); + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/Atom.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java similarity index 67% rename from semtypes/src/main/java/io/ballerina/semtype/Atom.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java index 1e9461723580..a418583d10af 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/Atom.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -15,12 +15,14 @@ * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; + +package io.ballerina.runtime.internal.types.semtype; /** - * Represent the BDD atom. + * Marker interface for SubTypeData. * - * @since 2.0.0 + * @since 2201.11.0 */ -public interface Atom { +public interface SubTypeData { + } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePair.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePair.java new file mode 100644 index 000000000000..d56e4bd33899 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePair.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.SubType; + +/** + * Represents the corresponding subtype pairs of two semtypes. + * + * @param typeCode the type code of the semtype + * @param subType1 the first subtype. This will if the first semtype don't have + * this subtype + * @param subType2 the second subtype. This will if the second semtype don't + * have this subtype + * @since 2201.11.0 + */ +public record SubtypePair(int typeCode, SubType subType1, SubType subType2) { + + public SubtypePair { + if (subType1 == null && subType2 == null) { + throw new IllegalArgumentException("both subType1 and subType2 cannot be null"); + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePairIterator.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePairIterator.java new file mode 100644 index 000000000000..f061b012896a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePairIterator.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Iterator; + +/** + * Iteration implementation of `SubtypePairIterator`. + * + * @since 2201.11.0 + */ +final class SubtypePairIterator implements Iterator { + + // NOTE: this needs to be very efficient since pretty much all type operations depends on it + private int index = 0; + private static final int maxIndex = BasicTypeCode.CODE_UNDEF + 1; + private final int some; + private final SemType t1; + private final SemType t2; + + SubtypePairIterator(SemType t1, SemType t2, int some) { + this.some = some; + this.t1 = t1; + this.t2 = t2; + incrementIndex(); + } + + @Override + public boolean hasNext() { + return index < maxIndex; + } + + private void incrementIndex() { + int rest = some >> index; + int offset = Integer.numberOfTrailingZeros(rest); + index += offset; + } + + @Override + public SubtypePair next() { + SubType subType1 = t1.subTypeByCode(index); + SubType subType2 = t2.subTypeByCode(index); + assert (!(subType1 == null && subType2 == null)); + int typeCode = index; + index++; + incrementIndex(); + return new SubtypePair(typeCode, subType1, subType2); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePairs.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePairs.java new file mode 100644 index 000000000000..95844611c535 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePairs.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.Iterator; + +/** + * Implements the iterable for `SubtypePairIteratorImpl`. + * + * @since 2201.11.0 + */ +public class SubtypePairs implements Iterable { + + private final SemType t1; + private final SemType t2; + private final int bits; + + public SubtypePairs(SemType t1, SemType t2, int bits) { + this.t1 = t1; + this.t2 = t2; + this.bits = bits; + } + + @Override + public Iterator iterator() { + return new SubtypePairIterator(t1, t2, bits); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TableUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TableUtils.java new file mode 100644 index 000000000000..158259252366 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TableUtils.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.Optional; + +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_UNLIMITED; + +/** + * Utility class for creating semtypes of table type. + * + * @since 2201.11.0 + */ +public final class TableUtils { + + private static final SemType[] EMPTY_SEMTYPE_ARR = new SemType[0]; + + private TableUtils() { + } + + public static SemType acceptedTypeContainingKeySpecifier(Context cx, SemType tableConstraint, String[] fieldNames) { + return tableContainingKeySpecifierInner(fieldNames, cx, tableConstraint, CELL_MUT_UNLIMITED); + } + + public static SemType tableContainingKeySpecifier(Context cx, SemType tableConstraint, String[] fieldNames) { + return tableContainingKeySpecifierInner(fieldNames, cx, tableConstraint, CELL_MUT_LIMITED); + } + + private static SemType tableContainingKeySpecifierInner(String[] fieldNames, Context cx, SemType tableConstraint, + CellAtomicType.CellMutability cellMutLimited) { + SemType[] fieldNameSingletons = new SemType[fieldNames.length]; + SemType[] fieldTypes = new SemType[fieldNames.length]; + for (int i = 0; i < fieldNames.length; i++) { + SemType key = Builder.getStringConst(fieldNames[i]); + fieldNameSingletons[i] = key; + fieldTypes[i] = Core.mappingMemberTypeInnerVal(cx, tableConstraint, key); + } + + SemType normalizedKs = + new ListDefinition().defineListTypeWrapped(cx.env, fieldNameSingletons, fieldNameSingletons.length, + Builder.getNeverType(), CELL_MUT_NONE); + + SemType normalizedKc = fieldNames.length > 1 ? new ListDefinition().defineListTypeWrapped(cx.env, fieldTypes, + fieldTypes.length, Builder.getNeverType(), CELL_MUT_NONE) : fieldTypes[0]; + + return tableContaining(cx.env, tableConstraint, normalizedKc, normalizedKs, cellMutLimited); + } + + public static SemType acceptedTypeContainingKeyConstraint(Context cx, SemType tableConstraint, + SemType keyConstraint) { + return tableContainingKeyConstraintInner(cx, tableConstraint, keyConstraint, CELL_MUT_UNLIMITED); + } + + public static SemType tableContainingKeyConstraint(Context cx, SemType tableConstraint, SemType keyConstraint) { + return tableContainingKeyConstraintInner(cx, tableConstraint, keyConstraint, CELL_MUT_LIMITED); + } + + private static SemType tableContainingKeyConstraintInner(Context cx, SemType tableConstraint, SemType keyConstraint, + CellAtomicType.CellMutability mut) { + Optional lat = Core.listAtomicType(cx, keyConstraint); + SemType normalizedKc = lat.map(atom -> { + FixedLengthArray member = atom.members(); + return switch (member.fixedLength()) { + case 0 -> Builder.getValType(); + case 1 -> Core.cellAtomicType(member.initial()[0]).orElseThrow().ty(); + default -> keyConstraint; + }; + }).orElse(keyConstraint); + return tableContaining(cx.env, tableConstraint, normalizedKc, Builder.getValType(), mut); + } + + public static SemType tableContaining(Env env, SemType tableConstraint) { + return tableContaining(env, tableConstraint, CELL_MUT_LIMITED); + } + + public static SemType acceptedType(Env env, SemType tableConstraint) { + return tableContaining(env, tableConstraint, CELL_MUT_UNLIMITED); + } + + private static SemType tableContaining(Env env, SemType tableConstraint, CellAtomicType.CellMutability mut) { + return tableContaining(env, tableConstraint, Builder.getValType(), Builder.getValType(), mut); + } + + private static SemType tableContaining(Env env, SemType tableConstraint, SemType normalizedKc, SemType normalizedKs, + CellAtomicType.CellMutability mut) { + tableConstraint = Core.intersect(tableConstraint, Builder.getMappingType()); + ListDefinition typeParamArrDef = new ListDefinition(); + SemType typeParamArray = typeParamArrDef.defineListTypeWrapped(env, EMPTY_SEMTYPE_ARR, 0, tableConstraint, mut); + + ListDefinition listDef = new ListDefinition(); + SemType tupleType = + listDef.defineListTypeWrapped(env, new SemType[]{typeParamArray, normalizedKc, normalizedKs}, 3, + Builder.getNeverType(), + CELL_MUT_LIMITED); + Bdd bdd = (Bdd) Core.subTypeData(tupleType, BasicTypeCode.BT_LIST); + return Core.createBasicSemType(BasicTypeCode.BT_TABLE, bdd); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TypedescUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TypedescUtils.java new file mode 100644 index 000000000000..4bf7b6a78406 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TypedescUtils.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +import static io.ballerina.runtime.api.types.semtype.Core.createBasicSemType; +import static io.ballerina.runtime.api.types.semtype.Core.subTypeData; + +/** + * Utility class for creating semtypes of typedesc type. + * + * @since 2201.11.0 + */ +public final class TypedescUtils { + + private static final MappingDefinition.Field[] EMPTY_FIELDS = new MappingDefinition.Field[0]; + + private TypedescUtils() { + + } + + public static SemType typedescContaining(Env env, SemType constraint) { + if (constraint == Builder.getValType()) { + return Builder.getTypeDescType(); + } + MappingDefinition md = new MappingDefinition(); + SemType mappingType = md.defineMappingTypeWrapped(env, EMPTY_FIELDS, constraint, + CellAtomicType.CellMutability.CELL_MUT_NONE); + Bdd bdd = (Bdd) subTypeData(mappingType, BasicTypeCode.BT_MAPPING); + return createBasicSemType(BasicTypeCode.BT_TYPEDESC, bdd); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/XmlUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/XmlUtils.java new file mode 100644 index 000000000000..f05813ead001 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/XmlUtils.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.BddAllOrNothing; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.RecAtom; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; + +/** + * Utility class for creating semtypes of XML type. + * + * @since 2201.11.0 + */ +public final class XmlUtils { + + public static final int XML_PRIMITIVE_NEVER = 1; + public static final int XML_PRIMITIVE_TEXT = 1 << 1; + public static final int XML_PRIMITIVE_ELEMENT_RO = 1 << 2; + public static final int XML_PRIMITIVE_PI_RO = 1 << 3; + public static final int XML_PRIMITIVE_COMMENT_RO = 1 << 4; + public static final int XML_PRIMITIVE_ELEMENT_RW = 1 << 5; + public static final int XML_PRIMITIVE_PI_RW = 1 << 6; + public static final int XML_PRIMITIVE_COMMENT_RW = 1 << 7; + + public static final int XML_PRIMITIVE_RO_SINGLETON = XML_PRIMITIVE_TEXT | XML_PRIMITIVE_ELEMENT_RO + | XML_PRIMITIVE_PI_RO | XML_PRIMITIVE_COMMENT_RO; + public static final int XML_PRIMITIVE_RO_MASK = XML_PRIMITIVE_NEVER | XML_PRIMITIVE_RO_SINGLETON; + public static final int XML_PRIMITIVE_RW_MASK = XML_PRIMITIVE_ELEMENT_RW | XML_PRIMITIVE_PI_RW + | XML_PRIMITIVE_COMMENT_RW; + public static final int XML_PRIMITIVE_SINGLETON = XML_PRIMITIVE_RO_SINGLETON | XML_PRIMITIVE_RW_MASK; + public static final int XML_PRIMITIVE_ALL_MASK = XML_PRIMITIVE_RO_MASK | XML_PRIMITIVE_RW_MASK; + + public static final SubTypeData XML_SUBTYPE_TOP = from(XML_PRIMITIVE_ALL_MASK, BddAllOrNothing.ALL); + public static final SubType XML_SUBTYPE_RO = + BXmlSubType.createDelegate(XML_PRIMITIVE_RO_MASK, + bddAtom(RecAtom.createRecAtom(XML_PRIMITIVE_RO_SINGLETON))); + + private XmlUtils() { + } + + public static SemType xmlSingleton(int primitive) { + if (XmlSingletonCache.isCached(primitive)) { + return XmlSingletonCache.get(primitive); + } + return createXmlSingleton(primitive); + } + + private static SemType createXmlSingleton(int primitive) { + return createXmlSemtype(createXmlSubtype(primitive, BddAllOrNothing.NOTHING)); + } + + private static SemType createXmlSemtype(SubTypeData xmlSubtype) { + if (xmlSubtype instanceof AllOrNothing) { + return xmlSubtype == AllOrNothing.ALL ? Builder.getXmlType() : Builder.getNeverType(); + } + assert xmlSubtype instanceof BXmlSubType : "subtype must be wrapped by delegate by now"; + return Builder.basicSubType(BasicTypeCode.BT_XML, (SubType) xmlSubtype); + } + + private static SubTypeData createXmlSubtype(int primitives, Bdd sequence) { + int p = primitives & XML_PRIMITIVE_ALL_MASK; + if (primitiveShouldIncludeNever(p)) { + p |= XML_PRIMITIVE_NEVER; + } + if (sequence == BddAllOrNothing.ALL && p == XML_PRIMITIVE_ALL_MASK) { + return AllOrNothing.ALL; + } else if (sequence == BddAllOrNothing.NOTHING && p == 0) { + return AllOrNothing.NOTHING; + } + return from(p, sequence); + } + + private static boolean primitiveShouldIncludeNever(int primitive) { + return (primitive & XML_PRIMITIVE_TEXT) == XML_PRIMITIVE_TEXT; + } + + public static SubTypeData from(int primitives, Bdd sequence) { + return BXmlSubType.createDelegate(primitives, sequence); + } + + public static SemType xmlSequence(SemType constituentType) { + assert Core.isSubtypeSimple(constituentType, Builder.getXmlType()) : + "It is a precondition that constituentType is a subtype of XML"; + if (Core.isNever(constituentType)) { + return xmlSequence(xmlSingleton(XML_PRIMITIVE_NEVER)); + } else if (constituentType.some() == 0) { + assert Core.isNever(Core.diff(Builder.getXmlType(), constituentType)); + return constituentType; + } else { + SubType xmlSubType = + Core.getComplexSubtypeData(constituentType, BasicTypeCode.BT_XML); + if (!xmlSubType.isAll() && !xmlSubType.isNothing()) { + xmlSubType = makeXmlSequence((BXmlSubType) xmlSubType); + } + return createXmlSemtype((SubTypeData) xmlSubType); + } + } + + private static SubType makeXmlSequence(BXmlSubType xmlSubType) { + int primitives = xmlSubType.primitives() | XML_PRIMITIVE_NEVER; + int atom = xmlSubType.primitives() & XML_PRIMITIVE_SINGLETON; + Bdd sequence = (Bdd) xmlSubType.bdd().union(bddAtom(RecAtom.createRecAtom(atom))); + return BXmlSubType.createDelegate(primitives, sequence); + } + + private static final class XmlSingletonCache { + + private static final Map CACHE = new ConcurrentHashMap<>(); + + private static boolean isCached(int primitive) { + return Integer.bitCount(primitive) < 3; + } + + private static SemType get(int primitive) { + return CACHE.computeIfAbsent(primitive, XmlUtils::createXmlSingleton); + } + + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/MapUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/MapUtils.java index 2ae27c4cd2cb..62ac70673626 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/MapUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/MapUtils.java @@ -20,22 +20,22 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.runtime.api.types.Field; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.errors.ErrorCodes; import io.ballerina.runtime.internal.errors.ErrorHelper; -import io.ballerina.runtime.internal.types.BMapType; import io.ballerina.runtime.internal.types.BRecordType; import io.ballerina.runtime.internal.types.BTypeReferenceType; -import io.ballerina.runtime.internal.types.BUnionType; import io.ballerina.runtime.internal.values.MapValue; -import java.util.List; - import static io.ballerina.runtime.api.constants.RuntimeConstants.MAP_LANG_LIB; import static io.ballerina.runtime.internal.errors.ErrorReasons.INHERENT_TYPE_VIOLATION_ERROR_IDENTIFIER; import static io.ballerina.runtime.internal.errors.ErrorReasons.MAP_KEY_NOT_FOUND_ERROR; @@ -56,7 +56,7 @@ public static void handleMapStore(MapValue mapValue, BString fi updateMapValue(TypeUtils.getImpliedType(mapValue.getType()), mapValue, fieldName, value); } - public static void handleInherentTypeViolatingMapUpdate(Object value, BMapType mapType) { + public static void handleInherentTypeViolatingMapUpdate(Object value, MapType mapType) { if (TypeChecker.checkIsType(value, mapType.getConstrainedType())) { return; } @@ -118,17 +118,7 @@ public static boolean handleInherentTypeViolatingRecordUpdate( } private static boolean containsNilType(Type type) { - type = TypeUtils.getImpliedType(type); - int tag = type.getTag(); - if (tag == TypeTags.UNION_TAG) { - List memTypes = ((BUnionType) type).getMemberTypes(); - for (Type memType : memTypes) { - if (containsNilType(memType)) { - return true; - } - } - } - return tag == TypeTags.NULL_TAG; + return Core.containsBasicType(SemType.tryInto(type), Builder.getNilType()); } public static BError createOpNotSupportedError(Type type, String op) { @@ -152,7 +142,7 @@ private static void updateMapValue(Type mapType, MapValue mapVa switch (mapType.getTag()) { case TypeTags.MAP_TAG: - handleInherentTypeViolatingMapUpdate(value, (BMapType) mapType); + handleInherentTypeViolatingMapUpdate(value, (MapType) mapType); mapValue.put(fieldName, value); return; case TypeTags.RECORD_TYPE_TAG: diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueComparisonUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueComparisonUtils.java index 48f33fda34ab..8bb1312eb5d2 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueComparisonUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueComparisonUtils.java @@ -219,14 +219,6 @@ public static int compareValues(Object lhsValue, Object rhsValue, String directi return codePointCompare(lhsValue.toString(), rhsValue.toString()); } - if (TypeTags.isIntegerTypeTag(lhsTypeTag) && TypeTags.isIntegerTypeTag(rhsTypeTag)) { - return Long.compare((long) lhsValue, (long) rhsValue); - } else if (TypeTags.isIntegerTypeTag(lhsTypeTag) && TypeTags.BYTE_TAG == rhsTypeTag) { - return Long.compare((long) lhsValue, (int) rhsValue); - } else if (TypeTags.BYTE_TAG == lhsTypeTag && TypeTags.isIntegerTypeTag(rhsTypeTag)) { - return Long.compare((int) lhsValue, (long) rhsValue); - } - if (lhsTypeTag == rhsTypeTag) { switch (lhsTypeTag) { case TypeTags.BOOLEAN_TAG: @@ -246,6 +238,14 @@ public static int compareValues(Object lhsValue, Object rhsValue, String directi } } + if (TypeTags.BYTE_TAG == lhsTypeTag && TypeTags.isIntegerTypeTag(rhsTypeTag)) { + return Long.compare((int) lhsValue, (long) rhsValue); + } else if (TypeTags.isIntegerTypeTag(lhsTypeTag) && TypeTags.BYTE_TAG == rhsTypeTag) { + return Long.compare((long) lhsValue, (int) rhsValue); + } else if (TypeTags.isIntegerTypeTag(lhsTypeTag) && TypeTags.isIntegerTypeTag(rhsTypeTag)) { + return Long.compare((long) lhsValue, (long) rhsValue); + } + if ((lhsTypeTag == TypeTags.ARRAY_TAG || lhsTypeTag == TypeTags.TUPLE_TAG) && (rhsTypeTag == TypeTags.ARRAY_TAG || rhsTypeTag == TypeTags.TUPLE_TAG)) { return compareArrayValues(lhsValue, rhsValue, lhsTypeTag, rhsTypeTag, direction); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueConverter.java index ec772ed9b133..b610c8a18316 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueConverter.java @@ -31,6 +31,10 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.TypedescType; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; @@ -57,6 +61,7 @@ import io.ballerina.runtime.internal.values.ReadOnlyUtils; import io.ballerina.runtime.internal.values.TableValueImpl; import io.ballerina.runtime.internal.values.TupleValueImpl; +import io.ballerina.runtime.internal.values.XmlSequence; import java.util.ArrayList; import java.util.HashMap; @@ -146,6 +151,7 @@ private static Object convert(Object value, Type targetType, Set if (matchingType.isReadOnly()) { newValue = CloneUtils.cloneReadOnly(newValue); } + newValue = xmlSequenceHack(newValue, matchingType); break; } @@ -171,6 +177,27 @@ private static Object convert(Object value, Type targetType, Set return newValue; } + // This is a hack to workaround #43231 + private static Object xmlSequenceHack(Object value, Type targetType) { + if (!(value instanceof XmlSequence xmlSequence)) { + return value; + } + Context cx = TypeChecker.context(); + List list = new ArrayList<>(); + SemType targetSemType = SemType.tryInto(targetType); + for (BXml child : xmlSequence.getChildrenList()) { + SemType childType = SemType.tryInto(child.getType()); + boolean isReadonly = + Core.isSubType(cx, Core.intersect(childType, targetSemType), Builder.getReadonlyType()); + if (isReadonly) { + list.add((BXml) CloneUtils.cloneReadOnly(child)); + } else { + list.add(child); + } + } + return new XmlSequence(list); + } + private static Type getTargetFromTypeDesc(Type targetType) { Type referredType = TypeUtils.getImpliedType(targetType); if (referredType.getTag() == TypeTags.TYPEDESC_TAG) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractArrayValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractArrayValue.java index 4768e51ecd52..a129148baf23 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractArrayValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractArrayValue.java @@ -20,12 +20,17 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.internal.errors.ErrorHelper; import io.ballerina.runtime.internal.json.JsonGenerator; import io.ballerina.runtime.internal.types.BTupleType; import io.ballerina.runtime.internal.types.BUnionType; +import io.ballerina.runtime.internal.types.TypeWithShape; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; import io.ballerina.runtime.internal.utils.IteratorUtils; import java.io.ByteArrayOutputStream; @@ -33,6 +38,7 @@ import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.Map; +import java.util.Optional; import java.util.Set; import static io.ballerina.runtime.api.constants.RuntimeConstants.ARRAY_LANG_LIB; @@ -51,9 +57,10 @@ * * @since 1.1.0 */ -public abstract class AbstractArrayValue implements ArrayValue { +public abstract class AbstractArrayValue implements ArrayValue, RecursiveValue { static final int SYSTEM_ARRAY_MAX = Integer.MAX_VALUE - 8; + private final ThreadLocal readonlyAttachedDefinition = new ThreadLocal<>(); /** * The maximum size of arrays to allocate. @@ -77,6 +84,9 @@ public void append(Object value) { @Override public boolean equals(Object o, Set visitedValues) { + if (!(o instanceof ArrayValue arrayValue)) { + return false; + } ValuePair compValuePair = new ValuePair(this, o); for (ValuePair valuePair : visitedValues) { if (valuePair.equals(compValuePair)) { @@ -85,7 +95,6 @@ public boolean equals(Object o, Set visitedValues) { } visitedValues.add(compValuePair); - ArrayValue arrayValue = (ArrayValue) o; if (arrayValue.size() != this.size()) { return false; } @@ -303,4 +312,25 @@ public boolean hasNext() { return cursor < length; } } + + @Override + public synchronized ListDefinition getReadonlyShapeDefinition() { + return readonlyAttachedDefinition.get(); + } + + @Override + public synchronized void setReadonlyShapeDefinition(ListDefinition definition) { + readonlyAttachedDefinition.set(definition); + } + + @Override + public synchronized void resetReadonlyShapeDefinition() { + readonlyAttachedDefinition.remove(); + } + + @Override + public Optional inherentTypeOf(Context cx) { + TypeWithShape typeWithShape = (TypeWithShape) getType(); + return typeWithShape.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractObjectValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractObjectValue.java index 07d20399f30d..5457ca79574e 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractObjectValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractObjectValue.java @@ -23,6 +23,9 @@ import io.ballerina.runtime.api.types.ObjectType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeId; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; @@ -35,10 +38,13 @@ import io.ballerina.runtime.internal.errors.ErrorCodes; import io.ballerina.runtime.internal.errors.ErrorHelper; import io.ballerina.runtime.internal.types.BObjectType; +import io.ballerina.runtime.internal.types.TypeWithShape; +import io.ballerina.runtime.internal.types.semtype.ObjectDefinition; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.StringJoiner; import static io.ballerina.runtime.api.constants.RuntimeConstants.DOT; @@ -57,10 +63,12 @@ * * @since 0.995.0 */ -public abstract class AbstractObjectValue implements ObjectValue { +public abstract class AbstractObjectValue implements ObjectValue, RecursiveValue { private BTypedesc typedesc; private final BObjectType objectType; private final Type type; + private SemType shape; + private final ThreadLocal readonlyAttachedDefinition = new ThreadLocal<>(); private final HashMap nativeData = new HashMap<>(); @@ -233,4 +241,33 @@ private void checkFieldUpdateType(String fieldName, Object value) { ErrorHelper.getErrorDetails(ErrorCodes.INVALID_OBJECT_FIELD_VALUE_ERROR, fieldName, fieldType, TypeChecker.getType(value))); } + + public final SemType shapeOf() { + return shape; + } + + public final void cacheShape(SemType semType) { + this.shape = semType; + } + + @Override + public Optional inherentTypeOf(Context cx) { + TypeWithShape typeWithShape = (TypeWithShape) getType(); + return typeWithShape.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); + } + + @Override + public ObjectDefinition getReadonlyShapeDefinition() { + return readonlyAttachedDefinition.get(); + } + + @Override + public void setReadonlyShapeDefinition(ObjectDefinition definition) { + readonlyAttachedDefinition.set(definition); + } + + @Override + public void resetReadonlyShapeDefinition() { + readonlyAttachedDefinition.remove(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValueImpl.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValueImpl.java index ad1896b75e76..6413e10fd648 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValueImpl.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValueImpl.java @@ -24,6 +24,7 @@ import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; @@ -86,6 +87,8 @@ public class ArrayValueImpl extends AbstractArrayValue { private double[] floatValues; private BString[] bStringValues; private BTypedesc typedesc; + + private SemType shape; // ------------------------ Constructors ------------------------------------------------------------------- public ArrayValueImpl(Object[] values, ArrayType type) { @@ -614,15 +617,15 @@ public void addRefValue(long index, Object value) { Type type = TypeChecker.getType(value); switch (this.elementReferredType.getTag()) { case TypeTags.BOOLEAN_TAG: - prepareForAdd(index, value, type, booleanValues.length); + prepareForAdd(index, value, booleanValues.length); this.booleanValues[(int) index] = (Boolean) value; return; case TypeTags.FLOAT_TAG: - prepareForAdd(index, value, type, floatValues.length); + prepareForAdd(index, value, floatValues.length); this.floatValues[(int) index] = (Double) value; return; case TypeTags.BYTE_TAG: - prepareForAdd(index, value, type, byteValues.length); + prepareForAdd(index, value, byteValues.length); this.byteValues[(int) index] = ((Number) value).byteValue(); return; case TypeTags.INT_TAG: @@ -632,16 +635,16 @@ public void addRefValue(long index, Object value) { case TypeTags.UNSIGNED32_INT_TAG: case TypeTags.UNSIGNED16_INT_TAG: case TypeTags.UNSIGNED8_INT_TAG: - prepareForAdd(index, value, type, intValues.length); + prepareForAdd(index, value, intValues.length); this.intValues[(int) index] = (Long) value; return; case TypeTags.STRING_TAG: case TypeTags.CHAR_STRING_TAG: - prepareForAdd(index, value, type, bStringValues.length); + prepareForAdd(index, value, bStringValues.length); this.bStringValues[(int) index] = (BString) value; return; default: - prepareForAdd(index, value, type, refValues.length); + prepareForAdd(index, value, refValues.length); this.refValues[(int) index] = value; } } @@ -659,27 +662,27 @@ public void setArrayRefTypeForcefully(ArrayType type, int size) { public void addInt(long index, long value) { if (intValues != null) { - prepareForAdd(index, value, PredefinedTypes.TYPE_INT, intValues.length); + prepareForAdd(index, value, intValues.length); intValues[(int) index] = value; return; } - prepareForAdd(index, value, TypeChecker.getType(value), byteValues.length); + prepareForAdd(index, value, byteValues.length); byteValues[(int) index] = (byte) ((Long) value).intValue(); } private void addBoolean(long index, boolean value) { - prepareForAdd(index, value, PredefinedTypes.TYPE_BOOLEAN, booleanValues.length); + prepareForAdd(index, value, booleanValues.length); booleanValues[(int) index] = value; } private void addByte(long index, byte value) { - prepareForAdd(index, value, PredefinedTypes.TYPE_BYTE, byteValues.length); + prepareForAdd(index, value, byteValues.length); byteValues[(int) index] = value; } private void addFloat(long index, double value) { - prepareForAdd(index, value, PredefinedTypes.TYPE_FLOAT, floatValues.length); + prepareForAdd(index, value, floatValues.length); floatValues[(int) index] = value; } @@ -689,7 +692,7 @@ private void addString(long index, String value) { } private void addBString(long index, BString value) { - prepareForAdd(index, value, PredefinedTypes.TYPE_STRING, bStringValues.length); + prepareForAdd(index, value, bStringValues.length); bStringValues[(int) index] = value; } @@ -1257,12 +1260,12 @@ protected void unshift(long index, Object[] vals) { // Private methods - private void prepareForAdd(long index, Object value, Type sourceType, int currentArraySize) { + private void prepareForAdd(long index, Object value, int currentArraySize) { // check types - if (!TypeChecker.checkIsType(null, value, sourceType, this.elementType)) { + if (!TypeChecker.checkIsType(value, this.elementType)) { throw ErrorCreator.createError(getModulePrefixedReason(ARRAY_LANG_LIB, INHERENT_TYPE_VIOLATION_ERROR_IDENTIFIER), ErrorHelper.getErrorDetails( - ErrorCodes.INCOMPATIBLE_TYPE, this.elementType, sourceType)); + ErrorCodes.INCOMPATIBLE_TYPE, this.elementType, TypeChecker.getType(value))); } prepareForAddWithoutTypeCheck(index, currentArraySize); } @@ -1406,4 +1409,14 @@ private int calculateHashCode(List visited) { } return result; } + + @Override + public void cacheShape(SemType semType) { + shape = semType; + } + + @Override + public SemType shapeOf() { + return shape; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/DecimalValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/DecimalValue.java index ec48ef7789af..d5e5e918e02e 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/DecimalValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/DecimalValue.java @@ -22,17 +22,22 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.values.BDecimal; import io.ballerina.runtime.api.values.BLink; import io.ballerina.runtime.internal.errors.ErrorCodes; import io.ballerina.runtime.internal.errors.ErrorHelper; import io.ballerina.runtime.internal.errors.ErrorReasons; +import io.ballerina.runtime.internal.types.BDecimalType; import io.ballerina.runtime.internal.utils.ErrorUtils; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.util.Map; +import java.util.Optional; /** *

@@ -60,8 +65,10 @@ public class DecimalValue implements SimpleValue, BDecimal { public DecimalValueKind valueKind = DecimalValueKind.OTHER; private final BigDecimal value; + private final Type singletonType; public DecimalValue(BigDecimal value) { + this.singletonType = BDecimalType.singletonType(value); this.value = getValidDecimalValue(value); if (!this.booleanValue()) { this.valueKind = DecimalValueKind.ZERO; @@ -83,7 +90,7 @@ public DecimalValue(String value) { throw exception; } this.value = getValidDecimalValue(bd); - + this.singletonType = BDecimalType.singletonType(this.value); if (!this.booleanValue()) { this.valueKind = DecimalValueKind.ZERO; } @@ -229,7 +236,7 @@ public BigDecimal value() { */ @Override public Type getType() { - return PredefinedTypes.TYPE_DECIMAL; + return singletonType; } //========================= Mathematical operations supported =============================== @@ -480,4 +487,9 @@ public static DecimalValue valueOfJ(BigDecimal value) { return new DecimalValue(new BigDecimal(value.toString(), MathContext.DECIMAL128) .setScale(1, BigDecimal.ROUND_HALF_EVEN)); } + + @Override + public Optional inherentTypeOf(Context cx) { + return Optional.of(Builder.getDecimalConst(value)); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ErrorValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ErrorValue.java index f8cd391a127a..140cd7d0dc08 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ErrorValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ErrorValue.java @@ -86,7 +86,7 @@ public class ErrorValue extends BError implements RefValue { private static final String STOP_FUNCTION_SUFFIX = "."; public ErrorValue(BString message) { - this(new BErrorType(TypeConstants.ERROR, PredefinedTypes.TYPE_ERROR.getPackage(), TYPE_MAP), + this(new BErrorType(TypeConstants.ERROR, PredefinedTypes.TYPE_ERROR.getPackage(), PredefinedTypes.TYPE_DETAIL), message, null, new MapValueImpl<>(PredefinedTypes.TYPE_ERROR_DETAIL)); } @@ -469,7 +469,9 @@ private boolean isCompilerAddedName(String name) { */ @Override public boolean equals(Object o, Set visitedValues) { - ErrorValue errorValue = (ErrorValue) o; + if (!(o instanceof ErrorValue errorValue)) { + return false; + } return isEqual(this.getMessage(), errorValue.getMessage(), visitedValues) && ((MapValueImpl) this.getDetails()).equals(errorValue.getDetails(), visitedValues) && isEqual(this.getCause(), errorValue.getCause(), visitedValues); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/FPValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/FPValue.java index 35dd9a28be60..6e116666a45b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/FPValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/FPValue.java @@ -20,12 +20,15 @@ import io.ballerina.runtime.api.Runtime; import io.ballerina.runtime.api.constants.RuntimeConstants; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.values.BFunctionPointer; import io.ballerina.runtime.api.values.BLink; import io.ballerina.runtime.api.values.BTypedesc; import io.ballerina.runtime.internal.BalRuntime; import java.util.Map; +import java.util.Optional; import java.util.function.Function; /** @@ -101,4 +104,9 @@ public String getName() { public String toString() { return RuntimeConstants.EMPTY; } + + @Override + public Optional inherentTypeOf(Context cx) { + return Optional.of(SemType.tryInto(getType())); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValueImpl.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValueImpl.java index 1361db3b3697..30630dc86aa8 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValueImpl.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValueImpl.java @@ -19,9 +19,13 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.types.Field; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BError; @@ -41,10 +45,11 @@ import io.ballerina.runtime.internal.json.JsonInternalUtils; import io.ballerina.runtime.internal.scheduling.Scheduler; import io.ballerina.runtime.internal.types.BField; -import io.ballerina.runtime.internal.types.BMapType; import io.ballerina.runtime.internal.types.BRecordType; import io.ballerina.runtime.internal.types.BTupleType; import io.ballerina.runtime.internal.types.BUnionType; +import io.ballerina.runtime.internal.types.TypeWithShape; +import io.ballerina.runtime.internal.types.semtype.MappingDefinition; import io.ballerina.runtime.internal.utils.CycleUtils; import io.ballerina.runtime.internal.utils.IteratorUtils; import io.ballerina.runtime.internal.utils.MapUtils; @@ -60,6 +65,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.StringJoiner; import java.util.stream.Collectors; @@ -93,13 +99,15 @@ * @since 0.995.0 */ public class MapValueImpl extends LinkedHashMap implements RefValue, CollectionValue, MapValue, - BMap { + BMap, RecursiveValue { private BTypedesc typedesc; private Type type; private Type referredType; private final Map nativeData = new HashMap<>(); private Type iteratorNextReturnType; + private SemType shape; + private final ThreadLocal readonlyAttachedDefinition = new ThreadLocal<>(); public MapValueImpl(TypedescValue typedesc) { this(typedesc.getDescribingType()); @@ -233,7 +241,7 @@ public V fillAndGet(Object key) { expectedType = recordType.restFieldType; } } else { - expectedType = ((BMapType) this.referredType).getConstrainedType(); + expectedType = ((MapType) this.referredType).getConstrainedType(); } if (!TypeChecker.hasFillerValue(expectedType)) { @@ -314,8 +322,8 @@ public void setTypeForcefully(Type type) { protected void populateInitialValues(BMapInitialValueEntry[] initialValues) { Map defaultValues = new HashMap<>(); - if (type.getTag() == TypeTags.RECORD_TYPE_TAG) { - defaultValues.putAll(((BRecordType) type).getDefaultValues()); + if (referredType.getTag() == TypeTags.RECORD_TYPE_TAG) { + defaultValues.putAll(((BRecordType) referredType).getDefaultValues()); } for (BMapInitialValueEntry initialValue : initialValues) { @@ -347,7 +355,7 @@ protected void populateInitialValues(BMapInitialValueEntry[] initialValues) { @Override public void populateInitialValue(K key, V value) { if (referredType.getTag() == TypeTags.MAP_TAG) { - MapUtils.handleInherentTypeViolatingMapUpdate(value, (BMapType) referredType); + MapUtils.handleInherentTypeViolatingMapUpdate(value, (MapType) referredType); putValue(key, value); } else { BString fieldName = (BString) key; @@ -609,6 +617,21 @@ public IteratorValue getIterator() { return new MapIterator<>(new LinkedHashSet<>(this.entrySet()).iterator()); } + @Override + public synchronized MappingDefinition getReadonlyShapeDefinition() { + return readonlyAttachedDefinition.get(); + } + + @Override + public synchronized void setReadonlyShapeDefinition(MappingDefinition definition) { + readonlyAttachedDefinition.set(definition); + } + + @Override + public synchronized void resetReadonlyShapeDefinition() { + readonlyAttachedDefinition.remove(); + } + /** * {@link MapIterator} iteration provider for ballerina maps. * @@ -688,7 +711,7 @@ public Map getNativeDataMap() { private void initializeIteratorNextReturnType() { Type type; if (this.referredType.getTag() == PredefinedTypes.TYPE_MAP.getTag()) { - BMapType mapType = (BMapType) this.referredType; + MapType mapType = (MapType) this.referredType; type = mapType.getConstrainedType(); } else { BRecordType recordType = (BRecordType) this.referredType; @@ -722,4 +745,20 @@ public Type getIteratorNextReturnType() { protected V putValue(K key, V value) { return super.put(key, value); } + + @Override + public void cacheShape(SemType semType) { + shape = semType; + } + + @Override + public SemType shapeOf() { + return shape; + } + + @Override + public Optional inherentTypeOf(Context cx) { + TypeWithShape typeWithShape = (TypeWithShape) type; + return typeWithShape.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ReadOnlyUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ReadOnlyUtils.java index c9ba580378da..8e23ef16c232 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ReadOnlyUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ReadOnlyUtils.java @@ -26,6 +26,7 @@ import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.IntersectableReferenceType; import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.ReferenceType; import io.ballerina.runtime.api.types.SelectivelyImmutableReferenceType; @@ -184,9 +185,9 @@ private static BIntersectionType setImmutableIntersectionType(Type type, Set Type of the definition + * + * @since 2201.11.0 + */ +interface RecursiveValue { + + E getReadonlyShapeDefinition(); + + void setReadonlyShapeDefinition(E definition); + + void resetReadonlyShapeDefinition(); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RegExpValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RegExpValue.java index c9a4fd8f30c7..a00800f64b53 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RegExpValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RegExpValue.java @@ -17,12 +17,17 @@ import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.values.BLink; import io.ballerina.runtime.api.values.BRegexpValue; import io.ballerina.runtime.api.values.BTypedesc; +import io.ballerina.runtime.internal.types.semtype.RegexUtils; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import static io.ballerina.runtime.internal.utils.ValueUtils.getTypedescValue; @@ -41,9 +46,11 @@ public class RegExpValue implements BRegexpValue, RefValue { private final RegExpDisjunction regExpDisjunction; private BTypedesc typedesc; private static final Type type = PredefinedTypes.TYPE_READONLY_ANYDATA; + private final SemType shape; public RegExpValue(RegExpDisjunction regExpDisjunction) { this.regExpDisjunction = regExpDisjunction; + this.shape = RegexUtils.regexShape(regExpDisjunction.stringValue(null)); } @Override @@ -76,11 +83,6 @@ public int hashCode() { return Objects.hash(this.regExpDisjunction); } - @Override - public boolean equals(Object obj) { - return this == obj; - } - @Override public BTypedesc getTypedesc() { if (this.typedesc == null) { @@ -123,4 +125,19 @@ public boolean equals(Object o, Set visitedValues) { } return this.stringValue(null).equals(rhsRegExpValue.stringValue(null)); } + + @Override + public SemType widenedType() { + return Builder.getRegexType(); + } + + @Override + public Optional shapeOf() { + return Optional.of(this.shape); + } + + @Override + public Optional inherentTypeOf(Context cx) { + return shapeOf(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/StringValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/StringValue.java index 3898b3aa8ca4..d553b300f4a8 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/StringValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/StringValue.java @@ -17,12 +17,16 @@ */ package io.ballerina.runtime.internal.values; -import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.values.BLink; import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.internal.types.BStringType; import java.util.Map; +import java.util.Optional; /** * Class representing ballerina strings. @@ -33,15 +37,19 @@ public abstract class StringValue implements BString, SimpleValue { final String value; final boolean isNonBmp; + private final Type type; + private final SemType shape; protected StringValue(String value, boolean isNonBmp) { this.value = value; this.isNonBmp = isNonBmp; + this.type = BStringType.singletonType(value); + this.shape = Builder.getStringConst(value); } @Override public Type getType() { - return PredefinedTypes.TYPE_STRING; + return type; } @Override @@ -100,4 +108,8 @@ public boolean equals(Object str) { return false; } + @Override + public Optional inherentTypeOf(Context cx) { + return Optional.of(shape); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TableValueImpl.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TableValueImpl.java index c027da267680..5ce57d89f406 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TableValueImpl.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TableValueImpl.java @@ -20,9 +20,13 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.Field; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.TableType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; @@ -37,12 +41,12 @@ import io.ballerina.runtime.internal.errors.ErrorCodes; import io.ballerina.runtime.internal.errors.ErrorHelper; import io.ballerina.runtime.internal.types.BIntersectionType; -import io.ballerina.runtime.internal.types.BMapType; import io.ballerina.runtime.internal.types.BRecordType; import io.ballerina.runtime.internal.types.BTableType; import io.ballerina.runtime.internal.types.BTupleType; import io.ballerina.runtime.internal.types.BTypeReferenceType; import io.ballerina.runtime.internal.types.BUnionType; +import io.ballerina.runtime.internal.types.TypeWithShape; import io.ballerina.runtime.internal.utils.CycleUtils; import io.ballerina.runtime.internal.utils.IteratorUtils; import io.ballerina.runtime.internal.utils.TableUtils; @@ -58,6 +62,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.StringJoiner; import java.util.TreeMap; @@ -430,7 +435,7 @@ private Type getTableConstraintField(Type constraintType, String fieldName) { Map fieldList = ((BRecordType) constraintType).getFields(); return fieldList.get(fieldName).getFieldType(); case TypeTags.MAP_TAG: - return ((BMapType) constraintType).getConstrainedType(); + return ((MapType) constraintType).getConstrainedType(); case TypeTags.INTERSECTION_TAG: Type effectiveType = ((BIntersectionType) constraintType).getEffectiveType(); return getTableConstraintField(effectiveType, fieldName); @@ -788,7 +793,7 @@ public MultiKeyWrapper() { Arrays.stream(fieldNames) .forEach(field -> keyTypes.add(recordType.getFields().get(field).getFieldType())); } else if (constraintType.getTag() == TypeTags.MAP_TAG) { - BMapType mapType = (BMapType) constraintType; + MapType mapType = (MapType) constraintType; Arrays.stream(fieldNames).forEach(field -> keyTypes.add(mapType.getConstrainedType())); } keyType = new BTupleType(keyTypes); @@ -904,4 +909,10 @@ public BObject getObjectValue(BString key) { public BArray getArrayValue(BString key) { return (BArray) get(key); } + + @Override + public Optional inherentTypeOf(Context cx) { + TypeWithShape typeWithShape = (TypeWithShape) type; + return typeWithShape.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TupleValueImpl.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TupleValueImpl.java index b6db397f4617..6086c28633ad 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TupleValueImpl.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TupleValueImpl.java @@ -21,6 +21,7 @@ import io.ballerina.runtime.api.types.TupleType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; @@ -75,6 +76,7 @@ public class TupleValueImpl extends AbstractArrayValue { private final boolean hasRestElement; // cached value for ease of access private BTypedesc typedesc; private TypedescValueImpl inherentType; + private SemType shape; // ------------------------ Constructors ------------------------------------------------------------------- public TupleValueImpl(Object[] values, TupleType type) { @@ -871,4 +873,14 @@ private void validateInherentTypeOfExistingMembers(int index, int offset) { } } } + + @Override + public void cacheShape(SemType semType) { + shape = semType; + } + + @Override + public SemType shapeOf() { + return shape; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/XmlValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/XmlValue.java index 30c606d8c4b2..ea862ca298db 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/XmlValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/XmlValue.java @@ -20,6 +20,9 @@ import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.XmlNodeType; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BLink; import io.ballerina.runtime.api.values.BMap; @@ -27,12 +30,14 @@ import io.ballerina.runtime.api.values.BTypedesc; import io.ballerina.runtime.api.values.BXml; import io.ballerina.runtime.api.values.BXmlQName; +import io.ballerina.runtime.internal.types.TypeWithShape; import io.ballerina.runtime.internal.utils.IteratorUtils; import io.ballerina.runtime.internal.xml.BallerinaXmlSerializer; import java.io.OutputStream; import java.util.List; import java.util.Map; +import java.util.Optional; import javax.xml.namespace.QName; @@ -75,30 +80,32 @@ public BString getAttribute(BXmlQName attributeName) { } /** - * Set the value of a single attribute. If the attribute already exsists, then the value will be updated. + * Set the value of a single attribute. If the attribute already exsists, then + * the value will be updated. * Otherwise a new attribute will be added. * * @param attributeName Qualified name of the attribute - * @param value Value of the attribute + * @param value Value of the attribute */ @Override @Deprecated public void setAttribute(BXmlQName attributeName, String value) { setAttributeOnInitialization(attributeName.getLocalName(), attributeName.getUri(), attributeName.getPrefix(), - value); + value); } /** - * Set the value of a single attribute. If the attribute already exsists, then the value will be updated. + * Set the value of a single attribute. If the attribute already exsists, then + * the value will be updated. * Otherwise a new attribute will be added. * * @param attributeName Qualified name of the attribute - * @param value Value of the attribute + * @param value Value of the attribute */ @Deprecated public void setAttribute(BXmlQName attributeName, BString value) { setAttributeOnInitialization(attributeName.getLocalName(), attributeName.getUri(), attributeName.getPrefix(), - value.getValue()); + value.getValue()); } /** @@ -147,12 +154,13 @@ public Type getType() { protected abstract void setAttributesOnInitialization(BMap attributes); protected abstract void setAttributeOnInitialization(String localName, String namespace, String prefix, - String value); + String value); // private methods protected static void handleXmlException(String message, Throwable t) { - // Here local message of the cause is logged whenever possible, to avoid java class being logged + // Here local message of the cause is logged whenever possible, to avoid java + // class being logged // along with the error message. if (t.getCause() != null) { throw ErrorCreator.createError(StringUtils.fromString(message + t.getCause().getMessage())); @@ -184,10 +192,12 @@ protected QName getQname(String qname) { } /** - * Recursively traverse and add the descendant with the given name to the descendants list. - * @param descendants List to add descendants + * Recursively traverse and add the descendant with the given name to the + * descendants list. + * + * @param descendants List to add descendants * @param currentElement Current node - * @param qnames Qualified names of the descendants to search + * @param qnames Qualified names of the descendants to search */ protected void addDescendants(List descendants, XmlItem currentElement, List qnames) { for (BXml child : currentElement.getChildrenSeq().getChildrenList()) { @@ -273,4 +283,9 @@ public Type getIteratorNextReturnType() { return iteratorNextReturnType; } + @Override + public Optional inherentTypeOf(Context cx) { + TypeWithShape typeWithShape = (TypeWithShape) type; + return typeWithShape.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/observability/tracer/BSpan.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/observability/tracer/BSpan.java index 1b9ecc424ceb..50b9be5606fe 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/observability/tracer/BSpan.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/observability/tracer/BSpan.java @@ -31,6 +31,7 @@ import io.opentelemetry.api.trace.SpanBuilder; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; import io.opentelemetry.context.propagation.TextMapGetter; @@ -149,6 +150,10 @@ public void addEvent(String eventName, Attributes attributes) { span.addEvent(eventName, attributes); } + public void setStatus(StatusCode statusCode) { + span.setStatus(statusCode); + } + public void addTags(Map tags) { for (Map.Entry entry : tags.entrySet()) { span.setAttribute(entry.getKey(), entry.getValue()); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/observability/tracer/TracingUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/observability/tracer/TracingUtils.java index 1747c16ac9b4..944e804d333f 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/observability/tracer/TracingUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/observability/tracer/TracingUtils.java @@ -20,6 +20,7 @@ import io.ballerina.runtime.internal.values.ErrorValue; import io.ballerina.runtime.observability.ObserverContext; import io.ballerina.runtime.observability.metrics.Tag; +import io.opentelemetry.api.trace.StatusCode; import java.util.Collections; import java.util.Map; @@ -79,6 +80,9 @@ public static void stopObservation(ObserverContext observerContext) { ErrorValue bError = (ErrorValue) observerContext.getProperty(PROPERTY_ERROR_VALUE); if (bError != null) { span.addTag(TAG_KEY_STR_ERROR_MESSAGE, bError.getPrintableStackTrace()); + span.setStatus(StatusCode.ERROR); + } else { + span.setStatus(StatusCode.OK); } // Adding specific error code to Trace Span diff --git a/bvm/ballerina-runtime/src/main/java/module-info.java b/bvm/ballerina-runtime/src/main/java/module-info.java index d09fee04a70a..fbb04210f3f4 100644 --- a/bvm/ballerina-runtime/src/main/java/module-info.java +++ b/bvm/ballerina-runtime/src/main/java/module-info.java @@ -28,6 +28,7 @@ exports io.ballerina.runtime.api.types; exports io.ballerina.runtime.api.utils; exports io.ballerina.runtime.api.values; + exports io.ballerina.runtime.api.types.semtype; exports io.ballerina.runtime.observability; exports io.ballerina.runtime.observability.metrics; @@ -62,4 +63,5 @@ exports io.ballerina.runtime.internal to ballerina.debug.adapter.core, io.ballerina.cli, io.ballerina.cli.utils, io.ballerina.java, io.ballerina.lang, io.ballerina.lang.array, io.ballerina.lang.bool, io.ballerina.lang.decimal, io.ballerina.lang.error, io.ballerina.lang.floatingpoint, io.ballerina.lang.function, io.ballerina.lang.integer, io.ballerina.lang.internal, io.ballerina.lang.map, io.ballerina.lang.regexp, io.ballerina.lang.table, io.ballerina.lang.test, io.ballerina.lang.transaction, io.ballerina.lang.value, io.ballerina.lang.xml, io.ballerina.log.api, io.ballerina.runtime.profiler, io.ballerina.shell, io.ballerina.testerina.core, io.ballerina.testerina.runtime, org.ballerinalang.debugadapter.runtime; exports io.ballerina.runtime.api.repository; exports io.ballerina.runtime.internal.repository to ballerina.debug.adapter.core, io.ballerina.cli, io.ballerina.cli.utils, io.ballerina.java, io.ballerina.lang, io.ballerina.lang.array, io.ballerina.lang.bool, io.ballerina.lang.decimal, io.ballerina.lang.error, io.ballerina.lang.floatingpoint, io.ballerina.lang.function, io.ballerina.lang.integer, io.ballerina.lang.internal, io.ballerina.lang.map, io.ballerina.lang.regexp, io.ballerina.lang.table, io.ballerina.lang.test, io.ballerina.lang.transaction, io.ballerina.lang.value, io.ballerina.lang.xml, io.ballerina.log.api, io.ballerina.runtime.profiler, io.ballerina.shell, io.ballerina.testerina.core, io.ballerina.testerina.runtime, org.ballerinalang.debugadapter.runtime; + exports io.ballerina.runtime.internal.types.semtype to io.ballerina.runtime.api.types.semtype, io.ballerina.runtime.internal.types; } diff --git a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/config/TomlProviderTest.java b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/config/TomlProviderTest.java index 8c4559c5a0dd..e14146289491 100644 --- a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/config/TomlProviderTest.java +++ b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/config/TomlProviderTest.java @@ -571,7 +571,7 @@ public void testTomlProviderWithString() { @Test(dataProvider = "map-data-provider") public void testTomlProviderMaps(String variableName, Type constraint, Map expectedValues) { - MapType type = TypeCreator.createMapType("MapType", constraint, ROOT_MODULE, false); + MapType type = TypeCreator.createMapType(variableName + "Type", constraint, ROOT_MODULE, false); IntersectionType mapType = new BIntersectionType(ROOT_MODULE, new Type[]{type, PredefinedTypes.TYPE_READONLY} , type, 1, true); VariableKey mapVar = new VariableKey(ROOT_MODULE, variableName, mapType, true); diff --git a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/config/negative/TomlProviderNegativeTest.java b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/config/negative/TomlProviderNegativeTest.java index a4d9fd911b17..4a16435e7be8 100644 --- a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/config/negative/TomlProviderNegativeTest.java +++ b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/config/negative/TomlProviderNegativeTest.java @@ -283,7 +283,7 @@ public void testTableNegativeConfig(String tomlFileName, String[] errorMessages) Field age = TypeCreator.createField(PredefinedTypes.TYPE_INT, "age", SymbolFlags.OPTIONAL); Map fields = Map.ofEntries(Map.entry("name", name), Map.entry("age", age), Map.entry("id", id)); RecordType type = - TypeCreator.createRecordType("Person", ROOT_MODULE, SymbolFlags.READONLY, fields, null, true, 6); + TypeCreator.createRecordType("PersonTPNT", ROOT_MODULE, SymbolFlags.READONLY, fields, null, true, 6); TableType tableType = TypeCreator.createTableType(type, new String[]{"name"}, true); IntersectionType intersectionType = new BIntersectionType(ROOT_MODULE, new Type[]{tableType, PredefinedTypes.TYPE_READONLY}, tableType, 1, true); @@ -296,16 +296,14 @@ public void testTableNegativeConfig(String tomlFileName, String[] errorMessages) @DataProvider(name = "table-negative-tests") public Object[][] getTableNegativeTests() { - return new Object[][]{ - {"MissingTableKey", new String[] { - "[MissingTableKey.toml:(6:1,8:9)] value required for key 'name' of type " + - "'table key(name) & readonly' in configurable variable 'tableVar'", - "[MissingTableKey.toml:(7:1,7:8)] unused configuration value 'tableVar.id'", - "[MissingTableKey.toml:(8:1,8:9)] unused configuration value 'tableVar.age'" - }}, + return new Object[][]{{"MissingTableKey", new String[]{ + "[MissingTableKey.toml:(6:1,8:9)] value required for key 'name' of type " + + "'table key(name) & readonly' in configurable variable 'tableVar'", + "[MissingTableKey.toml:(7:1,7:8)] unused configuration value 'tableVar.id'", + "[MissingTableKey.toml:(8:1,8:9)] unused configuration value 'tableVar.age'"}}, {"TableTypeError", new String[] { "[TableTypeError.toml:(1:1,3:9)] configurable variable 'tableVar' is expected to be of type" + - " 'table key(name) & readonly', but found 'record'", + " 'table key(name) & readonly', but found 'record'", "[TableTypeError.toml:(2:1,2:14)] unused configuration value 'test_module.tableVar.name'", "[TableTypeError.toml:(3:1,3:9)] unused configuration value 'test_module.tableVar.age'" }}, @@ -321,11 +319,11 @@ public Object[][] getTableNegativeTests() { }}, {"AdditionalField", new String[] { "[AdditionalField.toml:(4:1,4:17)] undefined field 'city' provided for closed record " + - "'test_module:Person'" + "'test_module:PersonTPNT'" }}, {"MissingTableField", new String[] { "[MissingTableField.toml:(1:1,3:9)] value not provided for non-defaultable required field " + - "'id' of record 'test_module:Person' in configurable variable 'tableVar'" + "'id' of record 'test_module:PersonTPNT' in configurable variable 'tableVar'" }}, {"TableInlineTypeError1", new String[] { "[TableInlineTypeError1.toml:(1:34,1:37)] configurable variable 'tableVar.name' is expected " + @@ -337,7 +335,7 @@ public Object[][] getTableNegativeTests() { }}, {"TableInlineTypeError3", new String[] { "[TableInlineTypeError3.toml:(1:24,1:53)] configurable variable 'tableVar' is expected to be " + - "of type 'table key(name) & readonly', but found 'array'" + "of type 'table key(name) & readonly', but found 'array'" }}, }; } @@ -634,7 +632,7 @@ public void testInvalidIntersectionArray() { @Test public void testRestFieldInvalidType() { - RecordType recordType = TypeCreator.createRecordType("Person", ROOT_MODULE, SymbolFlags.READONLY, + RecordType recordType = TypeCreator.createRecordType("PersonTPNT2", ROOT_MODULE, SymbolFlags.READONLY, new HashMap<>(), PredefinedTypes.TYPE_INT, false, 6); VariableKey recordVar = new VariableKey(ROOT_MODULE, "person", recordType, true); String error = "[RestFieldNegative.toml:(3:8,3:14)] configurable variable 'person.name' is expected to be of " + diff --git a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/semtype/CoreTests.java b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/semtype/CoreTests.java new file mode 100644 index 000000000000..fba49fa5c9e2 --- /dev/null +++ b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/semtype/CoreTests.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.test.semtype; + +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import org.testng.annotations.Test; + +public class CoreTests { + + @Test + public void testCellTypes() { + Env env = Env.getInstance(); + Context cx = Context.from(env); + SemType intTy = Builder.getIntType(); + SemType readonlyInt = Builder.getCellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_NONE); + assert Core.isSubType(cx, readonlyInt, readonlyInt); + SemType mutableInt = Builder.getCellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + assert Core.isSubType(cx, mutableInt, mutableInt); + assert Core.isSubType(cx, readonlyInt, mutableInt); + assert !Core.isSubType(cx, mutableInt, readonlyInt); + } + + @Test + public void testCellTypeCaching() { + Env env = Env.getInstance(); + SemType intTy = Builder.getIntType(); + SemType readonlyInt1 = Builder.getCellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_NONE); + SemType readonlyInt2 = Builder.getCellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_NONE); + assert readonlyInt1 == readonlyInt2; + } + + @Test + public void testSimpleList() { + Env env = Env.getInstance(); + SemType intTy = Builder.getIntType(); + // int[] + ListDefinition ld = new ListDefinition(); + SemType intListTy = + ld.defineListTypeWrapped(env, new SemType[0], 0, intTy, + CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + + // int[1] + ListDefinition ld1 = new ListDefinition(); + SemType[] members = {intTy}; + SemType intListTy1 = + ld1.defineListTypeWrapped(env, members, 1, Builder.getNeverType(), + CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + + Context cx = Context.from(env); + assert Core.isSubType(cx, intListTy1, intListTy); + } +} diff --git a/compiler/ballerina-lang/build.gradle b/compiler/ballerina-lang/build.gradle index 55f8b87b71ca..35fa4b1d947d 100644 --- a/compiler/ballerina-lang/build.gradle +++ b/compiler/ballerina-lang/build.gradle @@ -24,6 +24,7 @@ dependencies { implementation project(':ballerina-parser') implementation project(':ballerina-runtime') implementation project(':toml-parser') + implementation project(':semtypes') implementation project(':central-client') implementation project(':maven-resolver') implementation project(':identifier-util') diff --git a/compiler/ballerina-lang/spotbugs-exclude.xml b/compiler/ballerina-lang/spotbugs-exclude.xml index 01f5d928a3e3..5615cc897ee7 100644 --- a/compiler/ballerina-lang/spotbugs-exclude.xml +++ b/compiler/ballerina-lang/spotbugs-exclude.xml @@ -500,4 +500,11 @@ + + + + + + + diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/BallerinaSemanticModel.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/BallerinaSemanticModel.java index 73bf9e881f9b..edd726b9b112 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/BallerinaSemanticModel.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/BallerinaSemanticModel.java @@ -38,6 +38,7 @@ import io.ballerina.tools.text.LineRange; import io.ballerina.tools.text.TextDocument; import io.ballerina.tools.text.TextRange; +import io.ballerina.types.Core; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.symbols.SymbolKind; import org.ballerinalang.model.tree.NodeKind; @@ -53,7 +54,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BVarSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; -import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.tree.BLangClassDefinition; @@ -537,12 +537,12 @@ private boolean isInlineSingletonType(BSymbol symbol) { // !(symbol.kind == SymbolKind.TYPE_DEF) is checked to exclude type defs BType type = org.wso2.ballerinalang.compiler.semantics.analyzer.Types.getImpliedType(symbol.type); return !(symbol.kind == SymbolKind.TYPE_DEF) && type.tag == TypeTags.FINITE && - ((BFiniteType) type).getValueSpace().size() == 1; + Core.singleShape((symbol.type).semType()).isPresent(); } private boolean isInlineErrorType(BSymbol symbol) { return getImpliedType(symbol.type).tag == TypeTags.ERROR && - Symbols.isFlagOn(symbol.type.flags, Flags.ANONYMOUS); + Symbols.isFlagOn(symbol.type.getFlags(), Flags.ANONYMOUS); } private boolean isTypeSymbol(BSymbol tSymbol) { diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/LangLibFunctionBinder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/LangLibFunctionBinder.java index 1118cbb37226..5bb6b0339c2c 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/LangLibFunctionBinder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/LangLibFunctionBinder.java @@ -130,7 +130,8 @@ private BInvokableType duplicateType(BInvokableType original, List n paramTypes.addAll(original.paramTypes); } - BInvokableType duplicate = new BInvokableType(paramTypes, original.restType, original.retType, null); + BInvokableType duplicate = + new BInvokableType(types.typeEnv(), paramTypes, original.restType, original.retType, null); BInvokableTypeSymbol originalSym = (BInvokableTypeSymbol) original.tsymbol; BInvokableTypeSymbol duplicateTSym = new BInvokableTypeSymbol(original.tag, originalSym.flags, originalSym.pkgID, duplicate, diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/SymbolFactory.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/SymbolFactory.java index b91b0c6824c4..8ab3097ff9e8 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/SymbolFactory.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/SymbolFactory.java @@ -403,7 +403,7 @@ private boolean isReadonlyIntersectionArrayType(BType type) { type = Types.getReferredType(type); if (type.tag == TypeTags.INTERSECTION && type.tsymbol != null && type.tsymbol.getOrigin() == SymbolOrigin.VIRTUAL && - (type.flags & Flags.READONLY) == Flags.READONLY) { + Symbols.isFlagOn(type.getFlags(), Flags.READONLY)) { return true; } if (type.tag == TypeTags.ARRAY) { diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/TypeParamFinder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/TypeParamFinder.java index b11ed6ebfe97..d664cb2c237b 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/TypeParamFinder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/TypeParamFinder.java @@ -26,7 +26,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; @@ -37,7 +36,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; @@ -62,7 +60,7 @@ * * @since 2.0.0 */ -public class TypeParamFinder implements TypeVisitor { +public class TypeParamFinder extends TypeVisitor { private final Set visited = new HashSet<>(); private BType typeParam; @@ -99,10 +97,6 @@ public void visit(BArrayType bArrayType) { find(bArrayType.eType); } - @Override - public void visit(BBuiltInRefType bBuiltInRefType) { - } - @Override public void visit(BAnyType bAnyType) { } @@ -164,7 +158,7 @@ public void visit(BNeverType bNeverType) { } @Override - public void visit(BNilType bNilType) { + public void visitNilType(BType btype) { } @Override @@ -235,10 +229,6 @@ public void visit(BObjectType bObjectType) { } } - @Override - public void visit(BType bType) { - } - @Override public void visit(BFutureType bFutureType) { find(bFutureType.constraint); @@ -249,7 +239,7 @@ public void visit(BHandleType bHandleType) { } private void setContainsTypeParam(BType type) { - if (Symbols.isFlagOn(type.flags, Flags.TYPE_PARAM)) { + if (Symbols.isFlagOn(type.getFlags(), Flags.TYPE_PARAM)) { this.typeParam = type; } } diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/TypeParamResolver.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/TypeParamResolver.java index 9deeb9007caf..a15d5428db91 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/TypeParamResolver.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/TypeParamResolver.java @@ -18,6 +18,7 @@ package io.ballerina.compiler.api.impl; +import io.ballerina.types.Env; import org.ballerinalang.model.symbols.AnnotationAttachmentSymbol; import org.ballerinalang.model.symbols.SymbolKind; import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; @@ -30,7 +31,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; @@ -74,9 +74,12 @@ public class TypeParamResolver implements BTypeVisitor { private final Map boundTypes = new HashMap<>(); private final BType typeParam; private final Types types; + private final Env typeEnv; + public TypeParamResolver(BType typeParam, CompilerContext context) { this.typeParam = typeParam; types = Types.getInstance(context); + this.typeEnv = types.typeEnv(); } /** @@ -106,7 +109,7 @@ public BType resolve(BType typeParam, BType boundType) { @Override public BType visit(BType typeInSymbol, BType boundType) { - if (Symbols.isFlagOn(Flags.TYPE_PARAM, typeInSymbol.flags) + if (Symbols.isFlagOn(Flags.TYPE_PARAM, typeInSymbol.getFlags()) && types.isAssignable(typeInSymbol, this.typeParam)) { return boundType; } @@ -114,11 +117,6 @@ public BType visit(BType typeInSymbol, BType boundType) { return typeInSymbol; } - @Override - public BType visit(BBuiltInRefType typeInSymbol, BType boundType) { - return typeInSymbol; - } - @Override public BType visit(BAnyType typeInSymbol, BType boundType) { return typeInSymbol; @@ -137,7 +135,8 @@ public BType visit(BMapType typeInSymbol, BType boundType) { return typeInSymbol; } - return new BMapType(typeInSymbol.tag, boundConstraintType, typeInSymbol.tsymbol, typeInSymbol.flags); + return new BMapType(typeEnv, typeInSymbol.tag, boundConstraintType, typeInSymbol.tsymbol, + typeInSymbol.getFlags()); } @Override @@ -148,7 +147,7 @@ public BType visit(BXMLType typeInSymbol, BType boundType) { return typeInSymbol; } - return new BXMLType(boundConstraintType, typeInSymbol.tsymbol, typeInSymbol.flags); + return new BXMLType(boundConstraintType, typeInSymbol.tsymbol, typeInSymbol.getFlags()); } @Override @@ -164,8 +163,9 @@ public BType visit(BArrayType typeInSymbol, BType boundType) { return typeInSymbol; } - return new BArrayType(boundElemType, typeInSymbol.tsymbol, typeInSymbol.size, typeInSymbol.state, - typeInSymbol.flags); + return new BArrayType(typeEnv, boundElemType, typeInSymbol.tsymbol, + typeInSymbol.getSize(), + typeInSymbol.state, typeInSymbol.getFlags()); } @Override @@ -197,7 +197,7 @@ public BType visit(BObjectType typeInSymbol, BType boundType) { BObjectTypeSymbol newTypeSymbol = new BObjectTypeSymbol(objectTypeSymbol.tag, objectTypeSymbol.flags, objectTypeSymbol.name, objectTypeSymbol.pkgID, objectTypeSymbol.getType(), objectTypeSymbol.owner, objectTypeSymbol.pos, objectTypeSymbol.origin); - BObjectType newObjectType = new BObjectType(newTypeSymbol, typeInSymbol.flags); + BObjectType newObjectType = new BObjectType(typeEnv, newTypeSymbol, typeInSymbol.getFlags()); newObjectType.fields = newObjectFields; newTypeSymbol.attachedFuncs = newAttachedFuncs; @@ -217,7 +217,7 @@ public BType visit(BRecordType typeInSymbol, BType boundType) { } BType newRestType = resolve(typeInSymbol.restFieldType, boundType); - BRecordType newRecordType = new BRecordType(typeInSymbol.tsymbol, typeInSymbol.flags); + BRecordType newRecordType = new BRecordType(typeEnv, typeInSymbol.tsymbol, typeInSymbol.getFlags()); newRecordType.fields = newRecordFields; newRecordType.restFieldType = newRestType; @@ -244,8 +244,8 @@ public BType visit(BTupleType typeInSymbol, BType boundType) { return typeInSymbol; } - return new BTupleType(typeInSymbol.tsymbol, newTupleMembers, newRestType, typeInSymbol.flags, - typeInSymbol.isCyclic); + return new BTupleType(typeEnv, typeInSymbol.tsymbol, newTupleMembers, newRestType, + typeInSymbol.getFlags(), typeInSymbol.isCyclic); } @Override @@ -257,7 +257,7 @@ public BType visit(BStreamType typeInSymbol, BType boundType) { return typeInSymbol; } - return new BStreamType(typeInSymbol.tag, boundConstraintType, typeInSymbol.completionType, + return new BStreamType(typeEnv, typeInSymbol.tag, boundConstraintType, typeInSymbol.completionType, typeInSymbol.tsymbol); } @@ -270,8 +270,9 @@ public BType visit(BTableType typeInSymbol, BType boundType) { return typeInSymbol; } - BTableType bTableType = new BTableType(typeInSymbol.tag, boundConstraintType, typeInSymbol.tsymbol, - typeInSymbol.flags); + BTableType bTableType = new BTableType(typeEnv, boundConstraintType, + typeInSymbol.tsymbol, + typeInSymbol.getFlags()); bTableType.keyTypeConstraint = typeInSymbol.keyTypeConstraint; return bTableType; } @@ -311,7 +312,8 @@ public BType visit(BInvokableType typeInSymbol, BType boundType) { } invokableTypeSymbol.returnType = newReturnType; - BInvokableType type = new BInvokableType(newParamTypes, newRestParamType, newReturnType, invokableTypeSymbol); + BInvokableType type = new BInvokableType(typeEnv, newParamTypes, newRestParamType, newReturnType, + invokableTypeSymbol); invokableTypeSymbol.type = type; return type; @@ -336,7 +338,7 @@ public BType visit(BUnionType typeInSymbol, BType boundType) { return typeInSymbol; } - return BUnionType.create(typeInSymbol.tsymbol, newMembers); + return BUnionType.create(typeEnv, typeInSymbol.tsymbol, newMembers); } @Override diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/AbstractTypeSymbol.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/AbstractTypeSymbol.java index ff392b6d7f3d..95785eb48027 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/AbstractTypeSymbol.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/AbstractTypeSymbol.java @@ -134,7 +134,7 @@ public boolean equals(Object obj) { } Types types = Types.getInstance(this.context); - return types.isSameType(this.bType, ((AbstractTypeSymbol) obj).getBType()); + return types.isSameTypeIncludingTags(this.bType, ((AbstractTypeSymbol) obj).getBType()); } @Override diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaFunctionTypeSymbol.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaFunctionTypeSymbol.java index 273d3f54142b..90efd9c580df 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaFunctionTypeSymbol.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaFunctionTypeSymbol.java @@ -179,7 +179,7 @@ public String signature() { return this.signature; } - if (Symbols.isFlagOn(getBType().flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(getBType().getFlags(), Flags.ANY_FUNCTION)) { this.signature = "function"; return this.signature; } diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaNilTypeSymbol.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaNilTypeSymbol.java index a692ca8493df..021df64920bc 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaNilTypeSymbol.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaNilTypeSymbol.java @@ -20,7 +20,7 @@ import io.ballerina.compiler.api.SymbolVisitor; import io.ballerina.compiler.api.symbols.NilTypeSymbol; import io.ballerina.compiler.api.symbols.TypeDescKind; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; +import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.util.CompilerContext; /** @@ -30,7 +30,7 @@ */ public class BallerinaNilTypeSymbol extends AbstractTypeSymbol implements NilTypeSymbol { - public BallerinaNilTypeSymbol(CompilerContext context, BNilType nilType) { + public BallerinaNilTypeSymbol(CompilerContext context, BType nilType) { super(context, TypeDescKind.NIL, nilType); } diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaSingletonTypeSymbol.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaSingletonTypeSymbol.java index 6f9bda8b6dc4..c33968e035bf 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaSingletonTypeSymbol.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaSingletonTypeSymbol.java @@ -23,9 +23,7 @@ import io.ballerina.compiler.api.symbols.SingletonTypeSymbol; import io.ballerina.compiler.api.symbols.TypeDescKind; import io.ballerina.compiler.api.symbols.TypeSymbol; -import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; import org.wso2.ballerinalang.compiler.util.CompilerContext; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -39,35 +37,26 @@ public class BallerinaSingletonTypeSymbol extends AbstractTypeSymbol implements SingletonTypeSymbol { private final String typeName; - private final BLangLiteral shape; + private final BType broadType; private TypeSymbol originalType; - public BallerinaSingletonTypeSymbol(CompilerContext context, BLangLiteral shape, BType bType) { - super(context, TypeDescKind.SINGLETON, bType); - - if (shape.value == null && shape.originalValue == null) { - this.typeName = ""; - // Special case handling for `()` since in BLangLiteral, `null` is used to represent nil. - } else if (shape.getBType().tag == TypeTags.NIL) { - this.typeName = "()"; - // Special case handling for string type. - } else if (shape.getBType().tag == TypeTags.STRING) { - this.typeName = "\"" + shape + "\""; + public BallerinaSingletonTypeSymbol(CompilerContext context, BType broadType, String value, BType bFiniteType) { + super(context, TypeDescKind.SINGLETON, bFiniteType); + if (TypeTags.STRING == broadType.tag) { + this.typeName = "\"" + value + "\""; } else { - this.typeName = shape.toString(); + this.typeName = value; } - this.shape = shape; + this.broadType = broadType; } @Override public List langLibMethods() { if (this.langLibFunctions == null) { LangLibrary langLibrary = LangLibrary.getInstance(this.context); - BFiniteType internalType = (BFiniteType) this.getBType(); - BType valueType = internalType.getValueSpace().iterator().next().getBType(); - List functions = langLibrary.getMethods(valueType); - this.langLibFunctions = filterLangLibMethods(functions, valueType); + List functions = langLibrary.getMethods(broadType); + this.langLibFunctions = filterLangLibMethods(functions, broadType); } return this.langLibFunctions; @@ -95,8 +84,7 @@ public TypeSymbol originalType() { } TypesFactory typesFactory = TypesFactory.getInstance(this.context); - originalType = typesFactory.getTypeDescriptor(shape.getBType()); - + originalType = typesFactory.getTypeDescriptor(broadType); return originalType; } } diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaUnionTypeSymbol.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaUnionTypeSymbol.java index 101f370bd464..85714fa67c5d 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaUnionTypeSymbol.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaUnionTypeSymbol.java @@ -16,35 +16,51 @@ */ package io.ballerina.compiler.api.impl.symbols; -import io.ballerina.compiler.api.ModuleID; import io.ballerina.compiler.api.SymbolTransformer; import io.ballerina.compiler.api.SymbolVisitor; import io.ballerina.compiler.api.symbols.TypeDescKind; import io.ballerina.compiler.api.symbols.TypeSymbol; import io.ballerina.compiler.api.symbols.UnionTypeSymbol; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.StringSubtype; import org.ballerinalang.model.types.TypeKind; +import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.compiler.util.CompilerContext; +import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.StringJoiner; import java.util.regex.Pattern; import static io.ballerina.compiler.api.symbols.TypeDescKind.FUNCTION; import static io.ballerina.compiler.api.symbols.TypeDescKind.INTERSECTION; import static io.ballerina.compiler.api.symbols.TypeDescKind.NIL; +import static io.ballerina.types.BasicTypeCode.BT_BOOLEAN; +import static io.ballerina.types.BasicTypeCode.BT_DECIMAL; +import static io.ballerina.types.BasicTypeCode.BT_FLOAT; +import static io.ballerina.types.BasicTypeCode.BT_INT; +import static io.ballerina.types.BasicTypeCode.BT_STRING; +import static io.ballerina.types.Core.getComplexSubtypeData; +import static io.ballerina.types.SemTypes.isSubtypeSimple; /** * Represents an union type descriptor. @@ -60,13 +76,16 @@ public class BallerinaUnionTypeSymbol extends AbstractTypeSymbol implements Unio private List memberTypes; private List originalMemberTypes; private String signature; + private SymbolTable symTable; public BallerinaUnionTypeSymbol(CompilerContext context, BUnionType unionType) { super(context, TypeDescKind.UNION, unionType); + this.symTable = SymbolTable.getInstance(context); } public BallerinaUnionTypeSymbol(CompilerContext context, BFiniteType finiteType) { super(context, TypeDescKind.UNION, finiteType); + this.symTable = SymbolTable.getInstance(context); } @Override @@ -82,22 +101,10 @@ public List memberTypeDescriptors() { members.add(typesFactory.getTypeDescriptor(memberType)); continue; } - - BFiniteType finiteType = (BFiniteType) memberType; - for (BLangExpression value : finiteType.getValueSpace()) { - ModuleID moduleID = getModule().isPresent() ? getModule().get().id() : null; - BFiniteType bFiniteType = new BFiniteType(value.getBType().tsymbol, Set.of(value)); - members.add(new BallerinaSingletonTypeSymbol(this.context, (BLangLiteral) value, - bFiniteType)); - } + updateMembersForBFiniteType(members, (BFiniteType) memberType); } } else { - for (BLangExpression value : ((BFiniteType) this.getBType()).getValueSpace()) { - ModuleID moduleID = getModule().isPresent() ? getModule().get().id() : null; - BFiniteType bFiniteType = new BFiniteType(value.getBType().tsymbol, Set.of(value)); - members.add(new BallerinaSingletonTypeSymbol(this.context, (BLangLiteral) value, - bFiniteType)); - } + updateMembersForBFiniteType(members, (BFiniteType) this.getBType()); } this.memberTypes = Collections.unmodifiableList(members); @@ -106,6 +113,46 @@ public List memberTypeDescriptors() { return this.memberTypes; } + @SuppressWarnings("OptionalGetWithoutIsPresent") // xxxSubtypeSingleValue() are guaranteed to have a value + private void updateMembersForBFiniteType(List members, BFiniteType bFiniteType) { + for (SemNamedType semNamedType : bFiniteType.valueSpace) { + SemType s = semNamedType.semType(); + BFiniteType ft = BFiniteType.newSingletonBFiniteType(null, s); + if (PredefinedType.NIL.equals(s)) { + members.add(new BallerinaSingletonTypeSymbol(context, symTable.nilType, Names.NIL_VALUE.value, ft)); + continue; + } + + ComplexSemType cs = (ComplexSemType) s; + BType broadType; + String value; + if (isSubtypeSimple(s, PredefinedType.BOOLEAN)) { + broadType = symTable.booleanType; + boolean boolVal = BooleanSubtype.booleanSubtypeSingleValue(getComplexSubtypeData(cs, BT_BOOLEAN)).get(); + value = boolVal ? Names.TRUE.value : Names.FALSE.value; + } else if (isSubtypeSimple(s, PredefinedType.INT)) { + broadType = symTable.intType; + long longVal = IntSubtype.intSubtypeSingleValue(getComplexSubtypeData(cs, BT_INT)).get(); + value = Long.toString(longVal); + } else if (isSubtypeSimple(s, PredefinedType.FLOAT)) { + broadType = symTable.floatType; + double doubleVal = FloatSubtype.floatSubtypeSingleValue(getComplexSubtypeData(cs, BT_FLOAT)).get(); + value = Double.toString(doubleVal); + } else if (isSubtypeSimple(s, PredefinedType.DECIMAL)) { + broadType = symTable.decimalType; + BigDecimal bVal = DecimalSubtype.decimalSubtypeSingleValue(getComplexSubtypeData(cs, BT_DECIMAL)).get(); + value = bVal.toPlainString(); + } else if (isSubtypeSimple(s, PredefinedType.STRING)) { + broadType = symTable.stringType; + value = StringSubtype.stringSubtypeSingleValue(getComplexSubtypeData(cs, BT_STRING)).get(); + } else { + throw new IllegalStateException("Unexpected value space type: " + s); + } + + members.add(new BallerinaSingletonTypeSymbol(context, broadType, value, ft)); + } + } + @Override public List userSpecifiedMemberTypes() { if (this.originalMemberTypes == null) { @@ -118,11 +165,7 @@ public List userSpecifiedMemberTypes() { members.add(typesFactory.getTypeDescriptor(memberType)); } } else { - for (BLangExpression value : ((BFiniteType) this.getBType()).getValueSpace()) { - ModuleID moduleID = getModule().isPresent() ? getModule().get().id() : null; - members.add(new BallerinaSingletonTypeSymbol(this.context, (BLangLiteral) value, - value.getBType())); - } + updateMembersForBFiniteType(members, (BFiniteType) this.getBType()); } this.originalMemberTypes = Collections.unmodifiableList(members); @@ -164,7 +207,7 @@ private String getSignatureForUnion(BType type) { if (unionType.isCyclic && (unionType.tsymbol != null) && !unionType.tsymbol.getName().getValue().isEmpty()) { String typeStr; typeStr = unionType.tsymbol.getName().getValue(); - if (Symbols.isFlagOn(unionType.flags, Flags.TYPE_PARAM) && pCloneableType.matcher(typeStr).matches()) { + if (Symbols.isFlagOn(unionType.getFlags(), Flags.TYPE_PARAM) && pCloneableType.matcher(typeStr).matches()) { typeStr = CLONEABLE; } return typeStr; @@ -225,7 +268,7 @@ private boolean containsTwoElements(List types) { if (types.size() == 2) { for (TypeSymbol type : types) { BType internalType = ((AbstractTypeSymbol) type).getBType(); - if (internalType.tag == TypeTags.FINITE && ((BFiniteType) internalType).getValueSpace().size() > 1) { + if (internalType.tag == TypeTags.FINITE && Core.singleShape(internalType.semType()).isEmpty()) { return false; } } diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/TypesFactory.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/TypesFactory.java index 3c0705554eda..129b085a4ce3 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/TypesFactory.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/TypesFactory.java @@ -22,9 +22,13 @@ import io.ballerina.compiler.api.symbols.TypeDescKind; import io.ballerina.compiler.api.symbols.TypeSymbol; import io.ballerina.compiler.api.symbols.XMLTypeSymbol; +import io.ballerina.types.Core; +import io.ballerina.types.Value; import org.ballerinalang.model.symbols.SymbolKind; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.parser.BLangAnonymousModelHelper; +import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; +import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BClassSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BSymbol; @@ -44,7 +48,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BReadonlyType; @@ -59,13 +62,12 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLSubType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; import org.wso2.ballerinalang.compiler.util.CompilerContext; import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.util.Flags; -import java.util.Set; +import java.util.Objects; +import java.util.Optional; import static org.ballerinalang.model.types.TypeKind.PARAMETERIZED; import static org.wso2.ballerinalang.compiler.util.TypeTags.ANY; @@ -112,6 +114,7 @@ public class TypesFactory { private final CompilerContext context; private final SymbolFactory symbolFactory; private final BLangAnonymousModelHelper anonymousModelHelper; + private SymbolTable symTable; private TypesFactory(CompilerContext context) { context.put(TYPES_FACTORY_KEY, this); @@ -119,6 +122,7 @@ private TypesFactory(CompilerContext context) { this.context = context; this.symbolFactory = SymbolFactory.getInstance(context); this.anonymousModelHelper = BLangAnonymousModelHelper.getInstance(context); + this.symTable = SymbolTable.getInstance(context); } public static TypesFactory getInstance(CompilerContext context) { @@ -231,16 +235,16 @@ private TypeSymbol createTypeDescriptor(BType bType, BTypeSymbol tSymbol) { case TYPEDESC: return new BallerinaTypeDescTypeSymbol(this.context, (BTypedescType) bType); case NIL: - return new BallerinaNilTypeSymbol(this.context, (BNilType) bType); + return new BallerinaNilTypeSymbol(this.context, bType); case FINITE: BFiniteType finiteType = (BFiniteType) bType; - Set valueSpace = finiteType.getValueSpace(); - - if (valueSpace.size() == 1) { - BLangExpression shape = valueSpace.iterator().next(); - return new BallerinaSingletonTypeSymbol(this.context, (BLangLiteral) shape, bType); + Optional value = Core.singleShape(finiteType.semType()); + if (value.isPresent()) { + BType broadType = SemTypeHelper.broadTypes(finiteType, symTable).iterator() + .next(); + String valueString = Objects.toString(value.get().value, "()"); + return new BallerinaSingletonTypeSymbol(this.context, broadType, valueString, bType); } - return new BallerinaUnionTypeSymbol(this.context, finiteType); case FUNCTION: return new BallerinaFunctionTypeSymbol(this.context, (BInvokableTypeSymbol) tSymbol, bType); diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaArrayTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaArrayTypeBuilder.java index 72702a3e9105..eb01b5ba9838 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaArrayTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaArrayTypeBuilder.java @@ -86,7 +86,7 @@ public ArrayTypeSymbol build() { BTypeSymbol arrayTSymbol = Symbols.createTypeSymbol(SymTag.ARRAY_TYPE, Flags.PUBLIC, Names.EMPTY, symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, COMPILED_SOURCE); - BArrayType arrayType = new BArrayType(getBType(type), arrayTSymbol, size, state); + BArrayType arrayType = new BArrayType(symTable.typeEnv(), getBType(type), arrayTSymbol, size, state); arrayTSymbol.type = arrayType; ArrayTypeSymbol arrayTypeSymbol = (ArrayTypeSymbol) typesFactory.getTypeDescriptor(arrayType); this.size = -1; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaErrorTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaErrorTypeBuilder.java index 179cb64da617..04d1ffbd17af 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaErrorTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaErrorTypeBuilder.java @@ -71,7 +71,7 @@ public ErrorTypeSymbol build() { symTable.rootPkgSymbol.pkgID, symTable.errorType, symTable.rootPkgSymbol, symTable.builtinPos, COMPILED_SOURCE); - BErrorType errorType = new BErrorType(errorTSymbol, getBType(typeParam)); + BErrorType errorType = new BErrorType(symTable.typeEnv(), errorTSymbol, getBType(typeParam)); errorTSymbol.type = errorType; ErrorTypeSymbol errorTypeSymbol = (ErrorTypeSymbol) typesFactory.getTypeDescriptor(errorType); this.typeParam = null; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaFunctionTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaFunctionTypeBuilder.java index 178519d7121a..c64d8c6cd2cc 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaFunctionTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaFunctionTypeBuilder.java @@ -107,7 +107,8 @@ public FunctionTypeSymbol build() { tSymbol.returnType = returnType; tSymbol.params = getParamSymbols(parameterSymbols); tSymbol.restParam = getRestParamSymbol(restParam, restType); - BInvokableType bInvokableType = new BInvokableType(paramTypes, restType, returnType, tSymbol); + BInvokableType bInvokableType = + new BInvokableType(symTable.typeEnv(), paramTypes, restType, returnType, tSymbol); FunctionTypeSymbol functionTypeSymbol = (FunctionTypeSymbol) typesFactory.getTypeDescriptor(bInvokableType); parameterSymbols.clear(); restParam = null; @@ -139,7 +140,7 @@ private BType getRestType(ParameterSymbol restParam) { BTypeSymbol restArraySymbol = Symbols.createTypeSymbol(SymTag.ARRAY_TYPE, Flags.PUBLIC, Names.EMPTY, symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, COMPILED_SOURCE); - BArrayType restArrayType = new BArrayType(bType, restArraySymbol, -1, BArrayState.OPEN); + BArrayType restArrayType = new BArrayType(symTable.typeEnv(), bType, restArraySymbol, -1, BArrayState.OPEN); restArraySymbol.type = restArrayType; return restArrayType; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaFutureTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaFutureTypeBuilder.java index 073cf34788a8..af0fcab472d5 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaFutureTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaFutureTypeBuilder.java @@ -31,7 +31,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.util.CompilerContext; import org.wso2.ballerinalang.compiler.util.Names; -import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; import static org.ballerinalang.model.symbols.SymbolOrigin.COMPILED_SOURCE; @@ -63,7 +62,7 @@ public FutureTypeSymbol build() { BTypeSymbol futureTSymbol = Symbols.createTypeSymbol(SymTag.TYPE, Flags.PUBLIC, Names.EMPTY, symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, COMPILED_SOURCE); - BFutureType futureType = new BFutureType(TypeTags.FUTURE, getBType(typeParam), futureTSymbol); + BFutureType futureType = new BFutureType(symTable.typeEnv(), getBType(typeParam), futureTSymbol); futureTSymbol.type = futureType; FutureTypeSymbol futureTypeSymbol = (FutureTypeSymbol) typesFactory.getTypeDescriptor(futureType); this.typeParam = null; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaMapTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaMapTypeBuilder.java index a97976f1a1e8..f0df782d7a48 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaMapTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaMapTypeBuilder.java @@ -63,7 +63,7 @@ public MapTypeSymbol build() { symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, symTable.rootPkgSymbol.origin); - BMapType mapType = new BMapType(TypeTags.MAP, getBType(typeParam), mapTSymbol); + BMapType mapType = new BMapType(symTable.typeEnv(), TypeTags.MAP, getBType(typeParam), mapTSymbol); mapTSymbol.type = mapType; MapTypeSymbol mapTypeSymbol = (MapTypeSymbol) typesFactory.getTypeDescriptor(mapType); this.typeParam = null; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaObjectTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaObjectTypeBuilder.java index ed7387eab7c7..6dc6cfbce892 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaObjectTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaObjectTypeBuilder.java @@ -132,7 +132,7 @@ public ObjectTypeSymbol build() { BObjectTypeSymbol objectSymbol = Symbols.createObjectSymbol(Flags.asMask(flags), Names.EMPTY, symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol.owner, symTable.builtinPos, COMPILED_SOURCE); - BObjectType objectType = new BObjectType(objectSymbol, typeFlags); + BObjectType objectType = new BObjectType(symTable.typeEnv(), objectSymbol, typeFlags); objectType.fields = getObjectFields(objectFieldList, objectSymbol); objectType.typeInclusions.addAll(getTypeInclusions(typeInclusions)); objectSymbol.type = objectType; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaRecordTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaRecordTypeBuilder.java index 33c88117390f..cb7d7d80e089 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaRecordTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaRecordTypeBuilder.java @@ -95,7 +95,7 @@ public RecordTypeSymbol build() { symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, symTable.rootPkgSymbol.origin); - BRecordType recordType = new BRecordType(recordSymbol); + BRecordType recordType = new BRecordType(symTable.typeEnv(), recordSymbol); recordSymbol.type = recordType; recordType.typeInclusions = getTypeInclusions(typeInclusions); if (restField == null) { diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaSingletonTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaSingletonTypeBuilder.java index 148ebbed813b..fc0ab4dd8cbb 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaSingletonTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaSingletonTypeBuilder.java @@ -23,6 +23,7 @@ import io.ballerina.compiler.api.impl.symbols.TypesFactory; import io.ballerina.compiler.api.symbols.SingletonTypeSymbol; import io.ballerina.compiler.api.symbols.TypeSymbol; +import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.SymTag; @@ -34,8 +35,6 @@ import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.util.Flags; -import java.util.Set; - import static org.ballerinalang.model.symbols.SymbolOrigin.COMPILED_SOURCE; /** @@ -77,7 +76,8 @@ public SingletonTypeSymbol build() { Names.fromString(value.toString()), symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, COMPILED_SOURCE); - BFiniteType finiteType = new BFiniteType(finiteTypeSymbol, Set.of(valueLiteral)); + BFiniteType finiteType = BFiniteType.newSingletonBFiniteType(finiteTypeSymbol, + SemTypeHelper.resolveSingletonType(valueLiteral)); finiteTypeSymbol.type = finiteType; SingletonTypeSymbol singletonTypeSymbol = (SingletonTypeSymbol) typesFactory.getTypeDescriptor(finiteType, finiteTypeSymbol, true); diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaStreamTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaStreamTypeBuilder.java index e71fc54136c9..2c06b0e2876a 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaStreamTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaStreamTypeBuilder.java @@ -69,7 +69,7 @@ public StreamTypeSymbol build() { symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, symTable.rootPkgSymbol.origin); - BStreamType streamType = new BStreamType(TypeTags.STREAM, getValueBType(this.valueType), + BStreamType streamType = new BStreamType(symTable.typeEnv(), TypeTags.STREAM, getValueBType(this.valueType), getCompletionBType(this.completionType), streamSymbol); streamSymbol.type = streamType; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTableTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTableTypeBuilder.java index 18af7c102770..a749896d70ae 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTableTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTableTypeBuilder.java @@ -43,7 +43,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.util.CompilerContext; import org.wso2.ballerinalang.compiler.util.Names; -import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; import java.util.ArrayList; @@ -105,7 +104,7 @@ public TableTypeSymbol build() { symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, symTable.rootPkgSymbol.origin); - BTableType tableType = new BTableType(TypeTags.TABLE, rowBType, tableSymbol); + BTableType tableType = new BTableType(symTable.typeEnv(), rowBType, tableSymbol); tableSymbol.type = tableType; if (!keyTypes.isEmpty()) { tableType.keyTypeConstraint = getKeyConstraintBType(keyTypes, rowType); @@ -147,7 +146,7 @@ private boolean isReadOnlyField(RecordFieldSymbol recordField) { if (typeDescriptor instanceof AbstractTypeSymbol abstractTypeSymbol) { BType bType = abstractTypeSymbol.getBType(); - return Symbols.isFlagOn(bType.flags, Flags.READONLY); + return Symbols.isFlagOn(bType.getFlags(), Flags.READONLY); } return false; @@ -165,7 +164,7 @@ private BType getKeyConstraintBType(List keyTypes, TypeSymbol rowTyp tupleMembers.add(new BTupleMember(constraintType, varSymbol)); } - return new BTupleType(tupleMembers); + return new BTupleType(symTable.typeEnv(), tupleMembers); } private BType checkKeyConstraintBType(TypeSymbol keyType, TypeSymbol rowType) { @@ -193,7 +192,7 @@ private BType checkKeyConstraintBType(TypeSymbol keyType, TypeSymbol rowType) { private boolean isValidKeyConstraintType(TypeSymbol fieldType, TypeSymbol keyType) { if ((fieldType.typeKind() == keyType.typeKind() || keyType.subtypeOf(fieldType)) && fieldType instanceof AbstractTypeSymbol abstractTypeSymbol) { - return Symbols.isFlagOn(abstractTypeSymbol.getBType().flags, Flags.READONLY); + return Symbols.isFlagOn(abstractTypeSymbol.getBType().getFlags(), Flags.READONLY); } return false; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTupleTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTupleTypeBuilder.java index 9b401688b982..c4dbda213ef4 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTupleTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTupleTypeBuilder.java @@ -81,7 +81,7 @@ public TupleTypeSymbol build() { symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, SymbolOrigin.COMPILED_SOURCE); - BTupleType tupleType = new BTupleType(tupleSymbol, memberTypes); + BTupleType tupleType = new BTupleType(symTable.typeEnv(), tupleSymbol, memberTypes); tupleSymbol.type = tupleType; BType restBType = getRestType(restType); if (restBType != null) { diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTypeDescTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTypeDescTypeBuilder.java index d387090e8562..df8a1f09f4c3 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTypeDescTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTypeDescTypeBuilder.java @@ -62,7 +62,7 @@ public TypeDescTypeSymbol build() { symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, SymbolOrigin.COMPILED_SOURCE); - BTypedescType typedescType = new BTypedescType(getBType(typeParam), typedescSymbol); + BTypedescType typedescType = new BTypedescType(symTable.typeEnv(), getBType(typeParam), typedescSymbol); typedescSymbol.type = typedescType; TypeDescTypeSymbol typeDescTypeSymbol = (TypeDescTypeSymbol) typesFactory.getTypeDescriptor(typedescType); this.typeParam = null; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaUnionTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaUnionTypeBuilder.java index fd0d90aec70a..a9572608685f 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaUnionTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaUnionTypeBuilder.java @@ -77,7 +77,7 @@ public TypeBuilder.UNION withMemberTypes(TypeSymbol... memberTypes) { public UnionTypeSymbol build() { BTypeSymbol unionSymbol = Symbols.createTypeSymbol(SymTag.UNION_TYPE, Flags.PUBLIC, Names.EMPTY, symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, COMPILED_SOURCE); - BUnionType unionType = BUnionType.create(unionSymbol, getMemberBTypes()); + BUnionType unionType = BUnionType.create(symTable.typeEnv(), unionSymbol, getMemberBTypes()); UnionTypeSymbol unionTypeSymbol = (UnionTypeSymbol) typesFactory.getTypeDescriptor(unionType); memberTypes.clear(); diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/ModuleContext.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/ModuleContext.java index de2ec9c100ab..1bb02b26d59e 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/ModuleContext.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/ModuleContext.java @@ -35,6 +35,8 @@ import org.wso2.ballerinalang.compiler.bir.writer.BIRBinaryWriter; import org.wso2.ballerinalang.compiler.diagnostic.BLangDiagnosticLocation; import org.wso2.ballerinalang.compiler.semantics.analyzer.SymbolEnter; +import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; +import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BPackageSymbol; import org.wso2.ballerinalang.compiler.tree.BLangPackage; import org.wso2.ballerinalang.compiler.tree.BLangTestablePackage; @@ -244,7 +246,8 @@ List diagnostics() { } private void parseTestSources(BLangPackage pkgNode, PackageID pkgId, CompilerContext compilerContext) { - BLangTestablePackage testablePkg = TreeBuilder.createTestablePackageNode(); + Types types = Types.getInstance(compilerContext); + BLangTestablePackage testablePkg = TreeBuilder.createTestablePackageNode(types.typeEnv()); // TODO Not sure why we need to do this. It is there in the current implementation testablePkg.packageID = pkgId; testablePkg.flagSet.add(Flag.TESTABLE); @@ -375,7 +378,8 @@ static void compileInternal(ModuleContext moduleContext, CompilerContext compile SymbolEnter symbolEnter = SymbolEnter.getInstance(compilerContext); CompilerPhaseRunner compilerPhaseRunner = CompilerPhaseRunner.getInstance(compilerContext); - BLangPackage pkgNode = (BLangPackage) TreeBuilder.createPackageNode(); + Types types = Types.getInstance(compilerContext); + BLangPackage pkgNode = (BLangPackage) TreeBuilder.createPackageNode(types.typeEnv()); pkgNode.moduleContextDataHolder = new ModuleContextDataHolder( moduleContext.isExported(), moduleContext.descriptor(), @@ -490,11 +494,13 @@ private static ByteArrayOutputStream generateBIR(ModuleContext moduleContext, Co } // Can we improve this logic ByteArrayOutputStream birContent = new ByteArrayOutputStream(); + SymbolTable symTable = SymbolTable.getInstance(compilerContext); try { CompiledBinaryFile.BIRPackageFile birPackageFile = moduleContext.bLangPackage.symbol.birPackageFile; if (birPackageFile == null) { birPackageFile = new CompiledBinaryFile - .BIRPackageFile(new BIRBinaryWriter(moduleContext.bLangPackage.symbol.bir).serialize()); + .BIRPackageFile( + new BIRBinaryWriter(moduleContext.bLangPackage.symbol.bir, symTable.typeEnv()).serialize()); moduleContext.bLangPackage.symbol.birPackageFile = birPackageFile; } byte[] pkgBirBinaryContent = PackageFileWriter.writePackage(birPackageFile); diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/configschema/TypeConverter.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/configschema/TypeConverter.java index 17cdd218c171..668b0989786f 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/configschema/TypeConverter.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/configschema/TypeConverter.java @@ -19,6 +19,14 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.StringSubtype; import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; @@ -32,16 +40,23 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.semantics.model.types.BTypeReferenceType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.TypeTags; +import java.math.BigDecimal; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; +import static io.ballerina.types.Core.getComplexSubtypeData; +import static io.ballerina.types.SemTypes.isSubtypeSimple; +import static io.ballerina.types.BasicTypeCode.BT_BOOLEAN; +import static io.ballerina.types.BasicTypeCode.BT_DECIMAL; +import static io.ballerina.types.BasicTypeCode.BT_FLOAT; +import static io.ballerina.types.BasicTypeCode.BT_INT; +import static io.ballerina.types.BasicTypeCode.BT_STRING; import static org.wso2.ballerinalang.compiler.util.TypeTags.BOOLEAN; import static org.wso2.ballerinalang.compiler.util.TypeTags.BYTE; import static org.wso2.ballerinalang.compiler.util.TypeTags.DECIMAL; @@ -97,7 +112,7 @@ JsonObject getType(BType type) { typeNode.addProperty(TYPE, typeVal); return typeNode; } - + VisitedType visitedType = getVisitedType(effectiveType.toString()); if (visitedType != null) { if (visitedType.isCompleted()) { @@ -267,24 +282,33 @@ private void updateUnionMembers(LinkedHashSet members, JsonArray memberAr * @param enumArray JSON array to add the enum values * @param finiteType BFiniteType to retrieve enum values from */ + @SuppressWarnings("OptionalGetWithoutIsPresent") // xxxSubtypeSingleValue() are guaranteed to have a value private static void getEnumArray(JsonArray enumArray, BFiniteType finiteType) { - Object[] values = finiteType.getValueSpace().toArray(); - for (Object finiteValue : values) { - if (finiteValue instanceof BLangNumericLiteral numericLiteral) { - BType bType = numericLiteral.getBType(); - // In the BLangNumericLiteral the integer typed values are represented as numeric values - // while the decimal values are represented as String - Object value = numericLiteral.getValue(); - if (TypeTags.isIntegerTypeTag(bType.tag)) { - // Any integer can be considered as a long and added as a numeric value to the enum array - if (value instanceof Long l) { - enumArray.add(l); - } - } else { - enumArray.add(Double.parseDouble(value.toString())); - } - } else if (finiteValue instanceof BLangLiteral bLangLiteral) { - enumArray.add(bLangLiteral.getValue().toString()); + for (SemNamedType semNamedType : finiteType.valueSpace) { + SemType s = semNamedType.semType(); + if (PredefinedType.NIL.equals(s)) { + enumArray.add(Names.NIL_VALUE.value); + continue; + } + + ComplexSemType cs = (ComplexSemType) s; + if (isSubtypeSimple(s, PredefinedType.BOOLEAN)) { + boolean boolVal = BooleanSubtype.booleanSubtypeSingleValue(getComplexSubtypeData(cs, BT_BOOLEAN)).get(); + enumArray.add(boolVal ? Names.TRUE.value : Names.FALSE.value); + } else if (isSubtypeSimple(s, PredefinedType.INT)) { + long longVal = IntSubtype.intSubtypeSingleValue(getComplexSubtypeData(cs, BT_INT)).get(); + enumArray.add(longVal); + } else if (isSubtypeSimple(s, PredefinedType.FLOAT)) { + double doubleVal = FloatSubtype.floatSubtypeSingleValue(getComplexSubtypeData(cs, BT_FLOAT)).get(); + enumArray.add(doubleVal); + } else if (isSubtypeSimple(s, PredefinedType.DECIMAL)) { + BigDecimal bVal = DecimalSubtype.decimalSubtypeSingleValue(getComplexSubtypeData(cs, BT_DECIMAL)).get(); + enumArray.add(bVal.toString()); + } else if (isSubtypeSimple(s, PredefinedType.STRING)) { + String stringVal = StringSubtype.stringSubtypeSingleValue(getComplexSubtypeData(cs, BT_STRING)).get(); + enumArray.add(stringVal); + } else { + throw new IllegalStateException("Unexpected value space type: " + s); } } } diff --git a/compiler/ballerina-lang/src/main/java/module-info.java b/compiler/ballerina-lang/src/main/java/module-info.java index bf773acd7296..320ee6cc2817 100644 --- a/compiler/ballerina-lang/src/main/java/module-info.java +++ b/compiler/ballerina-lang/src/main/java/module-info.java @@ -16,6 +16,7 @@ requires org.apache.commons.io; requires io.ballerina.toml; requires io.ballerina.central.client; + requires io.ballerina.semtype; requires io.ballerina.identifier; requires java.semver; requires maven.resolver; diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/TreeBuilder.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/TreeBuilder.java index 7e269184fa01..891927afecda 100644 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/TreeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/TreeBuilder.java @@ -17,6 +17,7 @@ */ package org.ballerinalang.model; +import io.ballerina.types.Env; import org.ballerinalang.model.clauses.CollectClauseNode; import org.ballerinalang.model.clauses.DoClauseNode; import org.ballerinalang.model.clauses.GroupByClauseNode; @@ -395,12 +396,12 @@ public static CompilationUnitNode createCompilationUnit() { return new BLangCompilationUnit(); } - public static PackageNode createPackageNode() { - return new BLangPackage(); + public static PackageNode createPackageNode(Env typeEnv) { + return new BLangPackage(typeEnv); } - public static BLangTestablePackage createTestablePackageNode() { - return new BLangTestablePackage(); + public static BLangTestablePackage createTestablePackageNode(Env typeEnv) { + return new BLangTestablePackage(typeEnv); } public static IdentifierNode createIdentifierNode() { diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ConnectorType.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ConnectorType.java deleted file mode 100644 index c6f9a6e9212b..000000000000 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ConnectorType.java +++ /dev/null @@ -1,26 +0,0 @@ -/* -* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -* -* WSO2 Inc. licenses this file to you under the Apache License, -* Version 2.0 (the "License"); you may not use this file except -* in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ -package org.ballerinalang.model.types; - -/** - * {@code ConnectorType} represents the type of a connector in Ballerina. - * - * @since 0.94 - */ -public interface ConnectorType extends ReferenceType { -} diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ConstrainedType.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ConstrainedType.java index 394001fbf8cc..8cd4c1a858d3 100644 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ConstrainedType.java +++ b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ConstrainedType.java @@ -20,7 +20,7 @@ /** * @since 0.94 */ -public interface ConstrainedType extends ReferenceType { +public interface ConstrainedType { Type getConstraint(); } diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/FiniteType.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/FiniteType.java deleted file mode 100644 index 165b6832a561..000000000000 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/FiniteType.java +++ /dev/null @@ -1,32 +0,0 @@ -/* -* Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -* -* WSO2 Inc. licenses this file to you under the Apache License, -* Version 2.0 (the "License"); you may not use this file except -* in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ -package org.ballerinalang.model.types; - -import org.ballerinalang.model.tree.expressions.ExpressionNode; - -import java.util.Set; - -/** - * {@code FiniteType} represents the finite type interface. - * - */ -public interface FiniteType extends ReferenceType { - - Set getValueSpace(); - -} diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/NilType.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/NilType.java deleted file mode 100644 index 6f9667927726..000000000000 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/NilType.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.ballerinalang.model.types; - -/** - * {@code NilType} represents the singleton type returns by functions with no declared value. - * The value of the {@code NilType} is written as '()' - * - * @since 0.970.0 - */ -public interface NilType extends ReferenceType { -} diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/NullType.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/NullType.java deleted file mode 100644 index 144a5036682d..000000000000 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/NullType.java +++ /dev/null @@ -1,26 +0,0 @@ -/* -* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -* -* WSO2 Inc. licenses this file to you under the Apache License, -* Version 2.0 (the "License"); you may not use this file except -* in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ -package org.ballerinalang.model.types; - -/** - * {@code NullType} represents the type of the expression 'null'. - * - * @since 0.94 - */ -public interface NullType extends ReferenceType { -} diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ReferenceType.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ReferenceType.java deleted file mode 100644 index ebe029307e5a..000000000000 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ReferenceType.java +++ /dev/null @@ -1,27 +0,0 @@ -/* -* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -* -* WSO2 Inc. licenses this file to you under the Apache License, -* Version 2.0 (the "License"); you may not use this file except -* in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ -package org.ballerinalang.model.types; - -/** - * {@code ReferenceType} represents a reference type in Ballerina. - * These includes structs, connectors, array types, xml, json etc. - * - * @since 0.94 - */ -public interface ReferenceType extends Type { -} diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java index 63b9648cd3bd..6f1037b9bb94 100644 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java +++ b/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java @@ -778,7 +778,7 @@ public enum DiagnosticErrorCode implements DiagnosticCode { UNDEFINED_RESOURCE("BCE4028", "undefined.resource"), UNDEFINED_RESOURCE_METHOD("BCE4029", "undefined.resource.method"), AMBIGUOUS_RESOURCE_ACCESS_NOT_YET_SUPPORTED("BCE4030", "ambiguous.resource.access.not.yet.supported"), - UNSUPPORTED_COMPUTED_RESOURCE_ACCESS_PATH_SEGMENT_TYPE("BCE4031", + UNSUPPORTED_COMPUTED_RESOURCE_ACCESS_PATH_SEGMENT_TYPE("BCE4031", "unsupported.computed.resource.access.path.segment.type"), UNSUPPORTED_RESOURCE_ACCESS_REST_SEGMENT_TYPE("BCE4032", "unsupported.resource.access.rest.segment.type"), INVALID_RESOURCE_METHOD_RETURN_TYPE("BCE4033", "invalid.resource.method.return.type"), diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/BIRPackageSymbolEnter.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/BIRPackageSymbolEnter.java index 97e943698e19..044548c34fde 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/BIRPackageSymbolEnter.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/BIRPackageSymbolEnter.java @@ -18,8 +18,40 @@ package org.wso2.ballerinalang.compiler; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Atom; +import io.ballerina.types.AtomicType; +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.Bdd; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.CellSemType; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.EnumerableCharString; +import io.ballerina.types.EnumerableDecimal; +import io.ballerina.types.EnumerableFloat; +import io.ballerina.types.EnumerableString; +import io.ballerina.types.Env; +import io.ballerina.types.FixedLengthArray; +import io.ballerina.types.FunctionAtomicType; +import io.ballerina.types.ListAtomicType; +import io.ballerina.types.MappingAtomicType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.PredefinedTypeEnv; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.RecAtom; +import io.ballerina.types.SemType; +import io.ballerina.types.TypeAtom; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.CharStringSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.NonCharStringSubtype; +import io.ballerina.types.subtypedata.Range; +import io.ballerina.types.subtypedata.StringSubtype; +import io.ballerina.types.subtypedata.XmlSubtype; import org.ballerinalang.compiler.BLangCompilerException; -import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.AttachPoint; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.MarkdownDocAttachment; @@ -27,7 +59,6 @@ import org.ballerinalang.model.symbols.Annotatable; import org.ballerinalang.model.symbols.SymbolKind; import org.ballerinalang.model.symbols.SymbolOrigin; -import org.ballerinalang.model.tree.NodeKind; import org.ballerinalang.model.types.ConstrainedType; import org.wso2.ballerinalang.compiler.bir.writer.CPEntry; import org.wso2.ballerinalang.compiler.bir.writer.CPEntry.ByteCPEntry; @@ -65,6 +96,7 @@ import org.wso2.ballerinalang.compiler.semantics.model.symbols.SymTag; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnnotationType; +import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; @@ -86,11 +118,8 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BTypedescType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; -import org.wso2.ballerinalang.compiler.semantics.model.types.TypeFlags; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.compiler.tree.BLangConstantValue; -import org.wso2.ballerinalang.compiler.tree.BLangPackage; -import org.wso2.ballerinalang.compiler.tree.BLangTypeDefinition; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; import org.wso2.ballerinalang.compiler.util.BArrayState; import org.wso2.ballerinalang.compiler.util.CompilerContext; import org.wso2.ballerinalang.compiler.util.ImmutableTypeCloner; @@ -105,6 +134,8 @@ import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; @@ -116,13 +147,14 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.function.Consumer; +import static io.ballerina.types.PredefinedType.BDD_REC_ATOM_READONLY; import static org.ballerinalang.model.symbols.SymbolOrigin.COMPILED_SOURCE; import static org.ballerinalang.model.symbols.SymbolOrigin.VIRTUAL; import static org.ballerinalang.model.symbols.SymbolOrigin.toOrigin; -import static org.wso2.ballerinalang.compiler.parser.BLangAnonymousModelHelper.ANON_PREFIX; import static org.wso2.ballerinalang.compiler.semantics.model.Scope.NOT_FOUND_ENTRY; import static org.wso2.ballerinalang.util.LambdaExceptionUtils.rethrow; @@ -132,6 +164,7 @@ * @since 0.995.0 */ public class BIRPackageSymbolEnter { + private final PackageCache packageCache; private final SymbolResolver symbolResolver; private final SymbolTable symTable; @@ -144,6 +177,8 @@ public class BIRPackageSymbolEnter { private List structureTypes; // TODO find a better way private BStructureTypeSymbol currentStructure = null; private final LinkedList compositeStack = new LinkedList<>(); + private final Env typeEnv; + private AtomOffsets offsets; private static final CompilerContext.Key COMPILED_PACKAGE_SYMBOL_ENTER_KEY = new CompilerContext.Key<>(); @@ -168,6 +203,8 @@ private BIRPackageSymbolEnter(CompilerContext context) { this.names = Names.getInstance(context); this.typeParamAnalyzer = TypeParamAnalyzer.getInstance(context); this.types = Types.getInstance(context); + this.typeEnv = symTable.typeEnv(); + this.offsets = null; } public BPackageSymbol definePackage(PackageID packageId, byte[] packageBinaryContent) { @@ -231,6 +268,7 @@ private BPackageSymbol definePackage(DataInputStream dataInStream, int pkgCpInde PackageID pkgId = createPackageID(orgName, pkgName, moduleName, pkgVersion); this.env.pkgSymbol = Symbols.createPackageSymbol(pkgId, this.symTable, COMPILED_SOURCE); + this.offsets = AtomOffsets.from(typeEnv); // TODO Validate this pkdID with the requestedPackageID available in the env. @@ -261,6 +299,7 @@ private BPackageSymbol definePackage(DataInputStream dataInStream, int pkgCpInde populateReferencedFunctions(); this.typeReader = null; + this.offsets = null; return this.env.pkgSymbol; } @@ -606,7 +645,7 @@ private void skipPosition(DataInputStream dataInStream) throws IOException { } private void setInvokableTypeSymbol(BInvokableType invokableType) { - if (Symbols.isFlagOn(invokableType.flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(invokableType.getFlags(), Flags.ANY_FUNCTION)) { return; } BInvokableTypeSymbol tsymbol = (BInvokableTypeSymbol) invokableType.tsymbol; @@ -932,7 +971,7 @@ private void setParamSymbols(BInvokableSymbol invokableSymbol, DataInputStream d defineAnnotAttachmentSymbols(dataInStream, restParam); } - if (Symbols.isFlagOn(invokableSymbol.retType.flags, Flags.PARAMETERIZED)) { + if (Symbols.isFlagOn(invokableSymbol.retType.getFlags(), Flags.PARAMETERIZED)) { Map paramsMap = new HashMap<>(); for (BVarSymbol param : invokableSymbol.params) { if (paramsMap.put(param.getName(), param) != null) { @@ -1034,7 +1073,7 @@ private void populateParameterizedType(BType type, final Map p break; case TypeTags.INVOKABLE: BInvokableType invokableType = (BInvokableType) type; - if (Symbols.isFlagOn(invokableType.flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(invokableType.getFlags(), Flags.ANY_FUNCTION)) { break; } for (BType t : invokableType.paramTypes) { @@ -1132,6 +1171,7 @@ private static class UnresolvedType { private class BIRTypeReader { private final DataInputStream inputStream; + private final PredefinedTypeEnv predefinedTypeEnv = PredefinedTypeEnv.getInstance(); public BIRTypeReader(DataInputStream inputStream) { this.inputStream = inputStream; @@ -1220,8 +1260,6 @@ public BType readType(int cpI) throws IOException { Name name = Names.fromString(getStringCPEntryValue(inputStream)); long flags = inputStream.readLong(); - // Read the type flags to identify if type reference types are nullable. - int typeFlags = inputStream.readInt(); switch (tag) { case TypeTags.INT: return typeParamAnalyzer.getNominalType(symTable.intType, name, flags); @@ -1242,7 +1280,7 @@ public BType readType(int cpI) throws IOException { BType constraintType = readTypeFromCp(); BXMLType mutableXmlType = new BXMLType(constraintType, symTable.xmlType.tsymbol); if (Symbols.isFlagOn(flags, Flags.PARAMETERIZED)) { - mutableXmlType.flags |= Flags.PARAMETERIZED; + mutableXmlType.addFlags(Flags.PARAMETERIZED); } return isImmutable(flags) ? getEffectiveImmutableType(mutableXmlType) : mutableXmlType; case TypeTags.NIL: @@ -1270,7 +1308,7 @@ public BType readType(int cpI) throws IOException { COMPILED_SOURCE); recordSymbol.scope = new Scope(recordSymbol); - BRecordType recordType = new BRecordType(recordSymbol, flags); + BRecordType recordType = new BRecordType(symTable.typeEnv(), recordSymbol, flags); recordSymbol.type = recordType; compositeStack.push(recordType); @@ -1319,11 +1357,11 @@ public BType readType(int cpI) throws IOException { } SymbolEnv pkgEnv = symTable.pkgEnvMap.get(packageCache.getSymbol(pkgId)); - return getType(recordType, pkgEnv, Names.fromString(recordName)); + return lookupSymbolInMainSpace(pkgEnv, Names.fromString(recordName)); case TypeTags.TYPEDESC: - BTypedescType typedescType = new BTypedescType(null, symTable.typeDesc.tsymbol); + BTypedescType typedescType = new BTypedescType(symTable.typeEnv(), null, symTable.typeDesc.tsymbol); typedescType.constraint = readTypeFromCp(); - typedescType.flags = flags; + typedescType.setFlags(flags); return typedescType; case TypeTags.TYPEREFDESC: int pkgIndex = inputStream.readInt(); @@ -1337,9 +1375,7 @@ public BType readType(int cpI) throws IOException { Names.fromString(typeDefName), pkg, null, pkgSymbol, symTable.builtinPos, COMPILED_SOURCE); - boolean nullable = (typeFlags & TypeFlags.NILABLE) == TypeFlags.NILABLE; - - BTypeReferenceType typeReferenceType = new BTypeReferenceType(null, typeSymbol, flags, nullable); + BTypeReferenceType typeReferenceType = new BTypeReferenceType(null, typeSymbol, flags); addShapeCP(typeReferenceType, cpI); compositeStack.push(typeReferenceType); typeReferenceType.referredType = readTypeFromCp(); @@ -1350,17 +1386,19 @@ public BType readType(int cpI) throws IOException { case TypeTags.PARAMETERIZED_TYPE: BParameterizedType type = new BParameterizedType(null, null, null, name, -1); type.paramValueType = readTypeFromCp(); - type.flags = flags; + type.setFlags(flags); type.paramIndex = inputStream.readInt(); return type; case TypeTags.STREAM: - BStreamType bStreamType = new BStreamType(TypeTags.STREAM, null, null, symTable.streamType.tsymbol); + BStreamType bStreamType = new BStreamType(symTable.typeEnv(), TypeTags.STREAM, null, null, + symTable.streamType.tsymbol); bStreamType.constraint = readTypeFromCp(); bStreamType.completionType = readTypeFromCp(); - bStreamType.flags = flags; + bStreamType.setFlags(flags); return bStreamType; case TypeTags.TABLE: - BTableType bTableType = new BTableType(TypeTags.TABLE, null, symTable.tableType.tsymbol, flags); + BTableType bTableType = new BTableType(symTable.typeEnv(), null, + symTable.tableType.tsymbol, flags); bTableType.constraint = readTypeFromCp(); boolean hasFieldNameList = inputStream.readByte() == 1; @@ -1386,16 +1424,17 @@ public BType readType(int cpI) throws IOException { } return bTableType; case TypeTags.MAP: - BMapType bMapType = new BMapType(TypeTags.MAP, null, symTable.mapType.tsymbol, flags); + BMapType bMapType = new BMapType(symTable.typeEnv(), TypeTags.MAP, null, symTable.mapType.tsymbol, + flags); bMapType.constraint = readTypeFromCp(); return bMapType; case TypeTags.INVOKABLE: - BInvokableType bInvokableType = new BInvokableType(null, null, null, null); + BInvokableType bInvokableType = new BInvokableType(typeEnv, List.of(), null, null, null); bInvokableType.tsymbol = Symbols.createInvokableTypeSymbol(SymTag.FUNCTION_TYPE, flags, env.pkgSymbol.pkgID, null, env.pkgSymbol.owner, symTable.builtinPos, COMPILED_SOURCE); - bInvokableType.flags = flags; + bInvokableType.setFlags(flags); if (inputStream.readBoolean()) { // Return if an any function return bInvokableType; @@ -1413,11 +1452,7 @@ public BType readType(int cpI) throws IOException { return setTSymbolForInvokableType(bInvokableType, bInvokableType.retType); // All the above types are branded types case TypeTags.ANY: - BType anyNominalType = typeParamAnalyzer.getNominalType(symTable.anyType, name, flags); - return isImmutable(flags) ? getEffectiveImmutableType(anyNominalType, - symTable.anyType.tsymbol.pkgID, - symTable.anyType.tsymbol.owner) : - anyNominalType; + return isImmutable(flags) ? BAnyType.newImmutableBAnyType() : new BAnyType(name, flags); case TypeTags.HANDLE: return symTable.handleType; case TypeTags.READONLY: @@ -1433,7 +1468,8 @@ public BType readType(int cpI) throws IOException { Names.EMPTY, env.pkgSymbol.pkgID, null, env.pkgSymbol.owner, symTable.builtinPos, COMPILED_SOURCE); - BArrayType bArrayType = new BArrayType(null, arrayTypeSymbol, size, BArrayState.valueOf(state), + BArrayType bArrayType = + new BArrayType(symTable.typeEnv(), null, arrayTypeSymbol, size, BArrayState.valueOf(state), flags); bArrayType.eType = readTypeFromCp(); return bArrayType; @@ -1453,13 +1489,14 @@ public BType readType(int cpI) throws IOException { null, env.pkgSymbol, symTable.builtinPos, COMPILED_SOURCE); int unionMemberCount = inputStream.readInt(); - BUnionType unionType = BUnionType.create(unionTypeSymbol, new LinkedHashSet<>(unionMemberCount)); + BUnionType unionType = + BUnionType.create(types.typeEnv(), unionTypeSymbol, new LinkedHashSet<>(unionMemberCount)); unionType.name = unionName; addShapeCP(unionType, cpI); compositeStack.push(unionType); - unionType.flags = flags; + unionType.setFlags(flags); unionType.isCyclic = isCyclic; for (int i = 0; i < unionMemberCount; i++) { unionType.add(readTypeFromCp()); @@ -1486,7 +1523,7 @@ public BType readType(int cpI) throws IOException { } else { pkgEnv = symTable.pkgEnvMap.get(packageCache.getSymbol(unionsPkgId)); if (pkgEnv != null) { - BType existingUnionType = getType(unionType, pkgEnv, unionName); + BType existingUnionType = lookupSymbolInMainSpace(pkgEnv, unionName); if (existingUnionType != symTable.noType) { return existingUnionType; } @@ -1538,13 +1575,13 @@ public BType readType(int cpI) throws IOException { errorSymbol = new BErrorTypeSymbol(SymTag.ERROR, Flags.PUBLIC, Names.EMPTY, env.pkgSymbol.pkgID, null, env.pkgSymbol, symTable.builtinPos, COMPILED_SOURCE); } - BErrorType errorType = new BErrorType(errorSymbol); + BErrorType errorType = new BErrorType(symTable.typeEnv(), errorSymbol); addShapeCP(errorType, cpI); compositeStack.push(errorType); String errorName = getStringCPEntryValue(inputStream); BType detailsType = readTypeFromCp(); errorType.detailType = detailsType; - errorType.flags = flags; + errorType.setFlags(flags); errorSymbol.type = errorType; errorSymbol.pkgID = pkgId; errorSymbol.originalName = errorSymbol.name = Names.fromString(errorName); @@ -1583,8 +1620,8 @@ public BType readType(int cpI) throws IOException { tupleMembers.add(new BTupleMember(memberType, varSymbol)); } - BTupleType bTupleType = new BTupleType(tupleTypeSymbol, tupleMembers); - bTupleType.flags = flags; + BTupleType bTupleType = new BTupleType(symTable.typeEnv(), tupleTypeSymbol, tupleMembers); + bTupleType.setFlags(flags); if (inputStream.readBoolean()) { bTupleType.restType = readTypeFromCp(); @@ -1592,9 +1629,9 @@ public BType readType(int cpI) throws IOException { return bTupleType; case TypeTags.FUTURE: - BFutureType bFutureType = new BFutureType(TypeTags.FUTURE, null, symTable.futureType.tsymbol); + BFutureType bFutureType = new BFutureType(symTable.typeEnv(), null, symTable.futureType.tsymbol); bFutureType.constraint = readTypeFromCp(); - bFutureType.flags = flags; + bFutureType.setFlags(flags); return bFutureType; case TypeTags.FINITE: String finiteTypeName = getStringCPEntryValue(inputStream); @@ -1604,13 +1641,14 @@ public BType readType(int cpI) throws IOException { null, env.pkgSymbol, symTable.builtinPos, COMPILED_SOURCE); symbol.scope = new Scope(symbol); - BFiniteType finiteType = new BFiniteType(symbol); - finiteType.flags = flags; - symbol.type = finiteType; - int valueSpaceSize = inputStream.readInt(); - for (int i = 0; i < valueSpaceSize; i++) { - defineValueSpace(inputStream, finiteType, this); + int valueSpaceLength = inputStream.readInt(); + SemNamedType[] valueSpace = new SemNamedType[valueSpaceLength]; + for (int i = 0; i < valueSpaceLength; i++) { + valueSpace[i] = readSemNamedType(); } + BFiniteType finiteType = new BFiniteType(symbol, valueSpace); + finiteType.setFlags(flags); + symbol.type = finiteType; return finiteType; case TypeTags.OBJECT: pkgCpIndex = inputStream.readInt(); @@ -1633,8 +1671,8 @@ public BType readType(int cpI) throws IOException { objectSymbol.scope = new Scope(objectSymbol); BObjectType objectType; // Below is a temporary fix, need to fix this properly by using the type tag - objectType = new BObjectType(objectSymbol); - objectType.flags = flags; + objectType = new BObjectType(symTable.typeEnv(), objectSymbol); + objectType.setFlags(flags); objectSymbol.type = objectType; addShapeCP(objectType, cpI); compositeStack.push(objectType); @@ -1687,7 +1725,7 @@ public BType readType(int cpI) throws IOException { } pkgEnv = symTable.pkgEnvMap.get(packageCache.getSymbol(pkgId)); - return getType(objectType, pkgEnv, Names.fromString(objName)); + return lookupSymbolInMainSpace(pkgEnv, Names.fromString(objName)); case TypeTags.BYTE_ARRAY: // TODO fix break; @@ -1814,7 +1852,7 @@ private void populateIntersectionTypeReferencedFunctions(DataInputStream inputSt setInvokableTypeSymbol(attachedFuncType); - if (!Symbols.isFlagOn(attachedFuncType.flags, Flags.ANY_FUNCTION)) { + if (!Symbols.isFlagOn(attachedFuncType.getFlags(), Flags.ANY_FUNCTION)) { BInvokableTypeSymbol tsymbol = (BInvokableTypeSymbol) attachedFuncType.tsymbol; attachedFuncSymbol.params = tsymbol.params; attachedFuncSymbol.restParam = tsymbol.restParam; @@ -1825,45 +1863,294 @@ private void populateIntersectionTypeReferencedFunctions(DataInputStream inputSt objectSymbol.attachedFuncs.add(attachedFunction); objectSymbol.scope.define(funcName, attachedFuncSymbol); } - } - private BType getType(BType readShape, SymbolEnv pkgEnv, Name name) { - BType type = symbolResolver.lookupSymbolInMainSpace(pkgEnv, name).type; + private Optional readNullableString() throws IOException { + boolean hasNonNullString = inputStream.readBoolean(); + if (hasNonNullString) { + return Optional.of(getStringCPEntryValue(inputStream)); + } else { + return Optional.empty(); + } + } - if (type != symTable.noType && (!name.value.contains(ANON_PREFIX) || types.isSameBIRShape(readShape, type))) { - return type; + private SemNamedType readSemNamedType() throws IOException { + SemType semType = readSemType(); + Optional optName = readNullableString(); + return new SemNamedType(semType, optName); } - if (pkgEnv.node != null) { - for (BLangTypeDefinition typeDefinition : ((BLangPackage) pkgEnv.node).typeDefinitions) { - BSymbol symbol = typeDefinition.symbol; + // --------------------------------------- Read SemType ---------------------------------------------- - String typeDefName = typeDefinition.name.value; - if (typeDefName.contains(ANON_PREFIX)) { - BType anonType = symbol.type; + private SemType readSemType() throws IOException { + if (!inputStream.readBoolean()) { + return null; + } - if (types.isSameBIRShape(readShape, anonType)) { - return anonType; - } - } else if (typeDefName.equals(name.value)) { - return symbol.type; - } + if (inputStream.readBoolean()) { + int bitset = inputStream.readInt(); + return BasicTypeBitSet.from(bitset); } - } else { - for (Map.Entry value : pkgEnv.scope.entries.entrySet()) { - BSymbol symbol = value.getValue().symbol; - if (value.getKey().value.contains(ANON_PREFIX)) { - BType anonType = symbol.type; + int all = inputStream.readInt(); + int some = inputStream.readInt(); + byte subtypeDataListLength = inputStream.readByte(); + ProperSubtypeData[] subtypeList = new ProperSubtypeData[subtypeDataListLength]; + for (int i = 0; i < subtypeDataListLength; i++) { + subtypeList[i] = readProperSubtypeData(); + } + return createSemType(all, some, subtypeList); + } - if (types.isSameBIRShape(readShape, anonType)) { - return anonType; - } + private ProperSubtypeData readProperSubtypeData() throws IOException { + switch (inputStream.readByte()) { + case 1: + return readBdd(); + case 2: + return readIntSubtype(); + case 3: + return BooleanSubtype.from(inputStream.readBoolean()); + case 4: + return readFloatSubtype(); + case 5: + return readDecimalSubType(); + case 6: + return readStringSubtype(); + case 7: + return readXmlSubtype(); + default: + throw new IllegalStateException("Unexpected ProperSubtypeData kind"); + } + } + + private Bdd readBdd() throws IOException { + boolean isBddNode = inputStream.readBoolean(); + if (isBddNode) { + return readBddNode(); + } else { + boolean isAll = inputStream.readBoolean(); + return isAll ? BddAllOrNothing.bddAll() : BddAllOrNothing.bddNothing(); + } + } + + enum AtomKind { + REC, + INLINED, + TYPE + } + + private AtomKind readAtomKind() throws IOException { + return switch (inputStream.readByte()) { + case 0 -> AtomKind.REC; + case 1 -> AtomKind.INLINED; + case 2 -> AtomKind.TYPE; + default -> throw new IllegalStateException("Unexpected AtomKind kind"); + }; + } + + private BddNode readBddNode() throws IOException { + AtomKind atomKind = readAtomKind(); + Atom atom = switch (atomKind) { + case REC -> readRecAtom(); + case INLINED -> readInlinedAtom(); + case TYPE -> { + TypeAtom typeAtom = readTypeAtom(); + typeEnv.deserializeTypeAtom(typeAtom); + yield typeAtom; } + }; + + Bdd left = readBdd(); + Bdd middle = readBdd(); + Bdd right = readBdd(); + return BddNode.create(atom, left, middle, right); + } + + private Atom readInlinedAtom() throws IOException { + int recAtomIndex = inputStream.readInt(); + assert recAtomIndex != BDD_REC_ATOM_READONLY; + AtomicType atomicType = readTypeAtom().atomicType(); + Atom.Kind kind; + if (atomicType instanceof MappingAtomicType) { + recAtomIndex += offsets.mappingOffset(); + kind = Atom.Kind.MAPPING_ATOM; + } else if (atomicType instanceof ListAtomicType) { + recAtomIndex += offsets.listOffset(); + kind = Atom.Kind.LIST_ATOM; + } else if (atomicType instanceof FunctionAtomicType) { + recAtomIndex += offsets.functionOffset(); + kind = Atom.Kind.FUNCTION_ATOM; + } else { + throw new IllegalStateException("Unexpected inlined atomicType kind"); } + typeEnv.insertRecAtomAtIndex(recAtomIndex, atomicType); + RecAtom recAtom = RecAtom.createRecAtom(recAtomIndex); + recAtom.setKind(kind); + return recAtom; } - return type; + private TypeAtom readTypeAtom() throws IOException { + int index = inputStream.readInt() + offsets.atomOffset(); + AtomicType atomicType = switch (inputStream.readByte()) { + case 1 -> readMappingAtomicType(); + case 2 -> readListAtomicType(); + case 3 -> readFunctionAtomicType(); + case 4 -> readCellAtomicType(); + default -> throw new IllegalStateException("Unexpected atomicType kind"); + }; + return TypeAtom.createTypeAtom(index, atomicType); + } + + private RecAtom readRecAtom() throws IOException { + int index = inputStream.readInt(); + Optional predefinedRecAtom = predefinedTypeEnv.getPredefinedRecAtom(index); + if (predefinedRecAtom.isPresent()) { + return predefinedRecAtom.get(); + } + int kindOrdinal = inputStream.readInt(); + Atom.Kind kind = Atom.Kind.values()[kindOrdinal]; + int offset = switch (kind) { + case LIST_ATOM -> offsets.listOffset(); + case FUNCTION_ATOM -> offsets.functionOffset(); + case MAPPING_ATOM -> offsets.mappingOffset(); + case DISTINCT_ATOM -> (-offsets.distinctOffset()); + case XML_ATOM -> 0; + case CELL_ATOM -> throw new IllegalStateException("Cell atom cannot be recursive"); + }; + index += offset; + RecAtom recAtom = RecAtom.createRecAtom(index); + recAtom.setKind(kind); + return recAtom; + + } + + private CellAtomicType readCellAtomicType() throws IOException { + SemType ty = readSemType(); + byte ordinal = inputStream.readByte(); + CellAtomicType.CellMutability mut = CellAtomicType.CellMutability.values()[ordinal]; + return CellAtomicType.from(ty, mut); + } + + private MappingAtomicType readMappingAtomicType() throws IOException { + int namesLength = inputStream.readInt(); + String[] names = new String[namesLength]; + for (int i = 0; i < namesLength; i++) { + names[i] = getStringCPEntryValue(inputStream); + } + + int typesLength = inputStream.readInt(); + CellSemType[] types = new CellSemType[typesLength]; + for (int i = 0; i < typesLength; i++) { + types[i] = (CellSemType) readSemType(); + } + + CellSemType rest = (CellSemType) readSemType(); + return MappingAtomicType.from(names, types, rest); + } + + private ListAtomicType readListAtomicType() throws IOException { + int initialLength = inputStream.readInt(); + List initial = new ArrayList<>(initialLength); + for (int i = 0; i < initialLength; i++) { + initial.add((CellSemType) readSemType()); + } + + int fixedLength = inputStream.readInt(); + FixedLengthArray members = FixedLengthArray.from(initial, fixedLength); + + CellSemType rest = (CellSemType) readSemType(); + return ListAtomicType.from(members, rest); + } + + private static ComplexSemType createSemType(int all, int some, ProperSubtypeData[] subtypeList) { + if (some == PredefinedType.CELL.bitset && all == 0) { + return CellSemType.from(subtypeList); + } + return ComplexSemType.createComplexSemType(all, some, subtypeList); + } + + private FunctionAtomicType readFunctionAtomicType() throws IOException { + SemType paramType = readSemType(); + SemType retType = readSemType(); + SemType qualifiers = readSemType(); + boolean isGeneric = inputStream.readBoolean(); + return isGeneric ? FunctionAtomicType.genericFrom(paramType, retType, qualifiers) : + FunctionAtomicType.from(paramType, retType, qualifiers); + } + + private IntSubtype readIntSubtype() throws IOException { + int rangesLength = inputStream.readInt(); + Range[] ranges = new Range[rangesLength]; + for (int i = 0; i < rangesLength; i++) { + long min = inputStream.readLong(); + long max = inputStream.readLong(); + ranges[i] = new Range(min, max); + + } + return IntSubtype.createIntSubtype(ranges); + } + + private FloatSubtype readFloatSubtype() throws IOException { + boolean allowed = inputStream.readBoolean(); + int valuesLength = inputStream.readInt(); + EnumerableFloat[] values = new EnumerableFloat[valuesLength]; + for (int i = 0; i < valuesLength; i++) { + values[i] = EnumerableFloat.from(inputStream.readDouble()); + } + + return (FloatSubtype) FloatSubtype.createFloatSubtype(allowed, values); + } + + private DecimalSubtype readDecimalSubType() throws IOException { + boolean allowed = inputStream.readBoolean(); + int valuesLength = inputStream.readInt(); + EnumerableDecimal[] values = new EnumerableDecimal[valuesLength]; + for (int i = 0; i < valuesLength; i++) { + int scale = inputStream.readInt(); + int byteLen = inputStream.readInt(); + byte[] unscaleValueBytes = inputStream.readNBytes(byteLen); + BigDecimal bigDecimal = new BigDecimal(new BigInteger(unscaleValueBytes), scale); + values[i] = EnumerableDecimal.from(bigDecimal); + } + return (DecimalSubtype) DecimalSubtype.createDecimalSubtype(allowed, values); + } + + private StringSubtype readStringSubtype() throws IOException { + CharStringSubtype charStringSubtype = readCharStringSubtype(); + NonCharStringSubtype nonCharStringSubtype = readNonCharStringSubtype(); + return StringSubtype.from(charStringSubtype, nonCharStringSubtype); + } + + private CharStringSubtype readCharStringSubtype() throws IOException { + boolean allowed = inputStream.readBoolean(); + int valuesLength = inputStream.readInt(); + EnumerableCharString[] values = new EnumerableCharString[valuesLength]; + for (int i = 0; i < valuesLength; i++) { + values[i] = EnumerableCharString.from(getStringCPEntryValue(inputStream)); + } + return CharStringSubtype.from(allowed, values); + } + + private NonCharStringSubtype readNonCharStringSubtype() throws IOException { + boolean allowed = inputStream.readBoolean(); + int valuesLength = inputStream.readInt(); + EnumerableString[] values = new EnumerableString[valuesLength]; + for (int i = 0; i < valuesLength; i++) { + values[i] = EnumerableString.from(getStringCPEntryValue(inputStream)); + } + return NonCharStringSubtype.from(allowed, values); + } + + private XmlSubtype readXmlSubtype() throws IOException { + int primitives = inputStream.readInt(); + Bdd sequence = readBdd(); + return XmlSubtype.from(primitives, sequence); + } + + // --------------------------------------- End of SemType ----------------------------------------------- + } + + private BType lookupSymbolInMainSpace(SymbolEnv pkgEnv, Name name) { + return symbolResolver.lookupSymbolInMainSpace(pkgEnv, name).type; } private byte[] readDocBytes(DataInputStream inputStream) throws IOException { @@ -1886,54 +2173,6 @@ private PackageID getPackageId(int pkgCPIndex) { Names.fromString(moduleName), Names.fromString(version), null); } - private void defineValueSpace(DataInputStream dataInStream, BFiniteType finiteType, BIRTypeReader typeReader) - throws IOException { - BType valueType = typeReader.readTypeFromCp(); - - dataInStream.readInt(); // read and ignore value length - - BLangLiteral litExpr = createLiteralBasedOnType(valueType); - switch (valueType.tag) { - case TypeTags.INT: - int integerCpIndex = dataInStream.readInt(); - IntegerCPEntry integerCPEntry = (IntegerCPEntry) this.env.constantPool[integerCpIndex]; - litExpr.value = integerCPEntry.value; - break; - case TypeTags.BYTE: - int byteCpIndex = dataInStream.readInt(); - ByteCPEntry byteCPEntry = (ByteCPEntry) this.env.constantPool[byteCpIndex]; - litExpr.value = byteCPEntry.value; - break; - case TypeTags.FLOAT: - int floatCpIndex = dataInStream.readInt(); - FloatCPEntry floatCPEntry = (FloatCPEntry) this.env.constantPool[floatCpIndex]; - litExpr.value = Double.toString(floatCPEntry.value); - break; - case TypeTags.STRING: - case TypeTags.DECIMAL: - litExpr.value = getStringCPEntryValue(dataInStream); - break; - case TypeTags.BOOLEAN: - litExpr.value = dataInStream.readBoolean(); - break; - case TypeTags.NIL: - litExpr.originalValue = "null"; - break; - default: - throw new UnsupportedOperationException("finite type value is not supported for type: " + valueType); - } - - litExpr.setBType(valueType); - - finiteType.addValue(litExpr); - } - - private BLangLiteral createLiteralBasedOnType(BType valueType) { - NodeKind nodeKind = valueType.tag <= TypeTags.DECIMAL ? NodeKind.NUMERIC_LITERAL : NodeKind.LITERAL; - return nodeKind == NodeKind.LITERAL ? (BLangLiteral) TreeBuilder.createLiteralExpression() : - (BLangLiteral) TreeBuilder.createNumericLiteralExpression(); - } - private boolean isImmutable(long flags) { return Symbols.isFlagOn(flags, Flags.READONLY); } @@ -1948,4 +2187,18 @@ private BType getEffectiveImmutableType(BType type, PackageID pkgID, BSymbol own return ImmutableTypeCloner.getEffectiveImmutableType(null, types, type, pkgID, owner, symTable, null, names); } + + private record AtomOffsets(int atomOffset, int listOffset, int functionOffset, int mappingOffset, + int distinctOffset) { + + static AtomOffsets from(Env env) { + PredefinedTypeEnv predefinedTypeEnv = PredefinedTypeEnv.getInstance(); + int recAtomOffset = predefinedTypeEnv.reservedRecAtomCount(); + return new AtomOffsets(env.atomCount(), + env.recListAtomCount() - recAtomOffset, + env.recFunctionAtomCount(), + env.recMappingAtomCount() - recAtomOffset, + env.distinctAtomCount()); + } + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/BIRGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/BIRGen.java index b834de6199f8..21665336fe6b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/BIRGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/BIRGen.java @@ -22,6 +22,7 @@ import io.ballerina.tools.diagnostics.Location; import io.ballerina.tools.text.LinePosition; import io.ballerina.tools.text.LineRange; +import io.ballerina.types.PredefinedType; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; @@ -59,6 +60,7 @@ import org.wso2.ballerinalang.compiler.bir.model.VarScope; import org.wso2.ballerinalang.compiler.bir.optimizer.BIROptimizer; import org.wso2.ballerinalang.compiler.diagnostic.BLangDiagnosticLocation; +import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BAnnotationSymbol; @@ -634,7 +636,7 @@ public void visit(BLangFunction astFunc) { // TODO: Return variable with NIL type should be written to BIR // Special %0 location for storing return values - BType retType = unifier.build(astFunc.symbol.type.getReturnType()); + BType retType = unifier.build(symTable.typeEnv(), astFunc.symbol.type.getReturnType()); birFunc.returnVariable = new BIRVariableDcl(astFunc.pos, retType, this.env.nextLocalVarId(names), VarScope.FUNCTION, VarKind.RETURN, null); birFunc.localVars.add(0, birFunc.returnVariable); @@ -2119,7 +2121,7 @@ public void visit(BLangXMLElementLiteral xmlElementLiteral) { BIRNonTerminator.NewXMLElement newXMLElement = new BIRNonTerminator.NewXMLElement(xmlElementLiteral.pos, toVarRef, startTagNameIndex, defaultNsURIVarRef, - Symbols.isFlagOn(xmlElementLiteral.getBType().flags, + Symbols.isFlagOn(xmlElementLiteral.getBType().getFlags(), Flags.READONLY)); setScopeAndEmit(newXMLElement); @@ -2185,7 +2187,7 @@ public void visit(BLangXMLCommentLiteral xmlCommentLiteral) { BIRNonTerminator.NewXMLComment newXMLComment = new BIRNonTerminator.NewXMLComment(xmlCommentLiteral.pos, toVarRef, xmlCommentIndex, - Symbols.isFlagOn(xmlCommentLiteral.getBType().flags, + Symbols.isFlagOn(xmlCommentLiteral.getBType().getFlags(), Flags.READONLY)); setScopeAndEmit(newXMLComment); this.env.targetOperand = toVarRef; @@ -2206,7 +2208,7 @@ public void visit(BLangXMLProcInsLiteral xmlProcInsLiteral) { BIRNonTerminator.NewXMLProcIns newXMLProcIns = new BIRNonTerminator.NewXMLProcIns(xmlProcInsLiteral.pos, toVarRef, dataIndex, targetIndex, - Symbols.isFlagOn(xmlProcInsLiteral.getBType().flags, + Symbols.isFlagOn(xmlProcInsLiteral.getBType().getFlags(), Flags.READONLY)); setScopeAndEmit(newXMLProcIns); this.env.targetOperand = toVarRef; @@ -2284,7 +2286,8 @@ public void visit(BLangTableConstructorExpr tableConstructorExpr) { BLangArrayLiteral dataLiteral = new BLangArrayLiteral(); dataLiteral.pos = tableConstructorExpr.pos; dataLiteral.setBType( - new BArrayType(((BTableType) Types.getImpliedType(tableConstructorExpr.getBType())).constraint)); + new BArrayType(symTable.typeEnv(), + ((BTableType) Types.getImpliedType(tableConstructorExpr.getBType())).constraint)); dataLiteral.exprs = new ArrayList<>(tableConstructorExpr.recordLiteralList); dataLiteral.accept(this); BIROperand dataOp = this.env.targetOperand; @@ -2723,7 +2726,7 @@ private void generateListConstructorExpr(BLangListConstructorExpr listConstructo BType referredType = Types.getImpliedType(listConstructorExprType); if (referredType.tag == TypeTags.ARRAY && ((BArrayType) referredType).state != BArrayState.OPEN) { - size = ((BArrayType) referredType).size; + size = ((BArrayType) referredType).getSize(); } else if (referredType.tag == TypeTags.TUPLE) { typedescOp = this.env.targetOperand; size = exprs.size(); @@ -2809,10 +2812,7 @@ private void generateMappingAccess(BLangIndexBasedAccess astIndexBasedAccessExpr if (astIndexBasedAccessExpr.getKind() == NodeKind.XML_ATTRIBUTE_ACCESS_EXPR) { insKind = InstructionKind.XML_ATTRIBUTE_STORE; keyRegIndex = getQNameOP(astIndexBasedAccessExpr.indexExpr, keyRegIndex); - } else if (astAccessExprExprType.tag == TypeTags.OBJECT || - (astAccessExprExprType.tag == TypeTags.UNION && - Types.getImpliedType(((BUnionType) astAccessExprExprType).getMemberTypes().iterator() - .next()).tag == TypeTags.OBJECT)) { + } else if (SemTypeHelper.isSubtypeSimple(astAccessExprExprType, PredefinedType.OBJECT)) { insKind = InstructionKind.OBJECT_STORE; } else { insKind = InstructionKind.MAP_STORE; @@ -2841,10 +2841,7 @@ private void generateMappingAccess(BLangIndexBasedAccess astIndexBasedAccessExpr keyRegIndex); this.varAssignment = false; return; - } else if (astAccessExprExprType.tag == TypeTags.OBJECT || - (astAccessExprExprType.tag == TypeTags.UNION && - Types.getImpliedType(((BUnionType) astAccessExprExprType).getMemberTypes().iterator() - .next()).tag == TypeTags.OBJECT)) { + } else if (SemTypeHelper.isSubtypeSimple(astAccessExprExprType, PredefinedType.OBJECT)) { insKind = InstructionKind.OBJECT_LOAD; } else { insKind = InstructionKind.MAP_LOAD; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCastGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCastGen.java index d9a5a619fb5d..d24945da2fc8 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCastGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCastGen.java @@ -17,6 +17,7 @@ */ package org.wso2.ballerinalang.compiler.bir.codegen; +import io.ballerina.types.Env; import org.ballerinalang.compiler.BLangCompilerException; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; @@ -633,7 +634,7 @@ private void generateJCastToBAny(MethodVisitor mv, BIRVarToJVMIndexMap indexMap, mv.visitTypeInsn(INSTANCEOF, SIMPLE_VALUE); mv.visitJumpInsn(IFNE, afterHandle); } - if (isNillable(targetType)) { + if (targetType.isNullable()) { mv.visitInsn(DUP); mv.visitJumpInsn(IFNULL, afterHandle); } @@ -654,15 +655,6 @@ private void generateJCastToBAny(MethodVisitor mv, BIRVarToJVMIndexMap indexMap, } } - private static boolean isNillable(BType targetType) { - return switch (targetType.tag) { - case TypeTags.NIL, TypeTags.NEVER, TypeTags.JSON, TypeTags.ANY, TypeTags.ANYDATA, TypeTags.READONLY -> true; - case TypeTags.UNION, TypeTags.INTERSECTION, TypeTags.FINITE -> targetType.isNullable(); - case TypeTags.TYPEREFDESC -> isNillable(JvmCodeGenUtil.getImpliedType(targetType)); - default -> false; - }; - } - private void generateCheckCastJToBJSON(MethodVisitor mv, BIRVarToJVMIndexMap indexMap, JType sourceType) { if (sourceType.jTag == JTypeTags.JREF || sourceType.jTag == JTypeTags.JARRAY) { return; @@ -1130,7 +1122,7 @@ void generateCheckCastToByte(MethodVisitor mv, BType sourceType) { public void generateCheckCastToAnyData(MethodVisitor mv, BType type) { BType sourceType = JvmCodeGenUtil.getImpliedType(type); if (sourceType.tag == TypeTags.UNION || (types.isAssignable(sourceType, symbolTable.anyType) && - !Symbols.isFlagOn(sourceType.flags, Flags.READONLY))) { + !Symbols.isFlagOn(sourceType.getFlags(), Flags.READONLY))) { checkCast(mv, symbolTable.anydataType); } else { // if value types, then ad box instruction @@ -1407,4 +1399,9 @@ private void generateCastToAny(MethodVisitor mv, BType type) { private void generateXMLToAttributesMap(MethodVisitor mv) { mv.visitMethodInsn(INVOKEVIRTUAL, XML_VALUE, "getAttributesMap", GET_ATTRAIBUTE_MAP, false); } + + public Env typeEnv() { + assert types.typeEnv() != null; + return types.typeEnv(); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCodeGenUtil.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCodeGenUtil.java index 64d8c77249d9..379aef0495af 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCodeGenUtil.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCodeGenUtil.java @@ -20,6 +20,7 @@ import io.ballerina.identifier.Utils; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Env; import org.apache.commons.lang3.StringUtils; import org.ballerinalang.compiler.BLangCompilerException; import org.ballerinalang.model.elements.PackageID; @@ -230,8 +231,7 @@ public static String getFieldTypeSignature(BType bType) { case TypeTags.DECIMAL -> GET_BDECIMAL; case TypeTags.BOOLEAN -> "Z"; case TypeTags.NIL, TypeTags.NEVER, TypeTags.ANY, TypeTags.ANYDATA, TypeTags.UNION, TypeTags.JSON, - TypeTags.FINITE, TypeTags.READONLY -> - GET_OBJECT; + TypeTags.FINITE, TypeTags.READONLY -> GET_OBJECT; case TypeTags.MAP, TypeTags.RECORD -> GET_MAP_VALUE; case TypeTags.STREAM -> GET_STREAM_VALUE; case TypeTags.TABLE -> GET_TABLE_VALUE; @@ -334,18 +334,19 @@ private static String cleanupSourceFileName(String name) { return name.replace(".", FILE_NAME_PERIOD_SEPERATOR); } - public static String getMethodDesc(List paramTypes, BType retType) { - return INITIAL_METHOD_DESC + getMethodDescParams(paramTypes) + generateReturnType(retType); + public static String getMethodDesc(Env typeEnv, List paramTypes, BType retType) { + return INITIAL_METHOD_DESC + getMethodDescParams(paramTypes) + generateReturnType(retType, typeEnv); } - public static String getMethodDesc(List paramTypes, BType retType, BType attachedType) { + public static String getMethodDesc(Env typeEnv, List paramTypes, BType retType, BType attachedType) { return INITIAL_METHOD_DESC + getArgTypeSignature(attachedType) + getMethodDescParams(paramTypes) + - generateReturnType(retType); + generateReturnType(retType, typeEnv); } - public static String getMethodDesc(List paramTypes, BType retType, String attachedTypeClassName) { + public static String getMethodDesc(Env typeEnv, List paramTypes, BType retType, + String attachedTypeClassName) { return INITIAL_METHOD_DESC + "L" + attachedTypeClassName + ";" + getMethodDescParams(paramTypes) + - generateReturnType(retType); + generateReturnType(retType, typeEnv); } public static String getMethodDescParams(List paramTypes) { @@ -372,8 +373,7 @@ public static String getArgTypeSignature(BType bType) { case TypeTags.DECIMAL -> GET_BDECIMAL; case TypeTags.BOOLEAN -> "Z"; case TypeTags.NIL, TypeTags.NEVER, TypeTags.ANYDATA, TypeTags.UNION, TypeTags.JSON, TypeTags.FINITE, - TypeTags.ANY, TypeTags.READONLY -> - GET_OBJECT; + TypeTags.ANY, TypeTags.READONLY -> GET_OBJECT; case TypeTags.ARRAY, TypeTags.TUPLE -> GET_ARRAY_VALUE; case TypeTags.ERROR -> GET_ERROR_VALUE; case TypeTags.MAP, TypeTags.RECORD -> GET_MAP_VALUE; @@ -389,13 +389,13 @@ public static String getArgTypeSignature(BType bType) { }; } - public static String generateReturnType(BType bType) { + public static String generateReturnType(BType bType, Env typeEnv) { bType = JvmCodeGenUtil.getImpliedType(bType); if (bType == null) { return RETURN_JOBJECT; } - bType = JvmCodeGenUtil.UNIFIER.build(bType); + bType = JvmCodeGenUtil.UNIFIER.build(typeEnv, bType); if (bType == null || bType.tag == TypeTags.NIL || bType.tag == TypeTags.NEVER) { return RETURN_JOBJECT; } else if (TypeTags.isIntegerTypeTag(bType.tag)) { @@ -419,8 +419,7 @@ public static String generateReturnType(BType bType) { case TypeTags.FUTURE -> RETURN_FUTURE_VALUE; case TypeTags.TYPEDESC -> RETURN_TYPEDESC_VALUE; case TypeTags.ANY, TypeTags.ANYDATA, TypeTags.UNION, TypeTags.INTERSECTION, TypeTags.JSON, - TypeTags.FINITE, TypeTags.READONLY -> - RETURN_JOBJECT; + TypeTags.FINITE, TypeTags.READONLY -> RETURN_JOBJECT; case TypeTags.OBJECT -> RETURN_B_OBJECT; case TypeTags.INVOKABLE -> RETURN_FUNCTION_POINTER; case TypeTags.HANDLE -> RETURN_HANDLE_VALUE; @@ -629,12 +628,12 @@ public static void loadConstantValue(BType bType, Object constVal, MethodVisitor } } - private static String getStringConstantsClass(int varIndex, JvmConstantsGen jvmConstantsGen) { + static String getStringConstantsClass(int varIndex, JvmConstantsGen jvmConstantsGen) { int classIndex = varIndex / MAX_STRINGS_PER_METHOD; return jvmConstantsGen.getStringConstantsClass() + UNDERSCORE + classIndex; } - private static String removeDecimalDiscriminator(String value) { + static String removeDecimalDiscriminator(String value) { int length = value.length(); if (length < 2) { return value; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmDesugarPhase.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmDesugarPhase.java index 8f5a0f31cbc3..909ab80efe84 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmDesugarPhase.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmDesugarPhase.java @@ -19,6 +19,7 @@ package org.wso2.ballerinalang.compiler.bir.codegen; import io.ballerina.identifier.Utils; +import io.ballerina.types.Env; import org.ballerinalang.model.elements.PackageID; import org.wso2.ballerinalang.compiler.bir.codegen.methodgen.InitMethodGen; import org.wso2.ballerinalang.compiler.bir.model.BIRNode; @@ -61,9 +62,9 @@ public final class JvmDesugarPhase { private JvmDesugarPhase() { } - public static void addDefaultableBooleanVarsToSignature(BIRFunction func) { - func.type = new BInvokableType(func.type.paramTypes, func.type.restType, - func.type.retType, func.type.tsymbol); + public static void addDefaultableBooleanVarsToSignature(Env env, BIRFunction func) { + func.type = + new BInvokableType(env, func.type.paramTypes, func.type.restType, func.type.retType, func.type.tsymbol); BInvokableType type = func.type; func.type.paramTypes = updateParamTypesWithDefaultableBooleanVar(func.type.paramTypes, type.restType); @@ -94,7 +95,7 @@ private static List updateParamTypesWithDefaultableBooleanVar(List return paramTypes; } - static void rewriteRecordInits(List typeDefs) { + static void rewriteRecordInits(Env env, List typeDefs) { for (BIRTypeDefinition typeDef : typeDefs) { BType recordType = JvmCodeGenUtil.getImpliedType(typeDef.type); if (recordType.tag != TypeTags.RECORD) { @@ -102,12 +103,12 @@ static void rewriteRecordInits(List typeDefs) { } List attachFuncs = typeDef.attachedFuncs; for (BIRFunction func : attachFuncs) { - rewriteRecordInitFunction(func, (BRecordType) recordType); + rewriteRecordInitFunction(env, func, (BRecordType) recordType); } } } - private static void rewriteRecordInitFunction(BIRFunction func, BRecordType recordType) { + private static void rewriteRecordInitFunction(Env env, BIRFunction func, BRecordType recordType) { BIRVariableDcl receiver = func.receiver; @@ -129,7 +130,7 @@ private static void rewriteRecordInitFunction(BIRFunction func, BRecordType reco List updatedParamTypes = Lists.of(receiver.type); updatedParamTypes.addAll(func.type.paramTypes); - func.type = new BInvokableType(updatedParamTypes, func.type.restType, func.type.retType, null); + func.type = new BInvokableType(env, updatedParamTypes, func.type.restType, func.type.retType, null); List localVars = func.localVars; List updatedLocalVars = new ArrayList<>(); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmInstructionGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmInstructionGen.java index 7ba8024a01db..9f30abec25f5 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmInstructionGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmInstructionGen.java @@ -1620,7 +1620,7 @@ void generateObjectNewIns(BIRNonTerminator.NewInstance objectNewIns, int strandI } private void reloadObjectCtorAnnots(BType type, int strandIndex) { - if ((type.flags & Flags.OBJECT_CTOR) == Flags.OBJECT_CTOR) { + if ((type.getFlags() & Flags.OBJECT_CTOR) == Flags.OBJECT_CTOR) { this.mv.visitTypeInsn(CHECKCAST, OBJECT_TYPE_IMPL); mv.visitMethodInsn(INVOKEVIRTUAL, OBJECT_TYPE_IMPL, "duplicate", OBJECT_TYPE_DUPLICATE, false); this.mv.visitInsn(DUP); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmObservabilityGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmObservabilityGen.java index 11231092bd9e..d655f0f47495 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmObservabilityGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmObservabilityGen.java @@ -19,6 +19,7 @@ import io.ballerina.identifier.Utils; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.PredefinedType; import org.ballerinalang.compiler.BLangCompilerException; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; @@ -53,6 +54,7 @@ import org.wso2.ballerinalang.compiler.bir.model.VarKind; import org.wso2.ballerinalang.compiler.bir.model.VarScope; import org.wso2.ballerinalang.compiler.diagnostic.BLangDiagnosticLocation; +import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; import org.wso2.ballerinalang.compiler.semantics.model.Scope; import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BAttachedFunction; @@ -63,11 +65,9 @@ import org.wso2.ballerinalang.compiler.semantics.model.symbols.BSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BVarSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.SymTag; -import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.util.Name; import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -181,7 +181,7 @@ public void instrumentPackage(BIRPackage pkg) { if ((typeDef.flags & Flags.CLASS) != Flags.CLASS && bType.tag == TypeTags.OBJECT) { continue; } - boolean isService = (bType.flags & Flags.SERVICE) == Flags.SERVICE; + boolean isService = (bType.getFlags() & Flags.SERVICE) == Flags.SERVICE; String serviceName = null; if (isService) { for (BIRNode.BIRAnnotationAttachment annotationAttachment : typeDef.annotAttachments) { @@ -360,7 +360,7 @@ private void rewriteAsyncInvocations(BIRFunction func, BIRTypeDefinition attache } Name lambdaName = new Name(LAMBDA_PREFIX + "observability" + lambdaIndex++ + "$" + asyncCallIns.name.getValue().replace(".", "_")); - BInvokableType bInvokableType = new BInvokableType(argTypes, null, + BInvokableType bInvokableType = new BInvokableType(symbolTable.typeEnv(), argTypes, null, returnType, null); BIRFunction desugaredFunc = new BIRFunction(asyncCallIns.pos, lambdaName, 0, bInvokableType, func.workerName, 0, VIRTUAL); @@ -986,20 +986,7 @@ private boolean isObservable(Call callIns) { * @return True if an error can be assigned and false otherwise */ private boolean isErrorAssignable(BIRVariableDcl variableDcl) { - boolean isErrorAssignable = false; - if (variableDcl.type instanceof BUnionType returnUnionType) { - boolean b = false; - for (BType type : returnUnionType.getMemberTypes()) { - if (type instanceof BErrorType) { - b = true; - break; - } - } - isErrorAssignable = b; - } else if (variableDcl.type instanceof BErrorType) { - isErrorAssignable = true; - } - return isErrorAssignable; + return SemTypeHelper.containsBasicType(variableDcl.type, PredefinedType.ERROR); } /** diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java index 65643e45ef72..f5066bc4f697 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java @@ -19,6 +19,7 @@ package org.wso2.ballerinalang.compiler.bir.codegen; import io.ballerina.identifier.Utils; +import io.ballerina.types.Env; import org.ballerinalang.compiler.BLangCompilerException; import org.ballerinalang.model.elements.PackageID; import org.ballerinalang.model.symbols.SymbolKind; @@ -61,7 +62,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.symbols.BSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.util.Name; import org.wso2.ballerinalang.compiler.util.Names; @@ -149,6 +149,7 @@ public class JvmPackageGen { private final BLangDiagnosticLog dlog; private final Types types; private final boolean isRemoteMgtEnabled; + private final Env typeEnv; JvmPackageGen(SymbolTable symbolTable, PackageCache packageCache, BLangDiagnosticLog dlog, Types types, boolean isRemoteMgtEnabled) { @@ -163,6 +164,7 @@ public class JvmPackageGen { initMethodGen = new InitMethodGen(symbolTable); configMethodGen = new ConfigMethodGen(); JvmInstructionGen.anyType = symbolTable.anyType; + this.typeEnv = symbolTable.typeEnv(); } private static String getBvmAlias(String orgName, String moduleName) { @@ -315,20 +317,21 @@ private static void setCurrentModuleField(ClassWriter cw, MethodVisitor mv, JvmC mv.visitFieldInsn(PUTSTATIC, moduleInitClass, CURRENT_MODULE_VAR_NAME, GET_MODULE); } - public static BIRFunctionWrapper getFunctionWrapper(BIRFunction currentFunc, PackageID packageID, + public static BIRFunctionWrapper getFunctionWrapper(Env typeEnv, BIRFunction currentFunc, PackageID packageID, String moduleClass) { BInvokableType functionTypeDesc = currentFunc.type; BIRVariableDcl receiver = currentFunc.receiver; BType retType = functionTypeDesc.retType; - if (isExternFunc(currentFunc) && Symbols.isFlagOn(retType.flags, Flags.PARAMETERIZED)) { - retType = unifier.build(retType); + if (isExternFunc(currentFunc) && Symbols.isFlagOn(retType.getFlags(), Flags.PARAMETERIZED)) { + retType = unifier.build(typeEnv, retType); } String jvmMethodDescription; if (receiver == null) { - jvmMethodDescription = JvmCodeGenUtil.getMethodDesc(functionTypeDesc.paramTypes, retType); + jvmMethodDescription = JvmCodeGenUtil.getMethodDesc(typeEnv, functionTypeDesc.paramTypes, retType); } else { - jvmMethodDescription = JvmCodeGenUtil.getMethodDesc(functionTypeDesc.paramTypes, retType, receiver.type); + jvmMethodDescription = JvmCodeGenUtil.getMethodDesc(typeEnv, functionTypeDesc.paramTypes, retType, + receiver.type); } return new BIRFunctionWrapper(packageID, currentFunc, moduleClass, jvmMethodDescription); } @@ -487,11 +490,12 @@ private void linkTypeDefinitions(BIRPackage module, boolean isEntry) { } private void linkModuleFunction(PackageID packageID, String initClass, String funcName) { - BInvokableType funcType = new BInvokableType(Collections.emptyList(), null, new BNilType(), null); + BInvokableType funcType = + new BInvokableType(typeEnv, Collections.emptyList(), null, symbolTable.nilType, null); BIRFunction moduleStopFunction = new BIRFunction(null, new Name(funcName), 0, funcType, new Name(""), 0, VIRTUAL); birFunctionMap.put(JvmCodeGenUtil.getPackageName(packageID) + funcName, - getFunctionWrapper(moduleStopFunction, packageID, initClass)); + getFunctionWrapper(typeEnv, moduleStopFunction, packageID, initClass)); } private void linkModuleFunctions(BIRPackage birPackage, String initClass, boolean isEntry, @@ -513,20 +517,20 @@ private void linkModuleFunctions(BIRPackage birPackage, String initClass, boolea PackageID packageID = birPackage.packageID; jvmClassMap.put(initClass, klass); String pkgName = JvmCodeGenUtil.getPackageName(packageID); - birFunctionMap.put(pkgName + functionName, getFunctionWrapper(initFunc, packageID, initClass)); + birFunctionMap.put(pkgName + functionName, getFunctionWrapper(typeEnv, initFunc, packageID, initClass)); count += 1; // Add start function BIRFunction startFunc = functions.get(1); functionName = Utils.encodeFunctionIdentifier(startFunc.name.value); - birFunctionMap.put(pkgName + functionName, getFunctionWrapper(startFunc, packageID, initClass)); + birFunctionMap.put(pkgName + functionName, getFunctionWrapper(typeEnv, startFunc, packageID, initClass)); klass.functions.add(1, startFunc); count += 1; // Add stop function BIRFunction stopFunc = functions.get(2); functionName = Utils.encodeFunctionIdentifier(stopFunc.name.value); - birFunctionMap.put(pkgName + functionName, getFunctionWrapper(stopFunc, packageID, initClass)); + birFunctionMap.put(pkgName + functionName, getFunctionWrapper(typeEnv, stopFunc, packageID, initClass)); klass.functions.add(2, stopFunc); count += 1; int genMethodsCount = 0; @@ -585,12 +589,13 @@ private BIRFunctionWrapper getBirFunctionWrapper(boolean isEntry, PackageID pack BIRFunction birFunc, String birModuleClassName) { BIRFunctionWrapper birFuncWrapperOrError; if (isExternFunc(birFunc) && isEntry) { - birFuncWrapperOrError = createExternalFunctionWrapper(true, birFunc, packageID, birModuleClassName); + birFuncWrapperOrError = createExternalFunctionWrapper(typeEnv, true, birFunc, packageID, + birModuleClassName); } else { if (isEntry && birFunc.receiver == null) { - addDefaultableBooleanVarsToSignature(birFunc); + addDefaultableBooleanVarsToSignature(typeEnv, birFunc); } - birFuncWrapperOrError = getFunctionWrapper(birFunc, packageID, birModuleClassName); + birFuncWrapperOrError = getFunctionWrapper(typeEnv, birFunc, packageID, birModuleClassName); } return birFuncWrapperOrError; } @@ -685,8 +690,8 @@ CompiledJarFile generate(BIRPackage module) { // use a ByteArrayOutputStream to store class byte values final JarEntries jarEntries = compiledJarFile.jarEntries; // desugar parameter initialization - injectDefaultParamInits(module, initMethodGen); - injectDefaultParamInitsToAttachedFuncs(module, initMethodGen); + injectDefaultParamInits(typeEnv, module, initMethodGen); + injectDefaultParamInitsToAttachedFuncs(typeEnv, module, initMethodGen); BIRFunction mainFunc = getMainFunction(module); BIRFunction testExecuteFunc = getTestExecuteFunction(module); @@ -717,7 +722,7 @@ CompiledJarFile generate(BIRPackage module) { removeSourceAnnotationTypeDefs(module.typeDefs); // desugar the record init function - rewriteRecordInits(module.typeDefs); + rewriteRecordInits(typeEnv, module.typeDefs); // generate object/record value classes JvmValueGen valueGen = new JvmValueGen(module, this, methodGen, typeHashVisitor, types); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTerminatorGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTerminatorGen.java index 088e937ba560..42097b0a202c 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTerminatorGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTerminatorGen.java @@ -297,7 +297,7 @@ private void genGoToTerm(BIRTerminator.GOTO gotoIns, String funcName) { public void genReturnTerm(int returnVarRefIndex, BIRNode.BIRFunction func, int channelMapVarIndex, int sendWorkerChannelNamesVar, int receiveWorkerChannelNamesVar, int localVarOffset) { - BType bType = unifier.build(func.type.retType); + BType bType = unifier.build(symbolTable.typeEnv(), func.type.retType); generateReturnTermFromType(bType, func, returnVarRefIndex, channelMapVarIndex, sendWorkerChannelNamesVar, receiveWorkerChannelNamesVar, localVarOffset); } @@ -606,8 +606,8 @@ private void genStaticCall(BIRTerminator.Call callIns, PackageID packageID, int jvmClass = JvmCodeGenUtil.getModuleLevelClassName(packageID, JvmCodeGenUtil.cleanupPathSeparators(balFileName)); //TODO: add receiver: BType attachedType = type.r != null ? receiver.type : null; - BType retType = unifier.build(type.retType); - methodDesc = JvmCodeGenUtil.getMethodDesc(params, retType); + BType retType = unifier.build(symbolTable.typeEnv(), type.retType); + methodDesc = JvmCodeGenUtil.getMethodDesc(symbolTable.typeEnv(), params, retType); } this.mv.visitMethodInsn(INVOKESTATIC, jvmClass, encodedMethodName, methodDesc, false); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTypeGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTypeGen.java index 491a1fa6b564..d5ed32a31e04 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTypeGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTypeGen.java @@ -18,6 +18,16 @@ package org.wso2.ballerinalang.compiler.bir.codegen; import io.ballerina.identifier.Utils; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.StringSubtype; import org.ballerinalang.compiler.BLangCompilerException; import org.ballerinalang.model.elements.PackageID; import org.ballerinalang.model.symbols.SymbolKind; @@ -27,8 +37,7 @@ import org.objectweb.asm.MethodVisitor; import org.wso2.ballerinalang.compiler.bir.codegen.split.JvmConstantsGen; import org.wso2.ballerinalang.compiler.bir.model.BIRNode.BIRTypeDefinition; -import org.wso2.ballerinalang.compiler.semantics.analyzer.IsAnydataUniqueVisitor; -import org.wso2.ballerinalang.compiler.semantics.analyzer.IsPureTypeUniqueVisitor; +import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; import org.wso2.ballerinalang.compiler.semantics.analyzer.TypeHashVisitor; import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableSymbol; @@ -52,16 +61,23 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BTypedescType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.compiler.semantics.model.types.TypeFlags; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Set; +import static io.ballerina.types.BasicTypeCode.BT_BOOLEAN; +import static io.ballerina.types.BasicTypeCode.BT_DECIMAL; +import static io.ballerina.types.BasicTypeCode.BT_FLOAT; +import static io.ballerina.types.BasicTypeCode.BT_INT; +import static io.ballerina.types.BasicTypeCode.BT_STRING; +import static io.ballerina.types.Core.getComplexSubtypeData; +import static io.ballerina.types.SemTypes.isSubtypeSimple; import static org.objectweb.asm.Opcodes.AASTORE; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ACC_STATIC; @@ -84,13 +100,17 @@ import static org.objectweb.asm.Opcodes.POP; import static org.objectweb.asm.Opcodes.RETURN; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmCodeGenUtil.getModuleLevelClassName; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmCodeGenUtil.getStringConstantsClass; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmCodeGenUtil.removeDecimalDiscriminator; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmCodeGenUtil.toNameString; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.ADD_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.BOOLEAN_VALUE; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.B_STRING_VAR_PREFIX; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CALL_FUNCTION; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CREATE_ERROR_VALUE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CREATE_OBJECT_VALUE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CREATE_RECORD_VALUE; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.DECIMAL_VALUE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.DOUBLE_VALUE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.FINITE_TYPE_IMPL; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.FUNCTION_PARAMETER; @@ -162,6 +182,7 @@ import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_TABLE_TYPE_IMPL; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_TABLE_TYPE_WITH_FIELD_NAME_LIST; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_WITH_BOOLEAN; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_WITH_STRING; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INT_VALUE_OF_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.LOAD_ANYDATA_TYPE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.LOAD_ANY_TYPE; @@ -193,8 +214,6 @@ */ public class JvmTypeGen { - private final IsPureTypeUniqueVisitor isPureTypeUniqueVisitor; - private final IsAnydataUniqueVisitor isAnydataUniqueVisitor; private final JvmConstantsGen jvmConstantsGen; private final TypeHashVisitor typeHashVisitor; private final SymbolTable symbolTable; @@ -205,13 +224,12 @@ public class JvmTypeGen { private final String objectsClass; private final String errorsClass; private final String functionCallsClass; + private final Context semTypeCtx; public JvmTypeGen(JvmConstantsGen jvmConstantsGen, PackageID packageID, TypeHashVisitor typeHashVisitor, SymbolTable symbolTable) { this.jvmConstantsGen = jvmConstantsGen; this.packageID = packageID; - isPureTypeUniqueVisitor = new IsPureTypeUniqueVisitor(); - isAnydataUniqueVisitor = new IsAnydataUniqueVisitor(); this.typeHashVisitor = typeHashVisitor; this.symbolTable = symbolTable; this.anonTypesClass = getModuleLevelClassName(packageID, MODULE_ANON_TYPES_CLASS_NAME); @@ -220,6 +238,7 @@ public JvmTypeGen(JvmConstantsGen jvmConstantsGen, PackageID packageID, TypeHash this.objectsClass = getModuleLevelClassName(packageID, MODULE_OBJECTS_CREATOR_CLASS_NAME); this.errorsClass = getModuleLevelClassName(packageID, MODULE_ERRORS_CREATOR_CLASS_NAME); this.functionCallsClass = getModuleLevelClassName(packageID, MODULE_FUNCTION_CALLS_CLASS_NAME); + this.semTypeCtx = Context.from(symbolTable.typeEnv()); } /** @@ -350,10 +369,10 @@ private void generateFunctionCallMethod(ClassWriter cw, String moduleClass) { } public int typeFlag(BType type) { - isAnydataUniqueVisitor.reset(); - isPureTypeUniqueVisitor.reset(); - return TypeFlags.asMask(type.isNullable(), isAnydataUniqueVisitor.visit(type), - isPureTypeUniqueVisitor.visit(type)); + boolean isAnydata = SemTypeHelper.isSubtype(semTypeCtx, type, Core.createAnydata(semTypeCtx)); + boolean isPureType = isAnydata || SemTypeHelper.isSubtype(semTypeCtx, type, + Core.union(Core.createAnydata(semTypeCtx), PredefinedType.ERROR)); + return TypeFlags.asMask(type.isNullable(), isAnydata, isPureType); } // ------------------------------------------------------- @@ -388,25 +407,27 @@ public void loadType(MethodVisitor mv, BType bType) { case TypeTags.BOOLEAN -> typeFieldName = "TYPE_BOOLEAN"; case TypeTags.BYTE -> typeFieldName = "TYPE_BYTE"; case TypeTags.ANY -> - typeFieldName = Symbols.isFlagOn(bType.flags, Flags.READONLY) ? "TYPE_READONLY_ANY" : + typeFieldName = Symbols.isFlagOn(bType.getFlags(), Flags.READONLY) ? "TYPE_READONLY_ANY" : + "TYPE_ANY"; case TypeTags.ANYDATA, TypeTags.REGEXP -> - typeFieldName = Symbols.isFlagOn(bType.flags, Flags.READONLY) ? "TYPE_READONLY_ANYDATA" : + typeFieldName = Symbols.isFlagOn(bType.getFlags(), Flags.READONLY) ? "TYPE_READONLY_ANYDATA" : "TYPE_ANYDATA"; case TypeTags.JSON -> - typeFieldName = Symbols.isFlagOn(bType.flags, Flags.READONLY) ? "TYPE_READONLY_JSON" : + typeFieldName = Symbols.isFlagOn(bType.getFlags(), Flags.READONLY) ? "TYPE_READONLY_JSON" : + "TYPE_JSON"; case TypeTags.XML -> { loadXmlType(mv, (BXMLType) bType); return; } case TypeTags.XML_ELEMENT -> - typeFieldName = Symbols.isFlagOn(bType.flags, Flags.READONLY) ? "TYPE_READONLY_ELEMENT" : + typeFieldName = Symbols.isFlagOn(bType.getFlags(), Flags.READONLY) ? "TYPE_READONLY_ELEMENT" : "TYPE_ELEMENT"; - case TypeTags.XML_PI -> typeFieldName = Symbols.isFlagOn(bType.flags, Flags.READONLY) ? + case TypeTags.XML_PI -> typeFieldName = Symbols.isFlagOn(bType.getFlags(), Flags.READONLY) ? "TYPE_READONLY_PROCESSING_INSTRUCTION" : "TYPE_PROCESSING_INSTRUCTION"; case TypeTags.XML_COMMENT -> - typeFieldName = Symbols.isFlagOn(bType.flags, Flags.READONLY) ? "TYPE_READONLY_COMMENT" : + typeFieldName = Symbols.isFlagOn(bType.getFlags(), Flags.READONLY) ? "TYPE_READONLY_COMMENT" : "TYPE_COMMENT"; case TypeTags.XML_TEXT -> typeFieldName = "TYPE_TEXT"; case TypeTags.TYPEDESC -> { @@ -507,8 +528,7 @@ private String loadTypeClass(BType bType) { return switch (bType.tag) { case TypeTags.NEVER -> LOAD_NEVER_TYPE; case TypeTags.INT, TypeTags.UNSIGNED8_INT, TypeTags.UNSIGNED16_INT, TypeTags.UNSIGNED32_INT, - TypeTags.SIGNED8_INT, TypeTags.SIGNED16_INT, TypeTags.SIGNED32_INT -> - LOAD_INTEGER_TYPE; + TypeTags.SIGNED8_INT, TypeTags.SIGNED16_INT, TypeTags.SIGNED32_INT -> LOAD_INTEGER_TYPE; case TypeTags.FLOAT -> LOAD_FLOAT_TYPE; case TypeTags.STRING, TypeTags.CHAR_STRING -> LOAD_STRING_TYPE; case TypeTags.DECIMAL -> LOAD_DECIMAL_TYPE; @@ -519,7 +539,7 @@ private String loadTypeClass(BType bType) { case TypeTags.JSON -> LOAD_JSON_TYPE; case TypeTags.XML, TypeTags.XML_TEXT -> LOAD_XML_TYPE; case TypeTags.XML_ELEMENT, TypeTags.XML_PI, TypeTags.XML_COMMENT -> - Symbols.isFlagOn(bType.flags, Flags.READONLY) ? LOAD_TYPE : LOAD_XML_TYPE; + Symbols.isFlagOn(bType.getFlags(), Flags.READONLY) ? LOAD_TYPE : LOAD_XML_TYPE; case TypeTags.OBJECT -> Symbols.isService(bType.tsymbol) ? LOAD_SERVICE_TYPE : LOAD_OBJECT_TYPE; case TypeTags.HANDLE -> LOAD_HANDLE_TYPE; case TypeTags.READONLY -> LOAD_READONLY_TYPE; @@ -570,7 +590,7 @@ private void loadMapType(MethodVisitor mv, BMapType bType) { } public void loadReadonlyFlag(MethodVisitor mv, BType bType) { - if (Symbols.isFlagOn(bType.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(bType.getFlags(), Flags.READONLY)) { mv.visitInsn(ICONST_1); } else { mv.visitInsn(ICONST_0); @@ -670,7 +690,7 @@ private void loadErrorType(MethodVisitor mv, BErrorType errorType) { return; } - if (Symbols.isFlagOn(errorType.flags, Flags.ANONYMOUS)) { + if (Symbols.isFlagOn(errorType.getFlags(), Flags.ANONYMOUS)) { jvmConstantsGen.generateGetBErrorType(mv, jvmConstantsGen.getTypeConstantsVar(errorType, symbolTable)); } else { String typeOwner = JvmCodeGenUtil.getPackageName(pkgID) + MODULE_INIT_CLASS_NAME; @@ -815,7 +835,7 @@ private void loadUserDefinedType(MethodVisitor mv, BType bType) { boolean samePackage = JvmCodeGenUtil.isSameModule(this.packageID, pkgID); // if name contains $anon and doesn't belong to the same package, load type using getAnonType() method. - if (!samePackage && Symbols.isFlagOn(typeToLoad.flags, Flags.ANONYMOUS)) { + if (!samePackage && Symbols.isFlagOn(typeToLoad.getFlags(), Flags.ANONYMOUS)) { Integer hash = typeHashVisitor.visit(typeToLoad); String shape = typeToLoad.toString(); typeHashVisitor.reset(); @@ -876,8 +896,8 @@ public void loadInvokableType(MethodVisitor mv, BInvokableType bType) { mv.visitFieldInsn(GETSTATIC, jvmConstantsGen.getModuleConstantClass(), moduleName, GET_MODULE); } - if (Symbols.isFlagOn(bType.flags, Flags.ANY_FUNCTION)) { - mv.visitLdcInsn(bType.flags); + if (Symbols.isFlagOn(bType.getFlags(), Flags.ANY_FUNCTION)) { + mv.visitLdcInsn(bType.getFlags()); mv.visitMethodInsn(INVOKESPECIAL, FUNCTION_TYPE_IMPL, JVM_INIT_METHOD, INIT_FUNCTION_TYPE_IMPL, false); return; } @@ -894,7 +914,7 @@ public void loadInvokableType(MethodVisitor mv, BInvokableType bType) { // load return type loadType(mv, bType.retType); - mv.visitLdcInsn(bType.flags); + mv.visitLdcInsn(bType.getFlags()); mv.visitLdcInsn(bType.name.getValue()); // initialize the function type using the param types array and the return type mv.visitMethodInsn(INVOKESPECIAL, FUNCTION_TYPE_IMPL, JVM_INIT_METHOD, INIT_FUNCTION_TYPE_IMPL_WITH_PARAMS, @@ -1008,8 +1028,7 @@ public static String getTypeDesc(BType bType) { case TypeTags.FLOAT -> "D"; case TypeTags.BOOLEAN -> "Z"; case TypeTags.NIL, TypeTags.NEVER, TypeTags.ANY, TypeTags.ANYDATA, TypeTags.UNION, TypeTags.JSON, - TypeTags.FINITE, TypeTags.READONLY -> - GET_OBJECT; + TypeTags.FINITE, TypeTags.READONLY -> GET_OBJECT; case TypeTags.ARRAY, TypeTags.TUPLE -> GET_ARRAY_VALUE; case TypeTags.ERROR -> GET_ERROR_VALUE; case TypeTags.FUTURE -> GET_FUTURE_VALUE; @@ -1040,18 +1059,23 @@ private void loadFiniteType(MethodVisitor mv, BFiniteType finiteType) { mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, LINKED_HASH_SET, JVM_INIT_METHOD, VOID_METHOD_DESC, false); - for (BLangExpression valueTypePair : finiteType.getValueSpace()) { - Object value = ((BLangLiteral) valueTypePair).value; - BType valueType = valueTypePair.getBType(); + for (SemNamedType semNamedType : finiteType.valueSpace) { mv.visitInsn(DUP); - - JvmCodeGenUtil.loadConstantValue(valueType, value, mv, jvmConstantsGen); - - if (TypeTags.isIntegerTypeTag(JvmCodeGenUtil.getImpliedType(valueType).tag)) { - mv.visitMethodInsn(INVOKESTATIC, LONG_VALUE, VALUE_OF_METHOD, LONG_VALUE_OF, - false); + SemType s = semNamedType.semType(); + if (PredefinedType.NIL.equals(s)) { + mv.visitInsn(ACONST_NULL); + } else if (isSubtypeSimple(s, PredefinedType.BOOLEAN)) { + loadConstBoolean(mv, (ComplexSemType) s); + } else if (isSubtypeSimple(s, PredefinedType.INT)) { + loadConstInteger(mv, (ComplexSemType) s); + } else if (isSubtypeSimple(s, PredefinedType.FLOAT)) { + loadConstFloat(mv, (ComplexSemType) s); + } else if (isSubtypeSimple(s, PredefinedType.DECIMAL)) { + loadConstDecimal(mv, (ComplexSemType) s); + } else if (isSubtypeSimple(s, PredefinedType.STRING)) { + loadConstString(mv, (ComplexSemType) s); } else { - loadValueType(mv, valueType); + throw new IllegalStateException("Unexpected value space type: " + s); } // Add the value to the set @@ -1066,16 +1090,42 @@ private void loadFiniteType(MethodVisitor mv, BFiniteType finiteType) { mv.visitMethodInsn(INVOKESPECIAL, FINITE_TYPE_IMPL, JVM_INIT_METHOD, INIT_FINITE_TYPE_IMPL, false); } - private void loadValueType(MethodVisitor mv, BType valueType) { - valueType = JvmCodeGenUtil.getImpliedType(valueType); - switch (valueType.tag) { - case TypeTags.BOOLEAN -> mv.visitMethodInsn(INVOKESTATIC, BOOLEAN_VALUE, VALUE_OF_METHOD, - BOOLEAN_VALUE_OF_METHOD, false); - case TypeTags.FLOAT -> mv.visitMethodInsn(INVOKESTATIC, DOUBLE_VALUE, VALUE_OF_METHOD, - DOUBLE_VALUE_OF_METHOD, false); - case TypeTags.BYTE -> mv.visitMethodInsn(INVOKESTATIC, INT_VALUE, VALUE_OF_METHOD, - INT_VALUE_OF_METHOD, false); + private void loadConstString(MethodVisitor mv, ComplexSemType s) { + String stringVal = StringSubtype.stringSubtypeSingleValue(getComplexSubtypeData(s, BT_STRING)).orElseThrow(); + int index = jvmConstantsGen.getBStringConstantVarIndex(stringVal); + String varName = B_STRING_VAR_PREFIX + index; + String stringConstantsClass = getStringConstantsClass(index, jvmConstantsGen); + mv.visitFieldInsn(GETSTATIC, stringConstantsClass, varName, GET_BSTRING); + } + + private static void loadConstDecimal(MethodVisitor mv, ComplexSemType s) { + BigDecimal bVal = DecimalSubtype.decimalSubtypeSingleValue(getComplexSubtypeData(s, BT_DECIMAL)).orElseThrow(); + mv.visitTypeInsn(NEW, DECIMAL_VALUE); + mv.visitInsn(DUP); + mv.visitLdcInsn(removeDecimalDiscriminator(String.valueOf(bVal))); + mv.visitMethodInsn(INVOKESPECIAL, DECIMAL_VALUE, JVM_INIT_METHOD, INIT_WITH_STRING, false); + } + + private static void loadConstFloat(MethodVisitor mv, ComplexSemType s) { + double doubleVal = FloatSubtype.floatSubtypeSingleValue(getComplexSubtypeData(s, BT_FLOAT)).orElseThrow(); + mv.visitLdcInsn(doubleVal); + mv.visitMethodInsn(INVOKESTATIC, DOUBLE_VALUE, VALUE_OF_METHOD, DOUBLE_VALUE_OF_METHOD, false); + } + + private static void loadConstInteger(MethodVisitor mv, ComplexSemType s) { + long longVal = IntSubtype.intSubtypeSingleValue(getComplexSubtypeData(s, BT_INT)).orElseThrow(); + if (0 <= longVal && longVal <= 255) { + mv.visitLdcInsn((int) longVal); + mv.visitMethodInsn(INVOKESTATIC, INT_VALUE, VALUE_OF_METHOD, INT_VALUE_OF_METHOD, false); + } else { + mv.visitLdcInsn(longVal); + mv.visitMethodInsn(INVOKESTATIC, LONG_VALUE, VALUE_OF_METHOD, LONG_VALUE_OF, false); } } + private static void loadConstBoolean(MethodVisitor mv, ComplexSemType s) { + boolean boolVal = BooleanSubtype.booleanSubtypeSingleValue(getComplexSubtypeData(s, BT_BOOLEAN)).orElseThrow(); + mv.visitLdcInsn(boolVal); + mv.visitMethodInsn(INVOKESTATIC, BOOLEAN_VALUE, VALUE_OF_METHOD, BOOLEAN_VALUE_OF_METHOD, false); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTypeTestGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTypeTestGen.java index cd3a61752802..399b7cfea49b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTypeTestGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTypeTestGen.java @@ -18,13 +18,15 @@ package org.wso2.ballerinalang.compiler.bir.codegen; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.wso2.ballerinalang.compiler.bir.model.BIRNonTerminator; import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; -import org.wso2.ballerinalang.compiler.util.TypeTags; import static org.objectweb.asm.Opcodes.GOTO; import static org.objectweb.asm.Opcodes.ICONST_0; @@ -86,106 +88,92 @@ void generateTypeTestIns(BIRNonTerminator.TypeTest typeTestIns) { } /** - * Checks if the type tested for is nil. That is the target type is nil. Example instructions include 'a is ()' - * where 'a' is a variable of type say any or a union with nil. + * Checks if we have x is (). + *
+ * In that case we can simplify is-check to a x instanceof null check. * * @param sourceType the declared variable type * @param targetType the RHS type in the type check instruction. Type to be tested for * @return whether instruction could be optimized using 'instanceof` check */ private boolean canOptimizeNilCheck(BType sourceType, BType targetType) { - return JvmCodeGenUtil.getImpliedType(targetType).tag == TypeTags.NIL && - types.isAssignable(targetType, sourceType); + return PredefinedType.NIL.equals(targetType.semType()) && + SemTypes.containsBasicType(sourceType.semType(), PredefinedType.NIL); } /** - * This checks for any variable declaration containing a nil in a union of two types. Examples include string? or - * error? or int?. + * Checks if we have x is T in which x's static type is T'=T|(). + *
+ * In that case we can simplify is-check to a !(x instanceof null) check. * * @param sourceType the declared variable type * @param targetType the RHS type in the type check instruction. Type to be tested for * @return whether instruction could be optimized using 'instanceof` check for null */ private boolean canOptimizeNilUnionCheck(BType sourceType, BType targetType) { - sourceType = JvmCodeGenUtil.getImpliedType(sourceType); - if (isInValidUnionType(sourceType)) { + SemType sourceTy = sourceType.semType(); + if (!SemTypes.containsBasicType(sourceTy, PredefinedType.NIL)) { return false; } - boolean foundNil = false; - BType otherType = null; - for (BType bType : ((BUnionType) sourceType).getMemberTypes()) { - if (JvmCodeGenUtil.getImpliedType(bType).tag == TypeTags.NIL) { - foundNil = true; - } else { - otherType = bType; - } + + SemType tyButNil = Core.diff(sourceTy, PredefinedType.NIL); + if (Core.isNever(tyButNil)) { + return false; } - return foundNil && targetType.equals(otherType); + return SemTypes.isSameType(types.typeCtx(), tyButNil, targetType.semType()); } /** - * Checks if the type tested for is error. That is the target type is error. Example instructions include 'a is - * error' where 'a' is a variable of type say any or a union with nil. + * Checks if we have x is E where E is a subtype of error and error part + * of x is a subtype of E. + *
+ * In that case we can simplify is-check to a x instanceof BError check. * * @param sourceType the declared variable type * @param targetType the RHS type in the type check instruction. Type to be tested for * @return whether instruction could be optimized using 'instanceof` check for BError */ private boolean canOptimizeErrorCheck(BType sourceType, BType targetType) { - sourceType = JvmCodeGenUtil.getImpliedType(sourceType); - targetType = JvmCodeGenUtil.getImpliedType(targetType); - if (targetType.tag != TypeTags.ERROR || sourceType.tag != TypeTags.UNION) { + SemType targetTy = targetType.semType(); + if (!Core.isSubtypeSimple(targetTy, PredefinedType.ERROR)) { return false; } - BType errorType = null; - int foundError = 0; - for (BType bType : ((BUnionType) sourceType).getMemberTypes()) { - if (bType.tag == TypeTags.ERROR) { - foundError++; - errorType = bType; - } + + SemType errIntersect = SemTypes.intersect(sourceType.semType(), PredefinedType.ERROR); + if (Core.isNever(errIntersect)) { + return false; } - return (foundError == 1 && types.isAssignable(errorType, targetType)) || (foundError > 0 && "error".equals( - targetType.tsymbol.name.value)); + return SemTypes.isSubtype(types.typeCtx(), errIntersect, targetTy); } /** - * This checks for any variable declaration containing a error in a union of two types. Examples include - * string|error or error|error or int|error. + * Checks if we have x is T in which x's static type is T'=T|E where + * E is a non-empty error type. + *
+ * In that case we can simplify is-check to a !(x instanceof BError) check. * * @param sourceType the declared variable type * @param targetType the RHS type in the type check instruction. Type to be tested for * @return whether instruction could be optimized using 'instanceof` check for BError */ private boolean canOptimizeErrorUnionCheck(BType sourceType, BType targetType) { - sourceType = JvmCodeGenUtil.getImpliedType(sourceType); - if (isInValidUnionType(sourceType)) { + SemType sourceTy = sourceType.semType(); + if (!SemTypes.containsBasicType(sourceTy, PredefinedType.ERROR)) { return false; } - BType otherType = null; - int foundError = 0; - for (BType bType : ((BUnionType) sourceType).getMemberTypes()) { - if (JvmCodeGenUtil.getImpliedType(bType).tag == TypeTags.ERROR) { - foundError++; - } else { - otherType = bType; - } - } - return foundError == 1 && targetType.equals(otherType); - } - private boolean isInValidUnionType(BType rhsType) { - if (rhsType.tag != TypeTags.UNION) { - return true; + SemType tyButError = Core.diff(sourceTy, PredefinedType.ERROR); + if (Core.isNever(tyButError)) { + return false; } - return ((BUnionType) rhsType).getMemberTypes().size() != 2; + return SemTypes.isSameType(types.typeCtx(), tyButError, targetType.semType()); } private void handleNilUnionType(BIRNonTerminator.TypeTest typeTestIns) { jvmInstructionGen.loadVar(typeTestIns.rhsOp.variableDcl); jvmCastGen.addBoxInsn(this.mv, typeTestIns.rhsOp.variableDcl.type); Label ifLabel = new Label(); - if (JvmCodeGenUtil.getImpliedType(typeTestIns.type).tag == TypeTags.NIL) { + if (PredefinedType.NIL.equals(typeTestIns.type.semType())) { mv.visitJumpInsn(IFNONNULL, ifLabel); } else { mv.visitJumpInsn(IFNULL, ifLabel); @@ -206,7 +194,7 @@ private void loadBoolean(Label ifLabel) { private void handleErrorUnionType(BIRNonTerminator.TypeTest typeTestIns) { jvmInstructionGen.loadVar(typeTestIns.rhsOp.variableDcl); mv.visitTypeInsn(INSTANCEOF, BERROR); - if (JvmCodeGenUtil.getImpliedType(typeTestIns.type).tag != TypeTags.ERROR) { + if (!Core.isSubtypeSimple(typeTestIns.type.semType(), PredefinedType.ERROR)) { generateNegateBoolean(); } jvmInstructionGen.storeToVar(typeTestIns.lhsOp.variableDcl); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmValueGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmValueGen.java index 7d97b9ab36c9..e8266b89bc75 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmValueGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmValueGen.java @@ -17,6 +17,7 @@ */ package org.wso2.ballerinalang.compiler.bir.codegen; +import io.ballerina.types.Env; import org.ballerinalang.model.elements.PackageID; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; @@ -137,28 +138,29 @@ public class JvmValueGen { this.types = types; } - static void injectDefaultParamInitsToAttachedFuncs(BIRNode.BIRPackage module, InitMethodGen initMethodGen) { + static void injectDefaultParamInitsToAttachedFuncs(Env env, BIRNode.BIRPackage module, + InitMethodGen initMethodGen) { List typeDefs = module.typeDefs; for (BIRNode.BIRTypeDefinition optionalTypeDef : typeDefs) { BType bType = JvmCodeGenUtil.getImpliedType(optionalTypeDef.type); if ((bType.tag == TypeTags.OBJECT && Symbols.isFlagOn( bType.tsymbol.flags, Flags.CLASS)) || bType.tag == TypeTags.RECORD) { - desugarObjectMethods(optionalTypeDef.attachedFuncs, initMethodGen); + desugarObjectMethods(env, optionalTypeDef.attachedFuncs, initMethodGen); } } } - private static void desugarObjectMethods(List attachedFuncs, InitMethodGen initMethodGen) { + private static void desugarObjectMethods(Env env, List attachedFuncs, InitMethodGen initMethodGen) { for (BIRNode.BIRFunction birFunc : attachedFuncs) { if (JvmCodeGenUtil.isExternFunc(birFunc)) { if (birFunc instanceof JMethodBIRFunction jMethodBIRFunction) { - desugarInteropFuncs(jMethodBIRFunction, initMethodGen); + desugarInteropFuncs(env, jMethodBIRFunction, initMethodGen); initMethodGen.resetIds(); } else if (!(birFunc instanceof JFieldBIRFunction)) { initMethodGen.resetIds(); } } else { - addDefaultableBooleanVarsToSignature(birFunc); + addDefaultableBooleanVarsToSignature(env, birFunc); initMethodGen.resetIds(); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/ExternalMethodGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/ExternalMethodGen.java index 5d25387eb63e..65286884f9f3 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/ExternalMethodGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/ExternalMethodGen.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.bir.codegen.interop; +import io.ballerina.types.Env; import org.ballerinalang.model.elements.PackageID; import org.objectweb.asm.ClassWriter; import org.wso2.ballerinalang.compiler.bir.codegen.JvmCastGen; @@ -63,7 +64,7 @@ public static void genJMethodForBExternalFunc(BIRFunction birFunc, ClassWriter c } } - public static void injectDefaultParamInits(BIRPackage module, InitMethodGen initMethodGen) { + public static void injectDefaultParamInits(Env typeEnv, BIRPackage module, InitMethodGen initMethodGen) { // filter out functions. List functions = module.functions; if (!functions.isEmpty()) { @@ -74,7 +75,7 @@ public static void injectDefaultParamInits(BIRPackage module, InitMethodGen init BIRFunction birFunc = functions.get(count); count = count + 1; if (birFunc instanceof JMethodBIRFunction jMethodBIRFunction) { - desugarInteropFuncs(jMethodBIRFunction, initMethodGen); + desugarInteropFuncs(typeEnv, jMethodBIRFunction, initMethodGen); initMethodGen.resetIds(); } else if (!(birFunc instanceof JFieldBIRFunction)) { initMethodGen.resetIds(); @@ -83,12 +84,12 @@ public static void injectDefaultParamInits(BIRPackage module, InitMethodGen init } } - public static BIRFunctionWrapper createExternalFunctionWrapper(boolean isEntry, BIRFunction birFunc, + public static BIRFunctionWrapper createExternalFunctionWrapper(Env env, boolean isEntry, BIRFunction birFunc, PackageID packageID, String birModuleClassName) { if (isEntry) { - addDefaultableBooleanVarsToSignature(birFunc); + addDefaultableBooleanVarsToSignature(env, birFunc); } - return getFunctionWrapper(birFunc, packageID, birModuleClassName); + return getFunctionWrapper(env, birFunc, packageID, birModuleClassName); } private ExternalMethodGen() { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropMethodGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropMethodGen.java index 0b2d373cf1e9..37c764a58496 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropMethodGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropMethodGen.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.bir.codegen.interop; +import io.ballerina.types.Env; import org.ballerinalang.compiler.BLangCompilerException; import org.ballerinalang.model.elements.PackageID; import org.objectweb.asm.ClassWriter; @@ -141,11 +142,11 @@ static void genJFieldForInteropField(JFieldBIRFunction birFunc, ClassWriter clas // Generate method desc BType retType = birFunc.type.retType; - if (Symbols.isFlagOn(retType.flags, Flags.PARAMETERIZED)) { - retType = JvmCodeGenUtil.UNIFIER.build(birFunc.type.retType); + if (Symbols.isFlagOn(retType.getFlags(), Flags.PARAMETERIZED)) { + retType = JvmCodeGenUtil.UNIFIER.build(types.typeEnv(), birFunc.type.retType); } - String desc = JvmCodeGenUtil.getMethodDesc(birFunc.type.paramTypes, retType); + String desc = JvmCodeGenUtil.getMethodDesc(types.typeEnv(), birFunc.type.paramTypes, retType); int access = birFunc.receiver != null ? ACC_PUBLIC : ACC_PUBLIC + ACC_STATIC; MethodVisitor mv = classWriter.visitMethod(access, birFunc.name.value, desc, null, null); JvmInstructionGen instGen = new JvmInstructionGen(mv, indexMap, birModule, jvmPackageGen, jvmTypeGen, @@ -257,11 +258,11 @@ static void genJFieldForInteropField(JFieldBIRFunction birFunc, ClassWriter clas mv.visitEnd(); } - public static void desugarInteropFuncs(JMethodBIRFunction birFunc, InitMethodGen initMethodGen) { + public static void desugarInteropFuncs(Env typeEnv, JMethodBIRFunction birFunc, InitMethodGen initMethodGen) { // resetting the variable generation index BType retType = birFunc.type.retType; - if (Symbols.isFlagOn(retType.flags, Flags.PARAMETERIZED)) { - retType = JvmCodeGenUtil.UNIFIER.build(birFunc.type.retType); + if (Symbols.isFlagOn(retType.getFlags(), Flags.PARAMETERIZED)) { + retType = JvmCodeGenUtil.UNIFIER.build(typeEnv, birFunc.type.retType); } JMethod jMethod = birFunc.jMethod; Class[] jMethodParamTypes = jMethod.getParamTypes(); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropValidator.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropValidator.java index 59be56de92d3..f20f50ef2e2a 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropValidator.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropValidator.java @@ -204,7 +204,8 @@ private ClassLoader makeClassLoader(Set moduleDependencies) { JMethod validateAndGetJMethod(InteropValidationRequest.MethodValidationRequest methodValidationRequest, ClassLoader classLoader) { // Populate JMethodRequest from the BValue - JMethodRequest jMethodRequest = JMethodRequest.build(methodValidationRequest, classLoader); + JMethodRequest jMethodRequest = JMethodRequest.build(symbolTable.typeEnv(), methodValidationRequest, + classLoader); // Find the most specific Java method or constructor for the given request JMethodResolver methodResolver = new JMethodResolver(classLoader, symbolTable); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodRequest.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodRequest.java index dfa5278a93e8..c52cd1f4ab49 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodRequest.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodRequest.java @@ -17,14 +17,15 @@ */ package org.wso2.ballerinalang.compiler.bir.codegen.interop; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.symbols.SymbolKind; import org.wso2.ballerinalang.compiler.bir.codegen.model.JMethodKind; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BVarSymbol; import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; -import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.compiler.util.Unifier; import java.util.ArrayList; @@ -59,7 +60,7 @@ private JMethodRequest() { } - static JMethodRequest build(InteropValidationRequest.MethodValidationRequest methodValidationRequest, + static JMethodRequest build(Env typeEnv, InteropValidationRequest.MethodValidationRequest methodValidationRequest, ClassLoader classLoader) { JMethodRequest jMethodReq = new JMethodRequest(); @@ -94,18 +95,9 @@ static JMethodRequest build(InteropValidationRequest.MethodValidationRequest met jMethodReq.bParamTypes = paramTypes.toArray(new BType[0]); jMethodReq.pathParamSymbols = pathParams; - BType returnType = unifier.build(bFuncType.retType); + BType returnType = unifier.build(typeEnv, bFuncType.retType); jMethodReq.bReturnType = returnType; - if (returnType.tag == TypeTags.UNION) { - for (BType bType : ((BUnionType) returnType).getMemberTypes()) { - if (bType.tag == TypeTags.ERROR) { - jMethodReq.returnsBErrorType = true; - break; - } - } - } else { - jMethodReq.returnsBErrorType = returnType.tag == TypeTags.ERROR; - } + jMethodReq.returnsBErrorType = SemTypes.containsBasicType(returnType.semType(), PredefinedType.ERROR); jMethodReq.restParamExist = methodValidationRequest.restParamExist; return jMethodReq; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodResolver.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodResolver.java index 3289cfa31446..8f695617002f 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodResolver.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodResolver.java @@ -37,6 +37,7 @@ import org.wso2.ballerinalang.compiler.bir.codegen.exceptions.JInteropException; import org.wso2.ballerinalang.compiler.bir.codegen.model.JMethod; import org.wso2.ballerinalang.compiler.bir.codegen.model.JMethodKind; +import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BVarSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; @@ -45,7 +46,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.semantics.model.types.BTypeReferenceType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; @@ -253,7 +253,7 @@ private boolean hasEquivalentFunctionParamCount(JMethodRequest jMethodRequest, J // https://github.com/ballerina-platform/ballerina-lang/issues/42456. if (jMethodRequest.receiverType == null || functionParamCount < 1 || count < reducedParamCount || count > reducedParamCount + 2 - || Symbols.isFlagOn(jMethodRequest.bParamTypes[0].flags, Flags.SERVICE)) { + || Symbols.isFlagOn(jMethodRequest.bParamTypes[0].getFlags(), Flags.SERVICE)) { return false; } if (!isParamAssignableToBArray(paramTypes[count - 1]) @@ -387,18 +387,6 @@ private void validateExceptionTypes(JMethodRequest jMethodRequest, JMethod jMeth "Incompatible ballerina return type for Java method '" + jMethodRequest.methodName + "' which " + "throws checked exception found in class '" + jMethodRequest.declaringClass.getName() + "': expected '" + expectedRetTypeName + "', found '" + returnType + "'"); - } else if (jMethodRequest.returnsBErrorType && !throwsCheckedException && !returnsErrorValue) { - String errorMsgPart; - if (returnType instanceof BUnionType bUnionReturnType) { - BType modifiedRetType = BUnionType.create(null, getNonErrorMembers(bUnionReturnType)); - errorMsgPart = "expected '" + modifiedRetType + "', found '" + returnType + "'"; - } else { - errorMsgPart = "no return type expected but found '" + returnType + "'"; - } - throw new JInteropException(DiagnosticErrorCode.METHOD_SIGNATURE_DOES_NOT_MATCH, - "Incompatible ballerina return type for Java method '" + jMethodRequest.methodName + "' which " + - "throws 'java.lang.RuntimeException' found in class '" + - jMethodRequest.declaringClass.getName() + "': " + errorMsgPart); } } @@ -407,7 +395,8 @@ private String getExpectedReturnType(BType retType) { ((BTypeReferenceType) retType).referredType.tag == TypeTags.ERROR)) { return "error"; } else if (retType instanceof BUnionType bUnionReturnType) { - BType modifiedRetType = BUnionType.create(null, getNonErrorMembers(bUnionReturnType)); + BType modifiedRetType = + BUnionType.create(symbolTable.typeEnv(), null, getNonErrorMembers(bUnionReturnType)); return modifiedRetType + "|error"; } else { return retType + "|error"; @@ -510,7 +499,7 @@ private void bundlePathParams(JMethodRequest jMethodRequest, JMethod jMethod) { for (BVarSymbol param : pathParamSymbols) { paramTypes.remove(param.type); } - paramTypes.add(initialPathParamIndex, new BArrayType(symbolTable.anydataType)); + paramTypes.add(initialPathParamIndex, new BArrayType(symbolTable.typeEnv(), symbolTable.anydataType)); jMethodRequest.bParamTypes = paramTypes.toArray(new BType[0]); jMethodRequest.bFuncParamCount = jMethodRequest.bFuncParamCount - pathParamSymbols.size() + 1; jMethodRequest.pathParamCount = 1; @@ -522,7 +511,7 @@ private void bundleFunctionParams(JMethodRequest jMethodRequest, JMethod jMethod if (jMethodRequest.bFuncParamCount > jMethodRequest.pathParamCount) { paramTypes.subList(jMethodRequest.pathParamCount, jMethodRequest.bFuncParamCount).clear(); } - paramTypes.add(new BArrayType(symbolTable.anyType)); + paramTypes.add(new BArrayType(symbolTable.typeEnv(), symbolTable.anyType)); jMethodRequest.bParamTypes = paramTypes.toArray(new BType[0]); jMethodRequest.bFuncParamCount = jMethodRequest.pathParamCount + 1; jMethod.hasBundledFunctionParams = true; @@ -530,8 +519,8 @@ private void bundleFunctionParams(JMethodRequest jMethodRequest, JMethod jMethod private void bundleBothPathAndFunctionParameter(JMethodRequest jMethodRequest, JMethod jMethod) { List paramTypes = new ArrayList<>(); - paramTypes.add(new BArrayType(symbolTable.anydataType)); - paramTypes.add(new BArrayType(symbolTable.anyType)); + paramTypes.add(new BArrayType(symbolTable.typeEnv(), symbolTable.anydataType)); + paramTypes.add(new BArrayType(symbolTable.typeEnv(), symbolTable.anyType)); jMethodRequest.bParamTypes = paramTypes.toArray(new BType[0]); jMethodRequest.bFuncParamCount = 2; jMethodRequest.pathParamCount = 1; @@ -640,9 +629,8 @@ private boolean isInvalidParamBType(Class jType, BType bType, boolean isLastP if (jTypeName.equals(J_OBJECT_TNAME)) { return false; } - Set valueSpace = ((BFiniteType) bType).getValueSpace(); - for (BLangExpression value : valueSpace) { - if (isInvalidParamBType(jType, value.getBType(), isLastParam, restParamExist)) { + for (BType t : SemTypeHelper.broadTypes((BFiniteType) bType, symbolTable)) { + if (isInvalidParamBType(jType, t, isLastParam, restParamExist)) { return true; } } @@ -795,9 +783,8 @@ private boolean isValidReturnBType(Class jType, BType bType, JMethodRequest j if (jTypeName.equals(J_OBJECT_TNAME)) { return true; } - Set valueSpace = ((BFiniteType) bType).getValueSpace(); - for (BLangExpression value : valueSpace) { - if (isValidReturnBType(jType, value.getBType(), jMethodRequest, visitedSet)) { + for (BType t : SemTypeHelper.broadTypes((BFiniteType) bType, symbolTable)) { + if (isValidReturnBType(jType, t, jMethodRequest, visitedSet)) { return true; } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/InitMethodGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/InitMethodGen.java index 95692b46bd37..2dd4a90d648c 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/InitMethodGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/InitMethodGen.java @@ -131,7 +131,8 @@ public class InitMethodGen { public InitMethodGen(SymbolTable symbolTable) { this.symbolTable = symbolTable; - this.errorOrNilType = BUnionType.create(null, symbolTable.errorType, symbolTable.nilType); + this.errorOrNilType = + BUnionType.create(symbolTable.typeEnv(), null, symbolTable.errorType, symbolTable.nilType); } /** @@ -185,7 +186,7 @@ public void generateLambdaForModuleExecuteFunction(ClassWriter cw, String initCl jvmCastGen.addUnboxInsn(mv, paramType); paramIndex += 1; } - methodDesc = JvmCodeGenUtil.getMethodDesc(paramTypes, returnType); + methodDesc = JvmCodeGenUtil.getMethodDesc(symbolTable.typeEnv(), paramTypes, returnType); } mv.visitMethodInsn(INVOKESTATIC, initClass, MODULE_EXECUTE_METHOD, methodDesc, false); @@ -331,21 +332,21 @@ public void enrichPkgWithInitializers(Map birFunctio javaClass.functions.add(initFunc); pkg.functions.add(initFunc); birFunctionMap.put(JvmCodeGenUtil.getPackageName(pkg.packageID) + MODULE_INIT_METHOD, - JvmPackageGen.getFunctionWrapper(initFunc, pkg.packageID, typeOwnerClass)); + JvmPackageGen.getFunctionWrapper(symbolTable.typeEnv(), initFunc, pkg.packageID, typeOwnerClass)); BIRNode.BIRFunction startFunc = generateDefaultFunction(moduleImports, pkg, MODULE_START_METHOD, MethodGenUtils.START_FUNCTION_SUFFIX); javaClass.functions.add(startFunc); pkg.functions.add(startFunc); birFunctionMap.put(JvmCodeGenUtil.getPackageName(pkg.packageID) + MODULE_START_METHOD, - JvmPackageGen.getFunctionWrapper(startFunc, pkg.packageID, typeOwnerClass)); + JvmPackageGen.getFunctionWrapper(symbolTable.typeEnv(), startFunc, pkg.packageID, typeOwnerClass)); BIRNode.BIRFunction execFunc = generateExecuteFunction(pkg, mainFunc, testExecuteFunc ); javaClass.functions.add(execFunc); pkg.functions.add(execFunc); birFunctionMap.put(JvmCodeGenUtil.getPackageName(pkg.packageID) + MODULE_EXECUTE_METHOD, - JvmPackageGen.getFunctionWrapper(execFunc, pkg.packageID, typeOwnerClass)); + JvmPackageGen.getFunctionWrapper(symbolTable.typeEnv(), execFunc, pkg.packageID, typeOwnerClass)); } private BIRNode.BIRFunction generateExecuteFunction(BIRNode.BIRPackage pkg, @@ -355,7 +356,8 @@ private BIRNode.BIRFunction generateExecuteFunction(BIRNode.BIRPackage pkg, new Name("%ret"), VarScope.FUNCTION, VarKind.RETURN, null); BIROperand retVarRef = new BIROperand(retVar); List functionArgs = new ArrayList<>(); - BInvokableType funcType = new BInvokableType(Collections.emptyList(), null, errorOrNilType, null); + BInvokableType funcType = + new BInvokableType(symbolTable.typeEnv(), Collections.emptyList(), null, errorOrNilType, null); BIRNode.BIRFunction modExecFunc = new BIRNode.BIRFunction(null, new Name(MODULE_EXECUTE_METHOD), 0, funcType, null, 0, VIRTUAL); List paramTypes = new ArrayList<>(); @@ -511,7 +513,8 @@ private BIRNode.BIRFunction generateDefaultFunction(Set imprtMods, BI VarScope.FUNCTION, VarKind.RETURN, null); BIROperand retVarRef = new BIROperand(retVar); - BInvokableType funcType = new BInvokableType(Collections.emptyList(), null, errorOrNilType, null); + BInvokableType funcType = + new BInvokableType(symbolTable.typeEnv(), Collections.emptyList(), null, errorOrNilType, null); BIRNode.BIRFunction modInitFunc = new BIRNode.BIRFunction(symbolTable.builtinPos, new Name(funcName), 0, funcType, null, 0, VIRTUAL); modInitFunc.localVars.add(retVar); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/LambdaGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/LambdaGen.java index d013ccdf566f..888175ed2f4b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/LambdaGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/LambdaGen.java @@ -456,7 +456,7 @@ private String getLambdaMethodDesc(List paramTypes, BType retType, int cl StringBuilder desc = new StringBuilder(INITIAL_METHOD_DESC); appendClosureMaps(closureMapsCount, desc); appendParamTypes(paramTypes, desc); - desc.append(JvmCodeGenUtil.generateReturnType(retType)); + desc.append(JvmCodeGenUtil.generateReturnType(retType, jvmCastGen.typeEnv())); return desc.toString(); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MethodGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MethodGen.java index fd6d717c40fc..d4ab30941346 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MethodGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MethodGen.java @@ -19,6 +19,7 @@ package org.wso2.ballerinalang.compiler.bir.codegen.methodgen; import io.ballerina.identifier.Utils; +import io.ballerina.types.Env; import org.ballerinalang.compiler.BLangCompilerException; import org.ballerinalang.model.elements.PackageID; import org.objectweb.asm.ClassWriter; @@ -147,11 +148,13 @@ public class MethodGen { private final JvmPackageGen jvmPackageGen; private final SymbolTable symbolTable; private final Types types; + private final Env typeEnv; public MethodGen(JvmPackageGen jvmPackageGen, Types types) { this.jvmPackageGen = jvmPackageGen; this.symbolTable = jvmPackageGen.symbolTable; this.types = types; + this.typeEnv = types.typeEnv(); } public void generateMethod(BIRFunction birFunc, ClassWriter cw, BIRPackage birModule, BType attachedType, @@ -177,7 +180,7 @@ public void genJMethodWithBObjectMethodCall(BIRFunction func, ClassWriter cw, BI indexMap.addIfNotExists(STRAND, symbolTable.stringType); String funcName = func.name.value; BType retType = getReturnType(func); - String desc = JvmCodeGenUtil.getMethodDesc(func.type.paramTypes, retType); + String desc = JvmCodeGenUtil.getMethodDesc(typeEnv, func.type.paramTypes, retType); MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, funcName, desc, null, null); mv.visitCode(); Label methodStartLabel = new Label(); @@ -190,7 +193,7 @@ public void genJMethodWithBObjectMethodCall(BIRFunction func, ClassWriter cw, BI for (BIRNode.BIRFunctionParameter parameter : func.parameters) { instGen.generateVarLoad(mv, parameter, indexMap.addIfNotExists(parameter.name.value, parameter.type)); } - String methodDesc = JvmCodeGenUtil.getMethodDesc(func.type.paramTypes, retType, moduleClassName); + String methodDesc = JvmCodeGenUtil.getMethodDesc(typeEnv, func.type.paramTypes, retType, moduleClassName); mv.visitMethodInsn(INVOKESTATIC, splitClassName, encodedMethodName, methodDesc, false); Label methodEndLabel = new Label(); mv.visitLabel(methodEndLabel); @@ -248,9 +251,9 @@ public void genJMethodForBFunc(BIRFunction func, ClassWriter cw, BIRPackage modu BType retType = getReturnType(func); String desc; if (isObjectMethodSplit) { - desc = JvmCodeGenUtil.getMethodDesc(func.type.paramTypes, retType, moduleClassName); + desc = JvmCodeGenUtil.getMethodDesc(typeEnv, func.type.paramTypes, retType, moduleClassName); } else { - desc = JvmCodeGenUtil.getMethodDesc(func.type.paramTypes, retType); + desc = JvmCodeGenUtil.getMethodDesc(typeEnv, func.type.paramTypes, retType); } MethodVisitor mv = cw.visitMethod(access, funcName, desc, null, null); mv.visitCode(); @@ -336,8 +339,8 @@ private void handleParentModuleStart(MethodVisitor mv, PackageID packageID, Stri private BType getReturnType(BIRFunction func) { BType retType = func.type.retType; - if (JvmCodeGenUtil.isExternFunc(func) && Symbols.isFlagOn(retType.flags, Flags.PARAMETERIZED)) { - retType = JvmCodeGenUtil.UNIFIER.build(func.type.retType); + if (JvmCodeGenUtil.isExternFunc(func) && Symbols.isFlagOn(retType.getFlags(), Flags.PARAMETERIZED)) { + retType = JvmCodeGenUtil.UNIFIER.build(typeEnv, func.type.retType); } return retType; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/ModuleStopMethodGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/ModuleStopMethodGen.java index 4ce3e4d86dd5..9789ce923cc1 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/ModuleStopMethodGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/ModuleStopMethodGen.java @@ -28,7 +28,7 @@ import org.wso2.ballerinalang.compiler.bir.codegen.internal.AsyncDataCollector; import org.wso2.ballerinalang.compiler.bir.codegen.split.JvmConstantsGen; import org.wso2.ballerinalang.compiler.bir.model.BIRNode; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; +import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import java.util.Set; @@ -120,7 +120,7 @@ private void generateMethodBody(MethodVisitor mv, String initClass, String stopF JvmCodeGenUtil.createFunctionPointer(mv, initClass, stopFuncName); // no parent strand mv.visitInsn(ACONST_NULL); - jvmTypeGen.loadType(mv, new BNilType()); + jvmTypeGen.loadType(mv, BType.createNilType()); mv.visitLdcInsn("stop"); mv.visitInsn(ACONST_NULL); mv.visitIntInsn(BIPUSH, 1); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/optimizer/LargeMethodOptimizer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/optimizer/LargeMethodOptimizer.java index 579e108d25eb..52c0079e930a 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/optimizer/LargeMethodOptimizer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/optimizer/LargeMethodOptimizer.java @@ -800,7 +800,7 @@ private void createNewFuncForPeriodicSplit(BIRFunction parentFunc, List memberTypes = new LinkedHashSet<>(2); memberTypes.add(newFuncReturnType); memberTypes.add(symbolTable.errorType); - return new BUnionType(null, memberTypes, false, false); + return new BUnionType(symbolTable.typeEnv(), null, memberTypes, false); } private BIRBasicBlock handleNewFuncReturnVal(BIRFunction function, BIROperand splitFuncCallResultOp, @@ -1666,7 +1666,7 @@ private BIRFunction createNewBIRFunctionAcrossBB(BIRFunction parentFunc, Name fu for (BIRVariableDcl funcArg : currSplit.funcArgs) { paramTypes.add(funcArg.type); } - BInvokableType type = new BInvokableType(paramTypes, retType, null); + BInvokableType type = new BInvokableType(symbolTable.typeEnv(), paramTypes, retType, null); BIRFunction birFunc = new BIRFunction(null, funcName, funcName, 0, type, DEFAULT_WORKER_NAME, 0, SymbolOrigin.VIRTUAL); @@ -1869,7 +1869,7 @@ private BIRFunction createNewBIRFuncForSplitInBB(Name funcName, BIRNonTerminator for (BIRVariableDcl funcArg : funcArgs) { paramTypes.add(funcArg.type); } - BInvokableType type = new BInvokableType(paramTypes, retType, null); + BInvokableType type = new BInvokableType(symbolTable.typeEnv(), paramTypes, retType, null); BIRFunction birFunc = new BIRFunction(null, funcName, funcName, 0, type, DEFAULT_WORKER_NAME, 0, SymbolOrigin.VIRTUAL); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/JvmAnnotationsGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/JvmAnnotationsGen.java index fb1305081f4e..e2cda9df5aa3 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/JvmAnnotationsGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/JvmAnnotationsGen.java @@ -28,6 +28,7 @@ import org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures; import org.wso2.ballerinalang.compiler.bir.codegen.JvmTypeGen; import org.wso2.ballerinalang.compiler.bir.model.BIRNode; +import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; @@ -109,7 +110,7 @@ private int generateAnnotationsLoad(ClassWriter cw, List typeDefSet = new TreeSet<>(typeDefHashComparator); for (BIRTypeDefinition t : typeDefinitions) { - if (Symbols.isFlagOn(t.type.flags, Flags.ANONYMOUS)) { + if (Symbols.isFlagOn(t.type.getFlags(), Flags.ANONYMOUS)) { typeDefSet.add(t); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/types/JvmArrayTypeGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/types/JvmArrayTypeGen.java index f9e6631c66f9..cc67203f438c 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/types/JvmArrayTypeGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/types/JvmArrayTypeGen.java @@ -84,7 +84,7 @@ public void createArrayType(MethodVisitor mv, BArrayType arrayType, Types types) if (TypeTags.isSimpleBasicType(arrayType.eType.tag)) { // Load the element type jvmTypeGen.loadType(mv, arrayType.eType); - int arraySize = arrayType.size; + int arraySize = arrayType.getSize(); mv.visitLdcInsn((long) arraySize); mv.visitInsn(L2I); @@ -96,7 +96,7 @@ public void createArrayType(MethodVisitor mv, BArrayType arrayType, Types types) mv.visitLdcInsn(jvmTypeGen.typeFlag(arrayType.eType)); - int arraySize = arrayType.size; + int arraySize = arrayType.getSize(); mv.visitLdcInsn((long) arraySize); mv.visitInsn(L2I); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/types/JvmUnionTypeGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/types/JvmUnionTypeGen.java index 27aa4abfa6d7..4c3bc12bcadb 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/types/JvmUnionTypeGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/types/JvmUnionTypeGen.java @@ -109,7 +109,7 @@ public void createUnionType(MethodVisitor mv, BUnionType unionType) { jvmTypeGen.loadCyclicFlag(mv, unionType); - mv.visitLdcInsn(unionType.flags); + mv.visitLdcInsn(unionType.getFlags()); // initialize the union type without the members array if (nameLoaded) { mv.visitMethodInsn(INVOKESPECIAL, UNION_TYPE_IMPL, JVM_INIT_METHOD, diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/values/JvmObjectGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/values/JvmObjectGen.java index de5e9592c1c2..a87a368ecd65 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/values/JvmObjectGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/values/JvmObjectGen.java @@ -126,7 +126,7 @@ public void createAndSplitCallMethod(ClassWriter cw, List f String methodSig; // use index access, since retType can be nil. - methodSig = JvmCodeGenUtil.getMethodDesc(paramTypes, retType); + methodSig = JvmCodeGenUtil.getMethodDesc(jvmCastGen.typeEnv(), paramTypes, retType); // load self mv.visitVarInsn(ALOAD, 0); @@ -197,8 +197,8 @@ public void createAndSplitCallMethod(ClassWriter cw, List f } private boolean isListenerAttach(BIRNode.BIRFunction func) { - return func.name.value.equals("attach") && Symbols.isFlagOn(func.parameters.getFirst().type.flags, - Flags.SERVICE); + return func.name.value.equals("attach") && + Symbols.isFlagOn(func.parameters.getFirst().type.getFlags(), Flags.SERVICE); } public void createAndSplitGetMethod(ClassWriter cw, Map fields, String className, diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/emit/TypeEmitter.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/emit/TypeEmitter.java index c1f634476a63..d8090ed0d1fb 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/emit/TypeEmitter.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/emit/TypeEmitter.java @@ -126,7 +126,7 @@ private static String emitParameterizedType(BParameterizedType type, int tabs) { } private static String emitTableType(BTableType bType, int tabs) { - boolean readonly = Symbols.isFlagOn(bType.flags, Flags.READONLY); + boolean readonly = Symbols.isFlagOn(bType.getFlags(), Flags.READONLY); if (bType.constraint == null) { return readonly ? bType.toString().concat(" & readonly") : bType.toString(); } @@ -240,8 +240,8 @@ private static String emitBInvokableType(BInvokableType bType, int tabs) { private static String emitBArrayType(BArrayType bType, int tabs) { String arrStr = emitTypeRef(bType.eType, 0); arrStr += "["; - if (bType.size > 0) { - arrStr += bType.size; + if (bType.getSize() > 0) { + arrStr += bType.getSize(); } arrStr += "]"; return arrStr; @@ -256,7 +256,7 @@ private static String emitBRecordType(BRecordType bType, int tabs) { for (BField bField : bType.fields.values()) { if (bField != null) { recordStr.append(emitTabs(tabs + 1)); - String flags = emitFlags(bField.type.flags); + String flags = emitFlags(bField.type.getFlags()); recordStr.append(flags); if (!flags.isEmpty()) { recordStr.append(emitSpaces(1)); @@ -273,7 +273,7 @@ private static String emitBRecordType(BRecordType bType, int tabs) { } private static String emitBObjectType(BObjectType bType, int tabs) { - boolean isService = (bType.flags & Flags.SERVICE) == Flags.SERVICE; + boolean isService = Symbols.isFlagOn(bType.getFlags(), Flags.SERVICE); StringBuilder str = new StringBuilder(); str.append(isService ? "service object" : "object"); @@ -283,7 +283,7 @@ private static String emitBObjectType(BObjectType bType, int tabs) { for (BField bField : bType.fields.values()) { if (bField != null) { str.append(emitTabs(tabs + 1)); - String flags = emitFlags(bField.type.flags); + String flags = emitFlags(bField.type.getFlags()); str.append(flags); if (!flags.isEmpty()) { str.append(emitSpaces(1)); @@ -366,21 +366,7 @@ private static String emitBTypeDesc(BTypedescType bType, int tabs) { } private static String emitBFiniteType(BFiniteType bType, int tabs) { - - StringBuilder str = new StringBuilder(); - str.append("["); - int i = 0; - int length = bType.getValueSpace().size(); - for (Object v : bType.getValueSpace()) { - str.append(v.toString()); - i += 1; - if (i < length) { - str.append(","); - str.append(emitSpaces(1)); - } - } - str.append("]"); - return str.toString(); + return "[" + bType.toString() + "]"; } private static String emitBTypeHandle(BHandleType bType, int tabs) { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRBinaryWriter.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRBinaryWriter.java index 8df6c69efb68..85d5593465c8 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRBinaryWriter.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRBinaryWriter.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.bir.writer; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Env; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.ballerinalang.compiler.BLangCompilerException; @@ -49,16 +50,19 @@ */ public class BIRBinaryWriter { - private final ConstantPool cp = new ConstantPool(); + private final ConstantPool cp; private final BIRNode.BIRPackage birPackage; + private final Env typeEnv; - public BIRBinaryWriter(BIRNode.BIRPackage birPackage) { + public BIRBinaryWriter(BIRNode.BIRPackage birPackage, Env typeEnv) { this.birPackage = birPackage; + this.typeEnv = typeEnv; + cp = new ConstantPool(typeEnv); } public byte[] serialize() { ByteBuf birbuf = Unpooled.buffer(); - BIRTypeWriter typeWriter = new BIRTypeWriter(birbuf, cp); + BIRTypeWriter typeWriter = new BIRTypeWriter(birbuf, cp, typeEnv); // Write the package details in the form of constant pool entry @@ -391,7 +395,7 @@ private void writeAnnotation(ByteBuf buf, BIRTypeWriter typeWriter, BIRNode.BIRA } private void writeConstants(ByteBuf buf, List birConstList) { - BIRTypeWriter constTypeWriter = new BIRTypeWriter(buf, cp); + BIRTypeWriter constTypeWriter = new BIRTypeWriter(buf, cp, typeEnv); buf.writeInt(birConstList.size()); birConstList.forEach(constant -> writeConstant(buf, constTypeWriter, constant)); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRTypeWriter.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRTypeWriter.java index c429eba1ff33..27beec2068be 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRTypeWriter.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRTypeWriter.java @@ -17,17 +17,43 @@ */ package org.wso2.ballerinalang.compiler.bir.writer; +import io.ballerina.types.Atom; +import io.ballerina.types.AtomicType; +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.Bdd; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.CellSemType; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.EnumerableCharString; +import io.ballerina.types.EnumerableDecimal; +import io.ballerina.types.EnumerableFloat; +import io.ballerina.types.EnumerableString; +import io.ballerina.types.Env; +import io.ballerina.types.FixedLengthArray; +import io.ballerina.types.FunctionAtomicType; +import io.ballerina.types.ListAtomicType; +import io.ballerina.types.MappingAtomicType; +import io.ballerina.types.PredefinedTypeEnv; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.RecAtom; +import io.ballerina.types.SemType; +import io.ballerina.types.TypeAtom; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.CharStringSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.NonCharStringSubtype; +import io.ballerina.types.subtypedata.Range; +import io.ballerina.types.subtypedata.StringSubtype; +import io.ballerina.types.subtypedata.XmlSubtype; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.ballerinalang.model.elements.MarkdownDocAttachment; import org.ballerinalang.model.symbols.SymbolKind; -import org.wso2.ballerinalang.compiler.bir.writer.CPEntry.ByteCPEntry; -import org.wso2.ballerinalang.compiler.bir.writer.CPEntry.FloatCPEntry; -import org.wso2.ballerinalang.compiler.bir.writer.CPEntry.IntegerCPEntry; import org.wso2.ballerinalang.compiler.bir.writer.CPEntry.StringCPEntry; -import org.wso2.ballerinalang.compiler.semantics.analyzer.IsAnydataUniqueVisitor; -import org.wso2.ballerinalang.compiler.semantics.analyzer.IsPureTypeUniqueVisitor; -import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BAttachedFunction; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BConstantSymbol; @@ -45,7 +71,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; @@ -56,7 +81,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; @@ -73,13 +97,12 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BTypedescType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; -import org.wso2.ballerinalang.compiler.semantics.model.types.TypeFlags; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; -import org.wso2.ballerinalang.compiler.util.TypeTags; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.util.Flags; +import java.math.BigDecimal; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; @@ -89,29 +112,27 @@ /** * Writes bType to a Byte Buffer in binary format. * A ConstPool is used to store string typed information. - * + * * @since 0.995.0 */ -public class BIRTypeWriter implements TypeVisitor { +public class BIRTypeWriter extends TypeVisitor { private final ByteBuf buff; private final ConstantPool cp; - private static final IsPureTypeUniqueVisitor isPureTypeUniqueVisitor = new IsPureTypeUniqueVisitor(); - private static final IsAnydataUniqueVisitor isAnydataUniqueVisitor = new IsAnydataUniqueVisitor(); + private final Set visitedAtoms = new HashSet<>(); + private final Env typeEnv; + private final PredefinedTypeEnv predefinedTypeEnv = PredefinedTypeEnv.getInstance(); - public BIRTypeWriter(ByteBuf buff, ConstantPool cp) { + public BIRTypeWriter(ByteBuf buff, ConstantPool cp, Env typeEnv) { this.buff = buff; this.cp = cp; + this.typeEnv = typeEnv; } public void visitType(BType type) { buff.writeByte(type.tag); buff.writeInt(addStringCPEntry(type.name.getValue())); - buff.writeLong(type.flags); - isPureTypeUniqueVisitor.reset(); - isAnydataUniqueVisitor.reset(); - buff.writeInt(TypeFlags.asMask(type.isNullable(), isAnydataUniqueVisitor.visit(type), - isPureTypeUniqueVisitor.visit(type))); + buff.writeLong(type.getFlags()); type.accept(this); } @@ -127,15 +148,10 @@ public void visit(BAnnotationType bAnnotationType) { @Override public void visit(BArrayType bArrayType) { buff.writeByte(bArrayType.state.getValue()); - buff.writeInt(bArrayType.size); + buff.writeInt(bArrayType.getSize()); writeTypeCpIndex(bArrayType.getElementType()); } - @Override - public void visit(BBuiltInRefType bBuiltInRefType) { - throwUnimplementedError(bBuiltInRefType); - } - @Override public void visit(BAnyType bAnyType) { } @@ -174,20 +190,15 @@ public void visit(BFiniteType bFiniteType) { BTypeSymbol tsymbol = bFiniteType.tsymbol; buff.writeInt(addStringCPEntry(tsymbol.name.value)); buff.writeLong(tsymbol.flags); - buff.writeInt(bFiniteType.getValueSpace().size()); - for (BLangExpression valueLiteral : bFiniteType.getValueSpace()) { - if (!(valueLiteral instanceof BLangLiteral bLangLiteral)) { - throw new AssertionError( - "Type serialization is not implemented for finite type with value: " + valueLiteral.getKind()); - } - writeTypeCpIndex(valueLiteral.getBType()); - writeValue(bLangLiteral.value, valueLiteral.getBType()); + buff.writeInt(bFiniteType.valueSpace.length); + for (SemNamedType semNamedType:bFiniteType.valueSpace) { + writeSemNamedType(semNamedType); } } @Override public void visit(BInvokableType bInvokableType) { - boolean isAnyFunction = Symbols.isFlagOn(bInvokableType.flags, Flags.ANY_FUNCTION); + boolean isAnyFunction = Symbols.isFlagOn(bInvokableType.getFlags(), Flags.ANY_FUNCTION); buff.writeBoolean(isAnyFunction); // write 1 if it’s an any function if not write 0 if (isAnyFunction) { @@ -305,7 +316,7 @@ public void visit(BNeverType bNeverType) { } @Override - public void visit(BNilType bNilType) { + public void visitNilType(BType bType) { // Nothing to do } @@ -494,11 +505,6 @@ private void writeAttachFunction(BAttachedFunction attachedFunc) { writeTypeCpIndex(attachedFunc.type); } - @Override - public void visit(BType bType) { - // Nothing to do - } - @Override public void visit(BXMLType bxmlType) { writeTypeCpIndex(bxmlType.constraint); @@ -562,63 +568,251 @@ private int addStringCPEntry(String value) { return cp.addCPEntry(new StringCPEntry(value)); } - private int addIntCPEntry(long value) { - return cp.addCPEntry(new IntegerCPEntry(value)); - } - - private int addFloatCPEntry(double value) { - return cp.addCPEntry(new FloatCPEntry(value)); - } - - private int addByteCPEntry(int value) { - return cp.addCPEntry(new ByteCPEntry(value)); - } - - private void writeValue(Object value, BType typeOfValue) { - ByteBuf byteBuf = Unpooled.buffer(); - switch (Types.getImpliedType(typeOfValue).tag) { - case TypeTags.INT: - case TypeTags.SIGNED32_INT: - case TypeTags.SIGNED16_INT: - case TypeTags.SIGNED8_INT: - case TypeTags.UNSIGNED32_INT: - case TypeTags.UNSIGNED16_INT: - case TypeTags.UNSIGNED8_INT: - byteBuf.writeInt(addIntCPEntry((Long) value)); - break; - case TypeTags.BYTE: - int byteValue = ((Number) value).intValue(); - byteBuf.writeInt(addByteCPEntry(byteValue)); - break; - case TypeTags.FLOAT: - // TODO:Remove the instanceof check by converting the float literal instance in Semantic analysis phase - double doubleVal = - value instanceof String ? Double.parseDouble((String) value) : ((Number) value).doubleValue(); - byteBuf.writeInt(addFloatCPEntry(doubleVal)); - break; - case TypeTags.STRING: - case TypeTags.CHAR_STRING: - case TypeTags.DECIMAL: - byteBuf.writeInt(addStringCPEntry(String.valueOf(value))); - break; - case TypeTags.BOOLEAN: - byteBuf.writeBoolean((Boolean) value); - break; - case TypeTags.NIL: - break; - default: - throw new UnsupportedOperationException("finite type value is not supported for type: " + typeOfValue); - } - - int length = byteBuf.nioBuffer().limit(); - buff.writeInt(length); - buff.writeBytes(byteBuf.nioBuffer().array(), 0, length); - } - private void writeTypeInclusions(List inclusions) { buff.writeInt(inclusions.size()); for (BType inclusion : inclusions) { writeTypeCpIndex(inclusion); } } + + private void writeNullableString(String nullableString) { + boolean hasNonNullString = nullableString != null; + buff.writeBoolean(hasNonNullString); + if (hasNonNullString) { + buff.writeInt(addStringCPEntry(nullableString)); + } + } + + private void writeSemNamedType(SemNamedType semNamedType) { + writeSemType(semNamedType.semType()); + writeNullableString(semNamedType.optName().orElse(null)); + } + + // --------------------------------------- Writing SemType ---------------------------------------------- + + private void writeSemType(SemType semType) { + boolean hasSemType = semType != null; + buff.writeBoolean(hasSemType); + if (!hasSemType) { + return; + } + + boolean isUniformTypeBitSet = semType instanceof BasicTypeBitSet; + buff.writeBoolean(isUniformTypeBitSet); + + buff.writeInt(semType.all()); + if (isUniformTypeBitSet) { + return; + } + + ComplexSemType complexSemType = (ComplexSemType) semType; + buff.writeInt(complexSemType.some()); + + ProperSubtypeData[] subtypeDataList = complexSemType.subtypeDataList(); + buff.writeByte(subtypeDataList.length); + for (ProperSubtypeData psd : subtypeDataList) { + writeProperSubtypeData(psd); + } + } + + private void writeProperSubtypeData(ProperSubtypeData psd) { + if (psd instanceof Bdd bdd) { + buff.writeByte(1); + writeBdd(bdd); + } else if (psd instanceof IntSubtype intSubtype) { + buff.writeByte(2); + writeIntSubtype(intSubtype); + } else if (psd instanceof BooleanSubtype booleanSubtype) { + buff.writeByte(3); + buff.writeBoolean(booleanSubtype.value); + } else if (psd instanceof FloatSubtype floatSubtype) { + buff.writeByte(4); + writeFloatSubtype(floatSubtype); + } else if (psd instanceof DecimalSubtype decimalSubtype) { + buff.writeByte(5); + writeDecimalSubtype(decimalSubtype); + } else if (psd instanceof StringSubtype stringSubtype) { + buff.writeByte(6); + writeStringSubtype(stringSubtype); + } else if (psd instanceof XmlSubtype xmlSubtype) { + buff.writeByte(7); + buff.writeInt(xmlSubtype.primitives); + writeBdd(xmlSubtype.sequence); + } else { + throw new IllegalStateException("Unknown ProperSubtypeData"); + } + } + + private void writeBdd(Bdd bdd) { + buff.writeBoolean(bdd instanceof BddNode); + if (bdd instanceof BddNode bddNode) { + writeBddNode(bddNode); + } else { + BddAllOrNothing bddAllOrNothing = (BddAllOrNothing) bdd; + buff.writeBoolean(bddAllOrNothing.isAll()); + } + } + + private static final byte REC_ATOM_KIND = 0; + private static final byte INLINED_ATOM_KIND = 1; + private static final byte TYPE_ATOM_KIND = 2; + + private void writeBddNode(BddNode bddNode) { + Atom atom = bddNode.atom(); + if (atom instanceof RecAtom recAtom) { + writeRecAtom(recAtom); + } else { + buff.writeByte(TYPE_ATOM_KIND); + TypeAtom typeAtom = (TypeAtom) atom; + visitedAtoms.add(typeAtom.getIdentifier()); + writeTypeAtom(typeAtom); + } + writeBdd(bddNode.left()); + writeBdd(bddNode.middle()); + writeBdd(bddNode.right()); + } + + private void writeRecAtom(RecAtom recAtom) { + if (shouldInline(recAtom)) { + writeInlinedRecAtom(recAtom); + } else { + buff.writeByte(REC_ATOM_KIND); + int index = typeEnv.compactRecIndex(recAtom); + buff.writeInt(index); + if (!predefinedTypeEnv.isPredefinedRecAtom(index)) { + buff.writeInt(recAtom.kind().ordinal()); + } + } + } + + private void writeInlinedRecAtom(RecAtom recAtom) { + visitedAtoms.add(recAtom.getIdentifier()); + buff.writeByte(INLINED_ATOM_KIND); + buff.writeInt(typeEnv.compactRecIndex(recAtom)); + TypeAtom typeAtom = switch (recAtom.kind()) { + case LIST_ATOM -> typeEnv.listAtom(typeEnv.listAtomType(recAtom)); + case FUNCTION_ATOM -> typeEnv.functionAtom(typeEnv.functionAtomType(recAtom)); + case MAPPING_ATOM -> typeEnv.mappingAtom(typeEnv.mappingAtomType(recAtom)); + case XML_ATOM, DISTINCT_ATOM -> + throw new IllegalStateException("Should not happen. Handled before reaching here"); + case CELL_ATOM -> throw new IllegalStateException("Cell atom cannot be recursive"); + }; + writeTypeAtom(typeAtom); + } + + private boolean shouldInline(RecAtom recAtom) { + // We can have cases where none of the BDDs have the actual BDD node in them just reference to it using + // RecAtoms. But when we deserialize the nodes we need to get the actual BDD node somehow. Currently, we + // "inline" the actual node first time we see it in the tree. Exceptions to this rule are predefined rec atoms + // which are unique and every environment has the same atoms and XML atoms + if (predefinedTypeEnv.isPredefinedRecAtom(recAtom.index) || recAtom.kind() == Atom.Kind.XML_ATOM || + recAtom.kind() == Atom.Kind.DISTINCT_ATOM) { + return false; + } + return !visitedAtoms.contains(recAtom.getIdentifier()); + } + + private void writeTypeAtom(TypeAtom typeAtom) { + buff.writeInt(typeAtom.index()); + writeAtomicType(typeAtom.atomicType()); + } + + private void writeAtomicType(AtomicType atomicType) { + if (atomicType instanceof MappingAtomicType mappingAtomicType) { + buff.writeByte(1); + writeMappingAtomicType(mappingAtomicType); + } else if (atomicType instanceof ListAtomicType listAtomicType) { + buff.writeByte(2); + writeListAtomicType(listAtomicType); + } else if (atomicType instanceof FunctionAtomicType functionAtomicType) { + buff.writeByte(3); + writeFunctionAtomicType(functionAtomicType); + } else if (atomicType instanceof CellAtomicType cellAtomicType) { + buff.writeByte(4); + writeSemType(cellAtomicType.ty()); + buff.writeByte(cellAtomicType.mut().ordinal()); + } else { + throw new UnsupportedOperationException("Unexpected atomic type " + atomicType); + } + } + + private void writeFunctionAtomicType(FunctionAtomicType functionAtomicType) { + writeSemType(functionAtomicType.paramType()); + writeSemType(functionAtomicType.retType()); + writeSemType(functionAtomicType.qualifiers()); + buff.writeBoolean(functionAtomicType.isGeneric()); + } + + private void writeMappingAtomicType(MappingAtomicType mat) { + String[] names = mat.names(); + buff.writeInt(names.length); + for (String name : names) { + buff.writeInt(addStringCPEntry(name)); + } + CellSemType[] types = mat.types(); + buff.writeInt(types.length); + for (CellSemType type : types) { + writeSemType(type); + } + writeSemType(mat.rest()); + } + + private void writeListAtomicType(ListAtomicType lat) { + FixedLengthArray fla = lat.members(); + List initial = fla.initial(); + buff.writeInt(initial.size()); + for (SemType type : initial) { + writeSemType(type); + } + buff.writeInt(fla.fixedLength()); + writeSemType(lat.rest()); + } + + private void writeIntSubtype(IntSubtype intSubtype) { + Range[] ranges = intSubtype.ranges; + buff.writeInt(ranges.length); + for (Range range : ranges) { + buff.writeLong(range.min); + buff.writeLong(range.max); + } + } + + private void writeFloatSubtype(FloatSubtype floatSubtype) { + buff.writeBoolean(floatSubtype.allowed); + buff.writeInt(floatSubtype.values.length); + for (EnumerableFloat ef : floatSubtype.values) { + buff.writeDouble(ef.value); + } + } + + private void writeDecimalSubtype(DecimalSubtype decimalSubtype) { + buff.writeBoolean(decimalSubtype.allowed); + buff.writeInt(decimalSubtype.values.length); + for (EnumerableDecimal ed : decimalSubtype.values) { + BigDecimal bigDecimal = ed.value; + buff.writeInt(bigDecimal.scale()); + byte[] unscaledValueBytes = bigDecimal.unscaledValue().toByteArray(); + buff.writeInt(unscaledValueBytes.length); + buff.writeBytes(unscaledValueBytes); + } + } + + private void writeStringSubtype(StringSubtype stringSubtype) { + CharStringSubtype charData = stringSubtype.getChar(); + buff.writeBoolean(charData.allowed); + EnumerableCharString[] charValues = charData.values; + buff.writeInt(charValues.length); + for (EnumerableCharString ecs : charValues) { + buff.writeInt(addStringCPEntry(ecs.value)); + } + NonCharStringSubtype nonCharData = stringSubtype.getNonChar(); + buff.writeBoolean(nonCharData.allowed); + EnumerableString[] nonCharValues = nonCharData.values; + buff.writeInt(nonCharValues.length); + for (EnumerableString ecs : nonCharValues) { + buff.writeInt(addStringCPEntry(ecs.value)); + } + } + + // --------------------------------------- End of SemType ----------------------------------------------- } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/ConstantPool.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/ConstantPool.java index bba3434f9d2a..edb6d2e08fc1 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/ConstantPool.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/ConstantPool.java @@ -17,7 +17,7 @@ */ package org.wso2.ballerinalang.compiler.bir.writer; - +import io.ballerina.types.Env; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.ballerinalang.compiler.BLangCompilerException; @@ -45,6 +45,11 @@ public class ConstantPool { private final Map cpEntriesMap = new HashMap<>(); private final List cpEntries = new ArrayList<>(); + private final Env typeEnv; + + public ConstantPool(Env typeEnv) { + this.typeEnv = typeEnv; + } public int addCPEntry(CPEntry cpEntry) { int size = cpEntries.size(); @@ -118,7 +123,7 @@ private void writeToStream(DataOutputStream stream) throws IOException { case CP_ENTRY_SHAPE -> { CPEntry.ShapeCPEntry shapeCPEntry = (CPEntry.ShapeCPEntry) cpEntry; ByteBuf typeBuf = Unpooled.buffer(); - BIRTypeWriter birTypeWriter = new BIRTypeWriter(typeBuf, this); + BIRTypeWriter birTypeWriter = new BIRTypeWriter(typeBuf, this, typeEnv); birTypeWriter.visitType(shapeCPEntry.shape); byte[] bytes = Arrays.copyOfRange(typeBuf.array(), 0, typeBuf.writerIndex()); stream.writeInt(bytes.length); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ASTBuilderUtil.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ASTBuilderUtil.java index d36bb7fe379e..205071c02a21 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ASTBuilderUtil.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ASTBuilderUtil.java @@ -17,6 +17,7 @@ package org.wso2.ballerinalang.compiler.desugar; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Env; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; @@ -180,7 +181,7 @@ private static boolean isValueType(BType type) { static BLangExpression wrapToConversionExpr(BType sourceType, BLangExpression exprToWrap, SymbolTable symTable, Types types) { - if (types.isSameType(sourceType, exprToWrap.getBType()) || !isValueType(exprToWrap.getBType())) { + if (types.isSameTypeIncludingTags(sourceType, exprToWrap.getBType()) || !isValueType(exprToWrap.getBType())) { // No conversion needed. return exprToWrap; } @@ -329,7 +330,7 @@ static BLangReturn createReturnStmt(Location pos, BlockNode target) { public static BLangReturn createNilReturnStmt(Location pos, BType nilType) { final BLangReturn returnStmt = (BLangReturn) TreeBuilder.createReturnNode(); returnStmt.pos = pos; - returnStmt.expr = createLiteral(pos, nilType, Names.NIL_VALUE); + returnStmt.expr = createLiteral(pos, nilType, Names.NIL_VALUE.value); return returnStmt; } @@ -844,7 +845,7 @@ private static IdentifierNode createIdentifier(String value) { return node; } - public static BInvokableSymbol duplicateInvokableSymbol(BInvokableSymbol invokableSymbol) { + public static BInvokableSymbol duplicateInvokableSymbol(Env typeEnv, BInvokableSymbol invokableSymbol) { BInvokableSymbol dupFuncSymbol = Symbols.createFunctionSymbol(invokableSymbol.flags, invokableSymbol.name, invokableSymbol.originalName, invokableSymbol.pkgID, invokableSymbol.type, invokableSymbol.owner, @@ -864,18 +865,18 @@ public static BInvokableSymbol duplicateInvokableSymbol(BInvokableSymbol invokab new ArrayList<>((List) invokableSymbol.getAnnotations())); BInvokableType prevFuncType = (BInvokableType) invokableSymbol.type; - BInvokableType dupInvokableType = new BInvokableType(new ArrayList<>(prevFuncType.paramTypes), - prevFuncType.restType, prevFuncType.retType, - prevFuncType.tsymbol); + BInvokableType dupInvokableType = + new BInvokableType(typeEnv, List.copyOf(prevFuncType.paramTypes), + prevFuncType.restType, prevFuncType.retType, prevFuncType.tsymbol); if (Symbols.isFlagOn(invokableSymbol.flags, Flags.ISOLATED)) { dupFuncSymbol.flags |= Flags.ISOLATED; - dupInvokableType.flags |= Flags.ISOLATED; + dupInvokableType.addFlags(Flags.ISOLATED); } if (Symbols.isFlagOn(invokableSymbol.flags, Flags.TRANSACTIONAL)) { dupFuncSymbol.flags |= Flags.TRANSACTIONAL; - dupInvokableType.flags |= Flags.TRANSACTIONAL; + dupInvokableType.addFlags(Flags.TRANSACTIONAL); } dupFuncSymbol.type = dupInvokableType; @@ -884,7 +885,8 @@ public static BInvokableSymbol duplicateInvokableSymbol(BInvokableSymbol invokab return dupFuncSymbol; } - public static BInvokableSymbol duplicateFunctionDeclarationSymbol(BInvokableSymbol invokableSymbol, + public static BInvokableSymbol duplicateFunctionDeclarationSymbol(Env typeEnv, + BInvokableSymbol invokableSymbol, BSymbol owner, Name newName, PackageID newPkgID, @@ -914,9 +916,9 @@ public static BInvokableSymbol duplicateFunctionDeclarationSymbol(BInvokableSymb dupFuncSymbol.markdownDocumentation = invokableSymbol.markdownDocumentation; BInvokableType prevFuncType = (BInvokableType) invokableSymbol.type; - BType newFuncType = new BInvokableType(new ArrayList<>(prevFuncType.paramTypes), prevFuncType.restType, - prevFuncType.retType, prevFuncType.tsymbol); - newFuncType.flags |= prevFuncType.flags; + BType newFuncType = new BInvokableType(typeEnv, List.copyOf(prevFuncType.paramTypes), + prevFuncType.restType, prevFuncType.retType, prevFuncType.tsymbol); + newFuncType.addFlags(prevFuncType.getFlags()); dupFuncSymbol.type = newFuncType; return dupFuncSymbol; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/AnnotationDesugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/AnnotationDesugar.java index 79b4af59f064..28098fb1cee5 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/AnnotationDesugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/AnnotationDesugar.java @@ -223,7 +223,7 @@ private void defineClassAnnotations(BLangPackage pkgNode, SymbolEnv env2, BLangF owner); if (lambdaFunction != null) { BType type = classDef.getBType(); - if (Symbols.isFlagOn(type.flags, Flags.OBJECT_CTOR)) { + if (Symbols.isFlagOn(type.getFlags(), Flags.OBJECT_CTOR)) { if (normalMode) { // Add the lambda/invocation in a temporary block. BLangBlockStmt target = (BLangBlockStmt) TreeBuilder.createBlockNode(); @@ -503,7 +503,7 @@ private void defineFunctionAnnotations(BLangPackage pkgNode, SymbolEnv env, BLan int index; if (function.attachedFunction - && Symbols.isFlagOn(function.receiver.getBType().flags, Flags.OBJECT_CTOR)) { + && Symbols.isFlagOn(function.receiver.getBType().getFlags(), Flags.OBJECT_CTOR)) { addLambdaToGlobalAnnotMap(identifier, lambdaFunction, target); index = calculateIndex(initFnBody.stmts, function.receiver.getBType().tsymbol); } else { @@ -759,7 +759,7 @@ private void addVarArgsAnnotation(BLangFunction mainFunc, SymbolEnv env) { BLangListConstructorExpr.BLangArrayLiteral valueLiteral = (BLangListConstructorExpr.BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode(); - valueLiteral.setBType(new BArrayType(symTable.stringType)); + valueLiteral.setBType(new BArrayType(symTable.typeEnv(), symTable.stringType)); valueLiteral.typeChecked = true; valueLiteral.pos = pos; @@ -794,7 +794,7 @@ private void addVarArgsAnnotation(BLangFunction mainFunc, SymbolEnv env) { private BLangFunction defineFunction(Location pos, PackageID pkgID, BSymbol owner) { String funcName = ANNOT_FUNC + UNDERSCORE + annotFuncCount++; BLangFunction function = ASTBuilderUtil.createFunction(pos, funcName); - function.setBType(new BInvokableType(Collections.emptyList(), symTable.mapType, null)); + function.setBType(new BInvokableType(symTable.typeEnv(), Collections.emptyList(), symTable.mapType, null)); BLangBuiltInRefTypeNode anyMapType = (BLangBuiltInRefTypeNode) TreeBuilder.createBuiltInReferenceTypeNode(); anyMapType.typeKind = TypeKind.MAP; anyMapType.pos = pos; @@ -904,9 +904,9 @@ private BInvokableSymbol createInvokableSymbol(BLangFunction function, PackageID .toList(); functionSymbol.scope = new Scope(functionSymbol); functionSymbol.restParam = function.restParam != null ? function.restParam.symbol : null; - functionSymbol.type = new BInvokableType(Collections.emptyList(), + functionSymbol.type = new BInvokableType(symTable.typeEnv(), Collections.emptyList(), function.restParam != null ? function.restParam.getBType() : null, - new BMapType(TypeTags.MAP, symTable.anyType, null), + new BMapType(symTable.typeEnv(), TypeTags.MAP, symTable.anyType, null), null); function.symbol = functionSymbol; return functionSymbol; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureDesugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureDesugar.java index 36c7f958a3b5..b83ce79d4df1 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureDesugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureDesugar.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.desugar; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Env; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.symbols.SymbolKind; @@ -266,7 +267,7 @@ public void visit(BLangPackage pkgNode) { // Update function parameters. for (BLangFunction function : pkgNode.functions) { - updateFunctionParams(function); + updateFunctionParams(types.typeEnv(), function); } result = pkgNode; } @@ -369,10 +370,10 @@ private void addClosureMapToInit(BLangClassDefinition classDef, BVarSymbol mapSy initFunction.symbol.scope.define(paramSym.name, paramSym); initFunction.symbol.params.add(paramSym); BInvokableType initFuncSymbolType = (BInvokableType) initFunction.symbol.type; - initFuncSymbolType.paramTypes.add(mapSymbol.type); + initFuncSymbolType.addParamType(mapSymbol.type); BInvokableType initFnType = (BInvokableType) initFunction.getBType(); - initFnType.paramTypes.add(mapSymbol.type); + initFnType.addParamType(mapSymbol.type); BAttachedFunction attachedFunction = ((BObjectTypeSymbol) classDef.getBType().tsymbol).generatedInitializerFunc; attachedFunction.symbol.params.add(paramSym); @@ -644,21 +645,24 @@ private void createFunctionMap(BLangFunction funcNode, SymbolEnv funcEnv) { /** * Update the function parameters with closure parameter maps passed. * + * @param typeEnv type environment * @param funcNode function node */ - private static void updateFunctionParams(BLangFunction funcNode) { + private static void updateFunctionParams(Env typeEnv, BLangFunction funcNode) { // Add closure params to the required param list if there are any. - BInvokableSymbol dupFuncSymbol = ASTBuilderUtil.duplicateInvokableSymbol(funcNode.symbol); + BInvokableSymbol dupFuncSymbol = ASTBuilderUtil.duplicateInvokableSymbol(typeEnv, funcNode.symbol); funcNode.symbol = dupFuncSymbol; BInvokableType dupFuncType = (BInvokableType) dupFuncSymbol.type; int i = 0; + List newParamTypes = new ArrayList<>(dupFuncType.paramTypes); for (Map.Entry entry : funcNode.paramClosureMap.entrySet()) { BVarSymbol mapSymbol = entry.getValue(); dupFuncSymbol.params.add(i, mapSymbol); - dupFuncType.paramTypes.add(i, mapSymbol.type); + newParamTypes.add(i, mapSymbol.type); i++; } + dupFuncType.setParamTypes(newParamTypes); } /** diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureGenerator.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureGenerator.java index 5d091d631269..26aff82289b0 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureGenerator.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureGenerator.java @@ -655,9 +655,10 @@ private void updateFunctionParams(BLangFunction funcNode, List param VIRTUAL); funcSymbol.scope.define(symbolName, varSymbol); funcSymbol.params.add(varSymbol); - ((BInvokableType) funcSymbol.type).paramTypes.add(type); + BInvokableType funcType = (BInvokableType) funcSymbol.type; + funcType.addParamType(varSymbol.type); funcNode.requiredParams.add(ASTBuilderUtil.createVariable(pos, symbolName.value, type, null, - varSymbol)); + varSymbol)); } } @@ -728,7 +729,7 @@ private BLangFunction createFunction(String funcName, Location pos, PackageID pk function.flagSet.add(Flag.PUBLIC); BInvokableTypeSymbol invokableTypeSymbol = Symbols.createInvokableTypeSymbol(SymTag.FUNCTION_TYPE, Flags.PUBLIC, pkgID, bType, owner, pos, VIRTUAL); - function.setBType(new BInvokableType(new ArrayList<>(), bType, invokableTypeSymbol)); + function.setBType(new BInvokableType(symTable.typeEnv(), List.of(), bType, invokableTypeSymbol)); BLangBuiltInRefTypeNode typeNode = (BLangBuiltInRefTypeNode) TreeBuilder.createBuiltInReferenceTypeNode(); typeNode.setBType(bType); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java index 049701b1cd49..4b166c7b7b1a 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java @@ -723,8 +723,9 @@ protected void createInvokableSymbol(BLangFunction bLangFunction, SymbolEnv env) BType returnType = bLangFunction.returnTypeNode.getBType() == null ? symResolver.resolveTypeNode(bLangFunction.returnTypeNode, env) : bLangFunction.returnTypeNode.getBType(); - BInvokableType invokableType = new BInvokableType(new ArrayList<>(), getRestType(bLangFunction), - returnType, null); + BInvokableType invokableType = + new BInvokableType(symTable.typeEnv(), List.of(), getRestType(bLangFunction), + returnType, null); BInvokableSymbol functionSymbol = Symbols.createFunctionSymbol(Flags.asMask(bLangFunction.flagSet), new Name(bLangFunction.name.value), new Name(bLangFunction.name.originalValue), @@ -964,7 +965,7 @@ private List getConfigurableLangLibInvocationParam(BLangSimpleV BLangLiteral configNameLiteral = ASTBuilderUtil.createLiteral(configurableVar.pos, symTable.stringType, configVarName); BType type = configurableVar.getBType(); - BType typedescType = new BTypedescType(type, symTable.typeDesc.tsymbol); + BType typedescType = new BTypedescType(symTable.typeEnv(), type, symTable.typeDesc.tsymbol); BLangTypedescExpr typedescExpr = new BLangTypedescExpr(); typedescExpr.resolvedType = type; @@ -1379,7 +1380,7 @@ public void visit(BLangFunction funcNode) { // Duplicate the invokable symbol and the invokable type. funcNode.originalFuncSymbol = funcNode.symbol; - funcNode.symbol = ASTBuilderUtil.duplicateInvokableSymbol(funcNode.symbol); + funcNode.symbol = ASTBuilderUtil.duplicateInvokableSymbol(types.typeEnv(), funcNode.symbol); funcNode.requiredParams = rewrite(funcNode.requiredParams, funcEnv); funcNode.restParam = rewrite(funcNode.restParam, funcEnv); @@ -1715,7 +1716,7 @@ private void createVarDefStmts(BLangTupleVariable parentTupleVariable, BLangBloc if (variable.getKind() == NodeKind.TUPLE_VARIABLE) { // Else recursively create the var def statements. BLangTupleVariable tupleVariable = (BLangTupleVariable) variable; BLangIndexBasedAccess arrayAccessExpr = ASTBuilderUtil.createIndexBasesAccessExpr(tupleVariable.pos, - new BArrayType(symTable.anyType), tupleVarSymbol, indexExpr); + new BArrayType(symTable.typeEnv(), symTable.anyType), tupleVarSymbol, indexExpr); if (parentIndexAccessExpr != null) { arrayAccessExpr.expr = parentIndexAccessExpr; } @@ -1791,7 +1792,7 @@ private void createVarDefStmts(BLangRecordVariable parentRecordVariable, BLangBl if (recordFieldKeyValue.valueBindingPattern.getKind() == NodeKind.TUPLE_VARIABLE) { BLangTupleVariable tupleVariable = (BLangTupleVariable) recordFieldKeyValue.valueBindingPattern; BLangIndexBasedAccess arrayAccessExpr = ASTBuilderUtil.createIndexBasesAccessExpr(tupleVariable.pos, - new BArrayType(symTable.anyType), recordVarSymbol, indexExpr); + new BArrayType(symTable.typeEnv(), symTable.anyType), recordVarSymbol, indexExpr); if (parentIndexAccessExpr != null) { arrayAccessExpr.expr = parentIndexAccessExpr; } @@ -2024,12 +2025,12 @@ private BLangSimpleVariable generateRestFilter(BLangSimpleVarRef mapVarRef, Loca ASTBuilderUtil.createVariableRef(pos, mapVariable.symbol), typeCastExpr.getBType()); String entriesVarName = "$map$ref$entries$" + UNDERSCORE + restNum; BType constraintType = getRestFilterConstraintType(targetType); - BVarSymbol varSymbol = new BVarSymbol(constraintType.flags, null, null, constraintType, null, + BVarSymbol varSymbol = new BVarSymbol(constraintType.getFlags(), null, null, constraintType, null, null, null); BVarSymbol stringVarSymbol = new BVarSymbol(0, null, null, symTable.stringType, null, symTable.builtinPos, SymbolOrigin.VIRTUAL); - BType entriesType = new BMapType(TypeTags.MAP, - new BTupleType(Arrays.asList(new BTupleMember(symTable.stringType, stringVarSymbol), + BType entriesType = new BMapType(symTable.typeEnv(), TypeTags.MAP, + new BTupleType(symTable.typeEnv(), Arrays.asList(new BTupleMember(symTable.stringType, stringVarSymbol), new BTupleMember(constraintType, varSymbol))), null); BLangSimpleVariable entriesInvocationVar = defVariable(pos, entriesType, parentBlockStmt, types.addConversionExprIfRequired(entriesInvocation, entriesType), @@ -2131,7 +2132,9 @@ private BLangLambdaFunction generateEntriesToMapLambda(Location pos, BType const .map(param -> param.symbol) .toList(); functionSymbol.scope = env.scope; - functionSymbol.type = new BInvokableType(Collections.singletonList(getStringAnyTupleType()), constraint, null); + functionSymbol.type = + new BInvokableType(symTable.typeEnv(), Collections.singletonList(getStringAnyTupleType()), constraint, + null); function.symbol = functionSymbol; rewrite(function, env); env.enclPkg.addFunction(function); @@ -2282,7 +2285,7 @@ private BLangInvocation generateErrorCauseLanglibFunction(Location pos, BType ca private BLangInvocation generateCreateRecordValueInvocation(Location pos, BType targetType, BVarSymbol source) { - BType typedescType = new BTypedescType(targetType, symTable.typeDesc.tsymbol); + BType typedescType = new BTypedescType(symTable.typeEnv(), targetType, symTable.typeDesc.tsymbol); BLangInvocation invocationNode = createInvocationNode(CREATE_RECORD_VALUE, new ArrayList<>(), typedescType); BLangTypedescExpr typedescExpr = new BLangTypedescExpr(); @@ -2293,14 +2296,14 @@ private BLangInvocation generateCreateRecordValueInvocation(Location pos, invocationNode.symbol = symResolver.lookupLangLibMethod(typedescType, Names.fromString(CREATE_RECORD_VALUE), env); invocationNode.requiredArgs = Lists.of(ASTBuilderUtil.createVariableRef(pos, source), typedescExpr); - invocationNode.setBType(BUnionType.create(null, targetType, symTable.errorType)); + invocationNode.setBType(BUnionType.create(symTable.typeEnv(), null, targetType, symTable.errorType)); return invocationNode; } private BLangInvocation generateCloneWithTypeInvocation(Location pos, BType targetType, BVarSymbol source) { - BType typedescType = new BTypedescType(targetType, symTable.typeDesc.tsymbol); + BType typedescType = new BTypedescType(symTable.typeEnv(), targetType, symTable.typeDesc.tsymbol); BLangInvocation invocationNode = createInvocationNode(CLONE_WITH_TYPE, new ArrayList<>(), typedescType); BLangTypedescExpr typedescExpr = new BLangTypedescExpr(); @@ -2310,7 +2313,7 @@ private BLangInvocation generateCloneWithTypeInvocation(Location pos, invocationNode.expr = typedescExpr; invocationNode.symbol = symResolver.lookupLangLibMethod(typedescType, Names.fromString(CLONE_WITH_TYPE), env); invocationNode.requiredArgs = Lists.of(ASTBuilderUtil.createVariableRef(pos, source), typedescExpr); - invocationNode.setBType(BUnionType.create(null, targetType, symTable.errorType)); + invocationNode.setBType(BUnionType.create(symTable.typeEnv(), null, targetType, symTable.errorType)); return invocationNode; } @@ -2417,7 +2420,7 @@ private BInvokableSymbol createReturnTrueStatement(Location pos, BLangFunction f .map(param -> param.symbol) .toList(); functionSymbol.scope = env.scope; - functionSymbol.type = new BInvokableType(Collections.singletonList(getStringAnyTupleType()), + functionSymbol.type = new BInvokableType(symTable.typeEnv(), Collections.singletonList(getStringAnyTupleType()), getRestType(functionSymbol), symTable.booleanType, null); function.symbol = functionSymbol; rewrite(function, env); @@ -2449,7 +2452,7 @@ private BTupleType getStringAnyTupleType() { add(new BTupleMember(symTable.stringType, stringVarSymbol)); add(new BTupleMember(symTable.anyType, anyVarSymbol)); }}; - return new BTupleType(typeList); + return new BTupleType(symTable.typeEnv(), typeList); } /** @@ -2538,7 +2541,7 @@ public void visit(BLangTupleDestructure tupleDestructure) { final BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(tupleDestructure.pos); //create a array of any-type based on the dimension - BType runTimeType = new BArrayType(symTable.anyType); + BType runTimeType = new BArrayType(symTable.typeEnv(), symTable.anyType); //create a simple var for the array 'any[] x = (tuple)' based on the dimension for x String name = "tuple"; @@ -2687,7 +2690,7 @@ private void createVarRefAssignmentStmts(BLangTupleVarRef parentTupleVariable, B BLangTupleVarRef tupleVarRef = (BLangTupleVarRef) expression; BLangLiteral indexExpr = ASTBuilderUtil.createLiteral(tupleVarRef.pos, symTable.intType, (long) index); BLangIndexBasedAccess arrayAccessExpr = ASTBuilderUtil.createIndexBasesAccessExpr(tupleVarRef.pos, - new BArrayType(symTable.anyType), tupleVarSymbol, indexExpr); + new BArrayType(symTable.typeEnv(), symTable.anyType), tupleVarSymbol, indexExpr); if (parentIndexAccessExpr != null) { arrayAccessExpr.expr = parentIndexAccessExpr; } @@ -2793,7 +2796,7 @@ public void visit(BLangRecordDestructure recordDestructure) { final BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(recordDestructure.pos); - BType runTimeType = new BMapType(TypeTags.MAP, symTable.anyType, null); + BType runTimeType = new BMapType(symTable.typeEnv(), TypeTags.MAP, symTable.anyType, null); String name = "$map$_0"; final BLangSimpleVariable mapVariable = @@ -4161,9 +4164,9 @@ private BLangExpression createConditionForErrorArgListBindingPattern(BLangErrorB symTable.stringType, null, symTable.builtinPos, VIRTUAL); BVarSymbol anydataVarSymbol = new BVarSymbol(0, null, null, symTable.anydataType, null, symTable.builtinPos, VIRTUAL); - BMapType entriesType = new BMapType(TypeTags.MAP, new BTupleType(Arrays.asList( - new BTupleMember(symTable.stringType, stringVarSymbol), - new BTupleMember(symTable.anydataType, anydataVarSymbol))), null); + BMapType entriesType = new BMapType(symTable.typeEnv(), TypeTags.MAP, new BTupleType(symTable.typeEnv(), + Arrays.asList(new BTupleMember(symTable.stringType, stringVarSymbol), + new BTupleMember(symTable.anydataType, anydataVarSymbol))), null); BLangInvocation entriesInvocation = generateMapEntriesInvocation(errorDetailVarRef, entriesType); BLangSimpleVariableDef entriesVarDef = createVarDef("$entries$", entriesType, entriesInvocation, restPatternPos); @@ -4336,12 +4339,13 @@ private void createRestPattern(Location pos, List keysToRemove, BLangSim BType targetType, BLangBlockStmt blockStmt, BLangSimpleVarRef restMatchPatternVarRef) { BType constraintType = getRestFilterConstraintType(targetType); - BVarSymbol varSymbol = new BVarSymbol(constraintType.flags, null, null, constraintType, null, + BVarSymbol varSymbol = new BVarSymbol(constraintType.getFlags(), null, null, constraintType, null, null, null); BVarSymbol stringVarSymbol = new BVarSymbol(0, null, null, symTable.stringType, null, symTable.builtinPos, VIRTUAL); - BMapType entriesType = new BMapType(TypeTags.MAP, new BTupleType(Arrays.asList(new BTupleMember( - symTable.stringType, stringVarSymbol), new BTupleMember(constraintType, varSymbol))), null); + BMapType entriesType = new BMapType(symTable.typeEnv(), TypeTags.MAP, new BTupleType(symTable.typeEnv(), + Arrays.asList(new BTupleMember(symTable.stringType, stringVarSymbol), + new BTupleMember(constraintType, varSymbol))), null); BLangInvocation entriesInvocation = generateMapEntriesInvocation(matchExprVarRef, entriesType); BLangSimpleVariableDef entriesVarDef = createVarDef("$entries$", entriesType, entriesInvocation, pos); blockStmt.addStatement(entriesVarDef); @@ -4531,9 +4535,9 @@ private void createRestBindingPatternCondition(BLangMappingBindingPattern mappin BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(restType); BVarSymbol stringVarSymbol = new BVarSymbol(0, null, null, symTable.stringType, null, symTable.builtinPos, VIRTUAL); - BMapType entriesType = new BMapType(TypeTags.MAP, new BTupleType(Arrays.asList( - new BTupleMember(symTable.stringType, stringVarSymbol), - new BTupleMember(restType, varSymbol))), null); + BMapType entriesType = new BMapType(symTable.typeEnv(), TypeTags.MAP, new BTupleType(symTable.typeEnv(), + Arrays.asList(new BTupleMember(symTable.stringType, stringVarSymbol), + new BTupleMember(restType, varSymbol))), null); BLangInvocation entriesInvocation = generateMapEntriesInvocation(varRef, entriesType); BLangSimpleVariableDef entriesVarDef = createVarDef("$entries$", entriesType, entriesInvocation, restPatternPos); @@ -4649,12 +4653,12 @@ private BLangExpression createVarCheckConditionForMappingMatchPattern(BLangMappi Location restPatternPos = restMatchPattern.pos; List keysToRemove = getKeysToRemove(mappingMatchPattern); BType restType = ((BRecordType) restMatchPattern.getBType()).restFieldType; - BVarSymbol varSymbol = new BVarSymbol(restType.flags, null, null, restType, null, null, null); + BVarSymbol varSymbol = new BVarSymbol(restType.getFlags(), null, null, restType, null, null, null); BVarSymbol stringVarSymbol = new BVarSymbol(0, null, null, symTable.stringType, null, symTable.builtinPos, VIRTUAL); - BMapType entriesType = new BMapType(TypeTags.MAP, new BTupleType(Arrays.asList( - new BTupleMember(symTable.stringType, stringVarSymbol), - new BTupleMember(restType, varSymbol))), null); + BMapType entriesType = new BMapType(symTable.typeEnv(), TypeTags.MAP, new BTupleType(symTable.typeEnv(), + Arrays.asList(new BTupleMember(symTable.stringType, stringVarSymbol), + new BTupleMember(restType, varSymbol))), null); BLangInvocation entriesInvocation = generateMapEntriesInvocation(tempCastVarRef, entriesType); BLangSimpleVariableDef entriesVarDef = createVarDef("$entries$", entriesType, entriesInvocation, restPatternPos); @@ -4827,9 +4831,9 @@ private BLangExpression createConditionForErrorArgListMatchPattern(BLangErrorMat symTable.stringType, null, symTable.builtinPos, VIRTUAL); BVarSymbol anydataVarSymbol = new BVarSymbol(0, null, null, symTable.anydataType, null, symTable.builtinPos, VIRTUAL); - BMapType entriesType = new BMapType(TypeTags.MAP, new BTupleType(Arrays.asList( - new BTupleMember(symTable.stringType, stringVarSymbol), - new BTupleMember(symTable.anydataType, anydataVarSymbol))), null); + BMapType entriesType = new BMapType(symTable.typeEnv(), TypeTags.MAP, new BTupleType(symTable.typeEnv(), + Arrays.asList(new BTupleMember(symTable.stringType, stringVarSymbol), + new BTupleMember(symTable.anydataType, anydataVarSymbol))), null); BLangInvocation entriesInvocation = generateMapEntriesInvocation(errorDetailVarRef, entriesType); BLangSimpleVariableDef entriesVarDef = createVarDef("$entries$", entriesType, entriesInvocation, restPatternPos); @@ -5336,7 +5340,7 @@ private BLangFail createOnFailInvocation(BLangOnFailClause onFailClause, BLangFa } } - BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(fail.pos, symTable.nilType, Names.NIL_VALUE); + BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(fail.pos, symTable.nilType, Names.NIL_VALUE.value); BLangStatementExpression statementExpression = createStatementExpression(onFailBody, nilLiteral); statementExpression.setBType(symTable.nilType); @@ -5597,8 +5601,8 @@ public void visit(BLangLock lockNode) { enclLocks.push(lockStmt); - BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(lockNode.pos, symTable.nilType, Names.NIL_VALUE); - BType nillableError = BUnionType.create(null, symTable.errorType, symTable.nilType); + BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(lockNode.pos, symTable.nilType, Names.NIL_VALUE.value); + BType nillableError = BUnionType.create(symTable.typeEnv(), null, symTable.errorType, symTable.nilType); BLangStatementExpression statementExpression = createStatementExpression(lockNode.body, nilLiteral); statementExpression.setBType(symTable.nilType); @@ -5906,7 +5910,7 @@ private BLangOnFailClause createRetryInternalOnFail(Location pos, BLangUnaryExpr createNotBinaryExpression(Location pos, BLangExpression expression) { List paramTypes = new ArrayList<>(); paramTypes.add(symTable.booleanType); - BInvokableType type = new BInvokableType(paramTypes, symTable.booleanType, + BInvokableType type = new BInvokableType(symTable.typeEnv(), paramTypes, symTable.booleanType, null); BOperatorSymbol notOperatorSymbol = new BOperatorSymbol( Names.fromString(OperatorKind.NOT.value()), symTable.rootPkgSymbol.pkgID, type, symTable.rootPkgSymbol, @@ -5933,7 +5937,7 @@ BLangLambdaFunction createLambdaFunction(Location pos, String functionNamePrefix lambdaFunction.pos = pos; List paramTypes = new ArrayList<>(); lambdaFunctionVariable.forEach(variable -> paramTypes.add(variable.symbol.type)); - lambdaFunction.setBType(new BInvokableType(paramTypes, func.symbol.type.getReturnType(), + lambdaFunction.setBType(new BInvokableType(symTable.typeEnv(), paramTypes, func.symbol.type.getReturnType(), null)); return lambdaFunction; } @@ -5991,7 +5995,8 @@ public void visit(BLangListConstructorExpr listConstructor) { expr = new BLangTupleLiteral(listConstructor.pos, listConstructor.exprs, listConstructor.getBType()); result = rewriteExpr(expr); } else if (listConstructorType.tag == TypeTags.JSON) { - expr = new BLangJSONArrayLiteral(listConstructor.exprs, new BArrayType(listConstructor.getBType())); + expr = new BLangJSONArrayLiteral(listConstructor.exprs, + new BArrayType(symTable.typeEnv(), listConstructor.getBType())); result = rewriteExpr(expr); } else if (getElementType(listConstructorType).tag == TypeTags.JSON) { expr = new BLangJSONArrayLiteral(listConstructor.exprs, listConstructor.getBType()); @@ -6018,7 +6023,8 @@ public void visit(BLangArrayLiteral arrayLiteral) { arrayLiteral.exprs = rewriteExprs(arrayLiteral.exprs); BType arrayLiteralType = Types.getImpliedType(arrayLiteral.getBType()); if (arrayLiteralType.tag == TypeTags.JSON) { - result = new BLangJSONArrayLiteral(arrayLiteral.exprs, new BArrayType(arrayLiteral.getBType())); + result = new BLangJSONArrayLiteral(arrayLiteral.exprs, + new BArrayType(symTable.typeEnv(), arrayLiteral.getBType())); return; } else if (getElementType(arrayLiteralType).tag == TypeTags.JSON) { result = new BLangJSONArrayLiteral(arrayLiteral.exprs, arrayLiteral.getBType()); @@ -6050,8 +6056,8 @@ public void visit(BLangTupleLiteral tupleLiteral) { spreadOpType = Types.getImpliedType(spreadOpType); if (spreadOpType.tag == TypeTags.ARRAY) { BArrayType spreadOpBArray = (BArrayType) spreadOpType; - if (spreadOpBArray.size >= 0) { - i += spreadOpBArray.size; + if (spreadOpBArray.getSize() >= 0) { + i += spreadOpBArray.getSize(); continue; } } else { @@ -6166,7 +6172,7 @@ private void generateFieldsForUserUnspecifiedRecordFields(List fieldNames = getNamesOfUserSpecifiedRecordFields(userSpecifiedFields); Location pos = recordLiteral.pos; BRecordType recordType = (BRecordType) type; - boolean isReadonly = Symbols.isFlagOn(recordType.flags, Flags.READONLY); + boolean isReadonly = Symbols.isFlagOn(recordType.getFlags(), Flags.READONLY); generateFieldsForUserUnspecifiedRecordFields(recordType, userSpecifiedFields, fieldNames, pos, isReadonly); } @@ -6563,7 +6569,8 @@ private BLangStatementExpression rewriteLaxMapAccess(BLangFieldBasedAccess field BLangStatementExpression statementExpression = new BLangStatementExpression(); BLangBlockStmt block = new BLangBlockStmt(); statementExpression.stmt = block; - BUnionType fieldAccessType = BUnionType.create(null, fieldAccessExpr.getBType(), symTable.errorType); + BUnionType fieldAccessType = + BUnionType.create(symTable.typeEnv(), null, fieldAccessExpr.getBType(), symTable.errorType); Location pos = fieldAccessExpr.pos; BLangSimpleVariableDef result = createVarDef("$mapAccessResult$", fieldAccessType, null, pos); block.addStatement(result); @@ -6577,7 +6584,7 @@ private BLangStatementExpression rewriteLaxMapAccess(BLangFieldBasedAccess field BLangLiteral mapIndex = ASTBuilderUtil.createLiteral( fieldAccessExpr.field.pos, symTable.stringType, fieldAccessExpr.field.value); BLangMapAccessExpr mapAccessExpr = new BLangMapAccessExpr(pos, fieldAccessExpr.expr, mapIndex); - BUnionType xmlOrNil = BUnionType.create(null, fieldAccessExpr.getBType(), symTable.nilType); + BUnionType xmlOrNil = BUnionType.create(symTable.typeEnv(), null, fieldAccessExpr.getBType(), symTable.nilType); mapAccessExpr.setBType(xmlOrNil); BLangSimpleVariableDef mapResult = createVarDef("$mapAccess", xmlOrNil, mapAccessExpr, pos); BLangSimpleVarRef mapResultRef = ASTBuilderUtil.createVariableRef(pos, mapResult.var.symbol); @@ -6688,7 +6695,7 @@ public void visit(BLangIndexBasedAccess indexAccessExpr) { if (varRefType.tag == TypeTags.MAP) { targetVarRef = new BLangMapAccessExpr(indexAccessExpr.pos, indexAccessExpr.expr, indexAccessExpr.indexExpr, indexAccessExpr.isStoreOnCreation); - } else if (types.isSubTypeOfMapping(types.getSafeType(varRefType, true, false))) { + } else if (types.isSubTypeOfMapping(types.getNilLiftType(varRefType.semType()))) { targetVarRef = new BLangStructFieldAccessExpr(indexAccessExpr.pos, indexAccessExpr.expr, indexAccessExpr.indexExpr, (BVarSymbol) indexAccessExpr.symbol, false); @@ -6867,7 +6874,7 @@ public void visit(BLangInvocation.BLangActionInvocation actionInvocation) { } // Add `@strand {thread: "any"}` annotation to an isolated start-action. - if (actionInvocation.async && Symbols.isFlagOn(actionInvocation.symbol.type.flags, Flags.ISOLATED)) { + if (actionInvocation.async && Symbols.isFlagOn(actionInvocation.symbol.type.getFlags(), Flags.ISOLATED)) { addStrandAnnotationWithThreadAny(actionInvocation.pos); actionInvocation.addAnnotationAttachment(this.strandAnnotAttachement); ((BInvokableSymbol) actionInvocation.symbol) @@ -7042,7 +7049,8 @@ private BLangInvocation createInvocationForPathParams( BResourcePathSegmentSymbol lastPathSegmentSym = pathSegmentSymbols.get(pathSegmentSymbols.size() - 1); if (lastPathSegmentSym.kind == SymbolKind.RESOURCE_PATH_REST_PARAM_SEGMENT) { invokableSymbol.restParam = new BVarSymbol(0, Names.EMPTY, this.env.scope.owner.pkgID, - new BArrayType(lastPathSegmentSym.type), this.env.scope.owner, lastPathSegmentSym.pos, VIRTUAL); + new BArrayType(symTable.typeEnv(), lastPathSegmentSym.type), this.env.scope.owner, + lastPathSegmentSym.pos, VIRTUAL); pathSegmentCount--; } @@ -7099,9 +7107,10 @@ private BLangExpression rewriteInvocation(BLangInvocation invocation, boolean as result = invRef; BInvokableSymbol invSym = (BInvokableSymbol) invocation.symbol; - if (Symbols.isFlagOn(invSym.retType.flags, Flags.PARAMETERIZED)) { - BType retType = unifier.build(invSym.retType); - invocation.setBType(invocation.async ? new BFutureType(TypeTags.FUTURE, retType, null) : retType); + if (Symbols.isFlagOn(invSym.retType.getFlags(), Flags.PARAMETERIZED)) { + BType retType = unifier.build(symTable.typeEnv(), invSym.retType); + invocation.setBType(invocation.async ? + new BFutureType(symTable.typeEnv(), retType, null) : retType); } if (invocation.expr == null) { @@ -7136,7 +7145,7 @@ private BLangExpression rewriteInvocation(BLangInvocation invocation, boolean as private void populateOCEInvocation(BLangInvocation invocation, BLangInvocation invRef) { - if (invocation.objectInitMethod && Symbols.isFlagOn(invocation.expr.getBType().flags, Flags.OBJECT_CTOR)) { + if (invocation.objectInitMethod && Symbols.isFlagOn(invocation.expr.getBType().getFlags(), Flags.OBJECT_CTOR)) { BObjectType initializingObject = (BObjectType) invocation.expr.getBType(); BLangClassDefinition classDef = initializingObject.classDef; if (classDef.hasClosureVars) { @@ -7305,13 +7314,13 @@ private BLangInvocation desugarStreamTypeInit(BLangTypeInit typeInitExpr) { BStreamType referredStreamType = (BStreamType) Types.getImpliedType(typeInitExpr.getBType()); BType constraintType = referredStreamType.constraint; - BType constraintTdType = new BTypedescType(constraintType, symTable.typeDesc.tsymbol); + BType constraintTdType = new BTypedescType(symTable.typeEnv(), constraintType, symTable.typeDesc.tsymbol); BLangTypedescExpr constraintTdExpr = new BLangTypedescExpr(); constraintTdExpr.resolvedType = constraintType; constraintTdExpr.setBType(constraintTdType); BType completionType = referredStreamType.completionType; - BType completionTdType = new BTypedescType(completionType, symTable.typeDesc.tsymbol); + BType completionTdType = new BTypedescType(symTable.typeEnv(), completionType, symTable.typeDesc.tsymbol); BLangTypedescExpr completionTdExpr = new BLangTypedescExpr(); completionTdExpr.resolvedType = completionType; completionTdExpr.setBType(completionTdType); @@ -7322,7 +7331,8 @@ private BLangInvocation desugarStreamTypeInit(BLangTypeInit typeInitExpr) { } BLangInvocation streamConstructInvocation = ASTBuilderUtil.createInvocationExprForMethod( typeInitExpr.pos, symbol, args, symResolver); - streamConstructInvocation.setBType(new BStreamType(TypeTags.STREAM, constraintType, completionType, null)); + streamConstructInvocation.setBType(new BStreamType(symTable.typeEnv(), TypeTags.STREAM, constraintType, + completionType, null)); return streamConstructInvocation; } @@ -7715,7 +7725,7 @@ private void createTypeCastExprForArithmeticExpr(BLangBinaryExpr binaryExpr, int return; } if (TypeTags.isXMLTypeTag(lhsExprTypeTag) && !TypeTags.isXMLTypeTag(rhsExprTypeTag)) { - if (types.checkTypeContainString(binaryExpr.rhsExpr.getBType())) { + if (types.isStringSubType(binaryExpr.rhsExpr.getBType())) { binaryExpr.rhsExpr = ASTBuilderUtil.createXMLTextLiteralNode(binaryExpr, binaryExpr.rhsExpr, binaryExpr.rhsExpr.pos, symTable.xmlType); return; @@ -7724,7 +7734,7 @@ private void createTypeCastExprForArithmeticExpr(BLangBinaryExpr binaryExpr, int return; } if (TypeTags.isXMLTypeTag(rhsExprTypeTag) && !TypeTags.isXMLTypeTag(lhsExprTypeTag)) { - if (types.checkTypeContainString(binaryExpr.lhsExpr.getBType())) { + if (types.isStringSubType(binaryExpr.lhsExpr.getBType())) { binaryExpr.lhsExpr = ASTBuilderUtil.createXMLTextLiteralNode(binaryExpr, binaryExpr.lhsExpr, binaryExpr.rhsExpr.pos, symTable.xmlType); return; @@ -7848,7 +7858,7 @@ private BLangExpression addNilType(BType exprType, BLangExpression expr) { LinkedHashSet members = new LinkedHashSet<>(2); members.add(exprType); members.add(symTable.nilType); - BUnionType unionType = new BUnionType(null, members, true, false); + BUnionType unionType = new BUnionType(types.typeEnv(), null, members, false); return createTypeCastExpr(expr, unionType); } @@ -8111,8 +8121,8 @@ public void visit(BLangLambdaFunction bLangLambdaFunction) { bLangLambdaFunction.function = rewrite(bLangLambdaFunction.function, bLangLambdaFunction.capturedClosureEnv); BLangFunction function = bLangLambdaFunction.function; // Add `@strand {thread: "any"}` annotation to an isolated named worker declaration in an isolated function. - if (function.flagSet.contains(Flag.WORKER) && Symbols.isFlagOn(function.symbol.type.flags, Flags.ISOLATED) && - Symbols.isFlagOn(env.enclInvokable.symbol.flags, Flags.ISOLATED)) { + if (function.flagSet.contains(Flag.WORKER) && Symbols.isFlagOn(function.symbol.type.getFlags(), Flags.ISOLATED) + && Symbols.isFlagOn(env.enclInvokable.symbol.flags, Flags.ISOLATED)) { addStrandAnnotationWithThreadAny(function.pos); function.addAnnotationAttachment(this.strandAnnotAttachement); BInvokableSymbol funcSymbol = function.symbol; @@ -8170,8 +8180,8 @@ public void visit(BLangArrowFunction bLangArrowFunction) { funcSymbol.retType = funcNode.returnTypeNode.getBType(); // Create function type. List paramTypes = new ArrayList<>(paramSymbols.stream().map(paramSym -> paramSym.type).toList()); - funcNode.setBType(new BInvokableType(paramTypes, getRestType(funcSymbol), funcNode.returnTypeNode.getBType(), - funcSymbol.type.tsymbol)); + funcNode.setBType(new BInvokableType(symTable.typeEnv(), paramTypes, getRestType(funcSymbol), + funcNode.returnTypeNode.getBType(), funcSymbol.type.tsymbol)); lambdaFunction.function.pos = bLangArrowFunction.pos; lambdaFunction.function.body.pos = bLangArrowFunction.pos; @@ -8357,7 +8367,7 @@ private BLangClassDefinition desugarTemplateLiteralObjectTypedef(List(); - expr.setBType(new BArrayType(symTable.anyType)); + expr.setBType(new BArrayType(symTable.typeEnv(), symTable.anyType)); return expr; } @@ -9334,7 +9343,7 @@ private BLangLiteral createByteLiteral(Location pos, Byte value) { } private BLangExpression createTypeCastExpr(BLangExpression expr, BType targetType) { - if (types.isSameType(expr.getBType(), targetType)) { + if (types.isSameTypeIncludingTags(expr.getBType(), targetType)) { return expr; } @@ -9542,7 +9551,7 @@ private void reorderArguments(BLangInvocation iExpr) { if (refType.tag == TypeTags.ARRAY) { BArrayType arrayType = (BArrayType) refType; if (arrayType.state == BArrayState.CLOSED && - arrayType.size == (iExpr.requiredArgs.size() - originalRequiredArgCount)) { + arrayType.getSize() == (iExpr.requiredArgs.size() - originalRequiredArgCount)) { // If the array was a closed array that provided only for the non rest params, set the rest param // type as the element type to satisfy code gen. The foreach will not be executed at runtime. valueExpr.setBType(restParamType.eType); @@ -9806,7 +9815,7 @@ private BType getStructuredBindingPatternType(BLangVariable bindingPatternVariab memberTypes.add( new BTupleMember(member, varSymbol)); } - BTupleType tupleType = new BTupleType(memberTypes); + BTupleType tupleType = new BTupleType(symTable.typeEnv(), memberTypes); if (tupleVariable.restVariable != null) { BArrayType restArrayType = (BArrayType) getStructuredBindingPatternType(tupleVariable.restVariable); tupleType.restType = restArrayType.eType; @@ -9840,7 +9849,7 @@ private BType getStructuredBindingPatternType(BLangVariable bindingPatternVariab recordSymbol.scope.define(fieldName, fieldSymbol); } - BRecordType recordVarType = new BRecordType(recordSymbol); + BRecordType recordVarType = new BRecordType(symTable.typeEnv(), recordSymbol); recordVarType.fields = fields; // if rest param is null we treat it as an open record with anydata rest param @@ -9877,7 +9886,7 @@ private BType getStructuredBindingPatternType(BLangVariable bindingPatternVariab TypeDefBuilderHelper.createTypeDefinitionForTSymbol(detailType, detailType.tsymbol, recordTypeNode, env); } - BErrorType errorType = new BErrorType(errorTypeSymbol, detailType); + BErrorType errorType = new BErrorType(symTable.typeEnv(), errorTypeSymbol, detailType); errorTypeSymbol.type = errorType; TypeDefBuilderHelper.createTypeDefinitionForTSymbol(errorType, errorTypeSymbol, @@ -9936,7 +9945,7 @@ private BRecordType createAnonRecordType(Location pos) { env.enclPkg.symbol.pkgID, null, null, pos, VIRTUAL); detailRecordTypeSymbol.scope = new Scope(detailRecordTypeSymbol); - BRecordType detailRecordType = new BRecordType(detailRecordTypeSymbol); + BRecordType detailRecordType = new BRecordType(symTable.typeEnv(), detailRecordTypeSymbol); detailRecordType.restFieldType = symTable.anydataType; return detailRecordType; } @@ -10094,7 +10103,7 @@ private void handleSafeNavigation(BLangBlockStmt blockStmt, BLangAccessExpressio if (!(accessExpr.errorSafeNavigation || accessExpr.nilSafeNavigation)) { BType originalType = Types.getImpliedType(accessExpr.originalType); if (isMapJson(originalType, false)) { - accessExpr.setBType(BUnionType.create(null, originalType, symTable.errorType)); + accessExpr.setBType(BUnionType.create(symTable.typeEnv(), null, originalType, symTable.errorType)); } else { accessExpr.setBType(originalType); } @@ -10269,7 +10278,7 @@ private BLangMatchClause getMatchAllAndNilReturnClause(BLangExpression matchExpr BLangVariableReference tempResultVarRef = ASTBuilderUtil.createVariableRef(pos, tempResultVar.symbol); BLangAssignment assignmentStmt = ASTBuilderUtil.createAssignmentStmt(pos, tempResultVarRef, createLiteral(pos, symTable.nilType, - Names.NIL_VALUE)); + Names.NIL_VALUE.value)); BLangBlockStmt clauseBody = ASTBuilderUtil.createBlockStmt(pos, this.env.scope, Lists.of(assignmentStmt)); BLangWildCardMatchPattern wildCardMatchPattern = ASTBuilderUtil.createWildCardMatchPattern(matchExpr); @@ -10322,7 +10331,8 @@ private BLangMatchClause getSuccessPatternClause(BType type, BLangExpression mat // and may not reflect the actual type of the child/field expr. if (TypeTags.isXMLTypeTag(Types.getImpliedType(tempAccessExpr.expr.getBType()).tag)) { // todo: add discription why this is special here - tempAccessExpr.setBType(BUnionType.create(null, accessExpr.originalType, symTable.errorType, + tempAccessExpr.setBType( + BUnionType.create(symTable.typeEnv(), null, accessExpr.originalType, symTable.errorType, symTable.nilType)); } else { tempAccessExpr.setBType(accessExpr.originalType); @@ -10436,8 +10446,8 @@ private BLangFunction createInitFunctionForClassDefn(BLangClassDefinition classD } BLangFunction initFunction = - TypeDefBuilderHelper.createInitFunctionForStructureType(classDefinition.symbol, env, names, - GENERATED_INIT_SUFFIX, classDefinition.getBType(), returnType); + TypeDefBuilderHelper.createInitFunctionForStructureType(symTable.typeEnv(), classDefinition.symbol, env, + names, GENERATED_INIT_SUFFIX, classDefinition.getBType(), returnType); BObjectTypeSymbol typeSymbol = ((BObjectTypeSymbol) classDefinition.getBType().tsymbol); typeSymbol.generatedInitializerFunc = new BAttachedFunction(GENERATED_INIT_SUFFIX, initFunction.symbol, (BInvokableType) initFunction.getBType(), null); @@ -10649,7 +10659,8 @@ public BLangLambdaFunction createArrowFunctionForNavigation(Location pos, List compUnitList = this.bLangPackage.getTestablePkg().getCompilationUnits(); - BArrayType bArrayType = new BArrayType(symTable.stringType, null, -1, BArrayState.OPEN); + BArrayType bArrayType = new BArrayType(symTable.typeEnv(), symTable.stringType, null, -1, BArrayState.OPEN); List modifiedcompUnitList = new ArrayList<>(); for (BLangCompilationUnit compUnit : compUnitList) { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/QueryDesugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/QueryDesugar.java index e114284e819d..d52d0000dd98 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/QueryDesugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/QueryDesugar.java @@ -17,6 +17,9 @@ package org.wso2.ballerinalang.compiler.desugar; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.clauses.OrderKeyNode; import org.ballerinalang.model.elements.Flag; @@ -299,7 +302,7 @@ BLangStatementExpression desugar(BLangQueryExpr queryExpr, SymbolEnv env, queryBlock, stmtsToBePropagated); BLangExpression result = streamRef; BLangLiteral isReadonly = ASTBuilderUtil.createLiteral(pos, symTable.booleanType, - Symbols.isFlagOn(queryExpr.getBType().flags, Flags.READONLY)); + Symbols.isFlagOn(queryExpr.getBType().getFlags(), Flags.READONLY)); BType resultType = queryExpr.getBType(); if (queryExpr.isStream) { resultType = streamRef.getBType(); @@ -324,14 +327,16 @@ BLangStatementExpression desugar(BLangQueryExpr queryExpr, SymbolEnv env, result = getStreamFunctionVariableRef(queryBlock, COLLECT_QUERY_FUNCTION, Lists.of(streamRef), pos); } else { BType refType = Types.getImpliedType(queryExpr.getBType()); - BType safeType = types.getSafeType(refType, true, true); - if (isXml(safeType)) { - if (types.isSubTypeOfReadOnly(refType, env)) { + SemType refSemType = refType.semType(); + SemType safeType = types.getNilAndErrorLiftType(refSemType); + if (SemTypes.isSubtypeSimpleNotNever(safeType, PredefinedType.XML)) { + if (types.isSubTypeOfReadOnly(refSemType)) { isReadonly.value = true; } result = getStreamFunctionVariableRef(queryBlock, QUERY_TO_XML_FUNCTION, Lists.of(streamRef, isReadonly), pos); - } else if (TypeTags.isStringTypeTag(safeType.tag)) { + } else if (PredefinedType.STRING.equals(safeType) || + SemTypes.isSameType(types.typeCtx(), safeType, PredefinedType.STRING_CHAR)) { result = getStreamFunctionVariableRef(queryBlock, QUERY_TO_STRING_FUNCTION, Lists.of(streamRef), pos); } else { BType arrayType = refType; @@ -608,11 +613,11 @@ BLangVariableReference addPipeline(BLangBlockStmt blockStmt, Location pos, constraintType = ((BStreamType) refType).constraint; completionType = ((BStreamType) refType).completionType; } - BType constraintTdType = new BTypedescType(constraintType, symTable.typeDesc.tsymbol); + BType constraintTdType = new BTypedescType(symTable.typeEnv(), constraintType, symTable.typeDesc.tsymbol); BLangTypedescExpr constraintTdExpr = new BLangTypedescExpr(); constraintTdExpr.resolvedType = constraintType; constraintTdExpr.setBType(constraintTdType); - BType completionTdType = new BTypedescType(completionType, symTable.typeDesc.tsymbol); + BType completionTdType = new BTypedescType(symTable.typeEnv(), completionType, symTable.typeDesc.tsymbol); BLangTypedescExpr completionTdExpr = new BLangTypedescExpr(); completionTdExpr.resolvedType = completionType; completionTdExpr.setBType(completionTdType); @@ -811,11 +816,11 @@ BLangVariableReference addOrderByFunction(BLangBlockStmt blockStmt, BLangOrderBy BLangArrayLiteral sortFieldsArrayExpr = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode(); sortFieldsArrayExpr.exprs = new ArrayList<>(); - sortFieldsArrayExpr.setBType(new BArrayType(symTable.anydataType)); + sortFieldsArrayExpr.setBType(new BArrayType(symTable.typeEnv(), symTable.anydataType)); BLangArrayLiteral sortModesArrayExpr = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode(); sortModesArrayExpr.exprs = new ArrayList<>(); - sortModesArrayExpr.setBType(new BArrayType(symTable.booleanType)); + sortModesArrayExpr.setBType(new BArrayType(symTable.typeEnv(), symTable.booleanType)); // Each order-key expression is added to sortFieldsArrayExpr. // Corresponding order-direction is added to sortModesArrayExpr. @@ -843,7 +848,7 @@ BLangVariableReference addGroupByFunction(BLangBlockStmt blockStmt, BLangGroupBy Location pos = groupByClause.pos; BLangArrayLiteral keys = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode(); keys.exprs = new ArrayList<>(); - keys.setBType(new BArrayType(symTable.stringType)); + keys.setBType(new BArrayType(symTable.typeEnv(), symTable.stringType)); for (BLangGroupingKey key :groupByClause.groupingKeyList) { if (key.variableDef == null) { keys.exprs.add(createStringLiteral(key.pos, key.variableRef.variableName.value)); @@ -858,7 +863,7 @@ BLangVariableReference addGroupByFunction(BLangBlockStmt blockStmt, BLangGroupBy BLangArrayLiteral nonGroupingKeys = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode(); nonGroupingKeys.exprs = new ArrayList<>(); - nonGroupingKeys.setBType(new BArrayType(symTable.stringType)); + nonGroupingKeys.setBType(new BArrayType(symTable.typeEnv(), symTable.stringType)); for (String nonGroupingKey : groupByClause.nonGroupingKeys) { nonGroupingKeys.exprs.add(createStringLiteral(pos, nonGroupingKey)); } @@ -871,7 +876,7 @@ BLangVariableReference addCollectFunction(BLangBlockStmt blockStmt, BLangCollect Location pos = collectClause.pos; BLangArrayLiteral nonGroupingKeys = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode(); nonGroupingKeys.exprs = new ArrayList<>(); - nonGroupingKeys.setBType(new BArrayType(symTable.stringType)); + nonGroupingKeys.setBType(new BArrayType(symTable.typeEnv(), symTable.stringType)); for (String nonGroupingKey : collectClause.nonGroupingKeys) { nonGroupingKeys.exprs.add(createStringLiteral(pos, nonGroupingKey)); } @@ -1444,7 +1449,7 @@ private BLangSimpleVarRef defineNilFrameForType(List symbols, BLangB private void addNilValueToFrame(BLangSimpleVarRef frameToAddValueTo, String key, BLangBlockStmt blockStmt, Location pos) { BLangStatement addToFrameStmt = getAddToFrameStmt(pos, frameToAddValueTo, key, - ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE)); + ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE.value)); blockStmt.addStatement(addToFrameStmt); } @@ -1549,7 +1554,8 @@ private BLangValueType getBooleanTypeNode() { */ private BLangUnionTypeNode getFrameErrorNilTypeNode() { BType frameType = getFrameTypeSymbol().type; - BUnionType unionType = BUnionType.create(null, frameType, symTable.errorType, symTable.nilType); + BUnionType unionType = + BUnionType.create(symTable.typeEnv(), null, frameType, symTable.errorType, symTable.nilType); BLangUnionTypeNode unionTypeNode = (BLangUnionTypeNode) TreeBuilder.createUnionTypeNode(); unionTypeNode.setBType(unionType); unionTypeNode.memberTypeNodes.add(getFrameTypeNode()); @@ -1560,7 +1566,7 @@ private BLangUnionTypeNode getFrameErrorNilTypeNode() { } private BLangUnionTypeNode getBooleanErrorTypeNode() { - BUnionType unionType = BUnionType.create(null, symTable.errorType, symTable.booleanType); + BUnionType unionType = BUnionType.create(symTable.typeEnv(), null, symTable.errorType, symTable.booleanType); BLangUnionTypeNode unionTypeNode = (BLangUnionTypeNode) TreeBuilder.createUnionTypeNode(); unionTypeNode.setBType(unionType); unionTypeNode.memberTypeNodes.add(getErrorTypeNode()); @@ -1570,7 +1576,7 @@ private BLangUnionTypeNode getBooleanErrorTypeNode() { } private BLangUnionTypeNode getIntErrorTypeNode() { - BUnionType unionType = BUnionType.create(null, symTable.errorType, symTable.intType); + BUnionType unionType = BUnionType.create(symTable.typeEnv(), null, symTable.errorType, symTable.intType); BLangUnionTypeNode unionTypeNode = (BLangUnionTypeNode) TreeBuilder.createUnionTypeNode(); unionTypeNode.setBType(unionType); unionTypeNode.memberTypeNodes.add(getErrorTypeNode()); @@ -1585,7 +1591,7 @@ private BLangUnionTypeNode getIntErrorTypeNode() { * @return a any & error type node. */ private BLangUnionTypeNode getAnyAndErrorTypeNode() { - BUnionType unionType = BUnionType.create(null, symTable.anyType, symTable.errorType); + BUnionType unionType = BUnionType.create(symTable.typeEnv(), null, symTable.anyType, symTable.errorType); BLangUnionTypeNode unionTypeNode = (BLangUnionTypeNode) TreeBuilder.createUnionTypeNode(); unionTypeNode.memberTypeNodes.add(getAnyTypeNode()); unionTypeNode.memberTypeNodes.add(getErrorTypeNode()); @@ -1738,7 +1744,7 @@ public void visit(BLangCollectContextInvocation collectContextInvocation) { if (isNilReturnInvocationInCollectClause(invocation)) { Location pos = invocation.pos; BLangSimpleVarRef restArg = (BLangSimpleVarRef) invocation.argExprs.get(0); - BType invocationType = BUnionType.create(null, invocation.getBType(), symTable.nilType); + BType invocationType = BUnionType.create(symTable.typeEnv(), null, invocation.getBType(), symTable.nilType); BLangSimpleVariable tempResultVar = ASTBuilderUtil.createVariable(pos, "$invocationResult$", invocationType, null, new BVarSymbol(0, Names.fromString("$invocationResult$"), this.env.scope.owner.pkgID, invocationType, this.env.scope.owner, pos, VIRTUAL)); @@ -1812,7 +1818,7 @@ private BType changeSeqSymbolType(BSymbol symbol) { BType elementType = ((BSequenceType) symbol.type).elementType; List tupleMembers = new ArrayList<>(1); tupleMembers.add(new BTupleMember(elementType, Symbols.createVarSymbolForTupleMember(elementType))); - symbol.type = new BTupleType(null, tupleMembers, elementType, 0); + symbol.type = new BTupleType(symTable.typeEnv(), null, tupleMembers, elementType, 0); } return symbol.type; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ServiceDesugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ServiceDesugar.java index da39e5efca41..7f2c356e2c2c 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ServiceDesugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ServiceDesugar.java @@ -241,7 +241,7 @@ private BType getListenerTypeWithoutError(BType type) { } members.add(memberType); } - return BUnionType.create(null, members); + return BUnionType.create(symTable.typeEnv(), null, members); } return type; } @@ -268,7 +268,7 @@ private void addMethodInvocation(Location pos, BLangSimpleVarRef varRef, BInvoka // call is generated in BIRGen. Casting to the first listener type should be fine as actual method invocation // is based on the value rather than the type. BType listenerType = getListenerType(varRef.getBType()); - if (!types.isSameType(listenerType, varRef.getBType())) { + if (!types.isSameTypeIncludingTags(listenerType, varRef.getBType())) { BLangTypeConversionExpr castExpr = (BLangTypeConversionExpr) TreeBuilder.createTypeConversionNode(); castExpr.expr = varRef; castExpr.setBType(listenerType); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/TransactionDesugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/TransactionDesugar.java index d81adba0d815..4c3137efc1f3 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/TransactionDesugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/TransactionDesugar.java @@ -205,7 +205,7 @@ private BLangBlockStmt desugarTransactionBody(BLangTransaction transactionNode, BType transactionReturnType = symTable.errorOrNilType; // wraps content within transaction body inside a statement expression - BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE); + BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE.value); BLangStatementExpression statementExpression = createStatementExpression(transactionNode.transactionBody, nilLiteral); statementExpression.setBType(symTable.nilType); @@ -373,7 +373,7 @@ public void startTransactionCoordinatorOnce(SymbolEnv env, Location pos) { } BLangSimpleVariableDef createPrevAttemptInfoVarDef(SymbolEnv env, Location pos) { - BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE); + BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE.value); BLangSimpleVariable prevAttemptVariable = createPrevAttemptVariable(env, pos); prevAttemptVariable.expr = nilLiteral; return ASTBuilderUtil.createVariableDef(pos, prevAttemptVariable); @@ -384,7 +384,7 @@ private BLangSimpleVariable createPrevAttemptVariable(SymbolEnv env, Location po BSymbol infoRecordSymbol = symResolver. lookupSymbolInMainSpace(symTable.pkgEnvMap.get(symTable.langTransactionModuleSymbol), TRANSACTION_INFO_RECORD); - BType infoRecordType = BUnionType.create(null, infoRecordSymbol.type, symTable.nilType); + BType infoRecordType = BUnionType.create(symTable.typeEnv(), null, infoRecordSymbol.type, symTable.nilType); BVarSymbol prevAttemptVarSymbol = new BVarSymbol(0, new Name("prevAttempt" + uniqueId), env.scope.owner.pkgID, infoRecordType, env.scope.owner, pos, VIRTUAL); prevAttemptVarSymbol.closure = true; @@ -515,7 +515,7 @@ BLangStatementExpression invokeRollbackFunc(Location pos, BLangExpression rollba BLangExpressionStmt cleanUpTrx = ASTBuilderUtil.createExpressionStmt(pos, rollbackBlockStmt); cleanUpTrx.expr = createCleanupTrxStmt(pos, trxBlockId); BLangStatementExpression rollbackStmtExpr = createStatementExpression(rollbackBlockStmt, - ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE)); + ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE.value)); rollbackStmtExpr.setBType(symTable.nilType); //at this point, @@ -564,7 +564,7 @@ BLangStatementExpression desugar(BLangCommitExpr commitExpr, SymbolEnv env) { createInvocationExprForMethod(pos, commitTransactionInvokableSymbol, args, symResolver); commitTransactionInvocation.argExprs = args; - BType commitReturnType = BUnionType.create(null, symTable.stringType, symTable.errorType); + BType commitReturnType = BUnionType.create(symTable.typeEnv(), null, symTable.stringType, symTable.errorType); BVarSymbol commitTransactionVarSymbol = new BVarSymbol(0, new Name("commitResult"), env.scope.owner.pkgID, commitReturnType, env.scope.owner, pos, VIRTUAL); @@ -611,8 +611,7 @@ BLangStatementExpression desugar(BLangCommitExpr commitExpr, SymbolEnv env) { isTransactionFailedVariable.symbol); List paramTypes = new ArrayList<>(); paramTypes.add(symTable.booleanType); - BInvokableType type = new BInvokableType(paramTypes, symTable.booleanType, - null); + BInvokableType type = new BInvokableType(symTable.typeEnv(), paramTypes, symTable.booleanType, null); BOperatorSymbol notOperatorSymbol = new BOperatorSymbol( Names.fromString(OperatorKind.NOT.value()), symTable.rootPkgSymbol.pkgID, type, symTable.rootPkgSymbol, symTable.builtinPos, VIRTUAL); @@ -640,7 +639,7 @@ BLangStatementExpression desugar(BLangCommitExpr commitExpr, SymbolEnv env) { } private BLangSimpleVariableDef createCommitResultVarDef(SymbolEnv env, Location pos) { - BLangExpression nilLiteral = ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE); + BLangExpression nilLiteral = ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE.value); BVarSymbol outputVarSymbol = new BVarSymbol(0, new Name("$outputVar$"), env.scope.owner.pkgID, symTable.errorOrNilType, env.scope.owner, pos, VIRTUAL); BLangSimpleVariable outputVariable = diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java index aa698df5108e..83d503eb31ab 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java @@ -911,7 +911,6 @@ private void createAnonymousTypeDefForConstantDeclaration(BLangConstant constant BLangUnaryExpr unaryExpr = createBLangUnaryExpr(unaryConstant.pos, unaryConstant.operator, unaryConstant.expr); unaryExpr.setBType(unaryConstant.expr.getBType()); - unaryExpr.isConstant = true; finiteTypeNode.valueSpace.add(unaryExpr); } finiteTypeNode.pos = identifierPos; @@ -2794,7 +2793,7 @@ public BLangNode transform(ReturnStatementNode returnStmtNode) { } else { BLangLiteral nilLiteral = (BLangLiteral) TreeBuilder.createLiteralExpression(); nilLiteral.pos = getPosition(returnStmtNode); - nilLiteral.value = Names.NIL_VALUE; + nilLiteral.value = Names.NIL_VALUE.value; nilLiteral.setBType(symTable.nilType); bLReturn.expr = nilLiteral; } @@ -6115,6 +6114,7 @@ private BLangLiteral createSimpleLiteral(Node literal, boolean isFiniteType) { typeTag = TypeTags.INT; value = getIntegerLiteral(literal, textValue); originalValue = textValue; + // TODO: can we fix below? if (literalTokenKind == SyntaxKind.HEX_INTEGER_LITERAL_TOKEN && withinByteRange(value)) { typeTag = TypeTags.BYTE; } @@ -6184,9 +6184,9 @@ private BLangLiteral createSimpleLiteral(Node literal, boolean isFiniteType) { originalValue = textValue; bLiteral = (BLangLiteral) TreeBuilder.createLiteralExpression(); } else if (type == SyntaxKind.NIL_LITERAL) { - originalValue = "()"; + originalValue = Names.NIL_VALUE.value; typeTag = TypeTags.NIL; - value = "()"; + value = Names.NIL_VALUE.value; bLiteral = (BLangLiteral) TreeBuilder.createLiteralExpression(); } else if (type == SyntaxKind.NULL_LITERAL) { originalValue = "null"; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java index 75db8cf2452f..cccd4bdf22b0 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java @@ -399,6 +399,7 @@ private void cloneBLangType(BLangType source, BLangType clone) { clone.nullable = source.nullable; clone.grouped = source.grouped; clone.flagSet = cloneSet(source.flagSet, Flag.class); + clone.defn = source.defn; } private > EnumSet cloneSet(Set source, Class elementType) { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java index a3920e3d2720..9c5229a36704 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java @@ -19,6 +19,10 @@ import io.ballerina.identifier.Utils; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.Value; import org.ballerinalang.compiler.CompilerPhase; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; @@ -267,6 +271,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Stream; @@ -1086,16 +1091,8 @@ private HashMap getConstValue(BLangConstPattern constPattern) { } private Object getConstValueFromFiniteType(BFiniteType type) { - if (type.getValueSpace().size() == 1) { - BLangExpression expr = type.getValueSpace().iterator().next(); - switch (expr.getKind()) { - case NUMERIC_LITERAL: - return ((BLangNumericLiteral) expr).value; - case LITERAL: - return ((BLangLiteral) expr).value; - } - } - return null; + Optional value = Core.singleShape(type.semType()); + return value.map(v -> v.value).orElse(null); } private boolean checkSimilarListMatchPattern(BLangListMatchPattern firstListMatchPattern, @@ -1402,10 +1399,8 @@ public void visit(BLangVarBindingPatternMatchPattern varBindingPattern, Analyzer if (varBindingPattern.matchExpr == null) { return; } - varBindingPattern.isLastPattern = types.isSameType(varBindingPattern.matchExpr.getBType(), - varBindingPattern.getBType()) || types.isAssignable( - varBindingPattern.matchExpr.getBType(), - varBindingPattern.getBType()); + varBindingPattern.isLastPattern = types.isAssignable(varBindingPattern.matchExpr.getBType(), + varBindingPattern.getBType()); } } @@ -1534,7 +1529,8 @@ public void visit(BLangFail failNode, AnalyzerData data) { if (!data.failureHandled) { BType exprType = data.env.enclInvokable.getReturnTypeNode().getBType(); data.returnTypes.peek().add(exprType); - if (!types.isAssignable(types.getErrorTypes(failNode.expr.getBType()), exprType)) { + BType type = failNode.expr.getBType(); + if (!types.isSubtype(types.getErrorIntersection(type.semType()), exprType.semType())) { dlog.error(failNode.pos, DiagnosticErrorCode.FAIL_EXPR_NO_MATCHING_ERROR_RETURN_IN_ENCL_INVOKABLE); } } @@ -1653,7 +1649,7 @@ private void checkForExportableType(BTypeSymbol symbol, Location pos, HashSet 1) { - sendTypeWithNoMsgIgnored = BUnionType.create(null, returnTypeAndSendType); + sendTypeWithNoMsgIgnored = BUnionType.create(symTable.typeEnv(), null, returnTypeAndSendType); } else { sendTypeWithNoMsgIgnored = exprType; } @@ -2098,7 +2094,7 @@ private void setWorkerSendSendTypeDetails(BLangWorkerSendExpr workerSendExpr, BT lookup(Names.fromString(NO_MESSAGE_ERROR_TYPE)).symbol; returnTypeAndSendType.add(noMsgErrSymbol.getType()); if (returnTypeAndSendType.size() > 1) { - sendType = BUnionType.create(null, returnTypeAndSendType); + sendType = BUnionType.create(symTable.typeEnv(), null, returnTypeAndSendType); } else { sendType = exprType; } @@ -2143,7 +2139,7 @@ public void visit(BLangWorkerSyncSendExpr syncSendExpr, AnalyzerData data) { was.hasErrors = true; } - syncSendExpr.setBType(BUnionType.create(null, symTable.nilType, symTable.errorType)); + syncSendExpr.setBType(BUnionType.create(symTable.typeEnv(), null, symTable.nilType, symTable.errorType)); boolean withinIfOrOnFail = !invalidSendPos && withinIfOrOnFail(data.env.enclInvokable.body, data.env.node); setWorkerSendSendTypeDetails(syncSendExpr, syncSendExpr.expr.getBType(), withinIfOrOnFail, data); was.addWorkerAction(syncSendExpr); @@ -2254,7 +2250,7 @@ public BType createAccumulatedErrorTypeForMatchingSyncSend(AnalyzerData data, Lo returnTypeAndSendType.add(symTable.nilType); if (returnTypeAndSendType.size() > 1) { - return BUnionType.create(null, returnTypeAndSendType); + return BUnionType.create(symTable.typeEnv(), null, returnTypeAndSendType); } else { return symTable.nilType; } @@ -2279,25 +2275,7 @@ private void addErrorTypesToSet(BType returnType, LinkedHashSet errorType } private boolean hasNonErrorType(BType returnType) { - if (returnType == null) { - return false; - } - - BType effType = Types.getImpliedType(types.getTypeWithEffectiveIntersectionTypes(returnType)); - if (effType.tag == TypeTags.ERROR) { - return false; - } - - if (effType.tag == TypeTags.UNION) { - for (BType memberType : ((BUnionType) returnType).getMemberTypes()) { - if (hasNonErrorType(memberType)) { - return true; - } - } - return false; - } - - return true; + return !Core.isSubtypeSimple(returnType.semType(), PredefinedType.ERROR); } @Override @@ -3340,14 +3318,14 @@ public void visit(BLangCheckedExpr checkedExpr, AnalyzerData data) { BType exprType = Types.getImpliedType(enclInvokable.getReturnTypeNode().getBType()); BType checkedExprType = checkedExpr.expr.getBType(); - BType errorType = types.getErrorTypes(checkedExprType); + SemType errorType = types.getErrorIntersection(checkedExprType.semType()); - if (errorType == symTable.semanticError) { + if (Core.isNever(errorType)) { return; } boolean ignoreErrForCheckExpr = data.withinQuery && data.queryConstructType == Types.QueryConstructType.STREAM; - if (!data.failureHandled && !ignoreErrForCheckExpr && !types.isAssignable(errorType, exprType) + if (!data.failureHandled && !ignoreErrForCheckExpr && !types.isSubtype(errorType, exprType.semType()) && !types.isNeverTypeOrStructureTypeWithARequiredNeverMember(checkedExprType)) { dlog.error(checkedExpr.pos, DiagnosticErrorCode.CHECKED_EXPR_NO_MATCHING_ERROR_RETURN_IN_ENCL_INVOKABLE); @@ -3546,7 +3524,7 @@ public void visit(BLangTypeTestExpr typeTestExpr, AnalyzerData data) { // It'll be only possible iff, the target type has been assigned to the source // variable at some point. To do that, a value of target type should be assignable // to the type of the source variable. - if (!intersectionExists(expr, typeNodeType, data, typeTestExpr.pos)) { + if (!types.intersectionExists(expr.getBType().semType(), typeNodeType.semType())) { dlog.error(typeTestExpr.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPE_CHECK, exprType, typeNodeType); } } @@ -3575,20 +3553,6 @@ private void logDeprecatedWaring(String deprecatedConstruct, BSymbol symbol, Loc dlog.warning(pos, DiagnosticWarningCode.USAGE_OF_DEPRECATED_CONSTRUCT, deprecatedConstruct); } - private boolean intersectionExists(BLangExpression expression, BType testType, AnalyzerData data, - Location intersectionPos) { - BType expressionType = expression.getBType(); - - BType intersectionType = types.getTypeIntersection( - Types.IntersectionContext.typeTestIntersectionExistenceContext(intersectionPos), - expressionType, testType, data.env); - - // any and readonly has an intersection - return (intersectionType != symTable.semanticError) || - (Types.getImpliedType(expressionType).tag == TypeTags.ANY && - Types.getImpliedType(testType).tag == TypeTags.READONLY); - } - @Override public void visit(BLangInferredTypedescDefaultNode inferTypedescExpr, AnalyzerData data) { /* Ignore */ @@ -3817,7 +3781,7 @@ private void typeCheckAlternateReceive(BLangAlternateWorkerReceive altWorkerRv) BType actualType; if (altTypes.size() > 1) { - actualType = BUnionType.create(null, altTypes); + actualType = BUnionType.create(symTable.typeEnv(), null, altTypes); } else { actualType = altTypes.iterator().next(); } @@ -4295,7 +4259,7 @@ private void validateInvocationInMatchGuard(BLangInvocation invocation) { BType matchedExprType = matchedExpr.getBType(); if (types.isInherentlyImmutableType(matchedExprType) || - Symbols.isFlagOn(matchedExprType.flags, Flags.READONLY)) { + Symbols.isFlagOn(matchedExprType.getFlags(), Flags.READONLY)) { return; } @@ -4319,7 +4283,7 @@ private void validateInvocationInMatchGuard(BLangInvocation invocation) { BLangExpression streamImplementorExpr = argsExpr.get(0); BType type = streamImplementorExpr.getBType(); - if (!types.isInherentlyImmutableType(type) && !Symbols.isFlagOn(type.flags, Flags.READONLY)) { + if (!types.isInherentlyImmutableType(type) && !Symbols.isFlagOn(type.getFlags(), Flags.READONLY)) { dlog.error(streamImplementorExpr.pos, DiagnosticErrorCode.INVALID_CALL_WITH_MUTABLE_ARGS_IN_MATCH_GUARD); } @@ -4344,7 +4308,7 @@ private void validateInvocationInMatchGuard(BLangInvocation invocation) { if (type != symTable.semanticError && !types.isInherentlyImmutableType(type) && - !Symbols.isFlagOn(type.flags, Flags.READONLY)) { + !Symbols.isFlagOn(type.getFlags(), Flags.READONLY)) { dlog.error(arg.pos, DiagnosticErrorCode.INVALID_CALL_WITH_MUTABLE_ARGS_IN_MATCH_GUARD); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConditionResolver.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConditionResolver.java index 7b667249be30..a2fddc62af1f 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConditionResolver.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConditionResolver.java @@ -17,6 +17,8 @@ */ package org.wso2.ballerinalang.compiler.semantics.analyzer; +import io.ballerina.types.Core; +import io.ballerina.types.Value; import org.ballerinalang.model.tree.OperatorKind; import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.SymTag; @@ -29,8 +31,9 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangSimpleVarRef; import org.wso2.ballerinalang.compiler.tree.expressions.BLangTypeTestExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangUnaryExpr; +import org.wso2.ballerinalang.compiler.util.TypeTags; -import java.util.HashSet; +import java.util.Optional; /** * This analyzes the condition in if statement and while statement. @@ -51,9 +54,11 @@ static BType checkConstCondition(Types types, SymbolTable symTable, BLangExpress if (value instanceof Boolean) { return value == Boolean.TRUE ? symTable.trueType : symTable.falseType; } - return new BFiniteType(null, new HashSet<>() { { add(condition); } }); + return BFiniteType.newSingletonBFiniteType(null, + SemTypeHelper.resolveSingletonType((BLangLiteral) condition)); case NUMERIC_LITERAL: - return new BFiniteType(null, new HashSet<>() { { add(condition); } }); + return BFiniteType.newSingletonBFiniteType(null, + SemTypeHelper.resolveSingletonType((BLangLiteral) condition)); case TYPE_TEST_EXPR: BLangTypeTestExpr typeTestExpr = (BLangTypeTestExpr) condition; BType exprType = typeTestExpr.expr.getBType(); @@ -119,11 +124,21 @@ static BType checkConstCondition(Types types, SymbolTable symTable, BLangExpress BLangSimpleVarRef simpleVarRef = (BLangSimpleVarRef) condition; BType type = (simpleVarRef.symbol.tag & SymTag.CONSTANT) == SymTag.CONSTANT ? simpleVarRef.symbol.type : condition.getBType(); - if (!types.isSingletonType(type)) { + BType baseType = Types.getImpliedType(type); + + if (baseType.tag != TypeTags.FINITE) { + return symTable.semanticError; + } + + Optional val = Core.singleShape(baseType.semType()); + if (val.isEmpty()) { return symTable.semanticError; } - return checkConstCondition(types, symTable, ((BFiniteType) Types.getImpliedType(type)) - .getValueSpace().iterator().next()); + + if (val.get().value instanceof Boolean) { + return val.get().value == Boolean.TRUE ? symTable.trueType : symTable.falseType; + } + return baseType; default: return symTable.semanticError; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java index 19b37c0e15c3..07a643dfa4b1 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java @@ -19,6 +19,12 @@ import io.ballerina.tools.diagnostics.DiagnosticCode; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.Value; +import io.ballerina.types.subtypedata.StringSubtype; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; @@ -47,7 +53,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; @@ -58,7 +63,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; @@ -111,11 +115,13 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.function.BiFunction; import static org.ballerinalang.model.symbols.SymbolOrigin.SOURCE; import static org.ballerinalang.model.symbols.SymbolOrigin.VIRTUAL; +import static org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper.singleShapeBroadType; /** * Resolve the value and check the type of constant expression. @@ -148,7 +154,7 @@ public ConstantTypeChecker(CompilerContext context) { this.types = Types.getInstance(context); this.anonymousModelHelper = BLangAnonymousModelHelper.getInstance(context); this.typeChecker = TypeChecker.getInstance(context); - this.typeResolver = TypeResolver.getInstance(context); + this.typeResolver = TypeResolver.getInstance(context);; this.fillMembers = FillMembers.getInstance(context); } @@ -261,7 +267,7 @@ public void visit(BLangLiteral literalExpr, AnalyzerData data) { return; } - BType finiteType = getFiniteType(literalExpr.value, data.constantSymbol, literalExpr.pos, literalType); + BType finiteType = getFiniteType(literalExpr.value, data.constantSymbol, literalType); if (data.compoundExprCount == 0 && types.typeIncompatible(literalExpr.pos, finiteType, data.expType)) { data.resultType = symTable.semanticError; @@ -275,14 +281,14 @@ private BType rewriteByteArrayLiteral(BLangLiteral literalExpr, AnalyzerData dat List memberTypes = new ArrayList<>(); for (byte b : values) { - memberTypes.add(getFiniteType(Byte.toUnsignedLong(b), data.constantSymbol, literalExpr.pos, - symTable.intType)); + memberTypes.add(getFiniteType(Byte.toUnsignedLong(b), data.constantSymbol, symTable.intType)); } BType expType = Types.getImpliedType(data.expType); if (expType.tag == TypeTags.ARRAY && ((BArrayType) expType).state == BArrayState.INFERRED) { - ((BArrayType) expType).size = memberTypes.size(); - ((BArrayType) expType).state = BArrayState.CLOSED; + BArrayType expArrayType = (BArrayType) expType; + expArrayType.setSize(memberTypes.size()); + expArrayType.state = BArrayState.CLOSED; } return createNewTupleType(literalExpr.pos, memberTypes, data); @@ -459,7 +465,7 @@ public void visit(BLangBinaryExpr binaryExpr, AnalyzerData data) { data.resultType = symTable.semanticError; return; } - BType finiteType = getFiniteType(resolvedValue, constantSymbol, pos, resultType); + BType finiteType = getFiniteType(resolvedValue, constantSymbol, resultType); if (data.compoundExprCount == 0 && types.typeIncompatible(pos, finiteType, expType)) { data.resultType = symTable.semanticError; return; @@ -496,14 +502,13 @@ public void visit(BLangUnaryExpr unaryExpr, AnalyzerData data) { } BConstantSymbol constantSymbol = data.constantSymbol; - Object resolvedValue = evaluateUnaryOperator((BFiniteType) actualType, resultType, - unaryExpr.operator, data); + Object resolvedValue = evaluateUnaryOperator((BFiniteType) actualType, resultType, unaryExpr.operator, data); if (resolvedValue == null) { data.resultType = symTable.semanticError; return; } - BType finiteType = getFiniteType(resolvedValue, constantSymbol, unaryExpr.pos, resultType); + BType finiteType = getFiniteType(resolvedValue, constantSymbol, resultType); if (data.compoundExprCount == 0 && types.typeIncompatible(unaryExpr.pos, finiteType, data.expType)) { data.resultType = symTable.semanticError; return; @@ -513,7 +518,7 @@ public void visit(BLangUnaryExpr unaryExpr, AnalyzerData data) { private BRecordType createNewRecordType(BRecordTypeSymbol symbol, LinkedHashMap inferredFields, AnalyzerData data) { - BRecordType recordType = new BRecordType(symbol); + BRecordType recordType = new BRecordType(symTable.typeEnv(), symbol); recordType.restFieldType = symTable.noType; recordType.fields = inferredFields; symbol.type = recordType; @@ -537,8 +542,8 @@ private BType checkMappingConstructorCompatibility(BType expType, BLangRecordLit } if (tag == TypeTags.INTERSECTION) { - return checkMappingConstructorCompatibility(((BIntersectionType) expType).effectiveType, - mappingConstructor, data); + return checkMappingConstructorCompatibility(((BIntersectionType) expType).effectiveType, mappingConstructor, + data); } BType possibleType = getMappingConstructorCompatibleNonUnionType(expType, data); @@ -614,7 +619,7 @@ private BType checkMappingConstructorCompatibilityForUnionType(BType expType, BL mappingConstructor, data); if (memCompatibiltyType != symTable.semanticError && dlog.errorCount() == 0 && - isUniqueType(compatibleTypes, listCompatibleMemType)) { + types.isUniqueType(compatibleTypes, listCompatibleMemType)) { compatibleTypes.add(listCompatibleMemType); } } @@ -642,15 +647,15 @@ private BType getMappingConstructorCompatibleNonUnionType(BType type, AnalyzerDa case TypeTags.READONLY: return type; case TypeTags.JSON: - return !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.mapJsonType : + return !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.mapJsonType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.mapJsonType, data.env, symTable, anonymousModelHelper, names); case TypeTags.ANYDATA: - return !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.mapAnydataType : + return !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.mapAnydataType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.mapAnydataType, data.env, symTable, anonymousModelHelper, names); case TypeTags.ANY: - return !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.mapAllType : + return !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.mapAllType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.mapAllType, data.env, symTable, anonymousModelHelper, names); case TypeTags.INTERSECTION: @@ -758,20 +763,22 @@ private BType validateMapTypeAndInferredType(BLangRecordLiteral mappingConstruct BLangRecordLiteral.BLangRecordKeyValueField keyValue = (BLangRecordLiteral.BLangRecordKeyValueField) field; BLangRecordLiteral.BLangRecordKey key = keyValue.key; BType fieldName = checkConstExpr(key.expr, data); - if (fieldName.tag == TypeTags.FINITE && ((BFiniteType) fieldName).getValueSpace().size() == 1) { - BLangLiteral fieldNameLiteral = - (BLangLiteral) ((BFiniteType) fieldName).getValueSpace().iterator().next(); - if (fieldNameLiteral.getBType().tag == TypeTags.STRING) { - exprToCheck = keyValue.valueExpr; - if (data.commonAnalyzerData.nonErrorLoggingCheck) { - exprToCheck = nodeCloner.cloneNode(keyValue.valueExpr); - } - BType keyValueType = checkConstExpr(exprToCheck, expType, data); - if (!addFields(inferredFields, Types.getImpliedType(keyValueType), - fieldNameLiteral.getValue().toString(), key.pos, recordSymbol)) { - containErrors = true; + if (fieldName.tag == TypeTags.FINITE) { + SemType semtype = fieldName.semType(); + if (SemTypes.isSubtypeSimple(semtype, PredefinedType.STRING)) { + Optional str = StringSubtype.stringSubtypeSingleValue(Core.stringSubtype(semtype)); + if (str.isPresent()) { + exprToCheck = keyValue.valueExpr; + if (data.commonAnalyzerData.nonErrorLoggingCheck) { + exprToCheck = nodeCloner.cloneNode(keyValue.valueExpr); + } + BType keyValueType = checkConstExpr(exprToCheck, expType, data); + if (!addFields(inferredFields, Types.getImpliedType(keyValueType), str.get(), key.pos, + recordSymbol)) { + containErrors = true; + } + continue; } - continue; } } dlog.error(key.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, symTable.stringType, fieldName); @@ -898,33 +905,35 @@ private BType validateRecordType(BLangRecordLiteral mappingConstructor, BRecordT BLangRecordLiteral.BLangRecordKeyValueField keyValue = (BLangRecordLiteral.BLangRecordKeyValueField) field; BLangRecordLiteral.BLangRecordKey key = keyValue.key; BType fieldName = checkConstExpr(key.expr, data); - if (fieldName.tag == TypeTags.FINITE && ((BFiniteType) fieldName).getValueSpace().size() == 1) { - BLangLiteral fieldNameLiteral = - (BLangLiteral) ((BFiniteType) fieldName).getValueSpace().iterator().next(); - if (fieldNameLiteral.getBType().tag == TypeTags.STRING) { - String keyName = fieldNameLiteral.getValue().toString(); - if (!targetFields.containsKey(keyName)) { - if (expRecordType.sealed) { - containErrors = true; - dlog.error(keyValue.pos, DiagnosticErrorCode.UNDEFINED_STRUCTURE_FIELD_WITH_TYPE, - key, expRecordType.tsymbol.type.getKind().typeName(), expRecordType); - continue; + if (fieldName.tag == TypeTags.FINITE) { + SemType semtype = fieldName.semType(); + if (SemTypes.isSubtypeSimple(semtype, PredefinedType.STRING)) { + Optional str = StringSubtype.stringSubtypeSingleValue(Core.stringSubtype(semtype)); + if (str.isPresent()) { + String keyName = str.get(); + if (!targetFields.containsKey(keyName)) { + if (expRecordType.sealed) { + containErrors = true; + dlog.error(keyValue.pos, DiagnosticErrorCode.UNDEFINED_STRUCTURE_FIELD_WITH_TYPE, + key, expRecordType.tsymbol.type.getKind().typeName(), expRecordType); + continue; + } else { + expType = expRecordType.restFieldType; + } } else { - expType = expRecordType.restFieldType; + expType = targetFields.get(keyName).type; } - } else { - expType = targetFields.get(keyName).type; - } - exprToCheck = keyValue.valueExpr; - if (data.commonAnalyzerData.nonErrorLoggingCheck) { - exprToCheck = nodeCloner.cloneNode(keyValue.valueExpr); - } - BType keyValueType = checkConstExpr(exprToCheck, expType, data); - if (!addFields(inferredFields, Types.getImpliedType(keyValueType), - keyName, key.pos, recordSymbol)) { - containErrors = true; + exprToCheck = keyValue.valueExpr; + if (data.commonAnalyzerData.nonErrorLoggingCheck) { + exprToCheck = nodeCloner.cloneNode(keyValue.valueExpr); + } + BType keyValueType = checkConstExpr(exprToCheck, expType, data); + if (!addFields(inferredFields, Types.getImpliedType(keyValueType), + keyName, key.pos, recordSymbol)) { + containErrors = true; + } + continue; } - continue; } } dlog.error(key.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, fieldName, symTable.stringType); @@ -938,22 +947,6 @@ private BType validateRecordType(BLangRecordLiteral mappingConstructor, BRecordT return createNewRecordType(recordSymbol, inferredFields, data); } - private boolean isUniqueType(Iterable typeList, BType type) { - boolean isRecord = type.tag == TypeTags.RECORD; - - for (BType bType : typeList) { - - if (isRecord) { - if (type == bType) { - return false; - } - } else if (types.isSameType(type, bType)) { - return false; - } - } - return true; - } - private boolean validateRequiredFields(BRecordType type, List specifiedFields, Location pos, AnalyzerData data) { HashSet specFieldNames = getFieldNames(specifiedFields, data); @@ -1073,7 +1066,7 @@ private BType checkListConstructorCompatibility(BType expType, BLangListConstruc BType memCompatibiltyType = checkListConstructorCompatibility(listCompatibleMemType, listConstructor, data); if (memCompatibiltyType != symTable.semanticError && dlog.errorCount() == 0 && - isUniqueType(compatibleTypes, memCompatibiltyType)) { + types.isUniqueType(compatibleTypes, memCompatibiltyType)) { compatibleTypes.add(memCompatibiltyType); } } @@ -1101,8 +1094,8 @@ private BType checkListConstructorCompatibility(BType expType, BLangListConstruc } if (tag == TypeTags.INTERSECTION) { - return checkListConstructorCompatibility(((BIntersectionType) expType).effectiveType, - listConstructor, data); + return checkListConstructorCompatibility(((BIntersectionType) expType).effectiveType, listConstructor, + data); } BType possibleType = getListConstructorCompatibleNonUnionType(expType, data); @@ -1134,7 +1127,7 @@ private BType checkArrayType(BArrayType arrayType, BLangListConstructorExpr list switch (spreadOpType.tag) { case TypeTags.ARRAY: - int arraySize = ((BArrayType) spreadOpType).size; + int arraySize = ((BArrayType) spreadOpType).getSize(); if (arraySize >= 0) { listExprSize += arraySize; continue; @@ -1161,12 +1154,12 @@ private BType checkArrayType(BArrayType arrayType, BLangListConstructorExpr list BType eType = arrayType.eType; if (arrayType.state == BArrayState.INFERRED) { - arrayType.size = listExprSize; + arrayType.setSize(listExprSize); arrayType.state = BArrayState.CLOSED; - } else if (arrayType.state != BArrayState.OPEN && arrayType.size != listExprSize) { - if (arrayType.size < listExprSize) { - dlog.error(listConstructor.pos, DiagnosticErrorCode.MISMATCHING_ARRAY_LITERAL_VALUES, arrayType.size, - listExprSize); + } else if (arrayType.state != BArrayState.OPEN && arrayType.getSize() != listExprSize) { + if (arrayType.getSize() < listExprSize) { + dlog.error(listConstructor.pos, DiagnosticErrorCode.MISMATCHING_ARRAY_LITERAL_VALUES, + arrayType.getSize(), listExprSize); return symTable.semanticError; } @@ -1217,7 +1210,7 @@ private BType checkArrayType(BArrayType arrayType, BLangListConstructorExpr list // Create new tuple type using inferred members. BTupleType resultTupleType = createNewTupleType(listConstructor.pos, memberTypes, data); - if (arrayType.state == BArrayState.CLOSED && arrayType.size > listExprSize) { + if (arrayType.state == BArrayState.CLOSED && arrayType.getSize() > listExprSize) { if (!fillMembers.addFillMembers(resultTupleType, arrayType, data)) { return symTable.semanticError; } @@ -1245,7 +1238,7 @@ private BType checkTupleType(BTupleType tupleType, BLangListConstructorExpr list switch (spreadOpType.tag) { case TypeTags.ARRAY: - int arraySize = ((BArrayType) spreadOpType).size; + int arraySize = ((BArrayType) spreadOpType).getSize(); if (arraySize >= 0) { listExprSize += arraySize; continue; @@ -1359,7 +1352,7 @@ private BTupleType createNewTupleType(Location pos, List memberTypes, Ana List members = new ArrayList<>(); memberTypes.forEach(m -> members.add(new BTupleMember(m, Symbols.createVarSymbolForTupleMember(m)))); - BTupleType tupleType = new BTupleType(tupleTypeSymbol, members); + BTupleType tupleType = new BTupleType(symTable.typeEnv(), tupleTypeSymbol, members); tupleType.tsymbol.type = tupleType; return tupleType; } @@ -1386,13 +1379,13 @@ private BType getListConstructorCompatibleNonUnionType(BType type, AnalyzerData TypeTags.TUPLE, TypeTags.READONLY, TypeTags.TYPEDESC -> type; - case TypeTags.JSON -> !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.arrayJsonType : + case TypeTags.JSON -> !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.arrayJsonType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.arrayJsonType, data.env, symTable, anonymousModelHelper, names); - case TypeTags.ANYDATA -> !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.arrayAnydataType : + case TypeTags.ANYDATA -> !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.arrayAnydataType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.arrayAnydataType, data.env, symTable, anonymousModelHelper, names); - case TypeTags.ANY -> !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.arrayAllType : + case TypeTags.ANY -> !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.arrayAllType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.arrayAllType, data.env, symTable, anonymousModelHelper, names); case TypeTags.INTERSECTION -> ((BIntersectionType) type).effectiveType; @@ -1404,7 +1397,7 @@ private BType getBroadType(BType type) { if (type.tag != TypeTags.FINITE) { return type; } - return ((BFiniteType) type).getValueSpace().iterator().next().getBType(); + return singleShapeBroadType(type.semType(), symTable).get(); } private BSymbol getUnaryOpSymbol(BLangUnaryExpr unaryExpr, BType type, AnalyzerData data) { @@ -1422,7 +1415,7 @@ private BSymbol getUnaryOpSymbol(BLangUnaryExpr unaryExpr, BType type, AnalyzerD } if (symbol == symTable.notFoundSymbol) { - exprType = ((BFiniteType) type).getValueSpace().iterator().next().getBType(); + exprType = singleShapeBroadType(type.semType(), symTable).get(); symbol = symResolver.resolveUnaryOperator(unaryExpr.operator, exprType); if (symbol == symTable.notFoundSymbol) { symbol = symResolver.getUnaryOpsForTypeSets(unaryExpr.operator, exprType); @@ -1445,12 +1438,10 @@ private Object calculateSingletonValue(BFiniteType lhs, BFiniteType rhs, Operato return null; } - BLangLiteral lhsLiteral = (BLangLiteral) lhs.getValueSpace().iterator().next(); - BLangLiteral rhsLiteral = (BLangLiteral) rhs.getValueSpace().iterator().next(); - // See Types.isAllowedConstantType() for supported types. - Object lhsValue = getValue(lhsLiteral); - Object rhsValue = getValue(rhsLiteral); + Object lhsValue = Core.singleShape(lhs.semType()).get().value; + Object rhsValue = Core.singleShape(rhs.semType()).get().value; + try { switch (kind) { case ADD: @@ -1498,14 +1489,15 @@ private Object getValue(BLangLiteral lhsLiteral) { private Object evaluateUnaryOperator(BFiniteType finiteType, BType type, OperatorKind kind, AnalyzerData data) { // Calculate the value for the unary operation. - BLangLiteral lhsLiteral = (BLangLiteral) finiteType.getValueSpace().iterator().next(); - Object value = getValue(lhsLiteral); - if (value == null) { + Optional optionalValue = Core.singleShape(finiteType.semType()); + if (optionalValue.isEmpty()) { // This is a compilation error. // This is to avoid NPE exceptions in sub-sequent validations. return null; } + Object value = optionalValue.get().value; + try { switch (kind) { case ADD: @@ -1718,13 +1710,6 @@ private BType setLiteralValueAndGetType(BLangLiteral literalExpr, BType expType, // Get the type matching to the tag from the symbol table. BType literalType = symTable.getTypeFromTag(literalExpr.getBType().tag); - if (literalType.tag == TypeTags.STRING && types.isCharLiteralValue((String) literalValue)) { - boolean foundMember = types.isAssignableToFiniteType(symTable.noType, literalExpr); - if (foundMember) { - setLiteralValueForFiniteType(literalExpr, literalType, data); - return literalType; - } - } return literalType; } @@ -1783,15 +1768,13 @@ private BType getIntegerLiteralTypeUsingExpType(BLangLiteral literalExpr, Object literalExpr.value = String.valueOf(literalValue); return symTable.decimalType; case TypeTags.FINITE: - Set valueSpace = ((BFiniteType) expectedType).getValueSpace(); - if (valueSpace.size() > 1) { - LinkedHashSet memTypes = new LinkedHashSet<>(); - valueSpace.forEach(memExpr -> memTypes.add(memExpr.getBType())); - BUnionType unionType = new BUnionType(null, memTypes, false, false); - return getIntegerLiteralTypeUsingExpType(literalExpr, literalValue, unionType); - } - BType expBroadType = ((BFiniteType) expectedType).getValueSpace().iterator().next().getBType(); - return getIntegerLiteralTypeUsingExpType(literalExpr, literalValue, expBroadType); + Set broadTypes = SemTypeHelper.broadTypes((BFiniteType) expectedType, symTable); + if (broadTypes.size() == 1) { + return getIntegerLiteralTypeUsingExpType(literalExpr, literalValue, broadTypes.iterator().next()); + } + + BUnionType unionType = new BUnionType(types.typeEnv(), null, new LinkedHashSet<>(broadTypes), false); + return getIntegerLiteralTypeUsingExpType(literalExpr, literalValue, unionType); case TypeTags.UNION: BUnionType expectedUnionType = (BUnionType) expectedType; List validTypes = new ArrayList<>(); @@ -1837,12 +1820,6 @@ private BType getIntegerLiteralTypeUsingExpType(BLangLiteral literalExpr, Object return symTable.intType; } - public void setLiteralValueForFiniteType(BLangLiteral literalExpr, BType type, AnalyzerData data) { - types.setImplicitCastExpr(literalExpr, type, data.expType); - data.resultType = type; - literalExpr.isFiniteContext = true; - } - private BType getTypeOfLiteralWithDecimalDiscriminator(BLangLiteral literalExpr, Object literalValue) { literalExpr.value = NumericLiteralSupport.stripDiscriminator(String.valueOf(literalValue)); if (!types.isValidDecimalNumber(literalExpr.pos, literalExpr.value.toString())) { @@ -1880,7 +1857,7 @@ private BType getTypeOfDecimalFloatingPointLiteralUsingExpType(BLangLiteral lite } return symTable.semanticError; case TypeTags.FINITE: - BType expBroadType = ((BFiniteType) expectedType).getValueSpace().iterator().next().getBType(); + BType expBroadType = singleShapeBroadType(expectedType.semType(), symTable).get(); return getTypeOfDecimalFloatingPointLiteralUsingExpType(literalExpr, literalValue, expBroadType); case TypeTags.UNION: BUnionType expectedUnionType = (BUnionType) expectedType; @@ -1921,28 +1898,22 @@ public BType getTypeOfHexFloatingPointLiteral(BLangLiteral literalExpr, Object l return symTable.floatType; } - private BType getFiniteType(Object value, BConstantSymbol constantSymbol, Location pos, BType type) { - return switch (type.tag) { - case TypeTags.INT, - TypeTags.FLOAT, - TypeTags.DECIMAL, - TypeTags.BYTE -> { - BLangNumericLiteral numericLiteral = (BLangNumericLiteral) TreeBuilder.createNumericLiteralExpression(); - yield createFiniteType(constantSymbol, updateLiteral(numericLiteral, value, type, pos)); - } -// case TypeTags.BYTE -> { -// BLangNumericLiteral byteLiteral = (BLangNumericLiteral) TreeBuilder.createNumericLiteralExpression(); -// yield createFiniteType(constantSymbol, updateLiteral(byteLiteral, value, symTable.intType, pos)); -// } - case TypeTags.STRING, - TypeTags.NIL, - TypeTags.BOOLEAN -> { - BLangLiteral literal = (BLangLiteral) TreeBuilder.createLiteralExpression(); - yield createFiniteType(constantSymbol, updateLiteral(literal, value, type, pos)); - } - case TypeTags.UNION -> createFiniteType(constantSymbol, value, (BUnionType) type, pos); - default -> type; - }; + private BType getFiniteType(Object value, BConstantSymbol constantSymbol, BType type) { + switch (type.tag) { + case TypeTags.INT: + case TypeTags.FLOAT: + case TypeTags.DECIMAL: + case TypeTags.BYTE: + case TypeTags.STRING: + case TypeTags.NIL: + case TypeTags.BOOLEAN: + BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, constantSymbol.flags, + Names.EMPTY, constantSymbol.pkgID, null, constantSymbol.owner, constantSymbol.pos, VIRTUAL); + return BFiniteType.newSingletonBFiniteType(finiteTypeSymbol, + SemTypeHelper.resolveSingletonType(value, type.getKind())); + default: + return type; + } } private BLangLiteral getLiteral(Object value, Location pos, BType type) { @@ -1963,38 +1934,8 @@ private BLangLiteral updateLiteral(BLangLiteral literal, Object value, BType typ return literal; } - private BFiniteType createFiniteType(BConstantSymbol constantSymbol, BLangExpression expr) { - BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, constantSymbol.flags, Names.EMPTY, - constantSymbol.pkgID, null, constantSymbol.owner, - constantSymbol.pos, VIRTUAL); - BFiniteType finiteType = new BFiniteType(finiteTypeSymbol); - finiteType.addValue(expr); - finiteType.tsymbol.type = finiteType; - return finiteType; - } - - private BUnionType createFiniteType(BConstantSymbol constantSymbol, Object value, BUnionType type, Location pos) { - LinkedHashSet memberTypes = new LinkedHashSet<>(3); - for (BType memberType : type.getMemberTypes()) { - BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, constantSymbol.flags, - Names.EMPTY, constantSymbol.pkgID, null, constantSymbol.owner, constantSymbol.pos, VIRTUAL); - BFiniteType finiteType = new BFiniteType(finiteTypeSymbol); - Object memberValue = switch (memberType.tag) { - case TypeTags.FLOAT -> value instanceof String ? - Double.parseDouble((String) value) : ((Long) value).doubleValue(); - case TypeTags.DECIMAL -> new BigDecimal(String.valueOf(value)); - default -> value; - }; - finiteType.addValue(getLiteral(memberValue, pos, memberType)); - finiteType.tsymbol.type = finiteType; - memberTypes.add(finiteType); - } - - return BUnionType.create(null, memberTypes); - } - private boolean addFields(LinkedHashMap fields, BType keyValueType, String key, Location pos, - BRecordTypeSymbol recordSymbol) { + BRecordTypeSymbol recordSymbol) { Name fieldName = Names.fromString(key); if (fields.containsKey(key)) { dlog.error(pos, DiagnosticErrorCode.DUPLICATE_KEY_IN_MAPPING_CONSTRUCTOR, TypeKind.RECORD.typeName(), key); @@ -2053,7 +1994,7 @@ private BSymbol getOpSymbolBothUnion(BUnionType lhsType, BUnionType rhsType, lhsType.remove(memberTypeLhs); } if (memberTypes.size() != 1) { - data.resultType = BUnionType.create(null, memberTypes); + data.resultType = BUnionType.create(symTable.typeEnv(), null, memberTypes); } else { data.resultType = memberTypes.iterator().next(); } @@ -2089,7 +2030,7 @@ private BSymbol getOpSymbolLhsUnion(BUnionType lhsType, BType rhsType, lhsType.remove(memberTypeLhs); } if (memberTypes.size() != 1) { - data.resultType = BUnionType.create(null, memberTypes); + data.resultType = BUnionType.create(symTable.typeEnv(), null, memberTypes); } else { data.resultType = memberTypes.iterator().next(); } @@ -2152,7 +2093,7 @@ private BSymbol getOpSymbol(BType lhsType, BType rhsType, BLangBinaryExpr binary /** * @since 2201.7.0 */ - public static class FillMembers implements TypeVisitor { + public static class FillMembers extends TypeVisitor { private static final CompilerContext.Key FILL_MEMBERS_KEY = new CompilerContext.Key<>(); @@ -2190,7 +2131,7 @@ public boolean addFillMembers(BTupleType type, BType expType, AnalyzerData data) int tupleMemberCount = tupleTypes.size(); if (refType.tag == TypeTags.ARRAY) { BArrayType arrayType = (BArrayType) expType; - int noOfFillMembers = arrayType.size - tupleMemberCount; + int noOfFillMembers = arrayType.getSize() - tupleMemberCount; BType fillMemberType = getFillMembers(arrayType.eType, data); if (fillMemberType == symTable.semanticError) { return false; @@ -2236,7 +2177,7 @@ public void visit(BArrayType arrayType) { Flags.asMask(EnumSet.of(Flag.PUBLIC)), Names.EMPTY, data.env.enclPkg.symbol.pkgID, null, data.env.scope.owner, null, SOURCE); if (arrayType.state == BArrayState.OPEN) { - BTupleType resultTupleType = new BTupleType(tupleTypeSymbol, new ArrayList<>()); + BTupleType resultTupleType = new BTupleType(symTable.typeEnv(), tupleTypeSymbol, new ArrayList<>()); tupleTypeSymbol.type = resultTupleType; data.resultType = resultTupleType; return; @@ -2249,23 +2190,18 @@ public void visit(BArrayType arrayType) { data.resultType = symTable.semanticError; return; } - List tupleTypes = new ArrayList<>(arrayType.size); - for (int i = 0; i < arrayType.size; i++) { + List tupleTypes = new ArrayList<>(arrayType.getSize()); + for (int i = 0; i < arrayType.getSize(); i++) { tupleTypes.add(fillMemberType); } List members = new ArrayList<>(); tupleTypes.forEach(m -> members.add(new BTupleMember(m, Symbols.createVarSymbolForTupleMember(m)))); - BTupleType resultTupleType = new BTupleType(tupleTypeSymbol, members); + BTupleType resultTupleType = new BTupleType(symTable.typeEnv(), tupleTypeSymbol, members); tupleTypeSymbol.type = resultTupleType; data.resultType = resultTupleType; } - @Override - public void visit(BBuiltInRefType bBuiltInRefType) { - - } - @Override public void visit(BAnyType bAnyType) { data.resultType = symTable.nilType; @@ -2283,8 +2219,7 @@ public void visit(BErrorType bErrorType) { @Override public void visit(BFiniteType finiteType) { - Set valueSpace = finiteType.getValueSpace(); - if (valueSpace.size() > 1) { + if (Core.singleShape(finiteType.semType()).isEmpty()) { if (finiteType.isNullable()) { // Ex. 1|null data.resultType = symTable.nilType; return; @@ -2315,8 +2250,8 @@ public void visit(BJSONType bjsonType) { public void visit(BMapType bMapType) { BRecordTypeSymbol recordSymbol = constantTypeChecker.createRecordTypeSymbol(data.constantSymbol.pkgID, data.constantSymbol.pos, VIRTUAL, data); - recordSymbol.type = new BRecordType(recordSymbol); - BRecordType resultRecordType = new BRecordType(recordSymbol); + recordSymbol.type = new BRecordType(symTable.typeEnv(), recordSymbol); + BRecordType resultRecordType = new BRecordType(symTable.typeEnv(), recordSymbol); recordSymbol.type = resultRecordType; resultRecordType.tsymbol = recordSymbol; resultRecordType.sealed = true; @@ -2352,7 +2287,7 @@ public void visit(BNeverType bNeverType) { } @Override - public void visit(BNilType bNilType) { + public void visitNilType(BType bType) { data.resultType = symTable.nilType; } @@ -2389,7 +2324,7 @@ public void visit(BTupleType tupleType) { List members = new ArrayList<>(); tupleTypes.forEach(m -> members.add(new BTupleMember(m, Symbols.createVarSymbolForTupleMember(m)))); - BTupleType resultTupleType = new BTupleType(tupleTypeSymbol, members); + BTupleType resultTupleType = new BTupleType(symTable.typeEnv(), tupleTypeSymbol, members); tupleTypeSymbol.type = resultTupleType; data.resultType = resultTupleType; } @@ -2410,7 +2345,7 @@ public void visit(BUnionType unionType) { @Override public void visit(BIntersectionType intersectionType) { - data.resultType = getFillMembers(intersectionType.getEffectiveType(), data); + data.resultType = getFillMembers(intersectionType.effectiveType, data); } @Override @@ -2434,7 +2369,7 @@ public void visit(BRecordType recordType) { return; } } - BRecordType resultRecordType = new BRecordType(recordSymbol); + BRecordType resultRecordType = new BRecordType(symTable.typeEnv(), recordSymbol); recordSymbol.type = resultRecordType; resultRecordType.tsymbol = recordSymbol; resultRecordType.sealed = true; @@ -2450,12 +2385,22 @@ public void visit(BObjectType bObjectType) { } @Override - public void visit(BType type) { + public void visit(BType type) { // TODO: Can we get rid of refType switch? + switch (type.tag) { + case TypeTags.NIL: + visitNilType(type); + return; + } + + BConstantSymbol constantSymbol = data.constantSymbol; + BTypeSymbol finiteTypeSym = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, constantSymbol.flags, + Names.EMPTY, constantSymbol.pkgID, null, constantSymbol.owner, constantSymbol.pos, VIRTUAL); + BType refType = Types.getImpliedType(type); switch (refType.tag) { case TypeTags.BOOLEAN: data.resultType = symTable.falseType; - return; + break; case TypeTags.INT: case TypeTags.SIGNED8_INT: case TypeTags.SIGNED16_INT: @@ -2464,24 +2409,21 @@ public void visit(BType type) { case TypeTags.UNSIGNED16_INT: case TypeTags.UNSIGNED32_INT: case TypeTags.BYTE: - data.resultType = constantTypeChecker.getFiniteType(0L, data.constantSymbol, - null, symTable.intType); - return; + data.resultType = BFiniteType.newSingletonBFiniteType(finiteTypeSym, SemTypes.intConst(0)); + break; case TypeTags.FLOAT: - data.resultType = constantTypeChecker.getFiniteType(0.0d, data.constantSymbol, - null, symTable.floatType); - return; + data.resultType = BFiniteType.newSingletonBFiniteType(finiteTypeSym, SemTypes.floatConst(0)); + break; case TypeTags.DECIMAL: - data.resultType = constantTypeChecker.getFiniteType(new BigDecimal(0), data.constantSymbol, - null, symTable.decimalType); - return; + data.resultType = BFiniteType.newSingletonBFiniteType(finiteTypeSym, SemTypes.decimalConst("0")); + break; case TypeTags.STRING: case TypeTags.CHAR_STRING: - data.resultType = constantTypeChecker.getFiniteType("", data.constantSymbol, - null, symTable.stringType); - return; + data.resultType = BFiniteType.newSingletonBFiniteType(finiteTypeSym, SemTypes.stringConst("")); + break; default: data.resultType = symTable.semanticError; + break; } } @@ -2501,11 +2443,10 @@ public BLangConstantValue getConstantValue(BType type) { BType refType = Types.getImpliedType(type); switch (refType.tag) { case TypeTags.FINITE: - BLangExpression expr = ((BFiniteType) refType).getValueSpace().iterator().next(); - if (expr.getBType().tag == TypeTags.DECIMAL) { - return new BLangConstantValue ((((BLangNumericLiteral) expr).value).toString(), expr.getBType()); - } - return new BLangConstantValue (((BLangLiteral) expr).value, expr.getBType()); + BType t = singleShapeBroadType(refType.semType(), symTable).get(); + Value v = Core.singleShape(refType.semType()).get(); + // TODO: 12/9/23 merge t and v to a single object + return new BLangConstantValue (v.value, t); case TypeTags.RECORD: Map fields = new HashMap<>(); LinkedHashMap recordFields = ((BRecordType) refType).fields; @@ -2540,11 +2481,13 @@ public static class ResolveConstantExpressionType extends RESOLVE_CONSTANT_EXPRESSION_TYPE = new CompilerContext.Key<>(); private final Types types; private final ConstantTypeChecker constantTypeChecker; + private final SymbolTable symTable; public ResolveConstantExpressionType(CompilerContext context) { context.put(RESOLVE_CONSTANT_EXPRESSION_TYPE, this); this.types = Types.getInstance(context); this.constantTypeChecker = ConstantTypeChecker.getInstance(context); + this.symTable = SymbolTable.getInstance(context); } public static ResolveConstantExpressionType getInstance(CompilerContext context) { @@ -2599,7 +2542,7 @@ public void visit(BLangLiteral literalExpr, AnalyzerData data) { private void updateBlangExprType(BLangExpression expression, AnalyzerData data) { BType expressionType = expression.getBType(); if (expressionType.tag == TypeTags.FINITE) { - expressionType = ((BFiniteType) expressionType).getValueSpace().iterator().next().getBType(); + expressionType = singleShapeBroadType(expressionType.semType(), symTable).get(); expression.setBType(expressionType); types.setImplicitCastExpr(expression, data.expType, expressionType); return; @@ -2611,13 +2554,13 @@ private void updateBlangExprType(BLangExpression expression, AnalyzerData data) BType targetType; BType expType = data.expType; if (expType.tag == TypeTags.FINITE) { - targetType = ((BFiniteType) expType).getValueSpace().iterator().next().getBType(); + targetType = singleShapeBroadType(expType.semType(), symTable).get(); } else { targetType = expType; } for (BType memberType : ((BUnionType) expressionType).getMemberTypes()) { - BType type = ((BFiniteType) memberType).getValueSpace().iterator().next().getBType(); + BType type = singleShapeBroadType(memberType.semType(), symTable).get(); if (type.tag == targetType.tag || types.isAssignable(memberType, targetType)) { expression.setBType(type); @@ -2671,10 +2614,8 @@ public void visit(BLangRecordLiteral recordLiteral, AnalyzerData data) { (BLangRecordLiteral.BLangRecordKeyValueField) field; BLangRecordLiteral.BLangRecordKey computedKey = computedKeyValue.key; BType fieldName = constantTypeChecker.checkConstExpr(computedKey.expr, data); - BLangLiteral fieldNameLiteral = - (BLangLiteral) ((BFiniteType) fieldName).getValueSpace().iterator().next(); - expFieldType = - getResolvedFieldType(constantTypeChecker.getKeyName(fieldNameLiteral), resolvedType); + expFieldType = getResolvedFieldType(Core.singleShape(fieldName.semType()).get().value, + resolvedType); resolveConstExpr(computedKey.expr, expFieldType, data); resolveConstExpr(keyValueExpr, expFieldType, data); continue; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantValueResolver.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantValueResolver.java index b929c95ad4fe..ecedc5dd1a37 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantValueResolver.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantValueResolver.java @@ -698,13 +698,11 @@ private boolean isListOrMapping(int tag) { }; } - private BFiniteType createFiniteType(BConstantSymbol constantSymbol, BLangExpression expr) { + private BFiniteType createFiniteType(BConstantSymbol constantSymbol, BLangLiteral literal) { BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, constantSymbol.flags, Names.EMPTY, constantSymbol.pkgID, null, constantSymbol.owner, constantSymbol.pos, VIRTUAL); - BFiniteType finiteType = new BFiniteType(finiteTypeSymbol); - finiteType.addValue(expr); - return finiteType; + return BFiniteType.newSingletonBFiniteType(finiteTypeSymbol, SemTypeHelper.resolveSingletonType(literal)); } private BType checkType(BLangExpression expr, BConstantSymbol constantSymbol, Object value, BType type, @@ -845,7 +843,7 @@ private BType createRecordType(BLangExpression expr, BConstantSymbol constantSym constantSymbol.pkgID, null, constantSymbol.owner, pos, VIRTUAL); recordTypeSymbol.scope = constantSymbol.scope; - BRecordType recordType = new BRecordType(recordTypeSymbol); + BRecordType recordType = new BRecordType(symTable.typeEnv(), recordTypeSymbol); recordType.tsymbol.name = genName; recordType.sealed = true; recordType.restFieldType = new BNoType(TypeTags.NONE); @@ -860,7 +858,7 @@ private BType createRecordType(BLangExpression expr, BConstantSymbol constantSym createTypeDefinition(recordType, pos, env); updateRecordFields(recordType, pos, env); recordType.tsymbol.flags |= Flags.READONLY; - recordType.flags |= Flags.READONLY; + recordType.addFlags(Flags.READONLY); return recordType; } @@ -1026,8 +1024,8 @@ private BType createTupleType(BLangExpression expr, BConstantSymbol constantSymb Names.EMPTY, env.enclPkg.symbol.pkgID, null, env.scope.owner, pos, VIRTUAL); - return ImmutableTypeCloner.getImmutableIntersectionType(pos, types, new BTupleType(tupleTypeSymbol, tupleTypes), - env, symTable, anonymousModelHelper, names, - new HashSet<>()); + return ImmutableTypeCloner.getImmutableIntersectionType(pos, types, + new BTupleType(symTable.typeEnv(), tupleTypeSymbol, tupleTypes), env, symTable, anonymousModelHelper, + names, new HashSet<>()); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/DataflowAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/DataflowAnalyzer.java index 61f0b0af8994..663d32078caf 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/DataflowAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/DataflowAnalyzer.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.semantics.analyzer; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.PredefinedType; import org.ballerinalang.compiler.CompilerPhase; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.symbols.SymbolKind; @@ -2738,7 +2739,7 @@ private void checkFinalObjectFieldUpdate(BLangFieldBasedAccess fieldAccess) { BType exprType = Types.getImpliedType(expr.getBType()); - if (types.isSubTypeOfBaseType(exprType, TypeTags.OBJECT) && + if (types.isSubTypeOfBaseType(exprType, PredefinedType.OBJECT) && isFinalFieldInAllObjects(fieldAccess.pos, exprType, fieldAccess.field.value)) { dlog.error(fieldAccess.pos, DiagnosticErrorCode.CANNOT_UPDATE_FINAL_OBJECT_FIELD, fieldAccess.symbol.originalName); @@ -2816,11 +2817,7 @@ private void emitUnusedVariableWarnings(Map unusedLocalVariab } private boolean addVarIfInferredTypeIncludesError(BLangSimpleVariable variable) { - BType typeIntersection = - types.getTypeIntersection(Types.IntersectionContext.compilerInternalIntersectionContext(), - variable.getBType(), symTable.errorType, env); - if (typeIntersection != null && - typeIntersection != symTable.semanticError && typeIntersection != symTable.noType) { + if (types.containsErrorType(variable.getBType().semType())) { unusedErrorVarsDeclaredWithVar.put(variable.symbol, variable.pos); return true; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/EffectiveTypePopulator.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/EffectiveTypePopulator.java index 9d916811e724..4f25fd82f9fd 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/EffectiveTypePopulator.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/EffectiveTypePopulator.java @@ -33,7 +33,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; @@ -44,7 +43,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; @@ -83,7 +81,7 @@ * * @since 2201.7.0 */ -public class EffectiveTypePopulator implements TypeVisitor { +public class EffectiveTypePopulator extends TypeVisitor { private static final CompilerContext.Key UPDATE_IMMUTABLE_TYPE_KEY = new CompilerContext.Key<>(); @@ -151,11 +149,6 @@ public void visit(BArrayType bArrayType) { } } - @Override - public void visit(BBuiltInRefType bBuiltInRefType) { - - } - @Override public void visit(BAnyType bAnyType) { @@ -226,7 +219,7 @@ public void visit(BNeverType bNeverType) { } @Override - public void visit(BNilType bNilType) { + public void visitNilType(BType bNilType) { } @@ -271,7 +264,7 @@ public void visit(BTupleType bTupleType) { BTypeSymbol tsymbol = bTupleType.tsymbol; if (tsymbol != null && tsymbol.name != null && !tsymbol.name.value.isEmpty() - && !Symbols.isFlagOn(bTupleType.flags, Flags.EFFECTIVE_TYPE_DEF)) { + && !Symbols.isFlagOn(bTupleType.getFlags(), Flags.EFFECTIVE_TYPE_DEF)) { BLangTupleTypeNode tupleTypeNode = (BLangTupleTypeNode) TreeBuilder.createTupleTypeNode(); tupleTypeNode.setBType(bTupleType); BLangTypeDefinition typeDefinition = TypeDefBuilderHelper.addTypeDefinition(bTupleType, @@ -442,11 +435,6 @@ public void visit(BObjectType bObjectType) { } } - @Override - public void visit(BType bType) { - - } - @Override public void visit(BFutureType bFutureType) { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsAnydataUniqueVisitor.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsAnydataUniqueVisitor.java deleted file mode 100644 index cbbc0d8417df..000000000000 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsAnydataUniqueVisitor.java +++ /dev/null @@ -1,342 +0,0 @@ -package org.wso2.ballerinalang.compiler.semantics.analyzer; - -import org.wso2.ballerinalang.compiler.semantics.model.UniqueTypeVisitor; -import org.wso2.ballerinalang.compiler.semantics.model.types.BAnnotationType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BField; -import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BHandleType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BIntSubType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BIntersectionType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BParameterizedType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BRecordType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BStreamType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BStructureType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTableType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTupleType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTypeReferenceType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTypedescType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLSubType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; -import org.wso2.ballerinalang.compiler.util.TypeTags; - -import java.util.HashSet; - -/** - * IsAnydataUniqueVisitor to check if a type is anydata. - * - * This is introduced to handle cyclic unions. - * @since slp4 - */ -public class IsAnydataUniqueVisitor implements UniqueTypeVisitor { - - private final HashSet visited; - private final boolean isAnydata; - - public IsAnydataUniqueVisitor() { - visited = new HashSet<>(); - isAnydata = true; - } - - public IsAnydataUniqueVisitor(HashSet visited) { - this.visited = visited; - isAnydata = true; - } - - private boolean isAnydata(BType type) { - return switch (Types.getImpliedType(type).tag) { - case TypeTags.INT, - TypeTags.BYTE, - TypeTags.FLOAT, - TypeTags.DECIMAL, - TypeTags.STRING, - TypeTags.CHAR_STRING, - TypeTags.BOOLEAN, - TypeTags.JSON, - TypeTags.XML, - TypeTags.XML_TEXT, - TypeTags.XML_ELEMENT, - TypeTags.XML_COMMENT, - TypeTags.XML_PI, - TypeTags.NIL, - TypeTags.NEVER, - TypeTags.ANYDATA, - TypeTags.SIGNED8_INT, - TypeTags.SIGNED16_INT, - TypeTags.SIGNED32_INT, - TypeTags.UNSIGNED8_INT, - TypeTags.UNSIGNED16_INT, - TypeTags.UNSIGNED32_INT, - TypeTags.REGEXP -> true; - default -> false; - }; - } - - @Override - public boolean isVisited(BType type) { - return visited.contains(type); - } - - @Override - public void reset() { - visited.clear(); - } - - @Override - public Boolean visit(BAnnotationType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BArrayType type) { - if (isVisited(type)) { - return isAnydata; - } - visited.add(type); - return visit(type.eType); - } - - @Override - public Boolean visit(BBuiltInRefType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BAnyType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BAnydataType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BErrorType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BInvokableType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BJSONType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BMapType type) { - if (isVisited(type)) { - return isAnydata; - } - visited.add(type); - return visit(type.constraint); - } - - @Override - public Boolean visit(BStreamType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BTypedescType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BParameterizedType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BNeverType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BNilType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BNoType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BPackageType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BStructureType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BTupleType type) { - if (type.isAnyData != null) { - return type.isAnyData; - } - if (!visited.add(type)) { - return isAnydata; - } - for (BType memberType : type.getTupleTypes()) { - if (!visit(memberType)) { - type.isAnyData = false; - return false; - } - } - type.isAnyData = (type.restType == null) || visit(type.restType); - return isAnydata; - } - - @Override - public Boolean visit(BIntersectionType type) { - return visit(type.effectiveType); - } - - @Override - public Boolean visit(BTypeReferenceType type) { - return visit(type.referredType); - } - - @Override - public Boolean visit(BXMLType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BTableType type) { - return visit(type.constraint); - } - - @Override - public Boolean visit(BFiniteType type) { - if (type.isAnyData != null) { - return type.isAnyData; - } - for (BLangExpression value : type.getValueSpace()) { - if (!visit(value.getBType())) { - type.isAnyData = false; - return false; - } - } - type.isAnyData = true; - return isAnydata; - } - - @Override - public Boolean visit(BObjectType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BUnionType type) { - if (type.isAnyData != null) { - return type.isAnyData; - } - if (isVisited(type)) { - return isAnydata; - } - visited.add(type); - for (BType member : type.getMemberTypes()) { - if (!visit(member)) { - type.isAnyData = false; - return false; - } - } - type.isAnyData = isAnydata; - return isAnydata; - } - - @Override - public Boolean visit(BRecordType type) { - if (type.isAnyData != null) { - return type.isAnyData; - } - if (isVisited(type)) { - return isAnydata; - } - visited.add(type); - for (BField field : type.fields.values()) { - if (!visit(field.type)) { - type.isAnyData = false; - return false; - } - } - - if (!type.sealed && (type.restFieldType == null)) { - return false; - } - - type.isAnyData = type.sealed || visit(type.restFieldType); - return type.isAnyData; - } - - @Override - public Boolean visit(BType type) { - return switch (type.tag) { - case TypeTags.TABLE -> visit((BTableType) type); - case TypeTags.ANYDATA -> visit((BAnydataType) type); - case TypeTags.RECORD -> visit((BRecordType) type); - case TypeTags.ARRAY -> visit((BArrayType) type); - case TypeTags.UNION -> visit((BUnionType) type); - case TypeTags.TYPEDESC -> visit((BTypedescType) type); - case TypeTags.MAP -> visit((BMapType) type); - case TypeTags.FINITE -> visit((BFiniteType) type); - case TypeTags.TUPLE -> visit((BTupleType) type); - case TypeTags.INTERSECTION -> visit((BIntersectionType) type); - case TypeTags.TYPEREFDESC -> visit((BTypeReferenceType) type); - case TypeTags.SIGNED8_INT, - TypeTags.SIGNED16_INT, - TypeTags.SIGNED32_INT, - TypeTags.UNSIGNED8_INT, - TypeTags.UNSIGNED16_INT, - TypeTags.UNSIGNED32_INT -> visit((BIntSubType) type); - case TypeTags.XML_ELEMENT, - TypeTags.XML_PI, - TypeTags.XML_COMMENT, - TypeTags.XML_TEXT -> visit((BXMLSubType) type); - default -> isAnydata(type); - }; - } - - @Override - public Boolean visit(BFutureType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BHandleType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BIntSubType bHandleType) { - return true; - } - - @Override - public Boolean visit(BXMLSubType bxmlSubType) { - return true; - } -} diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsPureTypeUniqueVisitor.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsPureTypeUniqueVisitor.java deleted file mode 100644 index ca6528a68796..000000000000 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsPureTypeUniqueVisitor.java +++ /dev/null @@ -1,295 +0,0 @@ -package org.wso2.ballerinalang.compiler.semantics.analyzer; - -import org.wso2.ballerinalang.compiler.semantics.model.UniqueTypeVisitor; -import org.wso2.ballerinalang.compiler.semantics.model.types.BAnnotationType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BHandleType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BIntSubType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BIntersectionType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BParameterizedType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BRecordType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BStreamType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BStructureType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTableType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTupleType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTypeReferenceType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTypedescType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLSubType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; -import org.wso2.ballerinalang.compiler.util.TypeTags; - -import java.util.HashSet; - -/** - * IsPureTypeUniqueVisitor to check if a type is pure data. - * - * This is introduced to handle cyclic unions. - * @since slp4 - */ -public class IsPureTypeUniqueVisitor implements UniqueTypeVisitor { - - private final HashSet visited; - private final boolean isPureType; - - public IsPureTypeUniqueVisitor() { - visited = new HashSet<>(); - isPureType = true; - } - - public IsPureTypeUniqueVisitor(HashSet visited) { - this.visited = visited; - isPureType = true; - } - - private boolean isAnyData(BType type) { - return switch (Types.getImpliedType(type).tag) { - case TypeTags.INT, - TypeTags.BYTE, - TypeTags.FLOAT, - TypeTags.DECIMAL, - TypeTags.STRING, - TypeTags.BOOLEAN, - TypeTags.JSON, - TypeTags.XML, - TypeTags.XML_TEXT, - TypeTags.TABLE, - TypeTags.NIL, - TypeTags.NEVER, - TypeTags.ANYDATA, - TypeTags.SIGNED8_INT, - TypeTags.SIGNED16_INT, - TypeTags.SIGNED32_INT, - TypeTags.UNSIGNED8_INT, - TypeTags.UNSIGNED16_INT, - TypeTags.UNSIGNED32_INT, - TypeTags.CHAR_STRING -> true; - default -> false; - }; - } - - @Override - public boolean isVisited(BType type) { - return visited.contains(type); - } - - @Override - public void reset() { - visited.clear(); - } - - @Override - public Boolean visit(BAnnotationType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BArrayType type) { - if (isVisited(type)) { - return isPureType; - } - visited.add(type); - return visit(type.eType); - } - - @Override - public Boolean visit(BBuiltInRefType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BAnyType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BAnydataType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BErrorType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BFiniteType type) { - IsAnydataUniqueVisitor isAnydataUniqueVisitor = new IsAnydataUniqueVisitor(visited); - return isAnydataUniqueVisitor.visit(type); - } - - @Override - public Boolean visit(BInvokableType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BJSONType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BMapType type) { - if (isVisited(type)) { - return isPureType; - } - visited.add(type); - return visit(type.constraint); - } - - @Override - public Boolean visit(BStreamType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BTypedescType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BParameterizedType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BNeverType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BNilType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BNoType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BPackageType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BStructureType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BTupleType type) { - IsAnydataUniqueVisitor isAnydataUniqueVisitor = new IsAnydataUniqueVisitor(visited); - return isAnydataUniqueVisitor.visit(type); - } - - @Override - public Boolean visit(BUnionType type) { - if (type.isPureType != null) { - return type.isPureType; - } - if (isVisited(type)) { - return isPureType; - } - visited.add(type); - for (BType member : type.getMemberTypes()) { - if (!visit(member)) { - type.isPureType = false; - return false; - } - } - type.isPureType = isPureType; - return isPureType; - } - - @Override - public Boolean visit(BIntersectionType type) { - return visit(type.effectiveType); - } - - @Override - public Boolean visit(BTypeReferenceType type) { - return visit(type.referredType); - } - - @Override - public Boolean visit(BXMLType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BTableType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BRecordType type) { - - IsAnydataUniqueVisitor isAnydataUniqueVisitor = new IsAnydataUniqueVisitor(visited); - return isAnydataUniqueVisitor.visit(type); - } - - @Override - public Boolean visit(BObjectType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BType type) { - return switch (type.tag) { - case TypeTags.TABLE -> visit((BTableType) type); - case TypeTags.ANYDATA -> visit((BAnydataType) type); - case TypeTags.RECORD -> visit((BRecordType) type); - case TypeTags.ARRAY -> visit((BArrayType) type); - case TypeTags.UNION -> visit((BUnionType) type); - case TypeTags.TYPEDESC -> visit((BTypedescType) type); - case TypeTags.MAP -> visit((BMapType) type); - case TypeTags.FINITE -> visit((BFiniteType) type); - case TypeTags.TUPLE -> visit((BTupleType) type); - case TypeTags.INTERSECTION -> visit((BIntersectionType) type); - case TypeTags.TYPEREFDESC -> visit((BTypeReferenceType) type); - case TypeTags.SIGNED8_INT, - TypeTags.SIGNED16_INT, - TypeTags.SIGNED32_INT, - TypeTags.UNSIGNED8_INT, - TypeTags.UNSIGNED16_INT, - TypeTags.UNSIGNED32_INT -> visit((BIntSubType) type); - default -> isAnyData(type); - }; - } - - @Override - public Boolean visit(BFutureType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BHandleType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BIntSubType type) { - return true; - } - - @Override - public Boolean visit(BXMLSubType bxmlSubType) { - return isAnyData(bxmlSubType); - } -} diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsolationAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsolationAnalyzer.java index 298891dc56c8..182ec315066f 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsolationAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsolationAnalyzer.java @@ -50,7 +50,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; @@ -61,7 +60,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; @@ -359,7 +357,7 @@ public void visit(BLangPackage pkgNode) { } for (BLangClassDefinition classDefinition : pkgNode.classDefinitions) { - if (classDefinition.flagSet.contains(Flag.ANONYMOUS) && isIsolated(classDefinition.getBType().flags)) { + if (classDefinition.flagSet.contains(Flag.ANONYMOUS) && isIsolated(classDefinition.getBType().getFlags())) { // If this is a class definition for an object constructor expression, and the type is `isolated`, // that is due to the expected type being an `isolated` object. We now mark the class definition also // as `isolated`, to enforce the isolation validation. @@ -1516,7 +1514,7 @@ private boolean checkStrandAnnotationExists(List atta private boolean isValidIsolatedAsyncInvocation(BLangInvocation.BLangActionInvocation actionInvocation) { boolean isIsolatedStartAction = true; - if (!isIsolated(actionInvocation.symbol.type.flags)) { + if (!isIsolated(actionInvocation.symbol.type.getFlags())) { dlog.error(actionInvocation.name.pos, DiagnosticErrorCode.INVALID_ASYNC_INVOCATION_OF_NON_ISOLATED_FUNCTION_IN_ISOLATED_FUNCTION); isIsolatedStartAction = false; @@ -2114,7 +2112,7 @@ private void analyzeInvocation(BLangInvocation invocationExpr) { boolean expectsIsolation = inIsolatedFunction || recordFieldDefaultValue || objectFieldDefaultValueRequiringIsolation; - boolean isolatedFunctionCall = isIsolated(symbol.type.flags); + boolean isolatedFunctionCall = isIsolated(symbol.type.getFlags()); boolean inStartAction = invocationExpr.async && !invocationExpr.functionPointerInvocation; @@ -2168,7 +2166,7 @@ private void analyzeInvocation(BLangInvocation invocationExpr) { private void markFunctionDependentlyIsolatedOnStartAction(BInvokableSymbol enclInvokableSymbol, Set argsList, BInvokableSymbol symbol) { - boolean isIsolatedFunction = isIsolated(symbol.type.flags); + boolean isIsolatedFunction = isIsolated(symbol.type.getFlags()); if (!isIsolatedFunction && Symbols.isFlagOn(symbol.flags, Flags.PUBLIC)) { markDependsOnIsolationNonInferableConstructs(); return; @@ -2249,9 +2247,10 @@ private void analyzeAndSetArrowFuncFlagForIsolatedParamArg(BLangExpression arg) tsymbol.pkgID, null, tsymbol.owner, tsymbol.pos, tsymbol.origin); dupInvokableTypeSymbol.params = tsymbol.params == null ? null : new ArrayList<>(tsymbol.params); - BInvokableType dupInvokableType = new BInvokableType(invokableType.paramTypes, invokableType.restType, - invokableType.retType, dupInvokableTypeSymbol); - dupInvokableType.flags |= Flags.ISOLATED; + BInvokableType dupInvokableType = + new BInvokableType(symTable.typeEnv(), invokableType.paramTypes, invokableType.restType, + invokableType.retType, dupInvokableTypeSymbol); + dupInvokableType.addFlags(Flags.ISOLATED); dupInvokableTypeSymbol.type = dupInvokableType; argExpr.setBType(dupInvokableType); @@ -2472,16 +2471,16 @@ private BTupleType getRepresentativeTupleTypeForRemainingArgs(int paramCount, in members.add(new BTupleMember(eType, Symbols.createVarSymbolForTupleMember(eType))); } - if (arrayType.size > remReqArgCount) { - return new BTupleType(null, members, eType, 0); + if (arrayType.getSize() > remReqArgCount) { + return new BTupleType(symTable.typeEnv(), null, members, eType, 0); } - return new BTupleType(members); + return new BTupleType(symTable.typeEnv(), members); } private void analyzeRestArgsForRestParam(BLangInvocation invocationExpr, List restArgs, BInvokableSymbol symbol, boolean expectsIsolation) { - if (Symbols.isFlagOn(((BArrayType) symbol.restParam.type).eType.flags, Flags.ISOLATED)) { + if (Symbols.isFlagOn(((BArrayType) symbol.restParam.type).eType.getFlags(), Flags.ISOLATED)) { for (BLangExpression restArg : restArgs) { analyzeNode(restArg, env); } @@ -2543,7 +2542,7 @@ private void analyzeVarArgIsolatedness(BLangInvocation invocationExpr, BLangRest private void handleNonExplicitlyIsolatedArgForIsolatedParam(BLangInvocation invocationExpr, BLangExpression expr, boolean expectsIsolation, BType type, Location pos) { - if (Symbols.isFlagOn(type.flags, Flags.ISOLATED)) { + if (Symbols.isFlagOn(type.getFlags(), Flags.ISOLATED)) { return; } @@ -2579,7 +2578,7 @@ private boolean isInIsolatedFunction(BLangInvokableNode enclInvokable) { if (isNotInArrowFunctionBody(env)) { return false; } - return isIsolated(((BLangArrowFunction) env.enclEnv.node).funcType.flags); + return isIsolated(((BLangArrowFunction) env.enclEnv.node).funcType.getFlags()); } return isIsolated(enclInvokable.symbol.flags); @@ -3016,7 +3015,7 @@ private boolean isIsolatedExpression(BLangExpression expression, boolean logErro return isIsolatedExpression(argExprs.get(0), logErrors, visitRestOnError, nonIsolatedExpressions, inferring, publiclyExposedObjectTypes, classDefinitions, moduleLevelVariables, unresolvedSymbols); - } else if (isIsolated(invocationSymbol.type.flags) || + } else if (isIsolated(invocationSymbol.type.getFlags()) || (inferring && this.isolationInferenceInfoMap.containsKey(invocationSymbol) && inferFunctionIsolation(invocationSymbol, this.isolationInferenceInfoMap.get(invocationSymbol), publiclyExposedObjectTypes, @@ -3258,7 +3257,7 @@ private boolean isSelfOfObject(BLangSimpleVarRef varRefExpr) { } private boolean isSelfOfIsolatedObject(BLangSimpleVarRef varRefExpr) { - return isSelfOfObject(varRefExpr) && isIsolated(varRefExpr.symbol.type.flags); + return isSelfOfObject(varRefExpr) && isIsolated(varRefExpr.symbol.type.getFlags()); } private boolean hasRefDefinedOutsideLock(BLangExpression variableReference) { @@ -3351,7 +3350,7 @@ private boolean isInIsolatedObjectMethod(SymbolEnv env, boolean ignoreInit) { BType ownerType = Types.getImpliedType(enclFunction.symbol.owner.type); - return ownerType.tag == TypeTags.OBJECT && isIsolated(ownerType.flags); + return ownerType.tag == TypeTags.OBJECT && isIsolated(ownerType.getFlags()); } private BLangFunction getEnclNonAnonymousFunction(BLangFunction enclFunction) { @@ -3684,7 +3683,7 @@ private boolean isVarRequiringInference(BSymbol moduleLevelVarSymbol) { } BType type = moduleLevelVarSymbol.type; - return !types.isInherentlyImmutableType(type) && !Symbols.isFlagOn(type.flags, Flags.READONLY); + return !types.isInherentlyImmutableType(type) && !Symbols.isFlagOn(type.getFlags(), Flags.READONLY); } private void populateInferableClass(BLangClassDefinition classDefinition) { @@ -3694,7 +3693,7 @@ private void populateInferableClass(BLangClassDefinition classDefinition) { } BType type = classDefinition.getBType(); - if (Symbols.isFlagOn(type.flags, Flags.ISOLATED)) { + if (Symbols.isFlagOn(type.getFlags(), Flags.ISOLATED)) { return; } @@ -3850,7 +3849,7 @@ private void inferIsolation(Set moduleLevelVarSymbols, Set publi symbol.flags |= Flags.ISOLATED; if (!moduleLevelVarSymbols.contains(symbol)) { - symbol.type.flags |= Flags.ISOLATED; + symbol.type.addFlags(Flags.ISOLATED); } } continue; @@ -3873,7 +3872,7 @@ private void inferIsolation(Set moduleLevelVarSymbols, Set publi symbol.flags |= Flags.ISOLATED; if (isObjectType) { - symbol.type.flags |= Flags.ISOLATED; + symbol.type.addFlags(Flags.ISOLATED); } } } @@ -4184,7 +4183,7 @@ private void logServiceIsolationHints(List classDefinition } private void logServiceIsolationHints(BLangClassDefinition classDefinition) { - boolean isolatedService = isIsolated(classDefinition.getBType().flags); + boolean isolatedService = isIsolated(classDefinition.getBType().getFlags()); for (BLangFunction function : classDefinition.functions) { Set flagSet = function.flagSet; @@ -4193,7 +4192,7 @@ private void logServiceIsolationHints(BLangClassDefinition classDefinition) { continue; } - boolean isolatedMethod = isIsolated(function.getBType().flags); + boolean isolatedMethod = isIsolated(function.getBType().getFlags()); if (isolatedService && isolatedMethod) { continue; @@ -4307,7 +4306,7 @@ private class TemporaryArrowFunctionSymbol extends BInvokableSymbol { } } - private static class BPubliclyExposedInferableTypeCollector implements TypeVisitor { + private static class BPubliclyExposedInferableTypeCollector extends TypeVisitor { Set unresolvedTypes; Set exposedTypes; @@ -4338,10 +4337,6 @@ public void visit(BArrayType bArrayType) { visitType(bArrayType.eType); } - @Override - public void visit(BBuiltInRefType bBuiltInRefType) { - } - @Override public void visit(BAnyType bAnyType) { } @@ -4361,7 +4356,7 @@ public void visit(BFiniteType bFiniteType) { @Override public void visit(BInvokableType bInvokableType) { - if (Symbols.isFlagOn(bInvokableType.flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(bInvokableType.getFlags(), Flags.ANY_FUNCTION)) { return; } @@ -4406,7 +4401,7 @@ public void visit(BNeverType bNeverType) { } @Override - public void visit(BNilType bNilType) { + public void visitNilType(BType bType) { } @Override @@ -4479,10 +4474,6 @@ public void visit(BObjectType bObjectType) { } } - @Override - public void visit(BType bType) { - } - @Override public void visit(BFutureType bFutureType) { visitType(bFutureType.constraint); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java index dd92270be20c..73fea76ef339 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.semantics.analyzer; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.PredefinedType; import org.ballerinalang.model.clauses.OrderKeyNode; import org.ballerinalang.model.elements.PackageID; import org.ballerinalang.model.symbols.SymbolOrigin; @@ -195,7 +196,7 @@ public void checkQueryType(BLangQueryExpr queryExpr, TypeChecker.AnalyzerData da List collectionTypes = getCollectionTypes(clauses); BType completionType = getCompletionType(collectionTypes, Types.QueryConstructType.DEFAULT, data); if (completionType != null) { - queryType = BUnionType.create(null, queryType, completionType); + queryType = BUnionType.create(symTable.typeEnv(), null, queryType, completionType); } queryExpr.setDeterminedType(queryType); actualType = types.checkType(finalClauseExpr.pos, queryType, data.expType, @@ -302,15 +303,15 @@ public BType resolveQueryType(SymbolEnv env, BLangExpression selectExp, BType ta BType completionType = getCompletionType(collectionTypes, types.getQueryConstructType(queryExpr), data); if (queryExpr.isStream) { - return new BStreamType(TypeTags.STREAM, selectType, completionType, null); + return new BStreamType(symTable.typeEnv(), TypeTags.STREAM, selectType, completionType, null); } else if (queryExpr.isTable) { actualType = getQueryTableType(queryExpr, selectType, resolvedTypes.get(0), env); } else if (queryExpr.isMap) { BType mapConstraintType = getTypeOfTypeParameter(selectType, queryExpr.getSelectClause().expression.pos); if (mapConstraintType != symTable.semanticError) { - actualType = new BMapType(TypeTags.MAP, mapConstraintType, null); - if (Symbols.isFlagOn(resolvedTypes.get(0).flags, Flags.READONLY)) { + actualType = new BMapType(symTable.typeEnv(), TypeTags.MAP, mapConstraintType, null); + if (Symbols.isFlagOn(resolvedTypes.get(0).getFlags(), Flags.READONLY)) { actualType = ImmutableTypeCloner.getImmutableIntersectionType(null, types, actualType, env, symTable, anonymousModelHelper, names, null); } @@ -320,7 +321,8 @@ public BType resolveQueryType(SymbolEnv env, BLangExpression selectExp, BType ta } if (completionType != null && completionType.tag != TypeTags.NIL) { - return BUnionType.create(null, actualType, types.getSafeType(completionType, true, false)); + return BUnionType.create(symTable.typeEnv(), null, actualType, + types.getSafeType(completionType, true, false)); } else { return actualType; } @@ -349,7 +351,7 @@ void solveSelectTypeAndResolveType(BLangQueryExpr queryExpr, BLangExpression sel errorTypes.add(elementType); continue; } - BType queryResultType = new BArrayType(selectType); + BType queryResultType = new BArrayType(symTable.typeEnv(), selectType); resolvedType = getResolvedType(queryResultType, type, isReadonly, env); break; case TypeTags.TABLE: @@ -380,7 +382,7 @@ void solveSelectTypeAndResolveType(BLangQueryExpr queryExpr, BLangExpression sel BType memberType = ((BMapType) type).getConstraint(); BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(memberType); memberTypeList.add(new BTupleMember(memberType, varSymbol)); - BTupleType newExpType = new BTupleType(null, memberTypeList); + BTupleType newExpType = new BTupleType(symTable.typeEnv(), memberTypeList); selectType = checkExprSilent(selectExp, env, newExpType, data); if (selectType == symTable.semanticError) { errorTypes.add(newExpType); @@ -414,7 +416,7 @@ void solveSelectTypeAndResolveType(BLangQueryExpr queryExpr, BLangExpression sel case TypeTags.INTERSECTION: type = ((BIntersectionType) type).effectiveType; solveSelectTypeAndResolveType(queryExpr, selectExp, List.of(type), collectionType, selectTypes, - resolvedTypes, env, data, Symbols.isFlagOn(type.flags, Flags.READONLY)); + resolvedTypes, env, data, Symbols.isFlagOn(type.getFlags(), Flags.READONLY)); return; case TypeTags.NONE: default: @@ -472,7 +474,7 @@ void solveSelectTypeAndResolveType(BLangQueryExpr queryExpr, BLangExpression sel BType actualQueryType = silentTypeCheckExpr(queryExpr, symTable.noType, data); if (actualQueryType != symTable.semanticError) { types.checkType(queryExpr, actualQueryType, - BUnionType.create(null, new LinkedHashSet<>(expTypes))); + BUnionType.create(symTable.typeEnv(), null, new LinkedHashSet<>(expTypes))); errorTypes.forEach(expType -> { if (expType.tag == TypeTags.UNION) { checkExpr(nodeCloner.cloneNode(selectExp), env, expType, data); @@ -491,14 +493,14 @@ void solveSelectTypeAndResolveType(BLangQueryExpr queryExpr, BLangExpression sel } private BType getQueryTableType(BLangQueryExpr queryExpr, BType constraintType, BType resolvedType, SymbolEnv env) { - final BTableType tableType = new BTableType(TypeTags.TABLE, constraintType, null); + final BTableType tableType = new BTableType(symTable.typeEnv(), constraintType, null); if (!queryExpr.fieldNameIdentifierList.isEmpty()) { validateKeySpecifier(queryExpr.fieldNameIdentifierList, constraintType); markReadOnlyForConstraintType(constraintType); tableType.fieldNameList = queryExpr.fieldNameIdentifierList.stream() .map(identifier -> ((BLangIdentifier) identifier).value).toList(); } - if (Symbols.isFlagOn(resolvedType.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(resolvedType.getFlags(), Flags.READONLY)) { return ImmutableTypeCloner.getImmutableIntersectionType(null, types, tableType, env, symTable, anonymousModelHelper, names, null); } @@ -528,7 +530,7 @@ private void markReadOnlyForConstraintType(BType constraintType) { } } if (recordType.sealed) { - recordType.flags |= Flags.READONLY; + recordType.addFlags(Flags.READONLY); recordType.tsymbol.flags |= Flags.READONLY; } } @@ -549,7 +551,7 @@ private BType getTypeOfTypeParameter(BType selectType, Location pos) { } memberTypes.add(mapType); } - return new BUnionType(null, memberTypes, false, false); + return new BUnionType(types.typeEnv(), null, memberTypes, false); } else { return getQueryMapConstraintType(referredType, pos); } @@ -558,7 +560,7 @@ private BType getTypeOfTypeParameter(BType selectType, Location pos) { private BType getQueryMapConstraintType(BType type, Location pos) { if (type.tag == TypeTags.ARRAY) { BArrayType arrayType = (BArrayType) type; - if (arrayType.state != BArrayState.OPEN && arrayType.size == 2 && + if (arrayType.state != BArrayState.OPEN && arrayType.getSize() == 2 && types.isAssignable(arrayType.eType, symTable.stringType)) { return arrayType.eType; } @@ -632,7 +634,7 @@ private BType getCompletionType(List collectionTypes, Types.QueryConstruc if (completionTypes.size() == 1) { completionType = completionTypes.iterator().next(); } else { - completionType = BUnionType.create(null, completionTypes.toArray(new BType[0])); + completionType = BUnionType.create(symTable.typeEnv(), null, completionTypes.toArray(new BType[0])); } } return completionType; @@ -647,7 +649,7 @@ private List getCollectionTypes(List clauses) { private BType getResolvedType(BType initType, BType expType, boolean isReadonly, SymbolEnv env) { if (initType.tag != TypeTags.SEMANTIC_ERROR && (isReadonly || - Symbols.isFlagOn(expType.flags, Flags.READONLY))) { + Symbols.isFlagOn(expType.getFlags(), Flags.READONLY))) { return ImmutableTypeCloner.getImmutableIntersectionType(null, types, initType, env, symTable, anonymousModelHelper, names, null); } @@ -670,19 +672,19 @@ private BType getNonContextualQueryType(BType constraintType, BType basicType, L dlog.error(pos, INVALID_QUERY_CONSTRUCT_INFERRED_MAP); return symTable.semanticError; case TypeTags.XML: - if (types.isSubTypeOfBaseType(constraintType, symTable.xmlType.tag)) { + if (types.isSubTypeOfBaseType(constraintType, PredefinedType.XML)) { return new BXMLType(constraintType, null); } break; case TypeTags.STRING: - if (types.isSubTypeOfBaseType(constraintType, TypeTags.STRING)) { + if (types.isSubTypeOfBaseType(constraintType, PredefinedType.STRING)) { return symTable.stringType; } break; case TypeTags.ARRAY: case TypeTags.TUPLE: case TypeTags.OBJECT: - return new BArrayType(constraintType); + return new BArrayType(symTable.typeEnv(), constraintType); default: return symTable.semanticError; } @@ -743,24 +745,26 @@ private void handleInputClauseVariables(BLangInputClause bLangInputClause, Symbo BLangVariable variableNode = (BLangVariable) bLangInputClause.variableDefinitionNode.getVariable(); // Check whether the foreach node's variables are declared with var. + BType inputClauseVarType = bLangInputClause.varType; if (bLangInputClause.isDeclaredWithVar) { // If the foreach node's variables are declared with var, type is `varType`. - semanticAnalyzer.handleDeclaredVarInForeach(variableNode, bLangInputClause.varType, blockEnv); + semanticAnalyzer.handleDeclaredVarInForeach(variableNode, inputClauseVarType, blockEnv); return; } // If the type node is available, we get the type from it. BType typeNodeType = symResolver.resolveTypeNode(variableNode.typeNode, blockEnv); // Then we need to check whether the RHS type is assignable to LHS type. - if (types.isAssignable(bLangInputClause.varType, typeNodeType)) { - // If assignable, we set types to the variables. - semanticAnalyzer.handleDeclaredVarInForeach(variableNode, bLangInputClause.varType, blockEnv); - return; - } - // Log an error and define a symbol with the node's type to avoid undeclared symbol errors. - if (typeNodeType != symTable.semanticError) { + if (inputClauseVarType.tag != TypeTags.SEMANTIC_ERROR) { + if (types.isAssignable(inputClauseVarType, typeNodeType)) { + // If assignable, we set types to the variables. + semanticAnalyzer.handleDeclaredVarInForeach(variableNode, inputClauseVarType, blockEnv); + return; + } + // Log an error and define a symbol with the node's type to avoid undeclared symbol errors. dlog.error(variableNode.typeNode.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, - bLangInputClause.varType, typeNodeType); + inputClauseVarType, typeNodeType); } + semanticAnalyzer.handleDeclaredVarInForeach(variableNode, typeNodeType, blockEnv); } @@ -867,7 +871,8 @@ public void visit(BLangCollectClause collectClause, TypeChecker.AnalyzerData dat Name name = new Name(var); BSymbol originalSymbol = symResolver.lookupSymbolInMainSpace(collectEnv, name); BSequenceSymbol sequenceSymbol = new BSequenceSymbol(originalSymbol.flags, name, originalSymbol.pkgID, - new BSequenceType(originalSymbol.getType()), originalSymbol.owner, originalSymbol.pos); + new BSequenceType(symTable.typeEnv(), originalSymbol.getType()), originalSymbol.owner, + originalSymbol.pos); collectEnv.scope.define(name, sequenceSymbol); } } @@ -890,7 +895,7 @@ public void visit(BLangOnConflictClause onConflictClause, TypeChecker.AnalyzerDa if (data.queryData.completeEarlyErrorList != null) { BType possibleErrorType = type.tag == TypeTags.UNION ? types.getErrorType((BUnionType) type) : - types.getErrorType(BUnionType.create(null, type)); + types.getErrorType(BUnionType.create(symTable.typeEnv(), null, type)); data.queryData.completeEarlyErrorList.add(possibleErrorType); } } @@ -910,7 +915,7 @@ public void visit(BLangOrderByClause orderByClause, TypeChecker.AnalyzerData dat orderByClause.env = data.commonAnalyzerData.queryEnvs.peek(); for (OrderKeyNode orderKeyNode : orderByClause.getOrderKeyList()) { BType exprType = checkExpr((BLangExpression) orderKeyNode.getOrderKey(), orderByClause.env, data); - if (!types.isOrderedType(exprType, false)) { + if (exprType.tag != TypeTags.SEMANTIC_ERROR && !types.isOrderedType(exprType)) { dlog.error(((BLangOrderKey) orderKeyNode).expression.pos, DiagnosticErrorCode.ORDER_BY_NOT_SUPPORTED); } } @@ -945,7 +950,8 @@ public void visit(BLangGroupByClause groupByClause, TypeChecker.AnalyzerData dat Name name = new Name(var); BSymbol originalSymbol = symResolver.lookupSymbolInMainSpace(groupByEnv, name); BSequenceSymbol sequenceSymbol = new BSequenceSymbol(originalSymbol.flags, name, originalSymbol.pkgID, - new BSequenceType(originalSymbol.getType()), originalSymbol.owner, originalSymbol.pos); + new BSequenceType(symTable.typeEnv(), originalSymbol.getType()), originalSymbol.owner, + originalSymbol.pos); groupByEnv.scope.define(name, sequenceSymbol); } } @@ -1080,7 +1086,7 @@ public void visit(BLangCollectContextInvocation collectContextInvocation, TypeCh BLangInvocation invocation = collectContextInvocation.invocation; data.resultType = checkExpr(invocation, data.env, data); if (isNilReturnInvocationInCollectClause(invocation, data)) { - data.resultType = BUnionType.create(null, data.resultType, symTable.nilType); + data.resultType = BUnionType.create(symTable.typeEnv(), null, data.resultType, symTable.nilType); } collectContextInvocation.setBType(data.resultType); } @@ -1149,7 +1155,8 @@ public void visit(BLangSimpleVarRef varRefExpr, TypeChecker.AnalyzerData data) { dlog.error(varRefExpr.pos, DiagnosticErrorCode.VARIABLE_IS_SEQUENCED_MORE_THAN_ONCE, varName); } } else if ((symbol.tag & SymTag.TYPE_DEF) == SymTag.TYPE_DEF) { - actualType = symbol.type.tag == TypeTags.TYPEDESC ? symbol.type : new BTypedescType(symbol.type, null); + actualType = symbol.type.tag == TypeTags.TYPEDESC ? symbol.type : + new BTypedescType(symTable.typeEnv(), symbol.type, null); varRefExpr.symbol = symbol; } else if ((symbol.tag & SymTag.CONSTANT) == SymTag.CONSTANT) { BConstantSymbol constSymbol = (BConstantSymbol) symbol; @@ -1199,7 +1206,8 @@ public void visit(BLangListConstructorExpr listConstructor, TypeChecker.Analyzer checkExpr(expr, data.env, symTable.noType, data); data.queryData.withinSequenceContext = false; data.resultType = types.checkType(listConstructor.pos, - new BTupleType(null, new ArrayList<>(0), ((BSequenceType) type).elementType, 0), + new BTupleType(symTable.typeEnv(), null, new ArrayList<>(0), + ((BSequenceType) type).elementType, 0), expType, DiagnosticErrorCode.INCOMPATIBLE_TYPES); listConstructor.setBType(data.resultType); return; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemTypeHelper.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemTypeHelper.java new file mode 100644 index 000000000000..c1339a43921e --- /dev/null +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemTypeHelper.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.ballerinalang.compiler.semantics.analyzer; + +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Context; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.StringSubtype; +import org.ballerinalang.model.types.TypeKind; +import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; +import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; +import org.wso2.ballerinalang.compiler.semantics.model.types.BType; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; + +import java.math.BigDecimal; +import java.util.LinkedHashSet; +import java.util.Optional; +import java.util.Set; + +import static io.ballerina.types.BasicTypeCode.BT_BOOLEAN; +import static io.ballerina.types.BasicTypeCode.BT_DECIMAL; +import static io.ballerina.types.BasicTypeCode.BT_FLOAT; +import static io.ballerina.types.BasicTypeCode.BT_INT; +import static io.ballerina.types.BasicTypeCode.BT_STRING; +import static io.ballerina.types.Core.getComplexSubtypeData; +import static io.ballerina.types.Core.widenToBasicTypes; + +/** + * Contains helper methods related to sem-types. + * + * @since 2201.9.0 + */ +public final class SemTypeHelper { + + private SemTypeHelper() { + } + + public static SemType resolveSingletonType(BLangLiteral literal) { + return resolveSingletonType(literal.value, literal.getDeterminedType().getKind()); + } + + public static SemType resolveSingletonType(Object value, TypeKind targetTypeKind) { + switch (targetTypeKind) { + case FLOAT: + double doubleVal; + if (value instanceof Long) { + doubleVal = ((Long) value).doubleValue(); + } else if (value instanceof Double) { + doubleVal = (double) value; + } else { + // literal value will be a string if it wasn't within the bounds of what is supported by Java Long + // or Double when it was parsed in BLangNodeBuilder. + try { + doubleVal = Double.parseDouble((String) value); + } catch (NumberFormatException e) { + // We reach here when there is a syntax error. Mock the flow with default float value. + return FloatSubtype.floatConst(0); + } + } + return SemTypes.floatConst(doubleVal); + case INT: + case BYTE: + return SemTypes.intConst(((Number) value).longValue()); + case STRING: + return SemTypes.stringConst((String) value); + case BOOLEAN: + return SemTypes.booleanConst((Boolean) value); + case DECIMAL: + return SemTypes.decimalConst((String) value); + case NIL: + return PredefinedType.NIL; + case OTHER: + // We reach here when there is a semantic error + return PredefinedType.NEVER; + default: + throw new UnsupportedOperationException("Finite type not implemented for: " + targetTypeKind); + } + } + + public static boolean isSubtypeSimple(BType bt, BasicTypeBitSet bbs) { + return SemTypes.isSubtypeSimple(bt.semType(), bbs); + } + + public static boolean isSubtypeSimpleNotNever(BType bt, BasicTypeBitSet bbs) { + return SemTypes.isSubtypeSimpleNotNever(bt.semType(), bbs); + } + + public static boolean containsBasicType(BType bt, BasicTypeBitSet bbs) { + return SemTypes.containsBasicType(bt.semType(), bbs); + } + + public static boolean containsType(Context ctx, BType bt, SemType bbs) { + return SemTypes.containsType(ctx, bt.semType(), bbs); + } + + public static boolean isSubtype(Context context, BType bt, SemType st) { + return SemTypes.isSubtype(context, bt.semType(), st); + } + + public static boolean isSimpleOrString(TypeKind kind) { + switch (kind) { + case NIL: + case BOOLEAN: + case INT: + case BYTE: + case FLOAT: + case DECIMAL: + case STRING: + case FINITE: + return true; + default: + return false; + } + } + + /** + * Returns the basic type of singleton. + *

+ * This will replace the existing finiteType.getValueSpace().iterator().next().getBType() calls + * + * @param t SemType component of BFiniteType + */ + public static Optional singleShapeBroadType(SemType t, SymbolTable symTable) { + if (PredefinedType.NIL.equals(t)) { + return Optional.of(symTable.nilType); + } else if (t instanceof BasicTypeBitSet) { + return Optional.empty(); + } else if (SemTypes.isSubtypeSimple(t, PredefinedType.INT)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_INT); + Optional value = IntSubtype.intSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(symTable.intType); + } else if (SemTypes.isSubtypeSimple(t, PredefinedType.FLOAT)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_FLOAT); + Optional value = FloatSubtype.floatSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(symTable.floatType); + } else if (SemTypes.isSubtypeSimple(t, PredefinedType.STRING)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_STRING); + Optional value = StringSubtype.stringSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(symTable.stringType); + } else if (SemTypes.isSubtypeSimple(t, PredefinedType.BOOLEAN)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_BOOLEAN); + Optional value = BooleanSubtype.booleanSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(symTable.booleanType); + } else if (SemTypes.isSubtypeSimple(t, PredefinedType.DECIMAL)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_DECIMAL); + Optional value = DecimalSubtype.decimalSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(symTable.decimalType); + } + return Optional.empty(); + } + + /** + * Returns the basic types of singleton/union of singleton. + *

+ * This will replace the existing finiteType.getValueSpace().iterator() calls + * + * @param t SemType component of BFiniteType + */ + public static Set broadTypes(SemType t, SymbolTable symTable) { // Equivalent to getValueTypes() + Set types = new LinkedHashSet<>(7); + BasicTypeBitSet basicTypeBitSet = widenToBasicTypes(t); + if ((basicTypeBitSet.bitset & PredefinedType.NIL.bitset) != 0) { + types.add(symTable.nilType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.BOOLEAN.bitset) != 0) { + types.add(symTable.booleanType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.INT.bitset) != 0) { + types.add(symTable.intType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.FLOAT.bitset) != 0) { + types.add(symTable.floatType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.DECIMAL.bitset) != 0) { + types.add(symTable.decimalType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.STRING.bitset) != 0) { + types.add(symTable.stringType); + } + + return types; + } + + public static Set broadTypes(BFiniteType finiteType, SymbolTable symTable) { + Set types = new LinkedHashSet<>(7); + for (SemNamedType semNamedType: finiteType.valueSpace) { + SemType t = semNamedType.semType(); + BasicTypeBitSet basicTypeBitSet = widenToBasicTypes(t); + if ((basicTypeBitSet.bitset & PredefinedType.NIL.bitset) != 0) { + types.add(symTable.nilType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.BOOLEAN.bitset) != 0) { + types.add(symTable.booleanType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.INT.bitset) != 0) { + types.add(symTable.intType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.FLOAT.bitset) != 0) { + types.add(symTable.floatType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.DECIMAL.bitset) != 0) { + types.add(symTable.decimalType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.STRING.bitset) != 0) { + types.add(symTable.stringType); + } + } + return types; + } + + /** + * Counts number of bits set in bitset. + * Note: this is similar to lib:bitCount() in nBallerina + * This is the Brian Kernighan algorithm. + * This won't work if bits is less than 0. + * + * @param bitset bitset for bits to be counted + * @return the count + */ + public static int bitCount(int bitset) { + int n = 0; + int v = bitset; + while (v != 0) { + v &= v - 1; + n += 1; + } + return n; + } +} diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java index 5b8566b99307..140bb9aa8aa0 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java @@ -20,6 +20,8 @@ import io.ballerina.compiler.api.symbols.DiagnosticState; import io.ballerina.projects.ModuleDescriptor; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; import org.ballerinalang.compiler.CompilerPhase; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.AttachPoint; @@ -270,6 +272,7 @@ public class SemanticAnalyzer extends SimpleBLangNodeAnalyzer anonTypeNameSuffixes; private final CompilerContext compilerContext; + private final Env typeEnv; public static SemanticAnalyzer getInstance(CompilerContext context) { SemanticAnalyzer semAnalyzer = context.get(SYMBOL_ANALYZER_KEY); @@ -297,6 +300,7 @@ private SemanticAnalyzer(CompilerContext context) { this.anonModelHelper = BLangAnonymousModelHelper.getInstance(context); this.unifier = new Unifier(); this.anonTypeNameSuffixes = new ArrayDeque<>(); + this.typeEnv = types.typeEnv(); } public BLangPackage analyze(BLangPackage pkgNode) { @@ -562,8 +566,8 @@ public void visit(BLangFunction funcNode, AnalyzerData data) { validateIsolatedParamUsage(inIsolatedFunction, restParam, true, data); } - if (hasReturnType && Symbols.isFlagOn(returnTypeNode.getBType().flags, Flags.PARAMETERIZED)) { - unifier.validate(returnTypeNode.getBType(), funcNode, symTable, currentEnv, types, dlog); + if (hasReturnType && Symbols.isFlagOn(returnTypeNode.getBType().getFlags(), Flags.PARAMETERIZED)) { + unifier.validate(typeEnv, returnTypeNode.getBType(), funcNode, symTable, currentEnv, types, dlog); } validateObjectAttachedFunction(funcNode, data); @@ -797,8 +801,6 @@ public void visit(BLangTypeConversionExpr conversionExpr, AnalyzerData data) { @Override public void visit(BLangFiniteTypeNode finiteTypeNode, AnalyzerData data) { - boolean foundUnaryExpr = false; - boolean isErroredExprInFiniteType = false; NodeKind valueKind; BLangExpression value; @@ -807,11 +809,6 @@ public void visit(BLangFiniteTypeNode finiteTypeNode, AnalyzerData data) { valueKind = value.getKind(); if (valueKind == NodeKind.UNARY_EXPR) { - foundUnaryExpr = true; - BType resultType = typeChecker.checkExpr(value, data.env, symTable.noType, data.prevEnvs); - if (resultType == symTable.semanticError) { - isErroredExprInFiniteType = true; - } // Replacing unary expression with numeric literal type for + and - numeric values BLangNumericLiteral newNumericLiteral = Types.constructNumericLiteralFromUnaryExpr((BLangUnaryExpr) value); @@ -824,10 +821,6 @@ public void visit(BLangFiniteTypeNode finiteTypeNode, AnalyzerData data) { analyzeNode(value, data); } } - - if (foundUnaryExpr && isErroredExprInFiniteType) { - finiteTypeNode.setBType(symTable.semanticError); - } } @Override @@ -953,7 +946,7 @@ public void visit(BLangRecordTypeNode recordTypeNode, AnalyzerData data) { if (isRecordType && allReadOnlyFields) { type.tsymbol.flags |= Flags.READONLY; - type.flags |= Flags.READONLY; + type.addFlags(Flags.READONLY); } validateDefaultable(recordTypeNode); @@ -1160,10 +1153,14 @@ public void visit(BLangSimpleVariable varNode, AnalyzerData data) { validateWorkerAnnAttachments(varNode.expr, data); + if (varNode.typeNode != null) { + analyzeNode(varNode.typeNode, data); + } handleWildCardBindingVariable(varNode, currentEnv); BType lhsType = varNode.symbol.type; varNode.setBType(lhsType); + // Configurable variable type must be a subtype of anydata. if (configurable && varNode.typeNode != null && lhsType.tag != TypeTags.SEMANTIC_ERROR) { if (!types.isAssignable(lhsType, symTable.anydataType)) { @@ -1182,10 +1179,6 @@ public void visit(BLangSimpleVariable varNode, AnalyzerData data) { } } - if (varNode.typeNode != null) { - analyzeNode(varNode.typeNode, data); - } - // Analyze the init expression BLangExpression rhsExpr = varNode.expr; if (rhsExpr == null) { @@ -1207,7 +1200,7 @@ public void visit(BLangSimpleVariable varNode, AnalyzerData data) { if (isListenerDecl) { BType rhsType = typeChecker.checkExpr(rhsExpr, varInitEnv, - BUnionType.create(null, lhsType, symTable.errorType), data.prevEnvs, + BUnionType.create(typeEnv, null, lhsType, symTable.errorType), data.prevEnvs, data.commonAnalyzerData); validateListenerCompatibility(varNode, rhsType); } else { @@ -1257,7 +1250,7 @@ private Map getModuleKeys(Set configVars, String } private void validateMapConfigVariable(String configKey, BVarSymbol variable, Map configKeys) { - if (configKeys.containsKey(configKey) && types.isSubTypeOfMapping(variable.type)) { + if (configKeys.containsKey(configKey) && types.isSubTypeOfMapping(variable.type.semType())) { dlog.error(variable.pos, DiagnosticErrorCode.CONFIGURABLE_VARIABLE_MODULE_AMBIGUITY, variable.name.value, configKeys.get(configKey)); } @@ -1347,7 +1340,7 @@ private boolean isSupportedConfigType(BType type, List errors, String va case ANYDATA: break; case FINITE: - return types.isAnydata(type); + return types.isAnydata(type.semType()); case NIL: return !isRequired; case ARRAY: @@ -1424,21 +1417,9 @@ private boolean isSupportedConfigType(BType type, List errors, String va } private boolean isNilableDefaultField(BField field, BType fieldType) { - if (!Symbols.isFlagOn(field.symbol.flags, Flags.REQUIRED) && !Symbols.isFlagOn(field.symbol.flags, - Flags.OPTIONAL)) { - if (fieldType.tag == TypeTags.NIL) { - return true; - } - if (fieldType.tag == TypeTags.UNION) { - BUnionType unionType = (BUnionType) fieldType; - for (BType memberType : unionType.getMemberTypes()) { - if (memberType.tag == TypeTags.NIL) { - return true; - } - } - } - } - return false; + return !Symbols.isFlagOn(field.symbol.flags, Flags.REQUIRED) && + !Symbols.isFlagOn(field.symbol.flags, Flags.OPTIONAL) && + fieldType.isNullable(); } private void validateListenerCompatibility(BLangSimpleVariable varNode, BType rhsType) { @@ -1705,16 +1686,16 @@ private BType resolveTupleType(BLangTupleVariable varNode) { List members = new ArrayList<>(varNode.memberVariables.size()); for (BLangVariable memberVariable : varNode.memberVariables) { BType type = getTupleMemberType(memberVariable); - BVarSymbol varSymbol = new BVarSymbol(type.flags, null, null, type, null, null, null); + BVarSymbol varSymbol = new BVarSymbol(type.getFlags(), null, null, type, null, null, null); members.add(new BTupleMember(type, varSymbol)); } BLangVariable restVariable = varNode.restVariable; if (restVariable == null) { - return new BTupleType(members); + return new BTupleType(typeEnv, members); } - return new BTupleType(null, members, getTupleMemberType(restVariable), 0); + return new BTupleType(typeEnv, null, members, getTupleMemberType(restVariable), 0); } private BType getTupleMemberType(BLangVariable memberVariable) { @@ -1751,7 +1732,7 @@ public void visit(BLangErrorVariable varNode, AnalyzerData data) { // reason must be a const of subtype of string. // then we match the error with this specific reason. if (!varNode.reasonVarPrefixAvailable && varNode.getBType() == null) { - BErrorType errorType = new BErrorType(varNode.getBType().tsymbol, null); + BErrorType errorType = new BErrorType(typeEnv, varNode.getBType().tsymbol, null); if (Types.getImpliedType(varNode.getBType()).tag == TypeTags.UNION) { Set members = types.expandAndGetMemberTypesRecursive(varNode.getBType()); @@ -2030,7 +2011,7 @@ private BType getListenerType(BType bType) { } else if (compatibleTypes.size() == 1) { return compatibleTypes.iterator().next(); } else { - return BUnionType.create(null, compatibleTypes); + return BUnionType.create(typeEnv, null, compatibleTypes); } } @@ -2077,7 +2058,7 @@ void handleDeclaredVarInForeach(BLangVariable variable, BType rhsType, SymbolEnv BLangTupleVariable tupleVariable = (BLangTupleVariable) variable; if ((TypeTags.TUPLE != referredRhsType.tag && TypeTags.ARRAY != referredRhsType.tag && TypeTags.UNION != referredRhsType.tag) || - (variable.isDeclaredWithVar && !types.isSubTypeOfBaseType(rhsType, TypeTags.TUPLE))) { + (variable.isDeclaredWithVar && !types.isSubTypeOfBaseType(rhsType, PredefinedType.LIST))) { dlog.error(variable.pos, DiagnosticErrorCode.INVALID_LIST_BINDING_PATTERN_INFERENCE, rhsType); recursivelyDefineVariables(tupleVariable, blockEnv); return; @@ -2529,7 +2510,7 @@ private void checkArrayVarRefEquivalency(Location pos, BLangTupleVarRef target, BArrayType arraySource = (BArrayType) source; // For unsealed - if (arraySource.size < target.expressions.size() && arraySource.state != BArrayState.OPEN) { + if (arraySource.getSize() < target.expressions.size() && arraySource.state != BArrayState.OPEN) { dlog.error(rhsPos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, target.getBType(), arraySource); } @@ -2699,7 +2680,7 @@ private void checkErrorVarRefEquivalency(BLangErrorVarRef lhsRef, BType rhsType, if (lhsRef.restVar != null && !isIgnoreVar(lhsRef)) { setTypeOfVarRefInErrorBindingAssignment(lhsRef.restVar, data); checkInvalidTypeDef(lhsRef.restVar); - BMapType expRestType = new BMapType(TypeTags.MAP, wideType, null); + BMapType expRestType = new BMapType(typeEnv, TypeTags.MAP, wideType, null); BType restVarType = Types.getImpliedType(lhsRef.restVar.getBType()); if (restVarType.tag != TypeTags.MAP || !types.isAssignable(wideType, ((BMapType) restVarType).constraint)) { dlog.error(lhsRef.restVar.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, lhsRef.restVar.getBType(), @@ -2714,7 +2695,7 @@ private void checkErrorVarRefEquivalency(BLangErrorVarRef lhsRef, BType rhsType, private BType interpolateWideType(BRecordType rhsDetailType, List detailType) { Set extractedKeys = detailType.stream().map(detail -> detail.name.value).collect(Collectors.toSet()); - BUnionType wideType = BUnionType.create(null); + BUnionType wideType = BUnionType.create(typeEnv, null); for (BField field : rhsDetailType.fields.values()) { // avoid fields extracted from binding pattern if (!extractedKeys.contains(field.name.value)) { @@ -2835,7 +2816,8 @@ public void visit(BLangIf ifNode, AnalyzerData data) { if (existingNarrowedTypeInfo.containsKey(key)) { BType.NarrowedTypes existingNarrowTypes = existingNarrowedTypeInfo.get(key); BUnionType unionType = - BUnionType.create(null, existingNarrowTypes.trueType, existingNarrowTypes.falseType); + BUnionType.create(typeEnv, null, existingNarrowTypes.trueType, + existingNarrowTypes.falseType); BType.NarrowedTypes newPair = new BType.NarrowedTypes(existingNarrowTypes.trueType, unionType); falseTypesOfNarrowedTypes.put(key, newPair); } @@ -3005,10 +2987,10 @@ private void evaluateMatchPatternsTypeAccordingToMatchGuard(BLangMatchPattern ma for (BLangMatchPattern memberMatchPattern : listMatchPattern.matchPatterns) { evaluateMatchPatternsTypeAccordingToMatchGuard(memberMatchPattern, env); BType type = memberMatchPattern.getBType(); - BVarSymbol varSymbol = new BVarSymbol(type.flags, null, null, type, null, null, null); + BVarSymbol varSymbol = new BVarSymbol(type.getFlags(), null, null, type, null, null, null); members.add(new BTupleMember(type, varSymbol)); } - BTupleType matchPatternType = new BTupleType(members); + BTupleType matchPatternType = new BTupleType(typeEnv, members); if (listMatchPattern.restMatchPattern != null) { evaluateMatchPatternsTypeAccordingToMatchGuard(listMatchPattern.restMatchPattern, env); @@ -3059,7 +3041,7 @@ public void visit(BLangMappingMatchPattern mappingMatchPattern, AnalyzerData dat fields.put(fieldName.getValue(), field); mappingMatchPattern.declaredVars.putAll(fieldMatchPattern.declaredVars); } - BRecordType recordVarType = new BRecordType(recordSymbol); + BRecordType recordVarType = new BRecordType(typeEnv, recordSymbol); recordVarType.fields = fields; recordVarType.restFieldType = symTable.anyOrErrorType; if (mappingMatchPattern.restMatchPattern != null) { @@ -3067,7 +3049,7 @@ public void visit(BLangMappingMatchPattern mappingMatchPattern, AnalyzerData dat symbolEnter.createAnonRecordSymbol(currentEnv, mappingMatchPattern.pos); BLangRestMatchPattern restMatchPattern = mappingMatchPattern.restMatchPattern; BType restType = restMatchPattern.getBType(); - BRecordType matchPatternRecType = new BRecordType(matchPattenRecordSym); + BRecordType matchPatternRecType = new BRecordType(typeEnv, matchPattenRecordSym); matchPatternRecType.restFieldType = restType != null ? restType : symTable.anyOrErrorType; recordVarType.restFieldType = matchPatternRecType.restFieldType; restMatchPattern.setBType(matchPatternRecType); @@ -3119,7 +3101,7 @@ private void assignTypesToMemberPatterns(BLangMatchPattern matchPattern, BType b BType type = arrayType.eType; BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(type); BTupleType restTupleType = createTupleForClosedArray( - arrayType.size - listMatchPattern.matchPatterns.size(), + arrayType.getSize() - listMatchPattern.matchPatterns.size(), new BTupleMember(type, varSymbol)); listMatchPattern.restMatchPattern.setBType(restTupleType); BVarSymbol restMatchPatternSymbol = listMatchPattern.restMatchPattern.declaredVars @@ -3143,11 +3125,11 @@ private void assignTypesToMemberPatterns(BLangMatchPattern matchPattern, BType b for (int i = 0; i < matchPatterns.size(); i++) { assignTypesToMemberPatterns(matchPatterns.get(i), members.get(i).type, data); BType type = matchPatterns.get(i).getBType(); - BVarSymbol varSymbol = new BVarSymbol(type.flags, null, null, + BVarSymbol varSymbol = new BVarSymbol(type.getFlags(), null, null, type, null, null, null); newMembers.add(new BTupleMember(type, varSymbol)); } - BTupleType tupleType = new BTupleType(newMembers); + BTupleType tupleType = new BTupleType(typeEnv, newMembers); if (listMatchPattern.restMatchPattern == null) { listMatchPattern.setBType(tupleType); @@ -3200,7 +3182,7 @@ private void assignTypesToMemberPatterns(BLangMatchPattern matchPattern, BType b private BTupleType createTupleForClosedArray(int noOfElements, BTupleMember elementType) { List members = Collections.nCopies(noOfElements, elementType); - return new BTupleType(members); + return new BTupleType(typeEnv, members); } private BType createTypeForTupleRestType(int startIndex, List members, BType patternRestType) { @@ -3209,16 +3191,16 @@ private BType createTypeForTupleRestType(int startIndex, List memb remainingMembers.add(members.get(i)); } if (!remainingMembers.isEmpty()) { - BTupleType restTupleType = new BTupleType(remainingMembers); + BTupleType restTupleType = new BTupleType(typeEnv, remainingMembers); if (patternRestType != null) { restTupleType.restType = patternRestType; } return restTupleType; } else { if (patternRestType != null) { - return new BArrayType(patternRestType); + return new BArrayType(typeEnv, patternRestType); } else { - return new BArrayType(symTable.anyOrErrorType); + return new BArrayType(typeEnv, symTable.anyOrErrorType); } } } @@ -3318,18 +3300,18 @@ public void visit(BLangListBindingPattern listBindingPattern, AnalyzerData data) for (BLangBindingPattern bindingPattern : listBindingPattern.bindingPatterns) { analyzeNode(bindingPattern, data); BType type = bindingPattern.getBType(); - BVarSymbol varSymbol = new BVarSymbol(type.flags, null, null, + BVarSymbol varSymbol = new BVarSymbol(type.getFlags(), null, null, type, null, null, null); listMembers.add(new BTupleMember(type, varSymbol)); listBindingPattern.declaredVars.putAll(bindingPattern.declaredVars); } - BTupleType listBindingPatternType = new BTupleType(listMembers); + BTupleType listBindingPatternType = new BTupleType(typeEnv, listMembers); if (listBindingPattern.restBindingPattern != null) { BLangRestBindingPattern restBindingPattern = listBindingPattern.restBindingPattern; BType restBindingPatternType = restBindingPattern.getBType(); BType restType = restBindingPatternType != null ? restBindingPatternType : symTable.anyOrErrorType; - restBindingPattern.setBType(new BArrayType(restType)); + restBindingPattern.setBType(new BArrayType(typeEnv, restType)); restBindingPattern.accept(this, data); listBindingPattern.declaredVars.put(restBindingPattern.variableName.value, restBindingPattern.symbol); listBindingPatternType.restType = restType; @@ -3426,7 +3408,7 @@ public void visit(BLangErrorFieldBindingPatterns errorFieldBindingPatterns, Anal } if (errorFieldBindingPatterns.restBindingPattern != null) { errorFieldBindingPatterns.restBindingPattern.setBType( - new BMapType(TypeTags.MAP, symTable.anydataType, null)); + new BMapType(typeEnv, TypeTags.MAP, symTable.anydataType, null)); analyzeNode(errorFieldBindingPatterns.restBindingPattern, data); errorFieldBindingPatterns.declaredVars.putAll(errorFieldBindingPatterns.restBindingPattern.declaredVars); } @@ -3540,7 +3522,8 @@ public void visit(BLangErrorFieldMatchPatterns errorFieldMatchPatterns, Analyzer errorFieldMatchPatterns.declaredVars.putAll(namedArgMatchPattern.declaredVars); } if (errorFieldMatchPatterns.restMatchPattern != null) { - errorFieldMatchPatterns.restMatchPattern.setBType(new BMapType(TypeTags.MAP, symTable.anydataType, null)); + errorFieldMatchPatterns.restMatchPattern.setBType(new BMapType(typeEnv, TypeTags.MAP, + symTable.anydataType, null)); analyzeNode(errorFieldMatchPatterns.restMatchPattern, data); errorFieldMatchPatterns.declaredVars.putAll(errorFieldMatchPatterns.restMatchPattern.declaredVars); } @@ -3595,7 +3578,7 @@ public void visit(BLangMappingBindingPattern mappingBindingPattern, AnalyzerData fields.put(fieldName.getValue(), field); mappingBindingPattern.declaredVars.putAll(fieldBindingPattern.declaredVars); } - BRecordType recordVarType = new BRecordType(recordSymbol); + BRecordType recordVarType = new BRecordType(typeEnv, recordSymbol); recordVarType.fields = fields; recordVarType.restFieldType = symTable.anyOrErrorType; if (mappingBindingPattern.restBindingPattern != null) { @@ -3603,7 +3586,7 @@ public void visit(BLangMappingBindingPattern mappingBindingPattern, AnalyzerData BType restType = restBindingPattern.getBType(); BRecordTypeSymbol matchPattenRecordSym = symbolEnter.createAnonRecordSymbol(currentEnv, restBindingPattern.pos); - BRecordType matchPatternRecType = new BRecordType(matchPattenRecordSym); + BRecordType matchPatternRecType = new BRecordType(typeEnv, matchPattenRecordSym); matchPatternRecType.restFieldType = restType != null ? restType : symTable.anyOrErrorType; recordVarType.restFieldType = matchPatternRecType.restFieldType; restBindingPattern.setBType(matchPatternRecType); @@ -3657,18 +3640,18 @@ public void visit(BLangListMatchPattern listMatchPattern, AnalyzerData data) { for (BLangMatchPattern memberMatchPattern : listMatchPattern.matchPatterns) { memberMatchPattern.accept(this, data); BType type = memberMatchPattern.getBType(); - BVarSymbol varSymbol = new BVarSymbol(type.flags, null, null, type, null, null, null); + BVarSymbol varSymbol = new BVarSymbol(type.getFlags(), null, null, type, null, null, null); members.add(new BTupleMember(type, varSymbol)); checkForSimilarVars(listMatchPattern.declaredVars, memberMatchPattern.declaredVars, memberMatchPattern.pos); listMatchPattern.declaredVars.putAll(memberMatchPattern.declaredVars); } - BTupleType matchPatternType = new BTupleType(members); + BTupleType matchPatternType = new BTupleType(typeEnv, members); if (listMatchPattern.getRestMatchPattern() != null) { BLangRestMatchPattern restMatchPattern = (BLangRestMatchPattern) listMatchPattern.getRestMatchPattern(); BType restBindingPatternType = restMatchPattern.getBType(); BType restType = restBindingPatternType != null ? restBindingPatternType : symTable.anyOrErrorType; - restMatchPattern.setBType(new BArrayType(restType)); + restMatchPattern.setBType(new BArrayType(typeEnv, restType)); restMatchPattern.accept(this, data); checkForSimilarVars(listMatchPattern.declaredVars, restMatchPattern.declaredVars, restMatchPattern.pos); listMatchPattern.declaredVars.put(restMatchPattern.variableName.value, restMatchPattern.symbol); @@ -3725,7 +3708,7 @@ private void assignTypesToMemberPatterns(BLangBindingPattern bindingPattern, BTy BType type = arrayType.eType; BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(type); BTupleType restTupleType = createTupleForClosedArray( - arrayType.size - listBindingPattern.bindingPatterns.size(), + arrayType.getSize() - listBindingPattern.bindingPatterns.size(), new BTupleMember(type, varSymbol)); listBindingPattern.restBindingPattern.setBType(restTupleType); BVarSymbol restBindingPatternSymbol = listBindingPattern.restBindingPattern.declaredVars @@ -3749,10 +3732,10 @@ private void assignTypesToMemberPatterns(BLangBindingPattern bindingPattern, BTy for (int i = 0; i < bindingPatterns.size(); i++) { assignTypesToMemberPatterns(bindingPatterns.get(i), tupleMemebers.get(i).type, data); BType type = bindingPatterns.get(i).getBType(); - BVarSymbol varSymbol = new BVarSymbol(type.flags, null, null, type, null, null, null); + BVarSymbol varSymbol = new BVarSymbol(type.getFlags(), null, null, type, null, null, null); members.add(new BTupleMember(type, varSymbol)); } - BTupleType tupleType = new BTupleType(members); + BTupleType tupleType = new BTupleType(typeEnv, members); if (listBindingPattern.restBindingPattern == null) { bindingPattern.setBType(tupleType); @@ -3878,7 +3861,7 @@ public void visit(BLangOnFailClause onFailClause, AnalyzerData data) { if (currentOnFailErrTypes.size() == 1) { failErrorType = currentOnFailErrTypes.iterator().next(); } else if (currentOnFailErrTypes.size() > 1) { - failErrorType = BUnionType.create(null, currentOnFailErrTypes); + failErrorType = BUnionType.create(typeEnv, null, currentOnFailErrTypes); } else { failErrorType = symTable.neverType; } @@ -3992,7 +3975,7 @@ public void visit(BLangFail failNode, AnalyzerData data) { } } if (errorExpressionType != symTable.semanticError && - !types.isSubTypeOfBaseType(errorExpressionType, symTable.errorType.tag)) { + !types.isSubTypeOfBaseType(errorExpressionType, PredefinedType.ERROR)) { dlog.error(errorExpression.pos, DiagnosticErrorCode.ERROR_TYPE_EXPECTED, errorExpressionType); } data.notCompletedNormally = true; @@ -4032,8 +4015,6 @@ public void visit(BLangService serviceNode, AnalyzerData data) { dlog.error(attachExpr.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, LISTENER_NAME, exprType); } else if (exprType != symTable.semanticError && serviceNode.listenerType == null) { serviceNode.listenerType = exprType; - } else if (exprType != symTable.semanticError) { - this.types.isSameType(exprType, serviceNode.listenerType); } if (attachExpr.getKind() == NodeKind.SIMPLE_VARIABLE_REF) { @@ -4100,7 +4081,7 @@ private void inferServiceTypeFromListeners(BLangService serviceNode, AnalyzerDat for (BType attachType : listenerTypes) { typeIdSet.add(getTypeIds(attachType)); } - inferred = BUnionType.create(null, listenerTypes); + inferred = BUnionType.create(typeEnv, null, listenerTypes); } serviceNode.inferredServiceType = inferred; @@ -4223,7 +4204,7 @@ public void visit(BLangTransaction transactionNode, AnalyzerData data) { @Override public void visit(BLangRollback rollbackNode, AnalyzerData data) { if (rollbackNode.expr != null) { - BType expectedType = BUnionType.create(null, symTable.errorType, symTable.nilType); + BType expectedType = BUnionType.create(typeEnv, null, symTable.errorType, symTable.nilType); this.typeChecker.checkExpr(rollbackNode.expr, data.env, expectedType, data.prevEnvs, data.commonAnalyzerData); } @@ -4788,7 +4769,7 @@ private void validateInclusions(Set referencingTypeFlags, List for (BLangType typeRef : typeRefs) { BType type = typeRef.getBType(); - long flags = type.flags; + long flags = type.getFlags(); List mismatchedFlags = new ArrayList<>(); @@ -4941,7 +4922,7 @@ private void validateIsolatedParamUsage(boolean inIsolatedFunction, BLangSimpleV BType type = isRestParam ? ((BArrayType) variable.getBType()).eType : variable.getBType(); - if (!types.isSubTypeOfBaseType(type, TypeTags.INVOKABLE)) { + if (!types.isSubTypeOfBaseType(type, PredefinedType.FUNCTION)) { dlog.error(variable.pos, DiagnosticErrorCode.ISOLATED_PARAM_USED_WITH_INVALID_TYPE); } @@ -4990,7 +4971,7 @@ private void handleReadOnlyField(boolean isRecordType, LinkedHashMap unresolvedTypes; private Set unresolvedRecordDueToFields; private boolean resolveRecordsUnresolvedDueToFields; - private List unresolvedClasses; private final HashSet unknownTypeRefs; private final List importedPackages; private int typePrecedence; @@ -241,7 +242,6 @@ public class SymbolEnter extends BLangNodeVisitor { private final BLangMissingNodesHelper missingNodesHelper; private final PackageCache packageCache; private final List intersectionTypes; - private Map typeToTypeDef; private SymbolEnv env; private final boolean projectAPIInitiatedCompilation; @@ -274,6 +274,7 @@ public SymbolEnter(CompilerContext context) { this.unknownTypeRefs = new HashSet<>(); this.missingNodesHelper = BLangMissingNodesHelper.getInstance(context); this.packageCache = PackageCache.getInstance(context); + this.constResolver = ConstantValueResolver.getInstance(context); this.intersectionTypes = new ArrayList<>(); CompilerOptions options = CompilerOptions.getInstance(context); @@ -491,20 +492,6 @@ private void defineConstructs(BLangPackage pkgNode, SymbolEnv pkgEnv) { typeResolver.clearUnknowTypeRefs(); } - private void defineDependentFields(List typeDefNodes, SymbolEnv pkgEnv) { - for (BLangNode typeDef : typeDefNodes) { - if (typeDef.getKind() == NodeKind.CLASS_DEFN) { - BLangClassDefinition classDefinition = (BLangClassDefinition) typeDef; - if (isObjectCtor(classDefinition)) { - continue; - } - defineReferencedFieldsOfClassDef(classDefinition, pkgEnv); - } else if (typeDef.getKind() == NodeKind.TYPE_DEFINITION) { - defineReferencedFieldsOfRecordTypeDef((BLangTypeDefinition) typeDef); - } - } - } - public void defineReferencedFieldsOfClassDef(BLangClassDefinition classDefinition, SymbolEnv pkgEnv) { SymbolEnv typeDefEnv = classDefinition.typeDefEnv; BObjectTypeSymbol tSymbol = (BObjectTypeSymbol) classDefinition.symbol; @@ -513,13 +500,6 @@ public void defineReferencedFieldsOfClassDef(BLangClassDefinition classDefinitio defineReferencedClassFields(classDefinition, typeDefEnv, objType, false); } - private void defineIntersectionTypes(SymbolEnv env) { - for (BLangNode typeDescriptor : this.intersectionTypes) { - defineNode(typeDescriptor, env); - } - this.intersectionTypes.clear(); - } - private void defineErrorType(Location pos, BErrorType errorType, SymbolEnv env) { SymbolEnv pkgEnv = symTable.pkgEnvMap.get(env.enclPkg.symbol); BTypeSymbol errorTSymbol = errorType.tsymbol; @@ -528,18 +508,6 @@ private void defineErrorType(Location pos, BErrorType errorType, SymbolEnv env) if (symResolver.checkForUniqueSymbol(pos, pkgEnv, errorTSymbol)) { pkgEnv.scope.define(errorTSymbol.name, errorTSymbol); } - - SymbolEnv prevEnv = this.env; - this.env = pkgEnv; - this.env = prevEnv; - } - - private boolean isObjectCtor(BLangNode node) { - if (node.getKind() == NodeKind.CLASS_DEFN) { - BLangClassDefinition classDefinition = (BLangClassDefinition) node; - return isObjectCtor(classDefinition); - } - return false; } public boolean isObjectCtor(BLangClassDefinition classDefinition) { @@ -893,7 +861,7 @@ public void visit(BLangClassDefinition classDefinition) { typeFlags |= Flags.OBJECT_CTOR; } - BObjectType objectType = new BObjectType(tSymbol, typeFlags); + BObjectType objectType = new BObjectType(symTable.typeEnv(), tSymbol, typeFlags); if (classDefinition.isObjectContructorDecl || flags.contains(Flag.OBJECT_CTOR)) { classDefinition.oceEnvData.objectType = objectType; objectType.classDef = classDefinition; @@ -904,7 +872,7 @@ public void visit(BLangClassDefinition classDefinition) { } if (flags.contains(Flag.CLIENT)) { - objectType.flags |= Flags.CLIENT; + objectType.addFlags(Flags.CLIENT); } tSymbol.type = objectType; @@ -1233,83 +1201,6 @@ public void visit(BLangXMLNSStatement xmlnsStmtNode) { defineNode(xmlnsStmtNode.xmlnsDecl, env); } - private void defineTypeNodes(List typeDefs, SymbolEnv env) { - if (typeDefs.isEmpty()) { - return; - } - - this.unresolvedTypes = new ArrayList<>(typeDefs.size()); - this.unresolvedRecordDueToFields = new HashSet<>(typeDefs.size()); - this.resolveRecordsUnresolvedDueToFields = false; - for (BLangNode typeDef : typeDefs) { - if (isErrorIntersectionTypeCreatingNewType(typeDef, env)) { - populateUndefinedErrorIntersection((BLangTypeDefinition) typeDef, env); - continue; - } -// if (isObjectCtor(typeDef)) { -// continue; -// } - - defineNode(typeDef, env); - } - - if (typeDefs.size() <= unresolvedTypes.size()) { - - this.resolveRecordsUnresolvedDueToFields = true; - unresolvedTypes.removeAll(unresolvedRecordDueToFields); - for (BLangNode unresolvedType : unresolvedRecordDueToFields) { - defineNode(unresolvedType, env); - } - this.resolveRecordsUnresolvedDueToFields = false; - - // This situation can occur due to either a cyclic dependency or at least one of member types in type - // definition node cannot be resolved. So we iterate through each node recursively looking for cyclic - // dependencies or undefined types in type node. - - for (BLangNode unresolvedType : unresolvedTypes) { - Deque references = new ArrayDeque<>(); - NodeKind unresolvedKind = unresolvedType.getKind(); - if (unresolvedKind == NodeKind.TYPE_DEFINITION || unresolvedKind == NodeKind.CONSTANT) { - TypeDefinition def = (TypeDefinition) unresolvedType; - // We need to keep track of all visited types to print cyclic dependency. - references.push(def.getName().getValue()); - checkErrors(env, unresolvedType, (BLangNode) def.getTypeNode(), references, false); - } else if (unresolvedType.getKind() == NodeKind.CLASS_DEFN) { - BLangClassDefinition classDefinition = (BLangClassDefinition) unresolvedType; - references.push(classDefinition.getName().getValue()); - checkErrors(env, unresolvedType, classDefinition, references, true); - } - } - defineAllUnresolvedCyclicTypesInScope(env); - - Set alreadyDefinedTypeDefNames = new HashSet<>(); - int unresolvedTypeCount = unresolvedTypes.size(); - for (int i = 0; i < unresolvedTypeCount; i++) { - for (BLangNode node : this.unresolvedTypes) { - String name = getTypeOrClassName(node); - boolean symbolNotFound = false; - boolean isTypeOrClassDefinition = - node.getKind() == NodeKind.TYPE_DEFINITION || node.getKind() == NodeKind.CLASS_DEFN; - // Skip the type resolving in the first iteration (i == 0) - // as we want to define the type before trying to resolve it. - if (isTypeOrClassDefinition && i != 0) { // Do not skip the first iteration - BSymbol bSymbol = symResolver.lookupSymbolInMainSpace(env, Names.fromString(name)); - symbolNotFound = (bSymbol == symTable.notFoundSymbol); - } - - boolean notFoundInList = alreadyDefinedTypeDefNames.add(name); - - // Prevent defining already defined type names. - if (notFoundInList || symbolNotFound) { - defineNode(node, env); - } - } - } - return; - } - defineTypeNodes(unresolvedTypes, env); - } - private void populateUndefinedErrorIntersection(BLangTypeDefinition typeDef, SymbolEnv env) { long flags = 0; if (typeDef.flagSet.contains(Flag.PUBLIC)) { @@ -1557,7 +1448,7 @@ private void checkErrorsOfUserDefinedType(SymbolEnv env, BLangNode unresolvedTyp } } - public String getTypeOrClassName(BLangNode node) { + public static String getTypeOrClassName(BLangNode node) { if (node.getKind() == NodeKind.TYPE_DEFINITION || node.getKind() == NodeKind.CONSTANT) { return ((TypeDefinition) node).getName().getValue(); } else { @@ -1657,7 +1548,7 @@ public void visit(BLangTypeDefinition typeDefinition) { typeDefSymbol.pkgID, typeDefSymbol.type, typeDefSymbol.owner, typeDefSymbol.pos, typeDefSymbol.origin); typeSymbol.markdownDocumentation = typeDefSymbol.markdownDocumentation; ((BTypeDefinitionSymbol) typeDefSymbol).referenceType = new BTypeReferenceType(definedType, typeSymbol, - typeDefSymbol.type.flags); + typeDefSymbol.type.getFlags()); boolean isLabel = true; //todo remove after type ref introduced to runtime @@ -1718,7 +1609,7 @@ public void visit(BLangTypeDefinition typeDefinition) { dlog.error(typeDefinition.pos, DiagnosticErrorCode.TYPE_PARAM_OUTSIDE_LANG_MODULE); } } - definedType.flags |= typeDefSymbol.flags; + definedType.addFlags(typeDefSymbol.flags); typeDefinition.symbol = typeDefSymbol; if (typeDefinition.hasCyclicReference) { @@ -1755,7 +1646,7 @@ public void handleDistinctDefinition(BLangTypeDefinition typeDefinition, BSymbol if (((BTypeDefinitionSymbol) typeDefSymbol).referenceType != null) { ((BTypeDefinitionSymbol) typeDefSymbol).referenceType.referredType = distinctType; } - definedType.flags |= Flags.DISTINCT; + definedType.addFlags(Flags.DISTINCT); } } @@ -1818,12 +1709,12 @@ public void populateAllReadyDefinedErrorIntersection(BType definedType, BLangTyp alreadyDefinedErrorType.typeIdSet = errorType.typeIdSet; alreadyDefinedErrorType.detailType = errorType.detailType; - alreadyDefinedErrorType.flags = errorType.flags; + alreadyDefinedErrorType.setFlags(errorType.getFlags()); alreadyDefinedErrorType.name = errorType.name; intersectionType.effectiveType = alreadyDefinedErrorType; if (!errorType.typeIdSet.isEmpty()) { - definedType.flags |= Flags.DISTINCT; + definedType.addFlags(Flags.DISTINCT); } } @@ -1861,7 +1752,7 @@ private BObjectType getDistinctObjectType(BLangTypeDefinition typeDefinition, BO // `typeDefSymbol` is different to `definedObjType.tsymbol` in a type definition statement that use // already defined type as the base type. if (definedObjType.tsymbol != tSymbol) { - BObjectType objType = new BObjectType(tSymbol); + BObjectType objType = new BObjectType(symTable.typeEnv(), tSymbol); tSymbol.type = objType; definedObjType = objType; } @@ -1886,13 +1777,13 @@ private BType defineSymbolForCyclicTypeDefinition(BLangTypeDefinition typeDef, S typeDefSymbol = switch (typeDef.typeNode.getKind()) { case TUPLE_TYPE_NODE -> { - newTypeNode = new BTupleType(null, new ArrayList<>(), true); + newTypeNode = new BTupleType(symTable.typeEnv(), null, new ArrayList<>(), true); yield Symbols.createTypeSymbol(SymTag.TUPLE_TYPE, Flags.asMask(typeDef.flagSet), newTypeDefName, env.enclPkg.symbol.pkgID, newTypeNode, env.scope.owner, typeDef.name.pos, SOURCE); } default -> { - newTypeNode = BUnionType.create(null, new LinkedHashSet<>(), true); + newTypeNode = BUnionType.create(symTable.typeEnv(), null, new LinkedHashSet<>(), true); yield Symbols.createTypeSymbol(SymTag.UNION_TYPE, Flags.asMask(typeDef.flagSet), newTypeDefName, env.enclPkg.symbol.pkgID, newTypeNode, env.scope.owner, typeDef.name.pos, SOURCE); @@ -1901,7 +1792,7 @@ private BType defineSymbolForCyclicTypeDefinition(BLangTypeDefinition typeDef, S typeDef.symbol = typeDefSymbol; defineTypeInMainScope(typeDefSymbol, typeDef, env); newTypeNode.tsymbol = typeDefSymbol; - newTypeNode.flags |= typeDefSymbol.flags; + newTypeNode.addFlags(typeDefSymbol.flags); return newTypeNode; } @@ -1976,7 +1867,7 @@ private BErrorType getDistinctErrorType(BLangTypeDefinition typeDefinition, BErr if (definedErrorType.tsymbol != typeDefSymbol) { BTypeSymbol typeSymbol = new BTypeSymbol(SymTag.TYPE_DEF, typeDefSymbol.flags, typeDefSymbol.name, typeDefSymbol.pkgID, null, typeDefSymbol.owner, typeDefSymbol.pos, typeDefSymbol.origin); - BErrorType bErrorType = new BErrorType(typeSymbol); + BErrorType bErrorType = new BErrorType(symTable.typeEnv(), typeSymbol); typeSymbol.type = bErrorType; bErrorType.detailType = definedErrorType.detailType; typeDefSymbol.type = bErrorType; @@ -2484,8 +2375,8 @@ boolean checkTypeAndVarCountConsistency(BLangTupleVariable varNode, BTupleType t } if (memberTypes.size() > 1) { - BType type = BUnionType.create(null, memberTypes); - BVarSymbol varSymbol = new BVarSymbol(type.flags, null, null, type, null, + BType type = BUnionType.create(symTable.typeEnv(), null, memberTypes); + BVarSymbol varSymbol = new BVarSymbol(type.getFlags(), null, null, type, null, null, null); members.add(new BTupleMember(type, varSymbol)); } else { @@ -2494,7 +2385,7 @@ boolean checkTypeAndVarCountConsistency(BLangTupleVariable varNode, BTupleType t Symbols.createVarSymbolForTupleMember(m)))); } } - tupleTypeNode = new BTupleType(members); + tupleTypeNode = new BTupleType(symTable.typeEnv(), members); tupleTypeNode.restType = getPossibleRestTypeForUnion(varNode, possibleTypes); break; } @@ -2512,7 +2403,7 @@ boolean checkTypeAndVarCountConsistency(BLangTupleVariable varNode, BTupleType t BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(type); members.add(new BTupleMember(type, varSymbol)); } - tupleTypeNode = new BTupleType(members); + tupleTypeNode = new BTupleType(symTable.typeEnv(), members); tupleTypeNode.restType = getPossibleRestTypeForUnion(varNode, possibleTypes); break; case TypeTags.ANY: @@ -2522,7 +2413,7 @@ boolean checkTypeAndVarCountConsistency(BLangTupleVariable varNode, BTupleType t BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(referredType); memberTupleTypes.add(new BTupleMember(referredType, varSymbol)); } - tupleTypeNode = new BTupleType(memberTupleTypes); + tupleTypeNode = new BTupleType(symTable.typeEnv(), memberTupleTypes); if (varNode.restVariable != null) { tupleTypeNode.restType = referredType; } @@ -2533,9 +2424,9 @@ boolean checkTypeAndVarCountConsistency(BLangTupleVariable varNode, BTupleType t case TypeTags.ARRAY: List tupleTypes = new ArrayList<>(); BArrayType arrayType = (BArrayType) referredType; - tupleTypeNode = new BTupleType(tupleTypes); + tupleTypeNode = new BTupleType(symTable.typeEnv(), tupleTypes); BType eType = arrayType.eType; - for (int i = 0; i < arrayType.size; i++) { + for (int i = 0; i < arrayType.getSize(); i++) { BType type = arrayType.eType; BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(type); tupleTypes.add(new BTupleMember(type, varSymbol)); @@ -2591,11 +2482,11 @@ boolean checkTypeAndVarCountConsistency(BLangTupleVariable varNode, BTupleType t } } if (!members.isEmpty()) { - BTupleType restTupleType = new BTupleType(members); + BTupleType restTupleType = new BTupleType(symTable.typeEnv(), members); restTupleType.restType = restType; type = restTupleType; } else { - type = restType != null ? new BArrayType(restType) : null; + type = restType != null ? new BArrayType(symTable.typeEnv(), restType) : null; } defineMemberNode(varNode.restVariable, env, type); } @@ -2632,7 +2523,7 @@ private BType getPossibleRestTypeForUnion(BLangTupleVariable varNode, List 1 ? BUnionType.create(null, memberRestTypes) : + return memberRestTypes.size() > 1 ? BUnionType.create(symTable.typeEnv(), null, memberRestTypes) : memberRestTypes.iterator().next(); } else { return varNode.getBType(); @@ -2797,7 +2688,7 @@ private BType createRestFieldFromPossibleTypes(Location pos, SymbolEnv env, List } BType restFieldType = restFieldMemberTypes.size() > 1 ? - BUnionType.create(null, restFieldMemberTypes) : + BUnionType.create(symTable.typeEnv(), null, restFieldMemberTypes) : restFieldMemberTypes.iterator().next(); if (!possibleRecordFieldMapList.isEmpty()) { @@ -2817,7 +2708,7 @@ private BType createRestFieldFromPossibleTypes(Location pos, SymbolEnv env, List } unmappedMembers.putAll(optionalFields); - BRecordType restRecord = new BRecordType(null); + BRecordType restRecord = new BRecordType(symTable.typeEnv(), null); restRecord.fields = unmappedMembers; restRecord.restFieldType = restFieldType; restFieldType = restRecord; @@ -2885,7 +2776,7 @@ private LinkedHashMap populateAndGetPossibleFieldsForRecVar(Loca } BType fieldType = memberTypes.size() > 1 ? - BUnionType.create(null, memberTypes) : memberTypes.iterator().next(); + BUnionType.create(symTable.typeEnv(), null, memberTypes) : memberTypes.iterator().next(); BField field = new BField(Names.fromString(fieldName), pos, new BVarSymbol(0, Names.fromString(fieldName), env.enclPkg.symbol.pkgID, fieldType, recordSymbol, pos, SOURCE)); @@ -2900,7 +2791,7 @@ private BRecordType createSameTypedFieldsRecordType(BLangRecordVariable recordVa if (fieldTypes.isNullable()) { fieldType = fieldTypes; } else { - fieldType = BUnionType.create(null, fieldTypes, symTable.nilType); + fieldType = BUnionType.create(symTable.typeEnv(), null, fieldTypes, symTable.nilType); } BRecordTypeSymbol recordSymbol = Symbols.createRecordSymbol(Flags.ANONYMOUS, @@ -3037,21 +2928,31 @@ public BRecordTypeSymbol createAnonRecordSymbol(SymbolEnv env, Location pos) { return recordSymbol; } - BType getRestParamType(BRecordType recordType) { + BType getRestParamType(BRecordType recordType) { BType memberType; if (recordType.restFieldType != null) { memberType = recordType.restFieldType; - } else if (hasErrorTypedField(recordType)) { - memberType = hasOnlyPureTypedFields(recordType) ? symTable.pureType : - BUnionType.create(null, symTable.anyType, symTable.errorType); - } else { - memberType = hasOnlyAnyDataTypedFields(recordType) ? symTable.anydataType : symTable.anyType; + BType referredMemberType = Types.getImpliedType(memberType); + if (referredMemberType.tag == TypeTags.RECORD) { + return getRestParamType((BRecordType) referredMemberType); + } else { + return memberType; + } } - BType referredMemberType = Types.getImpliedType(memberType); - if (referredMemberType.tag == TypeTags.RECORD) { - memberType = getRestParamType((BRecordType) referredMemberType); + + SemType s = recordType.semType(); + SemType anydata = types.anydata(); + if (SemTypes.containsBasicType(s, PredefinedType.ERROR)) { + if (types.isSubtype(s, Core.union(anydata, PredefinedType.ERROR))) { + return symTable.pureType; + } else { + return BUnionType.create(symTable.typeEnv(), null, symTable.anyType, symTable.errorType); + } + } else if (types.isSubtype(s, anydata)) { + return symTable.anydataType; + } else { + return symTable.anyType; } - return memberType; } public BType getRestMatchPatternConstraintType(BRecordType recordType, @@ -3075,7 +2976,7 @@ public BType getRestMatchPatternConstraintType(BRecordType recordType, } else if (constraintTypes.size() == 1) { restConstraintType = constraintTypes.iterator().next(); } else { - restConstraintType = BUnionType.create(null, constraintTypes); + restConstraintType = BUnionType.create(symTable.typeEnv(), null, constraintTypes); } return restVarSymbolMapType.tag == TypeTags.NONE ? restConstraintType : this.types.mergeTypes(restVarSymbolMapType, restConstraintType); @@ -3085,7 +2986,7 @@ BRecordType createRecordTypeForRestField(Location pos, SymbolEnv env, BRecordTyp List variableList, BType restConstraint) { BRecordTypeSymbol recordSymbol = createAnonRecordSymbol(env, pos); - BRecordType recordVarType = new BRecordType(recordSymbol); + BRecordType recordVarType = new BRecordType(symTable.typeEnv(), recordSymbol); recordSymbol.type = recordVarType; LinkedHashMap unMappedFields = new LinkedHashMap<>() {{ putAll(recordType.fields); @@ -3167,43 +3068,6 @@ private long setSymbolAsOptional(long existingFlags) { return Flags.asMask(unmaskedFlags); } - private boolean hasOnlyAnyDataTypedFields(BRecordType recordType) { - IsAnydataUniqueVisitor isAnydataUniqueVisitor = new IsAnydataUniqueVisitor(); - return isAnydataUniqueVisitor.visit(recordType); - } - - private boolean hasOnlyPureTypedFields(BRecordType recordType) { - IsPureTypeUniqueVisitor isPureTypeUniqueVisitor = new IsPureTypeUniqueVisitor(); - for (BField field : recordType.fields.values()) { - BType fieldType = field.type; - if (!isPureTypeUniqueVisitor.visit(fieldType)) { - return false; - } - isPureTypeUniqueVisitor.reset(); - } - return recordType.sealed || isPureTypeUniqueVisitor.visit(recordType); - } - - private boolean hasErrorTypedField(BRecordType recordType) { - for (BField field : recordType.fields.values()) { - BType type = field.type; - if (hasErrorType(type)) { - return true; - } - } - return hasErrorType(recordType.restFieldType); - } - - private boolean hasErrorType(BType type) { - BType referredType = Types.getImpliedType(type); - int tag = referredType.tag; - if (tag != TypeTags.UNION) { - return tag == TypeTags.ERROR; - } - - return ((BUnionType) referredType).getMemberTypes().stream().anyMatch(this::hasErrorType); - } - @Override public void visit(BLangErrorVariable errorVar) { if (errorVar.isDeclaredWithVar) { @@ -3282,9 +3146,9 @@ boolean validateErrorVariable(BLangErrorVariable errorVariable, SymbolEnv env) { detailType.add(possibleErrType.detailType); } BType errorDetailType = detailType.size() > 1 - ? BUnionType.create(null, detailType) + ? BUnionType.create(symTable.typeEnv(), null, detailType) : detailType.iterator().next(); - errorType = new BErrorType(null, errorDetailType); + errorType = new BErrorType(symTable.typeEnv(), null, errorDetailType); } else { errorType = possibleTypes.get(0); } @@ -3333,7 +3197,7 @@ boolean validateErrorVariable(BLangErrorVariable errorVariable, SymbolEnv env) { env.enclPkg.packageID, symTable.errorType, env.scope.owner, errorVariable.pos, SOURCE); // TODO: detail type need to be a union representing all details of members of `errorType` - errorVariable.setBType(new BErrorType(errorTypeSymbol, symTable.detailType)); + errorVariable.setBType(new BErrorType(symTable.typeEnv(), errorTypeSymbol, symTable.detailType)); return validateErrorVariable(errorVariable, env); } @@ -3360,7 +3224,7 @@ private boolean validateErrorVariable(BLangErrorVariable errorVariable, BErrorTy BLangVariable boundVar = errorDetailEntry.valueBindingPattern; if (entryField != null) { if ((entryField.symbol.flags & Flags.OPTIONAL) == Flags.OPTIONAL) { - boundVar.setBType(BUnionType.create(null, entryField.type, symTable.nilType)); + boundVar.setBType(BUnionType.create(symTable.typeEnv(), null, entryField.type, symTable.nilType)); } else { boundVar.setBType(entryField.type); } @@ -3372,7 +3236,8 @@ private boolean validateErrorVariable(BLangErrorVariable errorVariable, BErrorTy boundVar.setBType(symTable.semanticError); return false; } else { - boundVar.setBType(BUnionType.create(null, recordType.restFieldType, symTable.nilType)); + boundVar.setBType( + BUnionType.create(symTable.typeEnv(), null, recordType.restFieldType, symTable.nilType)); } } @@ -3388,7 +3253,7 @@ private boolean validateErrorVariable(BLangErrorVariable errorVariable, BErrorTy // union of keys whose values are not matched in error binding/match pattern. BTypeSymbol typeSymbol = createTypeSymbol(SymTag.TYPE, env); BType constraint = getRestMapConstraintType(detailFields, matchedDetailFields, recordType); - BMapType restType = new BMapType(TypeTags.MAP, constraint, typeSymbol); + BMapType restType = new BMapType(symTable.typeEnv(), TypeTags.MAP, constraint, typeSymbol); typeSymbol.type = restType; errorVariable.restDetail.setBType(restType); defineMemberNode(errorVariable.restDetail, env, restType); @@ -3401,7 +3266,7 @@ BRecordType getDetailAsARecordType(BErrorType errorType) { if (detailType.getKind() == TypeKind.RECORD) { return (BRecordType) detailType; } - BRecordType detailRecord = new BRecordType(null); + BRecordType detailRecord = new BRecordType(symTable.typeEnv(), null); BMapType detailMap = (BMapType) detailType; detailRecord.sealed = false; detailRecord.restFieldType = detailMap.constraint; @@ -3410,7 +3275,7 @@ BRecordType getDetailAsARecordType(BErrorType errorType) { private BType getRestMapConstraintType(Map errorDetailFields, Set matchedDetailFields, BRecordType recordType) { - BUnionType restUnionType = BUnionType.create(null); + BUnionType restUnionType = BUnionType.create(symTable.typeEnv(), null); if (!recordType.sealed) { BType referredRestFieldType = Types.getImpliedType(recordType.restFieldType); if (referredRestFieldType.tag == TypeTags.UNION) { @@ -3694,38 +3559,39 @@ private void populateLangLibInSymTable(BPackageSymbol packageSymbol) { } } + /** + * Checks if annotation type descriptor is valid. + *

+ * The type must be a subtype of one of the following three types: + *

    + *
  • true
  • + *
  • map<value:Cloneable>
  • + *
  • map<value:Cloneable>[]
  • + *
+ * + * @param type type to be checked + * @return boolean + */ public boolean isValidAnnotationType(BType type) { - type = Types.getImpliedType(type); - if (type == symTable.semanticError) { - return false; + SemType t = type.semType(); + if (SemTypes.isSubtype(types.semTypeCtx, t, symTable.trueType.semType())) { + return true; } - switch (type.tag) { - case TypeTags.MAP: - return types.isAssignable(((BMapType) type).constraint, symTable.cloneableType); - case TypeTags.RECORD: - BRecordType recordType = (BRecordType) type; - for (BField field : recordType.fields.values()) { - if (!types.isAssignable(field.type, symTable.cloneableType)) { - return false; - } - } - - BType recordRestType = recordType.restFieldType; - if (recordRestType == null || recordRestType == symTable.noType) { - return true; - } + SemType cloneable = Core.createCloneable(types.semTypeCtx); + if (SemTypes.isSubtypeSimple(t, PredefinedType.MAPPING)) { + return SemTypes.isSubtype(types.semTypeCtx, t, cloneable); + } - return types.isAssignable(recordRestType, symTable.cloneableType); - case TypeTags.ARRAY: - BType elementType = Types.getImpliedType(((BArrayType) type).eType); - if ((elementType.tag == TypeTags.MAP) || (elementType.tag == TypeTags.RECORD)) { - return isValidAnnotationType(elementType); - } - return false; + if (SemTypes.isSubtypeSimple(t, PredefinedType.LIST)) { + // Using projection to get T from T[] + SemType memberTy = Core.listMemberTypeInnerVal(types.semTypeCtx, t, PredefinedType.INT); + if (SemTypes.isSubtypeSimple(memberTy, PredefinedType.MAPPING)) { + return SemTypes.isSubtype(types.semTypeCtx, memberTy, cloneable); + } } - return types.isAssignable(type, symTable.trueType); + return false; } /** @@ -3840,20 +3706,6 @@ private BType getDetailType(SymbolEnv typeDefEnv, BLangErrorType errorTypeNode) .orElse(symTable.detailType); } - public void defineFields(List typeDefNodes, SymbolEnv pkgEnv) { - for (BLangNode typeDef : typeDefNodes) { - if (typeDef.getKind() == NodeKind.CLASS_DEFN) { - BLangClassDefinition classDefinition = (BLangClassDefinition) typeDef; - if (isObjectCtor(classDefinition)) { - continue; - } - defineFieldsOfClassDef(classDefinition, pkgEnv); - } else if (typeDef.getKind() == NodeKind.TYPE_DEFINITION) { - defineFields((BLangTypeDefinition) typeDef, pkgEnv); - } - } - } - public void defineFieldsOfClassDef(BLangClassDefinition classDefinition, SymbolEnv env) { SymbolEnv typeDefEnv = SymbolEnv.createClassEnv(classDefinition, classDefinition.symbol.scope, env); BObjectTypeSymbol tSymbol = (BObjectTypeSymbol) classDefinition.symbol; @@ -4268,7 +4120,7 @@ private void validateFieldsAndSetReadOnlyType(List typeDefNodes, Symb BSymbol symbol = typeDef.symbol; BStructureType structureType = (BStructureType) Types.getImpliedType(symbol.type); - if (Symbols.isFlagOn(structureType.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(structureType.getFlags(), Flags.READONLY)) { if (structureType.tag != TypeTags.OBJECT) { continue; } @@ -4336,7 +4188,7 @@ private void validateFieldsAndSetReadOnlyType(List typeDefNodes, Symb if (allImmutableFields) { structureType.tsymbol.flags |= Flags.READONLY; - structureType.flags |= Flags.READONLY; + structureType.addFlags(Flags.READONLY); } } } @@ -4370,7 +4222,7 @@ private void setReadOnlynessOfClassDef(BLangClassDefinition classDef, SymbolEnv BObjectType objectType = (BObjectType) classDef.getBType(); Location pos = classDef.pos; - if (Symbols.isFlagOn(classDef.getBType().flags, Flags.READONLY)) { + if (Symbols.isFlagOn(classDef.getBType().getFlags(), Flags.READONLY)) { if (!types.isSelectivelyImmutableType(objectType, new HashSet<>(), pkgEnv.enclPkg.packageID)) { dlog.error(pos, DiagnosticErrorCode.INVALID_READONLY_OBJECT_TYPE, objectType); return; @@ -4386,13 +4238,13 @@ private void setReadOnlynessOfClassDef(BLangClassDefinition classDef, SymbolEnv for (BField field : fields) { if (!Symbols.isFlagOn(field.symbol.flags, Flags.FINAL) || - !Symbols.isFlagOn(field.type.flags, Flags.READONLY)) { + !Symbols.isFlagOn(field.type.getFlags(), Flags.READONLY)) { return; } } classDef.getBType().tsymbol.flags |= Flags.READONLY; - classDef.getBType().flags |= Flags.READONLY; + classDef.getBType().addFlags(Flags.READONLY); } } @@ -4404,11 +4256,11 @@ private void defineInvokableSymbol(BLangInvokableNode invokableNode, BInvokableS defineInvokableSymbolParams(invokableNode, funcSymbol, invokableEnv); if (Symbols.isFlagOn(funcSymbol.type.tsymbol.flags, Flags.ISOLATED)) { - funcSymbol.type.flags |= Flags.ISOLATED; + funcSymbol.type.addFlags(Flags.ISOLATED); } if (Symbols.isFlagOn(funcSymbol.type.tsymbol.flags, Flags.TRANSACTIONAL)) { - funcSymbol.type.flags |= Flags.TRANSACTIONAL; + funcSymbol.type.addFlags(Flags.TRANSACTIONAL); } } @@ -4581,7 +4433,7 @@ public void defineInvokableTypeNode(BLangFunctionTypeNode functionTypeNode, long bInvokableType.paramTypes = paramTypes; bInvokableType.retType = retType; bInvokableType.restType = restType; - bInvokableType.flags |= flags; + bInvokableType.addFlags(flags); functionTypeNode.setBType(bInvokableType); List allConstituentTypes = new ArrayList<>(paramTypes); @@ -4624,7 +4476,7 @@ void defineInvokableSymbolParams(BLangInvokableNode invokableNode, BInvokableSym functionTypeSymbol.restParam = invokableSymbol.restParam; restType = invokableSymbol.restParam.type; } - invokableSymbol.type = new BInvokableType(paramTypes, restType, retType, null); + invokableSymbol.type = new BInvokableType(symTable.typeEnv(), paramTypes, restType, retType, null); invokableSymbol.type.tsymbol = functionTypeSymbol; invokableSymbol.type.tsymbol.type = invokableSymbol.type; } @@ -4680,13 +4532,6 @@ public void defineTypeNarrowedSymbol(Location location, SymbolEnv targetEnv, BVa defineShadowedSymbol(location, varSymbol, targetEnv); } - private void defineSymbolWithCurrentEnvOwner(Location pos, BSymbol symbol) { - symbol.scope = new Scope(env.scope.owner); - if (symResolver.checkForUniqueSymbol(pos, env, symbol)) { - env.scope.define(symbol.name, symbol); - } - } - public BVarSymbol defineVarSymbol(Location pos, Set flagSet, BType varType, Name varName, SymbolEnv env, boolean isInternal) { return defineVarSymbol(pos, flagSet, varType, varName, varName, env, isInternal); @@ -4881,7 +4726,7 @@ private BAttachedFunction createResourceFunction(BLangFunction funcNode, BInvoka List pathSegmentSymbols = new ArrayList<>(resourcePathCount); BResourcePathSegmentSymbol parentResource = null; for (BLangResourcePathSegment pathSegment : pathSegments) { - Name resourcePathSymbolName = Names.fromString(pathSegment.name.value); + Name resourcePathSymbolName = Names.fromString(pathSegment.name.originalValue); BType resourcePathSegmentType = pathSegment.typeNode == null ? symTable.noType : symResolver.resolveTypeNode(pathSegment.typeNode, env); pathSegment.setBType(resourcePathSegmentType); @@ -4930,39 +4775,6 @@ private void validateResourceFunctionAttachedToObject(BLangFunction funcNode, BO } } - private StatementNode createAssignmentStmt(BLangSimpleVariable variable, BVarSymbol varSym, BSymbol fieldVar) { - //Create LHS reference variable - BLangSimpleVarRef varRef = (BLangSimpleVarRef) TreeBuilder.createSimpleVariableReferenceNode(); - varRef.pos = variable.pos; - varRef.variableName = (BLangIdentifier) createIdentifier(fieldVar.name.getValue()); - varRef.pkgAlias = (BLangIdentifier) TreeBuilder.createIdentifierNode(); - varRef.symbol = fieldVar; - varRef.setBType(fieldVar.type); - - //Create RHS variable reference - BLangSimpleVarRef exprVar = (BLangSimpleVarRef) TreeBuilder.createSimpleVariableReferenceNode(); - exprVar.pos = variable.pos; - exprVar.variableName = (BLangIdentifier) createIdentifier(varSym.name.getValue()); - exprVar.pkgAlias = (BLangIdentifier) TreeBuilder.createIdentifierNode(); - exprVar.symbol = varSym; - exprVar.setBType(varSym.type); - - //Create assignment statement - BLangAssignment assignmentStmt = (BLangAssignment) TreeBuilder.createAssignmentNode(); - assignmentStmt.expr = exprVar; - assignmentStmt.pos = variable.pos; - assignmentStmt.setVariable(varRef); - return assignmentStmt; - } - - private IdentifierNode createIdentifier(String value) { - IdentifierNode node = TreeBuilder.createIdentifierNode(); - if (value != null) { - node.setValue(value); - } - return node; - } - private boolean validateFuncReceiver(BLangFunction funcNode) { if (funcNode.receiver == null) { return true; @@ -4999,11 +4811,6 @@ private Name getFuncSymbolOriginalName(BLangFunction funcNode) { return names.originalNameFromIdNode(funcNode.name); } - private Name getFieldSymbolName(BLangSimpleVariable receiver, BLangSimpleVariable variable) { - return Names.fromString(Symbols.getAttachedFuncSymbolName( - receiver.getBType().tsymbol.name.value, variable.name.value)); - } - public MarkdownDocAttachment getMarkdownDocAttachment(BLangMarkdownDocumentation docNode) { if (docNode == null) { return new MarkdownDocAttachment(0); @@ -5201,8 +5008,8 @@ private void defineReferencedFunction(Location location, Set flagSet, Symb // If not, define the function symbol within the object. // Take a copy of the symbol, with the new name, and the package ID same as the object type. - BInvokableSymbol funcSymbol = ASTBuilderUtil.duplicateFunctionDeclarationSymbol(referencedFuncSymbol, - typeDefSymbol, funcName, typeDefSymbol.pkgID, typeRef.pos, getOrigin(funcName)); + BInvokableSymbol funcSymbol = ASTBuilderUtil.duplicateFunctionDeclarationSymbol(symTable.typeEnv(), + referencedFuncSymbol, typeDefSymbol, funcName, typeDefSymbol.pkgID, typeRef.pos, getOrigin(funcName)); defineSymbol(typeRef.pos, funcSymbol, objEnv); // Create and define the parameters and receiver. This should be done after defining the function symbol. @@ -5316,12 +5123,12 @@ private void setTypeFromLambdaExpr(BLangVariable variable) { BLangFunction function = ((BLangLambdaFunction) variable.expr).function; BInvokableType invokableType = (BInvokableType) function.symbol.type; if (function.flagSet.contains(Flag.ISOLATED)) { - invokableType.flags |= Flags.ISOLATED; + invokableType.addFlags(Flags.ISOLATED); invokableType.tsymbol.flags |= Flags.ISOLATED; } if (function.flagSet.contains(Flag.TRANSACTIONAL)) { - invokableType.flags |= Flags.TRANSACTIONAL; + invokableType.addFlags(Flags.TRANSACTIONAL); invokableType.tsymbol.flags |= Flags.TRANSACTIONAL; } @@ -5370,7 +5177,7 @@ private boolean isInvalidIncludedTypeInClass(BType includedType) { } private boolean isImmutable(BObjectType objectType) { - if (Symbols.isFlagOn(objectType.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(objectType.getFlags(), Flags.READONLY)) { return true; } @@ -5381,7 +5188,7 @@ private boolean isImmutable(BObjectType objectType) { for (BField field : fields) { if (!Symbols.isFlagOn(field.symbol.flags, Flags.FINAL) || - !Symbols.isFlagOn(field.type.flags, Flags.READONLY)) { + !Symbols.isFlagOn(field.type.getFlags(), Flags.READONLY)) { return false; } } @@ -5393,7 +5200,7 @@ private boolean isReadOnlyAndObjectIntersection(BIntersectionType referredType) BType effectiveType = referredType.effectiveType; if (Types.getImpliedType(effectiveType).tag != TypeTags.OBJECT || - !Symbols.isFlagOn(effectiveType.flags, Flags.READONLY)) { + !Symbols.isFlagOn(effectiveType.getFlags(), Flags.READONLY)) { return false; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolResolver.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolResolver.java index 449ef9f0c2ad..c0536ed6796b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolResolver.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolResolver.java @@ -19,6 +19,7 @@ import io.ballerina.tools.diagnostics.DiagnosticCode; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.PredefinedType; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.AttachPoint; import org.ballerinalang.model.elements.Flag; @@ -56,7 +57,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; -import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; import org.wso2.ballerinalang.compiler.semantics.model.types.BIntersectionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; @@ -418,7 +418,7 @@ public BSymbol resolveBinaryOperator(OperatorKind opKind, private BSymbol createEqualityOperator(OperatorKind opKind, BType lhsType, BType rhsType) { List paramTypes = Lists.of(lhsType, rhsType); BType retType = symTable.booleanType; - BInvokableType opType = new BInvokableType(paramTypes, retType, null); + BInvokableType opType = new BInvokableType(symTable.typeEnv(), paramTypes, retType, null); return new BOperatorSymbol(Names.fromString(opKind.value()), null, opType, null, symTable.builtinPos, VIRTUAL); } @@ -434,19 +434,19 @@ public BSymbol resolveOperator(Name name, List types) { private BSymbol createBinaryComparisonOperator(OperatorKind opKind, BType lhsType, BType rhsType) { List paramTypes = Lists.of(lhsType, rhsType); - BInvokableType opType = new BInvokableType(paramTypes, symTable.booleanType, null); + BInvokableType opType = new BInvokableType(symTable.typeEnv(), paramTypes, symTable.booleanType, null); return new BOperatorSymbol(Names.fromString(opKind.value()), null, opType, null, symTable.builtinPos, VIRTUAL); } private BSymbol createBinaryOperator(OperatorKind opKind, BType lhsType, BType rhsType, BType retType) { List paramTypes = Lists.of(lhsType, rhsType); - BInvokableType opType = new BInvokableType(paramTypes, retType, null); + BInvokableType opType = new BInvokableType(symTable.typeEnv(), paramTypes, retType, null); return new BOperatorSymbol(Names.fromString(opKind.value()), null, opType, null, symTable.builtinPos, VIRTUAL); } BSymbol createUnaryOperator(OperatorKind kind, BType type, BType retType) { List paramTypes = Lists.of(type); - BInvokableType opType = new BInvokableType(paramTypes, retType, null); + BInvokableType opType = new BInvokableType(symTable.typeEnv(), paramTypes, retType, null); return new BOperatorSymbol(Names.fromString(kind.value()), null, opType, null, symTable.builtinPos, VIRTUAL); } @@ -564,9 +564,9 @@ private BType resolveTypeNode(BLangType typeNode, AnalyzerData data, SymbolEnv e BUnionType unionType = (BUnionType) refType; unionType.add(symTable.nilType); } else if (typeNode.nullable && resultType.tag != TypeTags.JSON && resultType.tag != TypeTags.ANY) { - resultType = BUnionType.create(null, resultType, symTable.nilType); + resultType = BUnionType.create(symTable.typeEnv(), null, resultType, symTable.nilType); } else if (typeNode.nullable && refType.tag != TypeTags.JSON && refType.tag != TypeTags.ANY) { - resultType = BUnionType.create(null, resultType, symTable.nilType); + resultType = BUnionType.create(symTable.typeEnv(), null, resultType, symTable.nilType); } } @@ -1046,12 +1046,13 @@ public void bootstrapAnydataType() { continue; } BUnionType type = (BUnionType) Types.getImpliedType(entry.symbol.type); - symTable.anydataType = new BAnydataType(type); + symTable.anydataType = new BAnydataType(types.semTypeCtx, type); Optional immutableType = Types.getImmutableType(symTable, PackageID.ANNOTATIONS, type); if (immutableType.isPresent()) { Types.addImmutableType(symTable, PackageID.ANNOTATIONS, symTable.anydataType, immutableType.get()); } - symTable.anydataOrReadonly = BUnionType.create(null, symTable.anydataType, symTable.readonlyType); + symTable.anydataOrReadonly = + BUnionType.create(symTable.typeEnv(), null, symTable.anydataType, symTable.readonlyType); entry.symbol.type = symTable.anydataType; entry.symbol.origin = BUILTIN; @@ -1070,7 +1071,7 @@ public void bootstrapJsonType() { continue; } BUnionType type = (BUnionType) Types.getImpliedType(entry.symbol.type); - symTable.jsonType = new BJSONType(type); + symTable.jsonType = new BJSONType(types.semTypeCtx, type); Optional immutableType = Types.getImmutableType(symTable, PackageID.ANNOTATIONS, type); if (immutableType.isPresent()) { @@ -1098,21 +1099,24 @@ public void bootstrapCloneableType() { new BTypeSymbol(SymTag.TYPE, Flags.PUBLIC, Names.CLONEABLE, PackageID.VALUE, symTable.cloneableType, symTable.langValueModuleSymbol, symTable.builtinPos, BUILTIN); - symTable.detailType = new BMapType(TypeTags.MAP, symTable.cloneableType, null); - symTable.errorType = new BErrorType(null, symTable.detailType); + symTable.detailType = new BMapType(symTable.typeEnv(), TypeTags.MAP, symTable.cloneableType, null); + symTable.errorType = new BErrorType(symTable.typeEnv(), null, symTable.detailType); symTable.errorType.tsymbol = new BErrorTypeSymbol(SymTag.ERROR, Flags.PUBLIC, Names.ERROR, symTable.rootPkgSymbol.pkgID, symTable.errorType, symTable.rootPkgSymbol, symTable.builtinPos , BUILTIN); - symTable.errorOrNilType = BUnionType.create(null, symTable.errorType, symTable.nilType); - symTable.anyOrErrorType = BUnionType.create(null, symTable.anyType, symTable.errorType); + symTable.errorOrNilType = + BUnionType.create(symTable.typeEnv(), null, symTable.errorType, symTable.nilType); + symTable.anyOrErrorType = + BUnionType.create(symTable.typeEnv(), null, symTable.anyType, symTable.errorType); - symTable.mapAllType = new BMapType(TypeTags.MAP, symTable.anyOrErrorType, null); - symTable.arrayAllType = new BArrayType(symTable.anyOrErrorType); + symTable.mapAllType = new BMapType(symTable.typeEnv(), TypeTags.MAP, symTable.anyOrErrorType, null); + symTable.arrayAllType = new BArrayType(symTable.typeEnv(), symTable.anyOrErrorType); symTable.typeDesc.constraint = symTable.anyOrErrorType; symTable.futureType.constraint = symTable.anyOrErrorType; - symTable.pureType = BUnionType.create(null, symTable.anydataType, symTable.errorType); + symTable.pureType = + BUnionType.create(symTable.typeEnv(), null, symTable.anydataType, symTable.errorType); return; } throw new IllegalStateException("built-in 'lang.value:Cloneable' type not found"); @@ -1198,7 +1202,7 @@ public BType transform(BLangArrayType arrayTypeNode, AnalyzerData data) { data.env.enclPkg.symbol.pkgID, null, data.env.scope.owner, arrayTypeNode.pos, SOURCE); BArrayType arrType; if (arrayTypeNode.sizes.isEmpty()) { - arrType = new BArrayType(resultType, arrayTypeSymbol); + arrType = new BArrayType(symTable.typeEnv(), resultType, arrayTypeSymbol); } else { BLangExpression size = arrayTypeNode.sizes.get(i); if (size.getKind() == NodeKind.LITERAL || size.getKind() == NodeKind.NUMERIC_LITERAL) { @@ -1211,7 +1215,8 @@ public BType transform(BLangArrayType arrayTypeNode, AnalyzerData data) { } else { arrayState = BArrayState.CLOSED; } - arrType = new BArrayType(resultType, arrayTypeSymbol, sizeIndicator, arrayState); + arrType = + new BArrayType(symTable.typeEnv(), resultType, arrayTypeSymbol, sizeIndicator, arrayState); } else { if (size.getKind() != NodeKind.SIMPLE_VARIABLE_REF) { dlog.error(size.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, symTable.intType, @@ -1261,7 +1266,8 @@ public BType transform(BLangArrayType arrayTypeNode, AnalyzerData data) { } else { length = (int) lengthCheck; } - arrType = new BArrayType(resultType, arrayTypeSymbol, length, BArrayState.CLOSED); + arrType = + new BArrayType(symTable.typeEnv(), resultType, arrayTypeSymbol, length, BArrayState.CLOSED); } } arrayTypeSymbol.type = arrType; @@ -1291,7 +1297,7 @@ public BType transform(BLangUnionTypeNode unionTypeNode, AnalyzerData data) { Names.EMPTY, data.env.enclPkg.symbol.pkgID, null, data.env.scope.owner, unionTypeNode.pos, SOURCE); - BUnionType unionType = BUnionType.create(unionTypeSymbol, memberTypes); + BUnionType unionType = BUnionType.create(symTable.typeEnv(), unionTypeSymbol, memberTypes); unionTypeSymbol.type = unionType; markParameterizedType(unionType, memberTypes); @@ -1328,7 +1334,7 @@ public BType transform(BLangObjectTypeNode objectTypeNode, AnalyzerData data) { BTypeSymbol objectSymbol = Symbols.createObjectSymbol(Flags.asMask(flags), Names.EMPTY, data.env.enclPkg.symbol.pkgID, null, data.env.scope.owner, objectTypeNode.pos, SOURCE); - BObjectType objectType = new BObjectType(objectSymbol, typeFlags); + BObjectType objectType = new BObjectType(symTable.typeEnv(), objectSymbol, typeFlags); objectSymbol.type = objectType; objectTypeNode.symbol = objectSymbol; @@ -1348,7 +1354,7 @@ public BType transform(BLangRecordTypeNode recordTypeNode, AnalyzerData data) { data.env.enclPkg.symbol.pkgID, null, data.env.scope.owner, recordTypeNode.pos, recordTypeNode.isAnonymous ? VIRTUAL : SOURCE); - BRecordType recordType = new BRecordType(recordSymbol); + BRecordType recordType = new BRecordType(symTable.typeEnv(), recordSymbol); recordSymbol.type = recordType; recordTypeNode.symbol = recordSymbol; @@ -1375,7 +1381,7 @@ public BType transform(BLangStreamType streamTypeNode, AnalyzerData data) { return symTable.noType; } - BType streamType = new BStreamType(TypeTags.STREAM, constraintType, error, null); + BType streamType = new BStreamType(symTable.typeEnv(), TypeTags.STREAM, constraintType, error, null); BTypeSymbol typeSymbol = type.tsymbol; streamType.tsymbol = Symbols.createTypeSymbol(typeSymbol.tag, typeSymbol.flags, typeSymbol.name, typeSymbol.originalName, typeSymbol.pkgID, streamType, @@ -1398,7 +1404,7 @@ public BType transform(BLangTableTypeNode tableTypeNode, AnalyzerData data) { return symTable.noType; } - BTableType tableType = new BTableType(TypeTags.TABLE, constraintType, null); + BTableType tableType = new BTableType(symTable.typeEnv(), constraintType, null); BTypeSymbol typeSymbol = type.tsymbol; tableType.tsymbol = Symbols.createTypeSymbol(SymTag.TYPE, Flags.asMask(EnumSet.noneOf(Flag.class)), typeSymbol.name, typeSymbol.originalName, typeSymbol.pkgID, @@ -1436,22 +1442,7 @@ public BType transform(BLangTableTypeNode tableTypeNode, AnalyzerData data) { @Override public BType transform(BLangFiniteTypeNode finiteTypeNode, AnalyzerData data) { - BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, - Flags.asMask(EnumSet.of(Flag.PUBLIC)), Names.EMPTY, - data.env.enclPkg.symbol.pkgID, null, data.env.scope.owner, - finiteTypeNode.pos, SOURCE); - - // In case we encounter unary expressions in finite type, we will be replacing them with numeric literals - // Note: calling semanticAnalyzer form symbolResolver is a temporary fix. - semanticAnalyzer.analyzeNode(finiteTypeNode, data.env); - - BFiniteType finiteType = new BFiniteType(finiteTypeSymbol); - for (BLangExpression expressionOrLiteral : finiteTypeNode.valueSpace) { - finiteType.addValue(expressionOrLiteral); - } - finiteTypeSymbol.type = finiteType; - - return finiteType; + return typeResolver.resolveSingletonType(finiteTypeNode, data.env); } @Override @@ -1481,9 +1472,9 @@ public BType transform(BLangTupleTypeNode tupleTypeNode, AnalyzerData data) { } List tupleMembers = new ArrayList<>(); members.forEach(member -> tupleMembers.add(new BTupleMember(member.getBType(), - new BVarSymbol(member.getBType().flags, member.symbol.name, member.symbol.pkgID, member.getBType(), + new BVarSymbol(member.getBType().getFlags(), member.symbol.name, member.symbol.pkgID, member.getBType(), member.symbol.owner, member.pos, SOURCE)))); - BTupleType tupleType = new BTupleType(tupleTypeSymbol, tupleMembers); + BTupleType tupleType = new BTupleType(symTable.typeEnv(), tupleTypeSymbol, tupleMembers); tupleTypeSymbol.type = tupleType; if (tupleTypeNode.restParamType != null) { @@ -1530,8 +1521,8 @@ public BType transform(BLangErrorType errorTypeNode, AnalyzerData data) { symbolEnter.defineSymbol(errorTypeNode.pos, errorTypeSymbol, data.env); } - BErrorType errorType = new BErrorType(errorTypeSymbol, detailType); - errorType.flags |= errorTypeSymbol.flags; + BErrorType errorType = new BErrorType(symTable.typeEnv(), errorTypeSymbol, detailType); + errorType.addFlags(errorTypeSymbol.flags); errorTypeSymbol.type = errorType; markParameterizedType(errorType, detailType); @@ -1557,11 +1548,11 @@ public BType transform(BLangConstrainedType constrainedTypeNode, AnalyzerData da BType constrainedType; if (type.tag == TypeTags.FUTURE) { - constrainedType = new BFutureType(TypeTags.FUTURE, constraintType, null); + constrainedType = new BFutureType(symTable.typeEnv(), constraintType, null); } else if (type.tag == TypeTags.MAP) { - constrainedType = new BMapType(TypeTags.MAP, constraintType, null); + constrainedType = new BMapType(symTable.typeEnv(), TypeTags.MAP, constraintType, null); } else if (type.tag == TypeTags.TYPEDESC) { - constrainedType = new BTypedescType(constraintType, null); + constrainedType = new BTypedescType(symTable.typeEnv(), constraintType, null); } else if (type.tag == TypeTags.XML) { if (Types.getImpliedType(constraintType).tag == TypeTags.PARAMETERIZED_TYPE) { BType typedescType = ((BParameterizedType) constraintType).paramSymbol.type; @@ -1584,32 +1575,11 @@ public BType transform(BLangConstrainedType constrainedTypeNode, AnalyzerData da } public void validateXMLConstraintType(BType type, Location pos) { - BType constraintType = Types.getImpliedType(type); - int constrainedTag = constraintType.tag; - - if (constrainedTag == TypeTags.UNION) { - checkUnionTypeForXMLSubTypes((BUnionType) constraintType, pos); - return; - } - - if (!TypeTags.isXMLTypeTag(constrainedTag) && constrainedTag != TypeTags.NEVER) { + if (!SemTypeHelper.isSubtypeSimple(type, PredefinedType.XML)) { dlog.error(pos, DiagnosticErrorCode.INCOMPATIBLE_TYPE_CONSTRAINT, symTable.xmlType, type); } } - private void checkUnionTypeForXMLSubTypes(BUnionType constraintUnionType, Location pos) { - for (BType memberType : constraintUnionType.getMemberTypes()) { - memberType = Types.getImpliedType(memberType); - if (memberType.tag == TypeTags.UNION) { - checkUnionTypeForXMLSubTypes((BUnionType) memberType, pos); - } - if (!TypeTags.isXMLTypeTag(memberType.tag)) { - dlog.error(pos, DiagnosticErrorCode.INCOMPATIBLE_TYPE_CONSTRAINT, symTable.xmlType, - constraintUnionType); - } - } - } - @Override public BType transform(BLangUserDefinedType userDefinedTypeNode, AnalyzerData data) { String name = userDefinedTypeNode.typeName.value; @@ -1687,7 +1657,7 @@ public BType transform(BLangUserDefinedType userDefinedTypeNode, AnalyzerData da null, func.symbol, tempSymbol.pos, VIRTUAL); tSymbol.type = new BParameterizedType(paramValType, (BVarSymbol) tempSymbol, tSymbol, tempSymbol.name, parameterizedTypeInfo.index); - tSymbol.type.flags |= Flags.PARAMETERIZED; + tSymbol.type.addFlags(Flags.PARAMETERIZED); userDefinedTypeNode.symbol = tSymbol; return tSymbol.type; @@ -1729,7 +1699,7 @@ public BType transform(BLangUserDefinedType userDefinedTypeNode, AnalyzerData da tempSymbol.pos, VIRTUAL); tSymbol.type = new BParameterizedType(paramValType, (BVarSymbol) tempSymbol, tSymbol, tempSymbol.name, parameterizedTypeInfo.index); - tSymbol.type.flags |= Flags.PARAMETERIZED; + tSymbol.type.addFlags(Flags.PARAMETERIZED); userDefinedTypeNode.symbol = tSymbol; return tSymbol.type; } @@ -1759,8 +1729,8 @@ public BType transform(BLangUserDefinedType userDefinedTypeNode, AnalyzerData da if (symbol.kind == SymbolKind.TYPE_DEF && !Symbols.isFlagOn(symbol.flags, Flags.ANONYMOUS) && !isCloneableTypeDef) { BType referenceType = ((BTypeDefinitionSymbol) symbol).referenceType; - referenceType.flags |= symbol.type.flags; - referenceType.tsymbol.flags |= symbol.type.flags; + referenceType.addFlags(symbol.type.getFlags()); + referenceType.tsymbol.flags |= symbol.type.getFlags(); return referenceType; } @@ -1813,8 +1783,8 @@ public BType transform(BLangFunctionTypeNode functionTypeNode, AnalyzerData data BInvokableTypeSymbol invokableTypeSymbol; BInvokableType invokableType; if (functionTypeNode.flagSet.contains(Flag.ANY_FUNCTION)) { - invokableType = new BInvokableType(null, null, null, null); - invokableType.flags = Flags.asMask(functionTypeNode.flagSet); + invokableType = new BInvokableType(symTable.typeEnv(), List.of(), null, null, null); + invokableType.setFlags(Flags.asMask(functionTypeNode.flagSet)); invokableTypeSymbol = Symbols.createInvokableTypeSymbol(SymTag.FUNCTION_TYPE, Flags.asMask(functionTypeNode.flagSet), env.enclPkg.symbol.pkgID, invokableType, @@ -1828,7 +1798,7 @@ public BType transform(BLangFunctionTypeNode functionTypeNode, AnalyzerData data Flags.asMask(functionTypeNode.flagSet), env.enclPkg.symbol.pkgID, functionTypeNode.getBType(), env.scope.owner, functionTypeNode.pos, VIRTUAL); - invokableType = new BInvokableType(invokableTypeSymbol); + invokableType = new BInvokableType(symTable.typeEnv(), invokableTypeSymbol); invokableTypeSymbol.type = invokableType; invokableTypeSymbol.name = Names.fromString(anonymousModelHelper.getNextAnonymousTypeKey(env.enclPkg.packageID)); @@ -1888,9 +1858,7 @@ public BSymbol getBinaryEqualityForTypeSets(OperatorKind opKind, BType lhsType, break; case REF_EQUAL: case REF_NOT_EQUAL: - validEqualityIntersectionExists = - types.getTypeIntersection(Types.IntersectionContext.compilerInternalIntersectionTestContext(), - lhsType, rhsType, env) != symTable.semanticError; + validEqualityIntersectionExists = types.intersectionExists(lhsType.semType(), rhsType.semType()); break; default: return symTable.notFoundSymbol; @@ -1949,7 +1917,7 @@ public BSymbol getBitwiseShiftOpsForTypeSets(OperatorKind opKind, BType lhsType, private BSymbol createShiftOperator(OperatorKind opKind, BType lhsType, BType rhsType) { if (lhsType.isNullable() || rhsType.isNullable()) { - BType intOptional = BUnionType.create(null, symTable.intType, symTable.nilType); + BType intOptional = BUnionType.create(symTable.typeEnv(), null, symTable.intType, symTable.nilType); return createBinaryOperator(opKind, lhsType, rhsType, intOptional); } return createBinaryOperator(opKind, lhsType, rhsType, symTable.intType); @@ -1997,7 +1965,7 @@ public BSymbol getArithmeticOpsForTypeSets(OperatorKind opKind, BType lhsType, B BType returnType = compatibleType1.tag < compatibleType2.tag ? compatibleType2 : compatibleType1; if (lhsType.isNullable() || rhsType.isNullable()) { - returnType = BUnionType.create(null, returnType, symTable.nilType); + returnType = BUnionType.create(symTable.typeEnv(), null, returnType, symTable.nilType); } return createBinaryOperator(opKind, lhsType, rhsType, returnType); @@ -2038,7 +2006,8 @@ public BSymbol getUnaryOpsForTypeSets(OperatorKind opKind, BType type) { } if (type.isNullable()) { BType compatibleType = types.findCompatibleType(types.getSafeType(type, true, false)); - return createUnaryOperator(opKind, type, BUnionType.create(null, compatibleType, symTable.nilType)); + return createUnaryOperator(opKind, type, + BUnionType.create(symTable.typeEnv(), null, compatibleType, symTable.nilType)); } return createUnaryOperator(opKind, type, types.findCompatibleType(type)); } @@ -2067,7 +2036,7 @@ public BSymbol getBinaryBitwiseOpsForTypeSets(OperatorKind opKind, BType lhsType case TypeTags.UNSIGNED32_INT: return createBinaryOperator(opKind, lhsType, rhsType, lhsType); } - + switch (referredRhsType.tag) { case TypeTags.UNSIGNED8_INT: case TypeTags.BYTE: @@ -2075,16 +2044,18 @@ public BSymbol getBinaryBitwiseOpsForTypeSets(OperatorKind opKind, BType lhsType case TypeTags.UNSIGNED32_INT: return createBinaryOperator(opKind, lhsType, rhsType, rhsType); } - + if (referredLhsType.isNullable() || referredRhsType.isNullable()) { - BType intOptional = BUnionType.create(null, symTable.intType, symTable.nilType); + BType intOptional = + BUnionType.create(symTable.typeEnv(), null, symTable.intType, symTable.nilType); return createBinaryOperator(opKind, lhsType, rhsType, intOptional); } return createBinaryOperator(opKind, lhsType, rhsType, symTable.intType); case BITWISE_OR: case BITWISE_XOR: if (referredLhsType.isNullable() || referredRhsType.isNullable()) { - BType intOptional = BUnionType.create(null, symTable.intType, symTable.nilType); + BType intOptional = + BUnionType.create(symTable.typeEnv(), null, symTable.intType, symTable.nilType); return createBinaryOperator(opKind, lhsType, rhsType, intOptional); } return createBinaryOperator(opKind, lhsType, rhsType, symTable.intType); @@ -2108,8 +2079,7 @@ public BSymbol getBinaryComparisonOpForTypeSets(OperatorKind opKind, BType lhsTy case LESS_EQUAL: case GREATER_THAN: case GREATER_EQUAL: - validOrderedTypesExist = types.isOrderedType(lhsType, false) && - types.isOrderedType(rhsType, false) && types.isSameOrderedType(lhsType, rhsType); + validOrderedTypesExist = types.comparable(lhsType, rhsType); break; default: return symTable.notFoundSymbol; @@ -2166,16 +2136,16 @@ public boolean isBinaryComparisonOperator(OperatorKind binaryOpKind) { } public boolean markParameterizedType(BType type, BType constituentType) { - if (Symbols.isFlagOn(constituentType.flags, Flags.PARAMETERIZED)) { + if (Symbols.isFlagOn(constituentType.getFlags(), Flags.PARAMETERIZED)) { type.tsymbol.flags |= Flags.PARAMETERIZED; - type.flags |= Flags.PARAMETERIZED; + type.addFlags(Flags.PARAMETERIZED); return true; } return false; } public void markParameterizedType(BType enclosingType, Collection constituentTypes) { - if (Symbols.isFlagOn(enclosingType.flags, Flags.PARAMETERIZED)) { + if (Symbols.isFlagOn(enclosingType.getFlags(), Flags.PARAMETERIZED)) { return; } @@ -2202,7 +2172,7 @@ private BSymbol resolveOperator(ScopeEntry entry, List typeList) { for (int i = 0; i < typeList.size(); i++) { BType t = Types.getImpliedType(typeList.get(i)); if ((t.getKind() == TypeKind.UNION) && (opType.paramTypes.get(i).getKind() == TypeKind.UNION)) { - if (!this.types.isSameType(t, opType.paramTypes.get(i))) { + if (!this.types.isSameTypeIncludingTags(t, opType.paramTypes.get(i))) { match = false; } } else if (t.tag != opType.paramTypes.get(i).tag) { @@ -2383,11 +2353,11 @@ private BType computeIntersectionType(BLangIntersectionTypeNode intersectionType } if (types.isInherentlyImmutableType(potentialIntersectionType) || - (Symbols.isFlagOn(potentialIntersectionType.flags, Flags.READONLY) && + (Symbols.isFlagOn(potentialIntersectionType.getFlags(), Flags.READONLY) && // For objects, a new type has to be created. !types.isSubTypeOfBaseType(potentialIntersectionType, TypeTags.OBJECT))) { BTypeSymbol effectiveTypeTSymbol = potentialIntersectionType.tsymbol; - return createIntersectionType(potentialIntersectionType, constituentBTypes, effectiveTypeTSymbol.pkgID, + return createIntersectionType(potentialIntersectionType, constituentBTypes, effectiveTypeTSymbol.pkgID, effectiveTypeTSymbol.owner, intersectionTypeNode.pos); } @@ -2457,8 +2427,8 @@ private BIntersectionType createIntersectionType(BType effectiveType, BTypeSymbol intersectionTypeSymbol = Symbols.createTypeSymbol(SymTag.INTERSECTION_TYPE, 0, Names.EMPTY, pkgId, null, owner, pos, VIRTUAL); - BIntersectionType intersectionType = - new BIntersectionType(intersectionTypeSymbol, constituentBTypes, effectiveType, effectiveType.flags); + BIntersectionType intersectionType = new BIntersectionType(intersectionTypeSymbol, constituentBTypes, + effectiveType, effectiveType.getFlags()); intersectionTypeSymbol.type = intersectionType; return intersectionType; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java index cef5cdbdf1a3..4429826f62da 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java @@ -20,6 +20,24 @@ import io.ballerina.identifier.Utils; import io.ballerina.tools.diagnostics.DiagnosticCode; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Core; +import io.ballerina.types.EnumerableCharString; +import io.ballerina.types.EnumerableString; +import io.ballerina.types.EnumerableType; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.CharStringSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.NonCharStringSubtype; +import io.ballerina.types.subtypedata.Range; +import io.ballerina.types.subtypedata.StringSubtype; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.AttachPoint; import org.ballerinalang.model.elements.Flag; @@ -85,6 +103,7 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLSubType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.compiler.tree.BLangAnnotationAttachment; import org.wso2.ballerinalang.compiler.tree.BLangClassDefinition; import org.wso2.ballerinalang.compiler.tree.BLangFunction; @@ -193,11 +212,11 @@ import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Deque; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -212,6 +231,10 @@ import javax.xml.XMLConstants; +import static io.ballerina.types.BasicTypeCode.BT_INT; +import static io.ballerina.types.BasicTypeCode.BT_STRING; +import static io.ballerina.types.Core.getComplexSubtypeData; +import static io.ballerina.types.Core.widenToBasicTypes; import static org.ballerinalang.model.symbols.SymbolOrigin.SOURCE; import static org.ballerinalang.model.symbols.SymbolOrigin.VIRTUAL; import static org.ballerinalang.util.diagnostic.DiagnosticErrorCode.INVALID_NUM_INSERTIONS; @@ -254,6 +277,7 @@ public class TypeChecker extends SimpleBLangNodeAnalyzer key) { @@ -343,6 +368,7 @@ public TypeChecker(CompilerContext context, CompilerContext.Key key this.missingNodesHelper = BLangMissingNodesHelper.getInstance(context); this.unifier = new Unifier(); this.queryTypeChecker = null; + this.typeEnv = types.typeEnv(); } private BType checkExpr(BLangExpression expr, SymbolEnv env, AnalyzerData data) { @@ -530,52 +556,39 @@ private void checkXMLNamespacePrefixes(List filters, Anal } private int getPreferredMemberTypeTag(BFiniteType finiteType) { - for (int i = TypeTags.INT; i <= TypeTags.DECIMAL; i++) { - for (BLangExpression valueExpr : finiteType.getValueSpace()) { - int typeTag = Types.getImpliedType(valueExpr.getBType()).tag; - if (typeTag > TypeTags.DECIMAL) { - continue; - } - - if (typeTag == i) { - return i; - } - } + BasicTypeBitSet basicTypeBitSet = widenToBasicTypes(finiteType.semType()); + if ((basicTypeBitSet.bitset & PredefinedType.INT.bitset) != 0) { + return TypeTags.INT; + } else if ((basicTypeBitSet.bitset & PredefinedType.FLOAT.bitset) != 0) { + return TypeTags.FLOAT; + } else if ((basicTypeBitSet.bitset & PredefinedType.DECIMAL.bitset) != 0) { + return TypeTags.DECIMAL; + } else { + return TypeTags.NONE; } - return TypeTags.NONE; } - private BType getFiniteTypeMatchWithIntType(BLangLiteral literalExpr, BFiniteType finiteType, AnalyzerData data) { + private BType getFiniteTypeMatchWithIntType(BLangNumericLiteral literalExpr, BFiniteType finiteType, + AnalyzerData data) { if (literalAssignableToFiniteType(literalExpr, finiteType, TypeTags.INT)) { setLiteralValueForFiniteType(literalExpr, symTable.intType, data); return symTable.intType; - } else if (literalAssignableToFiniteType(literalExpr, finiteType, TypeTags.BYTE)) { - setLiteralValueForFiniteType(literalExpr, symTable.byteType, data); - return symTable.byteType; - - } else { - for (int tag = TypeTags.SIGNED32_INT; tag <= TypeTags.UNSIGNED8_INT; tag++) { - if (literalAssignableToFiniteType(literalExpr, finiteType, tag)) { - setLiteralValueForFiniteType(literalExpr, symTable.getTypeFromTag(tag), data); - return symTable.getTypeFromTag(tag); - } - } } return symTable.noType; } - private BType getFiniteTypeMatchWithIntLiteral(BLangLiteral literalExpr, BFiniteType finiteType, + private BType getFiniteTypeMatchWithIntLiteral(BLangNumericLiteral literalExpr, BFiniteType finiteType, Object literalValue, BType compatibleType, AnalyzerData data) { BType intLiteralType = getFiniteTypeMatchWithIntType(literalExpr, finiteType, data); if (intLiteralType != symTable.noType) { return intLiteralType; } - + int typeTag = getPreferredMemberTypeTag(finiteType); if (typeTag == TypeTags.NONE) { return symTable.intType; } - + if (literalAssignableToFiniteType(literalExpr, finiteType, typeTag)) { BType type = symTable.getTypeFromTag(typeTag); setLiteralValueForFiniteType(literalExpr, type, data); @@ -596,7 +609,7 @@ private BType getFiniteTypeMatchWithIntLiteral(BLangLiteral literalExpr, BFinite return compatibleType; } - private BType silentIntTypeCheck(BLangLiteral literalExpr, Object literalValue, BType expType, + private BType silentIntTypeCheck(BLangNumericLiteral literalExpr, Object literalValue, BType expType, AnalyzerData data) { boolean prevNonErrorLoggingCheck = data.commonAnalyzerData.nonErrorLoggingCheck; data.commonAnalyzerData.nonErrorLoggingCheck = true; @@ -615,37 +628,32 @@ private BType silentIntTypeCheck(BLangLiteral literalExpr, Object literalValue, return exprCompatibleType; } - private BType silentCompatibleLiteralTypeCheck(BFiniteType finiteType, BLangLiteral literalExpr, - Object literalValue, AnalyzerData data) { + private BType checkIfOutOfRangeAndReturnType(BFiniteType finiteType, BLangNumericLiteral literalExpr, + Object literalValue, AnalyzerData data) { BType resIntegerLiteralType = symTable.semanticError; List compatibleTypes = new ArrayList<>(); - for (BLangExpression valueExpr : finiteType.getValueSpace()) { - resIntegerLiteralType = silentIntTypeCheck(literalExpr, literalValue, valueExpr.getBType(), data); + Set broadTypes = SemTypeHelper.broadTypes(finiteType, symTable); + for (BType broadType : broadTypes) { + resIntegerLiteralType = silentIntTypeCheck(literalExpr, literalValue, broadType, data); if (resIntegerLiteralType != symTable.semanticError) { compatibleTypes.add(resIntegerLiteralType); } } + for (int i = TypeTags.INT; i <= TypeTags.DECIMAL; i++) { - for (BType type: compatibleTypes) { + for (BType type : compatibleTypes) { if (Types.getReferredType(type).tag == i) { return type; } } } - return resIntegerLiteralType; - } - private BType checkIfOutOfRangeAndReturnType(BFiniteType finiteType, BLangLiteral literalExpr, Object literalValue, - AnalyzerData data) { - BType compatibleType = silentCompatibleLiteralTypeCheck(finiteType, literalExpr, literalValue, data); - if (compatibleType == symTable.semanticError) { - dlog.error(literalExpr.pos, DiagnosticErrorCode.OUT_OF_RANGE, literalExpr.originalValue, - literalExpr.getBType()); - } - return compatibleType; + dlog.error(literalExpr.pos, DiagnosticErrorCode.OUT_OF_RANGE, literalExpr.originalValue, + literalExpr.getBType()); + return resIntegerLiteralType; } - public BType getIntegerLiteralType(BLangLiteral literalExpr, Object literalValue, BType expType, + public BType getIntegerLiteralType(BLangNumericLiteral literalExpr, Object literalValue, BType expType, AnalyzerData data) { BType expectedType = Types.getImpliedType(expType); if (expectedType.tag == TypeTags.BYTE || TypeTags.isIntegerTypeTag(expectedType.tag)) { @@ -662,8 +670,8 @@ public BType getIntegerLiteralType(BLangLiteral literalExpr, Object literalValue expectedType); return symTable.semanticError; } - if (literalValue instanceof Double doubleValue) { - literalExpr.value = doubleValue.doubleValue(); + if (literalValue instanceof Double) { + literalExpr.value = literalValue; } else { literalExpr.value = ((Long) literalValue).doubleValue(); } @@ -727,7 +735,7 @@ public BType getIntegerLiteralType(BLangLiteral literalExpr, Object literalValue return symTable.intType; } - public BType getTypeOfLiteralWithFloatDiscriminator(BLangLiteral literalExpr, Object literalValue, + public BType getTypeOfLiteralWithFloatDiscriminator(BLangNumericLiteral literalExpr, Object literalValue, BType expType, AnalyzerData data) { String numericLiteral = NumericLiteralSupport.stripDiscriminator(String.valueOf(literalValue)); if (!types.validateFloatLiteral(literalExpr.pos, numericLiteral)) { @@ -751,7 +759,7 @@ public BType getTypeOfLiteralWithFloatDiscriminator(BLangLiteral literalExpr, Ob return symTable.floatType; } - public BType getTypeOfLiteralWithDecimalDiscriminator(BLangLiteral literalExpr, Object literalValue, + public BType getTypeOfLiteralWithDecimalDiscriminator(BLangNumericLiteral literalExpr, Object literalValue, BType expType, AnalyzerData data) { literalExpr.value = NumericLiteralSupport.stripDiscriminator(String.valueOf(literalValue)); if (!types.isValidDecimalNumber(literalExpr.pos, literalExpr.value.toString())) { @@ -774,8 +782,8 @@ public BType getTypeOfLiteralWithDecimalDiscriminator(BLangLiteral literalExpr, return symTable.decimalType; } - public BType getTypeOfDecimalFloatingPointLiteral(BLangLiteral literalExpr, Object literalValue, BType expType, - AnalyzerData data) { + public BType getTypeOfDecimalFloatingPointLiteral(BLangNumericLiteral literalExpr, Object literalValue, + BType expType, AnalyzerData data) { BType expectedType = Types.getImpliedType(expType); String numericLiteral = String.valueOf(literalValue); if (expectedType != null) { @@ -791,25 +799,22 @@ public BType getTypeOfDecimalFloatingPointLiteral(BLangLiteral literalExpr, Obje } return symTable.floatType; } else if (expectedType.tag == TypeTags.FINITE) { - BFiniteType finiteType = (BFiniteType) expectedType; - for (int tag = TypeTags.FLOAT; tag <= TypeTags.DECIMAL; tag++) { - BType literalValueType = null; - for (BLangExpression valueExpr : finiteType.getValueSpace()) { - if (valueExpr.getBType().tag == tag) { - if (types.checkLiteralAssignabilityBasedOnType((BLangLiteral) valueExpr, literalExpr)) { - BType valueType = setLiteralValueAndGetType(literalExpr, - symTable.getTypeFromTag(tag), data); - setLiteralValueForFiniteType(literalExpr, valueType, data); - return valueType; - } - literalValueType = valueExpr.getBType(); - } - } - if (literalValueType != null) { - return literalValueType; - } + BType basicType; + BasicTypeBitSet basicTypeBitSet = widenToBasicTypes(expectedType.semType()); + if ((basicTypeBitSet.bitset & PredefinedType.FLOAT.bitset) != 0) { + basicType = symTable.floatType; + } else if ((basicTypeBitSet.bitset & PredefinedType.DECIMAL.bitset) != 0) { + basicType = symTable.decimalType; + } else { + return literalExpr.getBType(); } - return literalExpr.getBType(); + + if (literalAssignableToFiniteType(literalExpr, (BFiniteType) expectedType, basicType.tag)) { + BType valueType = setLiteralValueAndGetType(literalExpr, basicType, data); + setLiteralValueForFiniteType(literalExpr, valueType, data); + return valueType; + } + return basicType; } else if (expectedType.tag == TypeTags.UNION) { BUnionType unionType = (BUnionType) expectedType; for (int tag = TypeTags.FLOAT; tag <= TypeTags.DECIMAL; tag++) { @@ -828,7 +833,7 @@ public BType getTypeOfDecimalFloatingPointLiteral(BLangLiteral literalExpr, Obje ? symTable.floatType : symTable.semanticError; } - public BType getTypeOfHexFloatingPointLiteral(BLangLiteral literalExpr, Object literalValue, BType expType, + public BType getTypeOfHexFloatingPointLiteral(BLangNumericLiteral literalExpr, Object literalValue, BType expType, AnalyzerData data) { String numericLiteral = String.valueOf(literalValue); if (!types.validateFloatLiteral(literalExpr.pos, numericLiteral)) { @@ -858,19 +863,19 @@ public BType setLiteralValueAndGetType(BLangLiteral literalExpr, BType expType, BType expectedType = Types.getImpliedType(expType); if (literalExpr.getKind() == NodeKind.NUMERIC_LITERAL) { - NodeKind kind = ((BLangNumericLiteral) literalExpr).kind; - if (kind == NodeKind.INTEGER_LITERAL) { - return getIntegerLiteralType(literalExpr, literalValue, expectedType, data); - } else if (kind == NodeKind.DECIMAL_FLOATING_POINT_LITERAL) { - if (NumericLiteralSupport.isFloatDiscriminated(literalExpr.originalValue)) { - return getTypeOfLiteralWithFloatDiscriminator(literalExpr, literalValue, expectedType, data); - } else if (NumericLiteralSupport.isDecimalDiscriminated(literalExpr.originalValue)) { - return getTypeOfLiteralWithDecimalDiscriminator(literalExpr, literalValue, expectedType, data); + BLangNumericLiteral numericLiteral = (BLangNumericLiteral) literalExpr; + if (numericLiteral.kind == NodeKind.INTEGER_LITERAL) { + return getIntegerLiteralType(numericLiteral, literalValue, expectedType, data); + } else if (numericLiteral.kind == NodeKind.DECIMAL_FLOATING_POINT_LITERAL) { + if (NumericLiteralSupport.isFloatDiscriminated(numericLiteral.originalValue)) { + return getTypeOfLiteralWithFloatDiscriminator(numericLiteral, literalValue, expectedType, data); + } else if (NumericLiteralSupport.isDecimalDiscriminated(numericLiteral.originalValue)) { + return getTypeOfLiteralWithDecimalDiscriminator(numericLiteral, literalValue, expectedType, data); } else { - return getTypeOfDecimalFloatingPointLiteral(literalExpr, literalValue, expectedType, data); + return getTypeOfDecimalFloatingPointLiteral(numericLiteral, literalValue, expectedType, data); } } else { - return getTypeOfHexFloatingPointLiteral(literalExpr, literalValue, expectedType, data); + return getTypeOfHexFloatingPointLiteral(numericLiteral, literalValue, expectedType, data); } } @@ -928,9 +933,8 @@ public BType setLiteralValueAndGetType(BLangLiteral literalExpr, BType expType, if (referedType.tag == TypeTags.BYTE_ARRAY) { // check whether this is a byte array byte[] byteArray = types.convertToByteArray((String) literalExpr.value); - literalType = new BArrayType(symTable.byteType, null, byteArray.length, - BArrayState.CLOSED); - if (Symbols.isFlagOn(expectedType.flags, Flags.READONLY)) { + literalType = new BArrayType(typeEnv, symTable.byteType, null, byteArray.length, BArrayState.CLOSED); + if (Symbols.isFlagOn(expectedType.getFlags(), Flags.READONLY)) { literalType = ImmutableTypeCloner.getEffectiveImmutableType(literalExpr.pos, types, literalType, data.env, symTable, anonymousModelHelper, names); } @@ -938,7 +942,7 @@ public BType setLiteralValueAndGetType(BLangLiteral literalExpr, BType expType, if (expectedType.tag == TypeTags.ARRAY) { BArrayType arrayType = (BArrayType) expectedType; if (arrayType.state == BArrayState.INFERRED) { - arrayType.size = byteArray.length; + arrayType.setSize(byteArray.length); arrayType.state = BArrayState.CLOSED; } } @@ -969,7 +973,8 @@ private BType getTypeMatchingFloatOrDecimal(BType finiteType, List member } } if (finiteType.tag == TypeTags.FINITE) { - return checkIfOutOfRangeAndReturnType((BFiniteType) finiteType, literalExpr, literalExpr.value, data); + return checkIfOutOfRangeAndReturnType((BFiniteType) finiteType, (BLangNumericLiteral) literalExpr, + literalExpr.value, data); } return symTable.intType; } @@ -1000,15 +1005,9 @@ private BType getAndSetAssignableUnionMember(BLangLiteral literalExpr, BUnionTyp return symTable.noType; } - private boolean literalAssignableToFiniteType(BLangLiteral literalExpr, BFiniteType finiteType, - int targetMemberTypeTag) { - for (BLangExpression valueExpr : finiteType.getValueSpace()) { - if (Types.getImpliedType(valueExpr.getBType()).tag == targetMemberTypeTag && - types.checkLiteralAssignabilityBasedOnType((BLangLiteral) valueExpr, literalExpr)) { - return true; - } - } - return false; + private boolean literalAssignableToFiniteType(BLangNumericLiteral literalExpr, BFiniteType finiteType, + int targetTypeTag) { + return types.checkLiteralAssignabilityBasedOnType(literalExpr, finiteType, targetTypeTag); } public void setLiteralValueForFiniteType(BLangLiteral literalExpr, BType type, AnalyzerData data) { @@ -1018,6 +1017,9 @@ public void setLiteralValueForFiniteType(BLangLiteral literalExpr, BType type, A } private BType getFiniteTypeWithValuesOfSingleType(BUnionType unionType, BType matchType) { + assert matchType.tag == TypeTags.BYTE || matchType.tag == TypeTags.INT || + matchType.tag == TypeTags.FLOAT || matchType.tag == TypeTags.DECIMAL; + List finiteTypeMembers = types.getAllTypes(unionType, true).stream() .filter(memType -> Types.getImpliedType(memType).tag == TypeTags.FINITE) .map(memFiniteType -> (BFiniteType) memFiniteType) @@ -1027,24 +1029,20 @@ private BType getFiniteTypeWithValuesOfSingleType(BUnionType unionType, BType ma return symTable.semanticError; } - int tag = Types.getImpliedType(matchType).tag; - Set matchedValueSpace = new LinkedHashSet<>(); - + List newValueSpace = new ArrayList<>(); for (BFiniteType finiteType : finiteTypeMembers) { - Set set = new HashSet<>(); - for (BLangExpression expression : finiteType.getValueSpace()) { - if (Types.getImpliedType(expression.getBType()).tag == tag) { - set.add(expression); + for (SemNamedType semNamedType : finiteType.valueSpace) { + if (SemTypes.isSubtype(types.semTypeCtx, semNamedType.semType(), matchType.semType())) { + newValueSpace.add(semNamedType); } } - matchedValueSpace.addAll(set); } - if (matchedValueSpace.isEmpty()) { + if (newValueSpace.isEmpty()) { return symTable.semanticError; } - return new BFiniteType(null, matchedValueSpace); + return new BFiniteType(null, newValueSpace.toArray(SemNamedType[]::new)); } private BType getIntLiteralType(BType expType, Object literalValue, AnalyzerData data) { @@ -1155,7 +1153,7 @@ public void visit(BLangTableConstructorExpr tableConstructorExpr, AnalyzerData d recordLiteral.setBType(inherentMemberType); } } - BTableType tableType = new BTableType(TypeTags.TABLE, inherentMemberType, null); + BTableType tableType = new BTableType(typeEnv, inherentMemberType, null); if (!validateTableConstructorExpr(tableConstructorExpr, tableType, data)) { data.resultType = symTable.semanticError; return; @@ -1200,11 +1198,10 @@ public void visit(BLangTableConstructorExpr tableConstructorExpr, AnalyzerData d return; } - BTableType tableType = new BTableType(TypeTags.TABLE, inferTableMemberType(memTypes, applicableExpType), - null); + BTableType tableType = new BTableType(typeEnv, inferTableMemberType(memTypes, applicableExpType), null); - if (Symbols.isFlagOn(applicableExpType.flags, Flags.READONLY)) { - tableType.flags |= Flags.READONLY; + if (Symbols.isFlagOn(applicableExpType.getFlags(), Flags.READONLY)) { + tableType.addFlags(Flags.READONLY); } if (checkKeySpecifier(tableConstructorExpr, tableType, data)) { @@ -1240,7 +1237,7 @@ public void visit(BLangTableConstructorExpr tableConstructorExpr, AnalyzerData d BType resultType = checkExpr(clonedTableExpr, memType, data); if (resultType != symTable.semanticError && dlog.errorCount() == 0 && - isUniqueType(matchingTypes, resultType)) { + types.isUniqueType(matchingTypes, resultType)) { matchingTypes.add(resultType); } } @@ -1288,7 +1285,7 @@ private BType getInferredTableType(BLangTableConstructorExpr exprToLog, Analyzer } } - return new BTableType(TypeTags.TABLE, inferTableMemberType(memTypes, exprToLog, data), null); + return new BTableType(typeEnv, inferTableMemberType(memTypes, exprToLog, data), null); } private boolean checkKeySpecifier(BLangTableConstructorExpr tableConstructorExpr, BTableType tableType, @@ -1314,12 +1311,12 @@ private BType inferTableMemberType(List memTypes, BType expType) { result.add(memTypes.get(0)); - BUnionType unionType = BUnionType.create(null, result); + BUnionType unionType = BUnionType.create(typeEnv, null, result); for (int i = 1; i < memTypes.size(); i++) { BType source = memTypes.get(i); if (!types.isAssignable(source, unionType)) { result.add(source); - unionType = BUnionType.create(null, result); + unionType = BUnionType.create(typeEnv, null, result); } } @@ -1475,7 +1472,7 @@ private BRecordType createTableConstraintRecordType(Set inferredFields, recordSymbol.scope.define(field.name, field.symbol); } - BRecordType recordType = new BRecordType(recordSymbol); + BRecordType recordType = new BRecordType(typeEnv, recordSymbol); recordType.fields = inferredFields.stream().collect(getFieldCollector()); recordSymbol.type = recordType; @@ -1788,7 +1785,7 @@ private BType createTableKeyConstraint(List fieldNames, BType constraint return memTypes.get(0).type; } - return new BTupleType(memTypes); + return new BTupleType(typeEnv, memTypes); } protected BType checkListConstructorCompatibility(BType bType, BLangListConstructorExpr listConstructor, @@ -1826,7 +1823,7 @@ private BType checkListConstructorCompatibility(BType referredType, BType origin BType memCompatibiltyType = checkListConstructorCompatibility(listCompatibleMemType, listConstructor, data); if (memCompatibiltyType != symTable.semanticError && dlog.errorCount() == 0 && - isUniqueType(compatibleTypes, memCompatibiltyType)) { + types.isUniqueType(compatibleTypes, memCompatibiltyType)) { compatibleTypes.add(memCompatibiltyType); } } @@ -1899,14 +1896,14 @@ private BType checkListConstructorCompatibility(BType referredType, BType origin List members = new ArrayList<>(); inferredTupleDetails.fixedMemberTypes.forEach(memberType -> members.add(new BTupleMember(memberType, - new BVarSymbol(memberType.flags, null, null, memberType, null, null, null)))); - BTupleType tupleType = new BTupleType(members); + new BVarSymbol(memberType.getFlags(), null, null, memberType, null, null, null)))); + BTupleType tupleType = new BTupleType(typeEnv, members); if (!inferredTupleDetails.restMemberTypes.isEmpty()) { tupleType.restType = getRepresentativeBroadType(inferredTupleDetails.restMemberTypes); } listConstructor.typedescType = tupleType; - return new BTypedescType(listConstructor.typedescType, null); + return new BTypedescType(typeEnv, listConstructor.typedescType, null); } if (referredType == symTable.semanticError) { @@ -1951,7 +1948,7 @@ private void updateInferredTupleDetailsFromSpreadMember(Location spreadMemberPos } else if (spreadOpExprType.tag == TypeTags.ARRAY) { BArrayType bArrayType = (BArrayType) spreadOpExprType; if (bArrayType.state == BArrayState.CLOSED) { - for (int i = 0; i < bArrayType.size; i++) { + for (int i = 0; i < bArrayType.getSize(); i++) { BType memberType = bArrayType.eType; inferredTupleDetails.fixedMemberTypes.add(memberType); } @@ -1971,13 +1968,14 @@ private BType getListConstructorCompatibleNonUnionType(BType type, AnalyzerData TypeTags.TUPLE, TypeTags.READONLY, TypeTags.TYPEDESC -> type; - case TypeTags.JSON -> !Symbols.isFlagOn(referredType.flags, Flags.READONLY) ? symTable.arrayJsonType : + case TypeTags.JSON -> !Symbols.isFlagOn(referredType.getFlags(), Flags.READONLY) ? symTable.arrayJsonType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.arrayJsonType, data.env, symTable, anonymousModelHelper, names); - case TypeTags.ANYDATA -> !Symbols.isFlagOn(referredType.flags, Flags.READONLY) ? symTable.arrayAnydataType : + case TypeTags.ANYDATA -> !Symbols.isFlagOn(referredType.getFlags(), Flags.READONLY) ? + symTable.arrayAnydataType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.arrayAnydataType, data.env, symTable, anonymousModelHelper, names); - case TypeTags.ANY -> !Symbols.isFlagOn(referredType.flags, Flags.READONLY) ? symTable.arrayAllType : + case TypeTags.ANY -> !Symbols.isFlagOn(referredType.getFlags(), Flags.READONLY) ? symTable.arrayAllType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.arrayAllType, data.env, symTable, anonymousModelHelper, names); default -> symTable.semanticError; @@ -1999,7 +1997,7 @@ private BType checkArrayType(BLangListConstructorExpr listConstructor, BArrayTyp switch (spreadOpType.tag) { case TypeTags.ARRAY: - int arraySize = ((BArrayType) spreadOpType).size; + int arraySize = ((BArrayType) spreadOpType).getSize(); if (arraySize >= 0) { listExprSize += arraySize; continue; @@ -2025,11 +2023,12 @@ private BType checkArrayType(BLangListConstructorExpr listConstructor, BArrayTyp BType eType = arrayType.eType; if (arrayType.state == BArrayState.INFERRED) { - arrayType.size = listExprSize; + arrayType.setSize(listExprSize); arrayType.state = BArrayState.CLOSED; - } else if (arrayType.state != BArrayState.OPEN && arrayType.size != listExprSize) { - if (arrayType.size < listExprSize) { - dlog.error(listConstructor.pos, DiagnosticErrorCode.MISMATCHING_ARRAY_LITERAL_VALUES, arrayType.size, + } else if (arrayType.state != BArrayState.OPEN && arrayType.getSize() != listExprSize) { + if (arrayType.getSize() < listExprSize) { + dlog.error(listConstructor.pos, DiagnosticErrorCode.MISMATCHING_ARRAY_LITERAL_VALUES, + arrayType.getSize(), listExprSize); return symTable.semanticError; } @@ -2103,7 +2102,7 @@ private BType checkTupleType(BLangListConstructorExpr listConstructor, BTupleTyp switch (spreadOpType.tag) { case TypeTags.ARRAY: - int arraySize = ((BArrayType) spreadOpType).size; + int arraySize = ((BArrayType) spreadOpType).getSize(); if (arraySize >= 0) { listExprSize += arraySize; continue; @@ -2157,7 +2156,7 @@ private BType checkTupleType(BLangListConstructorExpr listConstructor, BTupleTyp BLangExpression spreadOpExpr = ((BLangListConstructorSpreadOpExpr) expr).expr; BType spreadOpType; if (restType != null && restType != symTable.noType && remainNonRestCount == 0) { - BType targetType = new BArrayType(restType); + BType targetType = new BArrayType(typeEnv, restType); BType possibleType = silentTypeCheckExpr(spreadOpExpr, targetType, data); if (possibleType == symTable.semanticError) { spreadOpType = checkExpr(spreadOpExpr, data); @@ -2173,7 +2172,7 @@ private BType checkTupleType(BLangListConstructorExpr listConstructor, BTupleTyp case TypeTags.ARRAY: BArrayType spreadOpArray = (BArrayType) spreadOpReferredType; if (spreadOpArray.state == BArrayState.CLOSED) { - for (int i = 0; i < spreadOpArray.size && nonRestTypeIndex < memberTypeSize; + for (int i = 0; i < spreadOpArray.getSize() && nonRestTypeIndex < memberTypeSize; i++, nonRestTypeIndex++) { if (types.typeIncompatible(spreadOpExpr.pos, spreadOpArray.eType, members.get(nonRestTypeIndex).type)) { @@ -2181,7 +2180,7 @@ private BType checkTupleType(BLangListConstructorExpr listConstructor, BTupleTyp } } - if (remainNonRestCount < spreadOpArray.size) { + if (remainNonRestCount < spreadOpArray.getSize()) { if (types.typeIncompatible(spreadOpExpr.pos, spreadOpArray.eType, restType)) { return symTable.semanticError; } @@ -2357,8 +2356,8 @@ protected BType getInferredTupleType(BLangListConstructorExpr listConstructor, B } List members = new ArrayList<>(); fixedMemberTypes.forEach(memberType -> members.add(new BTupleMember(memberType, - new BVarSymbol(memberType.flags, null, null, memberType, null, null, null)))); - BTupleType tupleType = new BTupleType(members); + new BVarSymbol(memberType.getFlags(), null, null, memberType, null, null, null)))); + BTupleType tupleType = new BTupleType(typeEnv, members); if (!restMemberTypes.isEmpty()) { tupleType.restType = getRepresentativeBroadType(restMemberTypes); } @@ -2367,11 +2366,10 @@ protected BType getInferredTupleType(BLangListConstructorExpr listConstructor, B return tupleType; } - tupleType.flags |= Flags.READONLY; + tupleType.addFlags(Flags.READONLY); return tupleType; } - @Override public void visit(BLangRecordLiteral recordLiteral, AnalyzerData data) { BType expType = data.expType; @@ -2393,7 +2391,7 @@ public BType getEffectiveMappingType(BLangRecordLiteral recordLiteral, BType app AnalyzerData data) { BType refType = Types.getImpliedType(applicableMappingType); if (applicableMappingType == symTable.semanticError || - (refType.tag == TypeTags.RECORD && Symbols.isFlagOn(applicableMappingType.flags, + (refType.tag == TypeTags.RECORD && Symbols.isFlagOn(applicableMappingType.getFlags(), Flags.READONLY))) { return applicableMappingType; } @@ -2472,7 +2470,7 @@ public BType getEffectiveMappingType(BLangRecordLiteral recordLiteral, BType app recordSymbol.scope.define(fieldName, fieldSymbol); } - BRecordType recordType = new BRecordType(recordSymbol, recordSymbol.flags); + BRecordType recordType = new BRecordType(typeEnv, recordSymbol, recordSymbol.flags); if (refType.tag == TypeTags.MAP) { recordType.sealed = false; recordType.restFieldType = ((BMapType) refType).constraint; @@ -2506,7 +2504,7 @@ public BType getEffectiveMappingType(BLangRecordLiteral recordLiteral, BType app recordType.restFieldType = applicableRecordType.restFieldType; if (recordType.sealed && allReadOnlyFields) { - recordType.flags |= Flags.READONLY; + recordType.addFlags(Flags.READONLY); recordType.tsymbol.flags |= Flags.READONLY; } @@ -2568,7 +2566,7 @@ public BType checkMappingConstructorCompatibility(BType bType, BLangRecordLitera mappingConstructor, data); if (memCompatibiltyType != symTable.semanticError && dlog.errorCount() == 0 && - isUniqueType(compatibleTypes, memCompatibiltyType)) { + types.isUniqueType(compatibleTypes, memCompatibiltyType)) { compatibleTypes.add(memCompatibiltyType); } } @@ -2658,15 +2656,15 @@ private BType getMappingConstructorCompatibleNonUnionType(BType type, AnalyzerDa case TypeTags.READONLY: return type; case TypeTags.JSON: - return !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.mapJsonType : + return !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.mapJsonType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.mapJsonType, data.env, symTable, anonymousModelHelper, names); case TypeTags.ANYDATA: - return !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.mapAnydataType : + return !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.mapAnydataType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.mapAnydataType, data.env, symTable, anonymousModelHelper, names); case TypeTags.ANY: - return !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.mapAllType : + return !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.mapAllType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.mapAllType, data.env, symTable, anonymousModelHelper, names); case TypeTags.INTERSECTION: @@ -2898,7 +2896,7 @@ public void visit(BLangWorkerFlushExpr workerFlushExpr, AnalyzerData data) { } } } - BType actualType = BUnionType.create(null, symTable.errorType, symTable.nilType); + BType actualType = BUnionType.create(typeEnv, null, symTable.errorType, symTable.nilType); data.resultType = types.checkType(workerFlushExpr, actualType, data.expType); } @@ -3199,11 +3197,10 @@ public void visit(BLangSimpleVarRef varRefExpr, AnalyzerData data) { if (symbol.kind == SymbolKind.TYPE_DEF) { BTypeDefinitionSymbol typeDefSym = (BTypeDefinitionSymbol) symbol; actualType = Types.getImpliedType(symbol.type).tag == TypeTags.TYPEDESC ? - typeDefSym.referenceType - : new BTypedescType(typeDefSym.referenceType, null); + typeDefSym.referenceType : new BTypedescType(typeEnv, typeDefSym.referenceType, null); } else { actualType = symbol.type.tag == TypeTags.TYPEDESC ? symbol.type - : new BTypedescType(symbol.type, null); + : new BTypedescType(typeEnv, symbol.type, null); } varRefExpr.symbol = symbol; } else if ((symbol.tag & SymTag.CONSTANT) == SymTag.CONSTANT) { @@ -3295,7 +3292,7 @@ public void visit(BLangRecordVarRef varRefExpr, AnalyzerData data) { return; } - BRecordType bRecordType = new BRecordType(recordSymbol); + BRecordType bRecordType = new BRecordType(typeEnv, recordSymbol); bRecordType.fields = fields; recordSymbol.type = bRecordType; varRefExpr.symbol = new BVarSymbol(0, recordSymbol.name, recordSymbol.getOriginalName(), @@ -3419,8 +3416,8 @@ public void visit(BLangErrorVarRef varRefExpr, AnalyzerData data) { BType errorDetailType = errorRefRestFieldType == symTable.anydataOrReadonly ? symTable.errorType.detailType - : new BMapType(TypeTags.MAP, errorRefRestFieldType, null, Flags.PUBLIC); - data.resultType = new BErrorType(symTable.errorType.tsymbol, errorDetailType); + : new BMapType(typeEnv, TypeTags.MAP, errorRefRestFieldType, null, Flags.PUBLIC); + data.resultType = new BErrorType(typeEnv, symTable.errorType.tsymbol, errorDetailType); } private void checkIndirectErrorVarRef(BLangErrorVarRef varRefExpr, AnalyzerData data) { @@ -3450,11 +3447,11 @@ public void visit(BLangTupleVarRef varRefExpr, AnalyzerData data) { for (int i = 0; i < varRefExpr.expressions.size(); i++) { ((BLangVariableReference) varRefExpr.expressions.get(i)).isLValue = true; BType memberType = checkExpr(varRefExpr.expressions.get(i), symTable.noType, data); - BVarSymbol varSymbol = new BVarSymbol(memberType.flags, null, null, memberType, null, + BVarSymbol varSymbol = new BVarSymbol(memberType.getFlags(), null, null, memberType, null, null, null); results.add(new BTupleMember(memberType, varSymbol)); } - BTupleType actualType = new BTupleType(results); + BTupleType actualType = new BTupleType(typeEnv, results); if (varRefExpr.restParam != null) { BLangExpression restExpr = varRefExpr.restParam; ((BLangVariableReference) restExpr).isLValue = true; @@ -3604,7 +3601,7 @@ private void checkFieldBasedAccess(BLangFieldBasedAccess fieldAccessExpr, boolea private boolean isAllReadonlyTypes(BType type) { type = Types.getImpliedType(type); if (type.tag != TypeTags.UNION) { - return Symbols.isFlagOn(type.flags, Flags.READONLY); + return Symbols.isFlagOn(type.getFlags(), Flags.READONLY); } for (BType memberType : ((BUnionType) type).getMemberTypes()) { @@ -3626,7 +3623,7 @@ private boolean isInitializationInInit(BType type, AnalyzerData data) { private boolean isInvalidReadonlyFieldUpdate(BType type, String fieldName) { if (Types.getImpliedType(type).tag == TypeTags.RECORD) { - if (Symbols.isFlagOn(type.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(type.getFlags(), Flags.READONLY)) { return true; } @@ -3660,11 +3657,11 @@ private boolean isXmlAccess(BLangFieldBasedAccess fieldAccessExpr) { return true; } - if (expr.getKind() == NodeKind.FIELD_BASED_ACCESS_EXPR && hasLaxOriginalType((BLangFieldBasedAccess) expr) + if (expr.getKind() == NodeKind.FIELD_BASED_ACCESS_EXPR && hasLaxOriginalType((BLangFieldBasedAccess) expr) && exprType.tag == TypeTags.UNION) { - Set memberTypes = ((BUnionType) exprType).getMemberTypes(); - return memberTypes.contains(symTable.xmlType) || memberTypes.contains(symTable.xmlElementType); - } + SemType s = exprType.semType(); + return SemTypes.containsType(types.semTypeCtx, s, PredefinedType.XML_ELEMENT); + } return false; } @@ -3798,7 +3795,7 @@ public void visit(BLangErrorConstructorExpr errorConstructorExpr, AnalyzerData d if (errorDetailTypes.size() == 1) { detailCandidate = errorDetailTypes.get(0); } else { - detailCandidate = BUnionType.create(null, new LinkedHashSet<>(errorDetailTypes)); + detailCandidate = BUnionType.create(typeEnv, null, new LinkedHashSet<>(errorDetailTypes)); } BLangRecordLiteral recordLiteral = createRecordLiteralForErrorConstructor(errorConstructorExpr); @@ -4001,11 +3998,12 @@ private List expandExpectedErrorTypes(BType candidateType) { List expandedCandidates = new ArrayList<>(); if (referredType.tag == TypeTags.UNION) { for (BType memberType : ((BUnionType) referredType).getMemberTypes()) { - if (types.isAssignable(memberType, symTable.errorType)) { + if (memberType.tag != TypeTags.SEMANTIC_ERROR && types.isAssignable(memberType, symTable.errorType)) { expandedCandidates.add(memberType); } } - } else if (types.isAssignable(candidateType, symTable.errorType)) { + } else if (candidateType.tag != TypeTags.SEMANTIC_ERROR && + types.isAssignable(candidateType, symTable.errorType)) { expandedCandidates.add(candidateType); } @@ -4115,7 +4113,7 @@ private BTupleType getResourcePathType(List pathSegm pathSegmentCount--; } - BTupleType resourcePathType = new BTupleType(new ArrayList<>()); + BTupleType resourcePathType = new BTupleType(typeEnv, new ArrayList<>()); if (pathSegmentCount > 0 && lastPathSegmentSym.kind != SymbolKind.RESOURCE_ROOT_PATH_SEGMENT) { for (BResourcePathSegmentSymbol s : pathSegmentSymbols.subList(0, pathSegmentCount)) { BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(s.type); @@ -4142,7 +4140,8 @@ public boolean validateResourceAccessPathSegmentTypes(BLangListConstructorExpr r if (clonedPathSegment.getKind() == NodeKind.LIST_CONSTRUCTOR_SPREAD_OP) { BLangExpression spreadOpExpr = ((BLangListConstructorSpreadOpExpr) clonedPathSegment).expr; BType pathSegmentType = checkExpr(spreadOpExpr, data); - if (!types.isAssignable(pathSegmentType, new BArrayType(symTable.pathParamAllowedType))) { + if (!types.isAssignable(pathSegmentType, + new BArrayType(typeEnv, symTable.pathParamAllowedType))) { dlog.error(clonedPathSegment.getPosition(), DiagnosticErrorCode.UNSUPPORTED_RESOURCE_ACCESS_REST_SEGMENT_TYPE, pathSegmentType); isValidResourceAccessPathSegmentTypes = false; @@ -4256,7 +4255,7 @@ private void checkInLangLib(BLangInvocation iExpr, BType varRefType, AnalyzerDat private boolean checkInvalidImmutableValueUpdate(BLangInvocation iExpr, BType varRefType, BSymbol langLibMethodSymbol, AnalyzerData data) { - if (!Symbols.isFlagOn(varRefType.flags, Flags.READONLY)) { + if (!Symbols.isFlagOn(varRefType.getFlags(), Flags.READONLY)) { return false; } @@ -4283,33 +4282,13 @@ private boolean checkInvalidImmutableValueUpdate(BLangInvocation iExpr, BType va return true; } - private boolean isFixedLengthList(BType type) { - type = Types.getImpliedType(type); - switch(type.tag) { - case TypeTags.ARRAY: - return (((BArrayType) type).state != BArrayState.OPEN); - case TypeTags.TUPLE: - return (((BTupleType) type).restType == null); - case TypeTags.UNION: - BUnionType unionType = (BUnionType) type; - for (BType member : unionType.getMemberTypes()) { - if (!isFixedLengthList(member)) { - return false; - } - } - return true; - default: - return false; - } - } - private void checkIllegalStorageSizeChangeMethodCall(BLangInvocation iExpr, BType varRefType, AnalyzerData data) { String invocationName = iExpr.name.getValue(); if (!LIST_LENGTH_MODIFIER_FUNCTIONS.contains(invocationName)) { return; } - if (isFixedLengthList(varRefType)) { + if (types.isFixedLengthList(varRefType)) { dlog.error(iExpr.name.pos, DiagnosticErrorCode.ILLEGAL_FUNCTION_CHANGE_LIST_SIZE, invocationName, varRefType); data.resultType = symTable.semanticError; @@ -4420,9 +4399,9 @@ public void visit(BLangObjectConstructorExpression objectCtorExpression, Analyze classNode.oceEnvData.typeInit = objectCtorExpression.typeInit; dlog.unmute(); - if (Symbols.isFlagOn(data.expType.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(data.expType.getFlags(), Flags.READONLY)) { handleObjectConstrExprForReadOnly(objectCtorExpression, actualObjectType, typeDefEnv, false, data); - } else if (!typeRefs.isEmpty() && Symbols.isFlagOn(typeRefs.get(0).getBType().flags, + } else if (!typeRefs.isEmpty() && Symbols.isFlagOn(typeRefs.get(0).getBType().getFlags(), Flags.READONLY)) { handleObjectConstrExprForReadOnly(objectCtorExpression, actualObjectType, typeDefEnv, true, data); } else { @@ -4677,7 +4656,7 @@ private BType checkObjectType(BType actualType, BLangTypeInit cIExpr, AnalyzerDa } private BUnionType createNextReturnType(Location pos, BStreamType streamType, AnalyzerData data) { - BRecordType recordType = new BRecordType(null, Flags.ANONYMOUS); + BRecordType recordType = new BRecordType(typeEnv, null, Flags.ANONYMOUS); recordType.restFieldType = symTable.noType; recordType.sealed = true; @@ -4698,7 +4677,7 @@ private BUnionType createNextReturnType(Location pos, BStreamType streamType, An retTypeMembers.add(recordType); retTypeMembers.addAll(types.getAllTypes(streamType.completionType, false)); - BUnionType unionType = BUnionType.create(null); + BUnionType unionType = BUnionType.create(typeEnv, null); unionType.addAll(retTypeMembers); unionType.tsymbol = Symbols.createTypeSymbol(SymTag.UNION_TYPE, 0, Names.EMPTY, data.env.enclPkg.symbol.pkgID, unionType, data.env.scope.owner, pos, VIRTUAL); @@ -4728,7 +4707,7 @@ private BType getObjectConstructorReturnType(BType objType, BType initRetType, A retTypeMembers.addAll(((BUnionType) initRetType).getMemberTypes()); retTypeMembers.remove(symTable.nilType); - BUnionType unionType = BUnionType.create(null, retTypeMembers); + BUnionType unionType = BUnionType.create(typeEnv, null, retTypeMembers); unionType.tsymbol = Symbols.createTypeSymbol(SymTag.UNION_TYPE, 0, Names.EMPTY, data.env.enclPkg.symbol.pkgID, unionType, data.env.scope.owner, symTable.builtinPos, VIRTUAL); @@ -4904,24 +4883,24 @@ private void setResultTypeForWaitForAllExpr(BLangWaitForAllExpr waitForAllExpr, checkTypesForMap(waitForAllExpr, ((BMapType) referredType).constraint, data); LinkedHashSet memberTypesForMap = collectWaitExprTypes(waitForAllExpr.keyValuePairs); if (memberTypesForMap.size() == 1) { - data.resultType = new BMapType(TypeTags.MAP, + data.resultType = new BMapType(typeEnv, TypeTags.MAP, memberTypesForMap.iterator().next(), symTable.mapType.tsymbol); break; } - BUnionType constraintTypeForMap = BUnionType.create(null, memberTypesForMap); - data.resultType = new BMapType(TypeTags.MAP, constraintTypeForMap, symTable.mapType.tsymbol); + BUnionType constraintTypeForMap = BUnionType.create(typeEnv, null, memberTypesForMap); + data.resultType = new BMapType(typeEnv, TypeTags.MAP, constraintTypeForMap, symTable.mapType.tsymbol); break; case TypeTags.NONE: case TypeTags.ANY: checkTypesForMap(waitForAllExpr, expType, data); LinkedHashSet memberTypes = collectWaitExprTypes(waitForAllExpr.keyValuePairs); if (memberTypes.size() == 1) { - data.resultType = - new BMapType(TypeTags.MAP, memberTypes.iterator().next(), symTable.mapType.tsymbol); + data.resultType = new BMapType(typeEnv, TypeTags.MAP, memberTypes.iterator().next(), + symTable.mapType.tsymbol); break; } - BUnionType constraintType = BUnionType.create(null, memberTypes); - data.resultType = new BMapType(TypeTags.MAP, constraintType, symTable.mapType.tsymbol); + BUnionType constraintType = BUnionType.create(typeEnv, null, memberTypes); + data.resultType = new BMapType(typeEnv, TypeTags.MAP, constraintType, symTable.mapType.tsymbol); break; default: dlog.error(waitForAllExpr.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, expType, @@ -4933,7 +4912,7 @@ private void setResultTypeForWaitForAllExpr(BLangWaitForAllExpr waitForAllExpr, private BRecordType getWaitForAllExprReturnType(BLangWaitForAllExpr waitExpr, Location pos, AnalyzerData data) { - BRecordType retType = new BRecordType(null, Flags.ANONYMOUS); + BRecordType retType = new BRecordType(typeEnv, null, Flags.ANONYMOUS); List keyVals = waitExpr.keyValuePairs; for (BLangWaitForAllExpr.BLangWaitKeyValue keyVal : keyVals) { @@ -5062,7 +5041,7 @@ private void checkWaitKeyValExpr(BLangWaitForAllExpr.BLangWaitKeyValue keyVal, B } else { expr = keyVal.valueExpr; } - BFutureType futureType = new BFutureType(TypeTags.FUTURE, type, null); + BFutureType futureType = new BFutureType(typeEnv, type, null); checkExpr(expr, futureType, data); setEventualTypeForExpression(expr, type, data); } @@ -5089,14 +5068,14 @@ private void setEventualTypeForExpression(BLangExpression expression, return; } - BUnionType eventualType = BUnionType.create(null, currentType, symTable.errorType); + BUnionType eventualType = BUnionType.create(typeEnv, null, currentType, symTable.errorType); BType referredExpType = Types.getImpliedType(currentExpectedType); if (((referredExpType.tag != TypeTags.NONE) && (referredExpType.tag != TypeTags.NIL)) && !types.isAssignable(eventualType, currentExpectedType)) { dlog.error(expression.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPE_WAIT_FUTURE_EXPR, currentExpectedType, eventualType, expression); } - futureType.constraint = eventualType; + futureType.setConstraint(eventualType); } private void setEventualTypeForWaitExpression(BLangExpression expression, Location pos, AnalyzerData data) { @@ -5109,7 +5088,7 @@ private void setEventualTypeForWaitExpression(BLangExpression expression, Locati } BType currentExpectedType = ((BFutureType) data.expType).constraint; BType referredExpType = Types.getImpliedType(currentExpectedType); - BUnionType eventualType = BUnionType.create(null, data.resultType, symTable.errorType); + BUnionType eventualType = BUnionType.create(typeEnv, null, data.resultType, symTable.errorType); if ((referredExpType.tag == TypeTags.NONE) || (referredExpType.tag == TypeTags.NIL)) { data.resultType = eventualType; return; @@ -5124,7 +5103,7 @@ private void setEventualTypeForWaitExpression(BLangExpression expression, Locati BType referredResultType = Types.getImpliedType(data.resultType); if (referredResultType.tag == TypeTags.FUTURE) { - ((BFutureType) data.resultType).constraint = eventualType; + ((BFutureType) data.resultType).setConstraint(eventualType); } else { data.resultType = eventualType; } @@ -5146,7 +5125,7 @@ private void setEventualTypeForAlternateWaitExpression(BLangExpression expressio BType currentExpectedType = ((BFutureType) data.expType).constraint; BType referredExpType = Types.getImpliedType(currentExpectedType); - BUnionType eventualType = BUnionType.create(null, data.resultType, symTable.errorType); + BUnionType eventualType = BUnionType.create(typeEnv, null, data.resultType, symTable.errorType); if ((referredExpType.tag == TypeTags.NONE) || (referredExpType.tag == TypeTags.NIL)) { data.resultType = eventualType; return; @@ -5161,7 +5140,7 @@ private void setEventualTypeForAlternateWaitExpression(BLangExpression expressio BType referredResultType = Types.getImpliedType(data.resultType); if (referredResultType.tag == TypeTags.FUTURE) { - ((BFutureType) referredResultType).constraint = eventualType; + ((BFutureType) referredResultType).setConstraint(eventualType); } else { data.resultType = eventualType; } @@ -5236,12 +5215,12 @@ private BType getConditionalExprType(BType lhsType, BType rhsType) { if (types.isAssignable(lhsType, rhsType)) { return rhsType; } - return BUnionType.create(null, lhsType, rhsType); + return BUnionType.create(typeEnv, null, lhsType, rhsType); } @Override public void visit(BLangWaitExpr waitExpr, AnalyzerData data) { - data.expType = new BFutureType(TypeTags.FUTURE, data.expType, null); + data.expType = new BFutureType(typeEnv, data.expType, null); checkExpr(waitExpr.getExpression(), data.expType, data); // Handle union types in lhs BType referredResultType = Types.getImpliedType(data.resultType); @@ -5251,7 +5230,7 @@ public void visit(BLangWaitExpr waitExpr, AnalyzerData data) { if (memberTypes.size() == 1) { data.resultType = memberTypes.toArray(new BType[0])[0]; } else { - data.resultType = BUnionType.create(null, memberTypes); + data.resultType = BUnionType.create(typeEnv, null, memberTypes); } } else if (data.resultType != symTable.semanticError) { // Handle other types except for semantic errors @@ -5312,7 +5291,7 @@ public void visit(BLangTrapExpr trapExpr, AnalyzerData data) { resultTypes.add(exprType); } resultTypes.add(symTable.errorType); - actualType = BUnionType.create(null, resultTypes); + actualType = BUnionType.create(typeEnv, null, resultTypes); } data.resultType = types.checkType(trapExpr, actualType, data.expType); @@ -5333,7 +5312,7 @@ public void visit(BLangBinaryExpr binaryExpr, AnalyzerData data) { data.resultType = symTable.semanticError; return; } - data.resultType = BUnionType.create(null, lhsResultType, rhsResultType); + data.resultType = BUnionType.create(typeEnv, null, lhsResultType, rhsResultType); return; } @@ -5375,7 +5354,8 @@ public void visit(BLangBinaryExpr binaryExpr, AnalyzerData data) { BType rightConstituent = getXMLConstituents(rhsType); if (leftConstituent != null && rightConstituent != null) { - actualType = new BXMLType(BUnionType.create(null, leftConstituent, rightConstituent), null); + actualType = + new BXMLType(BUnionType.create(typeEnv, null, leftConstituent, rightConstituent), null); break; } else if (leftConstituent != null || rightConstituent != null) { if (leftConstituent != null && types.isAssignable(rhsType, symTable.stringType)) { @@ -5447,23 +5427,19 @@ private BType getXmlStringBinaryOpResultType(BType opType, BType constituentType BTypeSymbol typeSymbol = Symbols.createTypeSymbol(SymTag.UNION_TYPE, 0, Names.EMPTY, env.enclPkg.symbol.pkgID, null, env.scope.owner, pos, VIRTUAL); - BType type = new BXMLType(BUnionType.create(typeSymbol, constituentType, symTable.xmlTextType), null); + BType type = + new BXMLType(BUnionType.create(typeEnv, typeSymbol, constituentType, symTable.xmlTextType), null); typeSymbol.type = type; return type; } public boolean isOptionalFloatOrDecimal(BType expectedType) { - if (expectedType.tag == TypeTags.UNION && expectedType.isNullable() && expectedType.tag != TypeTags.ANY) { - Iterator memberTypeIterator = ((BUnionType) expectedType).getMemberTypes().iterator(); - while (memberTypeIterator.hasNext()) { - BType memberType = Types.getImpliedType(memberTypeIterator.next()); - if (memberType.tag == TypeTags.FLOAT || memberType.tag == TypeTags.DECIMAL) { - return true; - } - } - + if (!expectedType.isNullable()) { + return false; } - return false; + + SemType t = Core.diff(expectedType.semType(), PredefinedType.NIL); + return PredefinedType.FLOAT.equals(t) || PredefinedType.DECIMAL.equals(t); } private BType checkAndGetType(BLangExpression expr, SymbolEnv env, BLangBinaryExpr binaryExpr, AnalyzerData data) { @@ -5495,7 +5471,7 @@ public void visit(BLangTransactionalExpr transactionalExpr, AnalyzerData data) { @Override public void visit(BLangCommitExpr commitExpr, AnalyzerData data) { - BType actualType = BUnionType.create(null, symTable.errorType, symTable.nilType); + BType actualType = BUnionType.create(typeEnv, null, symTable.errorType, symTable.nilType); data.resultType = types.checkType(commitExpr, actualType, data.expType); } @@ -5544,14 +5520,14 @@ public void visit(BLangTypedescExpr accessExpr, AnalyzerData data) { int resolveTypeTag = Types.getImpliedType(accessExpr.resolvedType).tag; final BType actualType; if (resolveTypeTag != TypeTags.TYPEDESC && resolveTypeTag != TypeTags.NONE) { - actualType = new BTypedescType(accessExpr.resolvedType, null); + actualType = new BTypedescType(typeEnv, accessExpr.resolvedType, null); } else { actualType = accessExpr.resolvedType; } data.resultType = types.checkType(accessExpr, actualType, data.expType); } - public LinkedHashSet getBasicNumericTypes(LinkedHashSet memberTypes) { + public LinkedHashSet getBasicNumericTypes(Set memberTypes) { LinkedHashSet basicNumericTypes = new LinkedHashSet<>(memberTypes.size()); for (BType value : memberTypes) { @@ -5567,8 +5543,7 @@ public LinkedHashSet getBasicNumericTypes(LinkedHashSet memberType basicNumericTypes.add(symTable.decimalType); break; } else if (typeTag == TypeTags.FINITE) { - LinkedHashSet typesInValueSpace = getTypesInFiniteValueSpace((BFiniteType) referredType); - basicNumericTypes.addAll(getBasicNumericTypes(typesInValueSpace)); + basicNumericTypes.addAll(SemTypeHelper.broadTypes((BFiniteType) referredType, symTable)); } } return basicNumericTypes; @@ -5579,29 +5554,20 @@ public BType createFiniteTypeForNumericUnaryExpr(BLangUnaryExpr unaryExpr, Analy BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, 0, Names.EMPTY, data.env.enclPkg.symbol.pkgID, null, data.env.scope.owner, unaryExpr.pos, SOURCE); - BFiniteType finiteType = new BFiniteType(finiteTypeSymbol); - finiteType.addValue(newNumericLiteral); + BFiniteType finiteType = BFiniteType.newSingletonBFiniteType(finiteTypeSymbol, + SemTypeHelper.resolveSingletonType(newNumericLiteral)); finiteTypeSymbol.type = finiteType; types.setImplicitCastExpr(unaryExpr, unaryExpr.expr.getBType(), data.expType); return finiteType; } - public LinkedHashSet getTypesInFiniteValueSpace(BFiniteType referredType) { - Set valueSpace = referredType.getValueSpace(); - LinkedHashSet typesInValueSpace = new LinkedHashSet<>(valueSpace.size()); - for (BLangExpression expr : valueSpace) { - typesInValueSpace.add(expr.getBType()); - } - return typesInValueSpace; - } - - public BType getNewExpectedTypeForFiniteAndUnion(LinkedHashSet numericTypes, BType newExpectedType) { + public BType getNewExpectedTypeForFiniteAndUnion(Set numericTypes, BType newExpectedType) { LinkedHashSet basicNumericTypes = getBasicNumericTypes(numericTypes); if (basicNumericTypes.size() == 1) { newExpectedType = basicNumericTypes.iterator().next(); } else if (basicNumericTypes.size() > 1) { - newExpectedType = BUnionType.create(null, basicNumericTypes); + newExpectedType = BUnionType.create(typeEnv, null, basicNumericTypes); } return newExpectedType; } @@ -5614,22 +5580,24 @@ public BType setExpectedTypeForSubtractionOperator(AnalyzerData data) { if (TypeTags.isIntegerTypeTag(referredTypeTag)) { newExpectedType = types.getTypeIntersection(Types.IntersectionContext.compilerInternalIntersectionTestContext(), - BUnionType.create(null, symTable.intType, symTable.floatType, symTable.decimalType), + BUnionType.create(typeEnv, null, symTable.intType, symTable.floatType, + symTable.decimalType), symTable.intType, data.env); } else if (referredTypeTag == TypeTags.FLOAT || referredTypeTag == TypeTags.DECIMAL) { newExpectedType = types.getTypeIntersection(Types.IntersectionContext.compilerInternalIntersectionTestContext(), - BUnionType.create(null, symTable.intType, symTable.floatType, symTable.decimalType), + BUnionType.create(typeEnv, null, symTable.intType, symTable.floatType, + symTable.decimalType), referredType, data.env); } else if (referredTypeTag == TypeTags.FINITE) { - LinkedHashSet typesInValueSpace = getTypesInFiniteValueSpace((BFiniteType) referredType); + Set typesInValueSpace = SemTypeHelper.broadTypes((BFiniteType) referredType, symTable); newExpectedType = getNewExpectedTypeForFiniteAndUnion(typesInValueSpace, newExpectedType); } else if (referredTypeTag == TypeTags.UNION) { newExpectedType = getNewExpectedTypeForFiniteAndUnion(((BUnionType) referredType).getMemberTypes(), newExpectedType); } else if (referredTypeTag == TypeTags.JSON || referredTypeTag == TypeTags.ANYDATA || referredTypeTag == TypeTags.ANY) { - newExpectedType = BUnionType.create(null, symTable.intType, symTable.floatType, + newExpectedType = BUnionType.create(typeEnv, null, symTable.intType, symTable.floatType, symTable.decimalType); } return newExpectedType; @@ -5687,7 +5655,7 @@ public BType getActualTypeForOtherUnaryExpr(BLangUnaryExpr unaryExpr, AnalyzerDa // basic type (int) when checking the expression. LinkedHashSet intTypesInUnion = getIntSubtypesInUnionType((BUnionType) referredType); if (!intTypesInUnion.isEmpty()) { - BType newReferredType = BUnionType.create(null, intTypesInUnion); + BType newReferredType = BUnionType.create(typeEnv, null, intTypesInUnion); BType tempActualType = checkCompatibilityWithConstructedNumericLiteral(unaryExpr, newReferredType, data); if (tempActualType != symTable.semanticError) { @@ -5782,7 +5750,7 @@ public void visit(BLangUnaryExpr unaryExpr, AnalyzerData data) { } else if (OperatorKind.TYPEOF.equals(unaryExpr.operator)) { exprType = checkExpr(unaryExpr.expr, data); if (exprType != symTable.semanticError) { - actualType = new BTypedescType(exprType, null); + actualType = new BTypedescType(typeEnv, exprType, null); } } else { actualType = getActualTypeForOtherUnaryExpr(unaryExpr, data); @@ -5847,7 +5815,7 @@ public void visit(BLangTypeConversionExpr conversionExpr, AnalyzerData data) { } BType exprType = expr.getBType(); - if (types.isTypeCastable(expr, exprType, targetType, data.env)) { + if (types.isTypeCastable(exprType, targetType)) { // We reach this block only if the cast is valid, so we set the target type as the actual type. actualType = targetType; } else if (exprType != symTable.semanticError && exprType != symTable.noType) { @@ -5908,7 +5876,7 @@ public void visit(BLangArrowFunction bLangArrowFunction, AnalyzerData data) { expectedType = invokableType; } } - if (expectedType.tag != TypeTags.INVOKABLE || Symbols.isFlagOn(expectedType.flags, Flags.ANY_FUNCTION)) { + if (expectedType.tag != TypeTags.INVOKABLE || Symbols.isFlagOn(expectedType.getFlags(), Flags.ANY_FUNCTION)) { dlog.error(bLangArrowFunction.pos, DiagnosticErrorCode.ARROW_EXPRESSION_CANNOT_INFER_TYPE_FROM_LHS); data.resultType = symTable.semanticError; @@ -6113,7 +6081,7 @@ public void visit(BLangXMLElementLiteral bLangXMLElementLiteral, AnalyzerData da data.resultType = checkXmlSubTypeLiteralCompatibility(bLangXMLElementLiteral.pos, symTable.xmlElementType, data.expType, data); - if (Symbols.isFlagOn(data.resultType.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(data.resultType.getFlags(), Flags.READONLY)) { markChildrenAsImmutable(bLangXMLElementLiteral, data); } } @@ -6330,8 +6298,8 @@ private boolean evaluateRawTemplateExprs(List exprs, if (listType.tag == TypeTags.ARRAY) { BArrayType arrayType = (BArrayType) listType; - if (arrayType.state == BArrayState.CLOSED && (exprs.size() != arrayType.size)) { - dlog.error(pos, code, arrayType.size, exprs.size()); + if (arrayType.state == BArrayState.CLOSED && (exprs.size() != arrayType.getSize())) { + dlog.error(pos, code, arrayType.getSize(), exprs.size()); return false; } @@ -6369,16 +6337,7 @@ private boolean evaluateRawTemplateExprs(List exprs, } private boolean containsAnyType(BType bType) { - BType type = Types.getImpliedType(bType); - if (type == symTable.anyType) { - return true; - } - - if (type.tag == TypeTags.UNION) { - return ((BUnionType) type).getMemberTypes().contains(symTable.anyType); - } - - return false; + return SemTypeHelper.containsType(types.semTypeCtx, bType, PredefinedType.ANY); } private BType getCompatibleRawTemplateType(BType bType, Location pos) { @@ -6476,7 +6435,7 @@ protected void visitCheckAndCheckPanicExpr(BLangCheckedExpr checkedExpr, Analyze } else { BType exprType = getCandidateType(checkedExpr, data.expType, data); if (exprType == symTable.semanticError) { - checkExprCandidateType = BUnionType.create(null, data.expType, symTable.errorType); + checkExprCandidateType = BUnionType.create(typeEnv, null, data.expType, symTable.errorType); } else { checkExprCandidateType = addDefaultErrorIfNoErrorComponentFound(data.expType); } @@ -6498,7 +6457,8 @@ protected void visitCheckAndCheckPanicExpr(BLangCheckedExpr checkedExpr, Analyze } } - boolean isErrorType = types.isAssignable(exprType, symTable.errorType); + boolean isErrorType = exprType.tag != TypeTags.SEMANTIC_ERROR && + types.isAssignable(exprType, symTable.errorType); BType referredExprType = Types.getImpliedType(exprType); if (referredExprType.tag != TypeTags.UNION && !isErrorType) { if (referredExprType.tag == TypeTags.READONLY) { @@ -6566,7 +6526,7 @@ protected void visitCheckAndCheckPanicExpr(BLangCheckedExpr checkedExpr, Analyze } else if (nonErrorTypes.size() == 1) { actualType = nonErrorTypes.get(0); } else { - actualType = BUnionType.create(null, new LinkedHashSet<>(nonErrorTypes)); + actualType = BUnionType.create(typeEnv, null, new LinkedHashSet<>(nonErrorTypes)); } data.resultType = types.checkType(checkedExpr, actualType, data.expType); @@ -6582,12 +6542,12 @@ private void rewriteWithEnsureTypeFunc(BLangCheckedExpr checkedExpr, BType type, if (rhsType == symTable.semanticError) { rhsType = getCandidateType(checkedExpr, rhsType, data); } - BType candidateLaxType = getCandidateLaxType(checkedExpr.expr, rhsType); + SemType candidateLaxType = getCandidateLaxType(checkedExpr.expr, rhsType); if (!types.isLaxFieldAccessAllowed(candidateLaxType)) { return; } ArrayList argExprs = new ArrayList<>(); - BType typedescType = new BTypedescType(data.expType, null); + BType typedescType = new BTypedescType(typeEnv, data.expType, null); BLangTypedescExpr typedescExpr = new BLangTypedescExpr(); typedescExpr.resolvedType = data.expType; typedescExpr.setBType(typedescType); @@ -6599,11 +6559,12 @@ private void rewriteWithEnsureTypeFunc(BLangCheckedExpr checkedExpr, BType type, checkedExpr.expr = invocation; } - private BType getCandidateLaxType(BLangNode expr, BType rhsType) { + private SemType getCandidateLaxType(BLangNode expr, BType rhsType) { + SemType t = rhsType.semType(); if (expr.getKind() == NodeKind.FIELD_BASED_ACCESS_EXPR) { - return types.getSafeType(rhsType, false, true); + return types.getErrorLiftType(t); } - return rhsType; + return t; } private BType getCandidateType(BLangCheckedExpr checkedExpr, BType checkExprCandidateType, AnalyzerData data) { @@ -6635,7 +6596,7 @@ private BType addDefaultErrorIfNoErrorComponentFound(BType type) { return type; } } - return BUnionType.create(null, type, symTable.errorType); + return BUnionType.create(typeEnv, null, type, symTable.errorType); } @Override @@ -6667,7 +6628,7 @@ public void visit(BLangAnnotAccessExpr annotAccessExpr, AnalyzerData data) { annotAccessExpr.annotationSymbol = (BAnnotationSymbol) symbol; BType annotType = ((BAnnotationSymbol) symbol).attachedType == null ? symTable.trueType : ((BAnnotationSymbol) symbol).attachedType; - actualType = BUnionType.create(null, annotType, symTable.nilType); + actualType = BUnionType.create(typeEnv, null, annotType, symTable.nilType); } data.resultType = this.types.checkType(annotAccessExpr, actualType, data.expType); @@ -6729,7 +6690,7 @@ private BType getEffectiveReadOnlyType(Location pos, BType type, AnalyzerData da return type; } - BUnionType nonReadOnlyUnion = BUnionType.create(null, nonReadOnlyTypes); + BUnionType nonReadOnlyUnion = BUnionType.create(typeEnv, null, nonReadOnlyTypes); nonReadOnlyUnion.add(ImmutableTypeCloner.getImmutableIntersectionType(pos, types, data.expType, data.env, symTable, anonymousModelHelper, names, new HashSet<>())); @@ -7146,7 +7107,7 @@ private void checkActionInvocation(BLangInvocation.BLangActionInvocation aInv, B return; } if (Symbols.isFlagOn(remoteFuncSymbol.flags, Flags.REMOTE) && - Symbols.isFlagOn(expType.flags, Flags.CLIENT) && + Symbols.isFlagOn(expType.getFlags(), Flags.CLIENT) && types.isNeverTypeOrStructureTypeWithARequiredNeverMember ((BType) ((InvokableSymbol) remoteFuncSymbol).getReturnType())) { dlog.error(aInv.pos, DiagnosticErrorCode.INVALID_CLIENT_REMOTE_METHOD_CALL); @@ -7235,7 +7196,7 @@ private BVarSymbol checkForIncRecordParamAllowAdditionalFields(BInvokableSymbol } private BType checkInvocationParam(BLangInvocation iExpr, AnalyzerData data) { - if (Symbols.isFlagOn(iExpr.symbol.type.flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(iExpr.symbol.type.getFlags(), Flags.ANY_FUNCTION)) { dlog.error(iExpr.pos, DiagnosticErrorCode.INVALID_FUNCTION_POINTER_INVOCATION_WITH_TYPE); return symTable.semanticError; } @@ -7487,7 +7448,7 @@ private BType checkInvocationArgs(BLangInvocation iExpr, List paramTypes, PackageID pkgID = data.env.enclPkg.symbol.pkgID; List tupleMembers = new ArrayList<>(); BRecordTypeSymbol recordSymbol = createRecordTypeSymbol(pkgID, null, VIRTUAL, data); - mappingTypeRestArg = new BRecordType(recordSymbol); + mappingTypeRestArg = new BRecordType(typeEnv, recordSymbol); LinkedHashMap fields = new LinkedHashMap<>(); BType tupleRestType = null; BVarSymbol fieldSymbol; @@ -7519,7 +7480,7 @@ private BType checkInvocationArgs(BLangInvocation iExpr, List paramTypes, } } - BTupleType tupleType = new BTupleType(tupleMembers); + BTupleType tupleType = new BTupleType(typeEnv, tupleMembers); tupleType.restType = tupleRestType; listTypeRestArg = tupleType; referredListTypeRestArg = tupleType; @@ -7555,7 +7516,7 @@ private BType checkInvocationArgs(BLangInvocation iExpr, List paramTypes, LinkedHashSet restTypes = new LinkedHashSet<>(); restTypes.add(listTypeRestArg); restTypes.add(mappingTypeRestArg); - BType actualType = BUnionType.create(null, restTypes); + BType actualType = BUnionType.create(typeEnv, null, restTypes); checkTypeParamExpr(vararg, actualType, iExpr.langLibInvocation, data); } else { checkTypeParamExpr(vararg, listTypeRestArg, iExpr.langLibInvocation, data); @@ -7597,8 +7558,8 @@ private BType checkInvocationArgs(BLangInvocation iExpr, List paramTypes, long invokableSymbolFlags = invokableSymbol.flags; if (restType != symTable.semanticError && (Symbols.isFlagOn(invokableSymbolFlags, Flags.INTERFACE) || Symbols.isFlagOn(invokableSymbolFlags, Flags.NATIVE)) && - Symbols.isFlagOn(retType.flags, Flags.PARAMETERIZED)) { - retType = unifier.build(retType, data.expType, iExpr, types, symTable, dlog); + Symbols.isFlagOn(retType.getFlags(), Flags.PARAMETERIZED)) { + retType = unifier.build(typeEnv, retType, data.expType, iExpr, types, symTable, dlog); } // check argument types in arr:sort function @@ -7667,7 +7628,7 @@ private void checkArrayLibSortFuncArgs(BLangInvocation iExpr) { BLangExpression arrExpr = argExprs.get(0); BType arrType = arrExpr.getBType(); - boolean isOrderedType = types.isOrderedType(arrType, false); + boolean isOrderedType = types.isOrderedType(arrType); if (keyFunction == null) { if (!isOrderedType) { dlog.error(arrExpr.pos, DiagnosticErrorCode.INVALID_SORT_ARRAY_MEMBER_TYPE, arrType); @@ -7707,7 +7668,7 @@ private void checkArrayLibSortFuncArgs(BLangInvocation iExpr) { returnType = keyLambdaFunction.function.getBType().getReturnType(); } - if (!types.isOrderedType(returnType, false)) { + if (!types.isOrderedType(returnType)) { dlog.error(pos, DiagnosticErrorCode.INVALID_SORT_FUNC_RETURN_TYPE, returnType); } } @@ -7741,7 +7702,7 @@ private BVarSymbol checkParameterNameForDefaultArgument(BLangIdentifier argName, private BFutureType generateFutureType(BInvokableSymbol invocableSymbol, BType retType) { boolean isWorkerStart = Symbols.isFlagOn(invocableSymbol.flags, Flags.WORKER); - return new BFutureType(TypeTags.FUTURE, retType, null, isWorkerStart); + return new BFutureType(typeEnv, retType, null, isWorkerStart); } protected void checkTypeParamExpr(BLangExpression arg, BType expectedType, AnalyzerData data) { @@ -8029,7 +7990,7 @@ private TypeSymbolPair checkRecordLiteralKeyExpr(BLangExpression keyExpr, boolea fieldTypes.add(recordType.restFieldType); } - return new TypeSymbolPair(null, BUnionType.create(null, fieldTypes)); + return new TypeSymbolPair(null, BUnionType.create(typeEnv, null, fieldTypes)); } else if (keyExpr.getKind() == NodeKind.SIMPLE_VARIABLE_REF) { BLangSimpleVarRef varRef = (BLangSimpleVarRef) keyExpr; fieldName = names.fromIdNode(varRef.variableName); @@ -8075,7 +8036,7 @@ private BType getAllFieldType(BRecordType recordType) { possibleTypes.add(restFieldType); } - return BUnionType.create(null, possibleTypes); + return BUnionType.create(typeEnv, null, possibleTypes); } private boolean checkValidJsonOrMapLiteralKeyExpr(BLangExpression keyExpr, boolean computedKey, AnalyzerData data) { @@ -8162,12 +8123,12 @@ private BType checkObjectFieldAccess(BLangFieldBasedAccess bLangFieldBasedAccess return symTable.semanticError; } - if (Symbols.isFlagOn(fieldSymbol.type.flags, Flags.ISOLATED) && - !Symbols.isFlagOn(objectType.flags, Flags.ISOLATED)) { - fieldSymbol = ASTBuilderUtil.duplicateInvokableSymbol((BInvokableSymbol) fieldSymbol); + if (Symbols.isFlagOn(fieldSymbol.type.getFlags(), Flags.ISOLATED) && + !Symbols.isFlagOn(objectType.getFlags(), Flags.ISOLATED)) { + fieldSymbol = ASTBuilderUtil.duplicateInvokableSymbol(typeEnv, (BInvokableSymbol) fieldSymbol); fieldSymbol.flags &= ~Flags.ISOLATED; - fieldSymbol.type.flags &= ~Flags.ISOLATED; + fieldSymbol.type.setFlags(fieldSymbol.type.getFlags() & ~Flags.ISOLATED); } // Setting the field symbol. This is used during the code generation phase @@ -8221,7 +8182,7 @@ private void checkStringTemplateExprs(List exprs, Ana if (!types.isNonNilSimpleBasicTypeOrString(type)) { dlog.error(expr.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, - BUnionType.create(null, symTable.intType, symTable.floatType, + BUnionType.create(typeEnv, null, symTable.intType, symTable.floatType, symTable.decimalType, symTable.stringType, symTable.booleanType), type); } @@ -8278,7 +8239,7 @@ private List concatSimilarKindXMLNodes(List ex !TypeTags.isIntegerTypeTag(referredType.tag) && !TypeTags.isStringTypeTag(referredType.tag)) { if (referredType != symTable.semanticError && !TypeTags.isXMLTypeTag(referredType.tag)) { dlog.error(expr.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, - BUnionType.create(null, symTable.intType, symTable.floatType, + BUnionType.create(typeEnv, null, symTable.intType, symTable.floatType, symTable.decimalType, symTable.stringType, symTable.booleanType, symTable.xmlType), type); } @@ -8355,7 +8316,7 @@ private BType checkObjectFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr, return fieldTypeMembers.iterator().next(); } - return BUnionType.create(null, fieldTypeMembers); + return BUnionType.create(typeEnv, null, fieldTypeMembers); } private BType checkRecordFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr, BType type, Name fieldName, @@ -8402,7 +8363,7 @@ private BType checkRecordFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr, return fieldTypeMembers.iterator().next(); } - return BUnionType.create(null, fieldTypeMembers); + return BUnionType.create(typeEnv, null, fieldTypeMembers); } private boolean isFieldOptionalInRecords(BUnionType unionType, Name fieldName, @@ -8452,7 +8413,7 @@ private BType checkRecordFieldAccessLhsExpr(BLangFieldBasedAccess fieldAccessExp return fieldTypeMembers.iterator().next(); } - return BUnionType.create(null, fieldTypeMembers); + return BUnionType.create(typeEnv, null, fieldTypeMembers); } private BType checkOptionalRecordFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr, BType varRefType, @@ -8499,7 +8460,7 @@ private BType checkOptionalRecordFieldAccessExpr(BLangFieldBasedAccess fieldAcce if (fieldTypeMembers.size() == 1) { fieldType = fieldTypeMembers.iterator().next(); } else { - fieldType = BUnionType.create(null, fieldTypeMembers); + fieldType = BUnionType.create(typeEnv, null, fieldTypeMembers); } return nonMatchedRecordExists ? types.addNilForNillableAccessType(fieldType) : fieldType; @@ -8623,7 +8584,7 @@ private BType checkFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr, BType resolveXMLNamespace((BLangFieldBasedAccess.BLangPrefixedFieldBasedAccess) fieldAccessExpr, data); } BType laxFieldAccessType = getLaxFieldAccessType(varRefType); - actualType = BUnionType.create(null, laxFieldAccessType, symTable.errorType); + actualType = BUnionType.create(typeEnv, null, laxFieldAccessType, symTable.errorType); fieldAccessExpr.originalType = laxFieldAccessType; } else if (fieldAccessExpr.expr.getKind() == NodeKind.FIELD_BASED_ACCESS_EXPR && hasLaxOriginalType(((BLangFieldBasedAccess) fieldAccessExpr.expr))) { @@ -8632,7 +8593,7 @@ private BType checkFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr, BType if (fieldAccessExpr.fieldKind == FieldKind.WITH_NS) { resolveXMLNamespace((BLangFieldBasedAccess.BLangPrefixedFieldBasedAccess) fieldAccessExpr, data); } - actualType = BUnionType.create(null, laxFieldAccessType, symTable.errorType); + actualType = BUnionType.create(typeEnv, null, laxFieldAccessType, symTable.errorType); fieldAccessExpr.errorSafeNavigation = true; fieldAccessExpr.originalType = laxFieldAccessType; } else if (TypeTags.isXMLTypeTag(referredVarRefType.tag)) { @@ -8688,12 +8649,13 @@ private BType getLaxFieldAccessType(BType exprType) { return ((BMapType) exprType).constraint; case TypeTags.UNION: BUnionType unionType = (BUnionType) exprType; - if (types.isSameType(symTable.jsonType, unionType)) { + if (types.isSameType(Core.createJson(types.semTypeCtx), unionType.semType())) { return symTable.jsonType; } LinkedHashSet memberTypes = new LinkedHashSet<>(); unionType.getMemberTypes().forEach(bType -> memberTypes.add(getLaxFieldAccessType(bType))); - return memberTypes.size() == 1 ? memberTypes.iterator().next() : BUnionType.create(null, memberTypes); + return memberTypes.size() == 1 ? memberTypes.iterator().next() : + BUnionType.create(typeEnv, null, memberTypes); } return symTable.semanticError; } @@ -8718,7 +8680,7 @@ private BType checkOptionalFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr } referredType = nilRemovedSet.size() == 1 ? nilRemovedSet.iterator().next() : - BUnionType.create(null, nilRemovedSet); + BUnionType.create(typeEnv, null, nilRemovedSet); } } @@ -8735,7 +8697,8 @@ private BType checkOptionalFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr } else if (types.isLaxFieldAccessAllowed(referredType)) { BType laxFieldAccessType = getLaxFieldAccessType(referredType); actualType = accessCouldResultInError(referredType) ? - BUnionType.create(null, laxFieldAccessType, symTable.errorType) : laxFieldAccessType; + BUnionType.create(typeEnv, null, laxFieldAccessType, symTable.errorType) : + laxFieldAccessType; if (fieldAccessExpr.fieldKind == FieldKind.WITH_NS) { resolveXMLNamespace((BLangFieldBasedAccess.BLangPrefixedFieldBasedAccess) fieldAccessExpr, data); } @@ -8747,7 +8710,8 @@ private BType checkOptionalFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr BType laxFieldAccessType = getLaxFieldAccessType(((BLangFieldBasedAccess) fieldAccessExpr.expr).originalType); actualType = accessCouldResultInError(referredType) ? - BUnionType.create(null, laxFieldAccessType, symTable.errorType) : laxFieldAccessType; + BUnionType.create(typeEnv, null, laxFieldAccessType, symTable.errorType) : + laxFieldAccessType; if (fieldAccessExpr.fieldKind == FieldKind.WITH_NS) { resolveXMLNamespace((BLangFieldBasedAccess.BLangPrefixedFieldBasedAccess) fieldAccessExpr, data); } @@ -8761,31 +8725,16 @@ private BType checkOptionalFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr } if (nillableExprType && actualType != symTable.semanticError && !actualType.isNullable()) { - actualType = BUnionType.create(null, actualType, symTable.nilType); + actualType = BUnionType.create(typeEnv, null, actualType, symTable.nilType); } return actualType; } private boolean accessCouldResultInError(BType bType) { - BType type = Types.getImpliedType(bType); - if (type.tag == TypeTags.JSON) { - return true; - } - - if (type.tag == TypeTags.MAP) { - return false; - } - - if (types.isAssignable(bType, symTable.xmlType)) { - return true; - } - - if (type.tag == TypeTags.UNION) { - return ((BUnionType) type).getMemberTypes().stream().anyMatch(this::accessCouldResultInError); - } else { - return false; - } + SemType s = bType.semType(); + return SemTypes.containsBasicType(s, PredefinedType.XML) || + SemTypes.containsType(types.semTypeCtx, s, Core.createJson(types.semTypeCtx)); } private BType checkIndexAccessExpr(BLangIndexBasedAccess indexBasedAccessExpr, AnalyzerData data) { @@ -8808,9 +8757,9 @@ private BType checkIndexAccessExpr(BLangIndexBasedAccess indexBasedAccessExpr, A if (nillableExprType) { varRefType = nilRemovedSet.size() == 1 ? nilRemovedSet.iterator().next() : - BUnionType.create(null, nilRemovedSet); + BUnionType.create(typeEnv, null, nilRemovedSet); - if (!types.isSubTypeOfMapping(varRefType)) { + if (!types.isSubTypeOfMapping(varRefType.semType())) { // Member access is allowed on optional types only with mappings. dlog.error(indexBasedAccessExpr.pos, DiagnosticErrorCode.OPERATION_DOES_NOT_SUPPORT_MEMBER_ACCESS, @@ -8832,7 +8781,10 @@ private BType checkIndexAccessExpr(BLangIndexBasedAccess indexBasedAccessExpr, A BLangExpression indexExpr = indexBasedAccessExpr.indexExpr; BType actualType = symTable.semanticError; - if (types.isSubTypeOfMapping(varRefType)) { + if (varRefType == symTable.semanticError) { + indexBasedAccessExpr.indexExpr.setBType(symTable.semanticError); + return symTable.semanticError; + } else if (types.isSubTypeOfMapping(varRefType.semType())) { checkExpr(indexExpr, symTable.stringType, data); if (indexExpr.getBType() == symTable.semanticError) { @@ -8940,9 +8892,6 @@ private BType checkIndexAccessExpr(BLangIndexBasedAccess indexBasedAccessExpr, A actualType = types.addNilForNillableAccessType(constraint); indexBasedAccessExpr.originalType = indexBasedAccessExpr.leafNode || !nillableExprType ? actualType : types.getTypeWithoutNil(actualType); - } else if (varRefType == symTable.semanticError) { - indexBasedAccessExpr.indexExpr.setBType(symTable.semanticError); - return symTable.semanticError; } else { indexBasedAccessExpr.indexExpr.setBType(symTable.semanticError); dlog.error(indexBasedAccessExpr.pos, DiagnosticErrorCode.OPERATION_DOES_NOT_SUPPORT_MEMBER_ACCESS, @@ -8951,7 +8900,7 @@ private BType checkIndexAccessExpr(BLangIndexBasedAccess indexBasedAccessExpr, A } if (nillableExprType && !actualType.isNullable()) { - actualType = BUnionType.create(null, actualType, symTable.nilType); + actualType = BUnionType.create(typeEnv, null, actualType, symTable.nilType); } return actualType; @@ -8972,7 +8921,7 @@ private BType getXmlMemberAccessType(BType varRefType) { effectiveMemberTypes.add(getXMLConstituents(memberType)); } xmlMemberAccessType = effectiveMemberTypes.size() == 1 ? effectiveMemberTypes.iterator().next() : - BUnionType.create(null, effectiveMemberTypes); + BUnionType.create(typeEnv, null, effectiveMemberTypes); } else { xmlMemberAccessType = getXMLConstituents(varRefType); } @@ -8982,7 +8931,7 @@ private BType getXmlMemberAccessType(BType varRefType) { } else if (types.isAssignable(symTable.xmlNeverType, xmlMemberAccessType)) { return xmlMemberAccessType; } - return BUnionType.create(null, xmlMemberAccessType, symTable.xmlNeverType); + return BUnionType.create(typeEnv, null, xmlMemberAccessType, symTable.xmlNeverType); } private Long getConstIndex(BLangExpression indexExpr) { @@ -9024,22 +8973,23 @@ private BType checkArrayIndexBasedAccess(BLangIndexBasedAccess indexBasedAccess, return arrayType.eType; } Long indexVal = getConstIndex(indexExpr); - return indexVal >= arrayType.size || indexVal < 0 ? symTable.semanticError : arrayType.eType; + return indexVal >= arrayType.getSize() || indexVal < 0 ? symTable.semanticError : arrayType.eType; } switch (tag) { case TypeTags.FINITE: - BFiniteType finiteIndexExpr = (BFiniteType) indexExprType; - boolean validIndexExists = false; - for (BLangExpression finiteMember : finiteIndexExpr.getValueSpace()) { - int indexValue = ((Long) ((BLangLiteral) finiteMember).value).intValue(); - if (indexValue >= 0 && - (arrayType.state == BArrayState.OPEN || indexValue < arrayType.size)) { - validIndexExists = true; - break; - } + SemType t = indexExprType.semType(); + long maxIndexValue; + if (arrayType.state == BArrayState.OPEN) { + maxIndexValue = Long.MAX_VALUE; + } else { + maxIndexValue = arrayType.getSize() - 1; } - if (!validIndexExists) { + + SemType allowedInts = PredefinedType.basicSubtype(BasicTypeCode.BT_INT, + IntSubtype.createSingleRangeSubtype(0, maxIndexValue)); + + if (Core.isEmpty(types.semTypeCtx, SemTypes.intersect(t, allowedInts))) { return symTable.semanticError; } actualType = arrayType.eType; @@ -9048,7 +8998,7 @@ private BType checkArrayIndexBasedAccess(BLangIndexBasedAccess indexBasedAccess, // address the case where we have a union of types List finiteTypes = new ArrayList<>(); for (BType memType : ((BUnionType) indexExprType).getMemberTypes()) { - memType = Types.getImpliedType(memType); + memType = Types.getReferredType(memType); if (memType.tag == TypeTags.FINITE) { finiteTypes.add((BFiniteType) memType); } else { @@ -9059,7 +9009,12 @@ private BType checkArrayIndexBasedAccess(BLangIndexBasedAccess indexBasedAccess, } } if (!finiteTypes.isEmpty()) { - BFiniteType finiteType = createFiniteTypeFromFiniteTypeList(finiteTypes); + List newValueSpace = new ArrayList<>(); + for (BFiniteType ft : finiteTypes) { + newValueSpace.addAll(Arrays.asList(ft.valueSpace)); + } + + BFiniteType finiteType = new BFiniteType(null, newValueSpace.toArray(SemNamedType[]::new)); BType possibleType = checkArrayIndexBasedAccess(indexBasedAccess, finiteType, arrayType); if (possibleType == symTable.semanticError) { return symTable.semanticError; @@ -9099,7 +9054,7 @@ private BType checkListIndexBasedAccess(BLangIndexBasedAccess accessExpr, BType if (fieldTypeMembers.size() == 1) { return fieldTypeMembers.iterator().next(); } - return BUnionType.create(null, fieldTypeMembers); + return BUnionType.create(typeEnv, null, fieldTypeMembers); } private BType checkTupleIndexBasedAccess(BLangIndexBasedAccess accessExpr, BTupleType tuple, BType currentType) { @@ -9114,27 +9069,36 @@ private BType checkTupleIndexBasedAccess(BLangIndexBasedAccess accessExpr, BTupl } LinkedHashSet tupleTypes = collectTupleFieldTypes(tuple, new LinkedHashSet<>()); - return tupleTypes.size() == 1 ? tupleTypes.iterator().next() : BUnionType.create(null, tupleTypes); + return tupleTypes.size() == 1 ? tupleTypes.iterator().next() : + BUnionType.create(typeEnv, null, tupleTypes); } switch (tag) { case TypeTags.FINITE: - BFiniteType finiteIndexExpr = (BFiniteType) currentType; LinkedHashSet possibleTypes = new LinkedHashSet<>(); - for (BLangExpression finiteMember : finiteIndexExpr.getValueSpace()) { - int indexValue = ((Long) ((BLangLiteral) finiteMember).value).intValue(); - BType fieldType = checkTupleFieldType(tuple, indexValue); - if (fieldType.tag != TypeTags.SEMANTIC_ERROR) { - possibleTypes.add(fieldType); + SemType t = currentType.semType(); + + Optional properSubtypeData = getProperSubtypeData(t, BT_INT); + if (properSubtypeData.isEmpty()) { + return symTable.semanticError; + } + + IntSubtype intSubtype = (IntSubtype) properSubtypeData.get(); + for (Range range : intSubtype.ranges) { + for (long indexVal = range.min; indexVal <= range.max; indexVal++) { + BType fieldType = checkTupleFieldType(tuple, (int) indexVal); + if (fieldType.tag != TypeTags.SEMANTIC_ERROR) { + possibleTypes.add(fieldType); + } } } + if (possibleTypes.isEmpty()) { return symTable.semanticError; } actualType = possibleTypes.size() == 1 ? possibleTypes.iterator().next() : - BUnionType.create(null, possibleTypes); + BUnionType.create(typeEnv, null, possibleTypes); break; - case TypeTags.UNION: LinkedHashSet possibleTypesByMember = new LinkedHashSet<>(); List finiteTypes = new ArrayList<>(); @@ -9151,8 +9115,14 @@ private BType checkTupleIndexBasedAccess(BLangIndexBasedAccess accessExpr, BTupl } } }); + if (!finiteTypes.isEmpty()) { - BFiniteType finiteType = createFiniteTypeFromFiniteTypeList(finiteTypes); + List newValueSpace = new ArrayList<>(); + for (BFiniteType ft : finiteTypes) { + newValueSpace.addAll(Arrays.asList(ft.valueSpace)); + } + + BFiniteType finiteType = new BFiniteType(null, newValueSpace.toArray(SemNamedType[]::new)); BType possibleType = checkTupleIndexBasedAccess(accessExpr, tuple, finiteType); if (possibleType.tag == TypeTags.UNION) { possibleTypesByMember.addAll(((BUnionType) possibleType).getMemberTypes()); @@ -9160,11 +9130,12 @@ private BType checkTupleIndexBasedAccess(BLangIndexBasedAccess accessExpr, BTupl possibleTypesByMember.add(possibleType); } } + if (possibleTypesByMember.contains(symTable.semanticError)) { return symTable.semanticError; } actualType = possibleTypesByMember.size() == 1 ? possibleTypesByMember.iterator().next() : - BUnionType.create(null, possibleTypesByMember); + BUnionType.create(typeEnv, null, possibleTypesByMember); } return actualType; } @@ -9222,7 +9193,7 @@ private BType checkMappingIndexBasedAccess(BLangIndexBasedAccess accessExpr, BTy if (fieldTypeMembers.size() == 1) { fieldType = fieldTypeMembers.iterator().next(); } else { - fieldType = BUnionType.create(null, fieldTypeMembers); + fieldType = BUnionType.create(typeEnv, null, fieldTypeMembers); } return nonMatchedRecordExists ? types.addNilForNillableAccessType(fieldType) : fieldType; @@ -9274,13 +9245,19 @@ private BType checkRecordIndexBasedAccess(BLangIndexBasedAccess accessExpr, BRec } actualType = fieldTypes.size() == 1 ? fieldTypes.iterator().next() : - BUnionType.create(null, fieldTypes); + BUnionType.create(typeEnv, null, fieldTypes); break; case TypeTags.FINITE: - BFiniteType finiteIndexExpr = (BFiniteType) currentType; LinkedHashSet possibleTypes = new LinkedHashSet<>(); - for (BLangExpression finiteMember : finiteIndexExpr.getValueSpace()) { - String fieldName = (String) ((BLangLiteral) finiteMember).value; + SemType t = currentType.semType(); + + Optional properSubtypeData = getProperSubtypeData(t, BT_STRING); + if (properSubtypeData.isEmpty()) { + return symTable.semanticError; + } + + Set values = getStringValueSpace((StringSubtype) properSubtypeData.get()); + for (String fieldName : values) { BType fieldType = checkRecordRequiredFieldAccess(accessExpr, Names.fromString(fieldName), record, data); if (fieldType == symTable.semanticError) { @@ -9311,7 +9288,7 @@ private BType checkRecordIndexBasedAccess(BLangIndexBasedAccess accessExpr, BRec } actualType = possibleTypes.size() == 1 ? possibleTypes.iterator().next() : - BUnionType.create(null, possibleTypes); + BUnionType.create(typeEnv, null, possibleTypes); break; case TypeTags.UNION: LinkedHashSet possibleTypesByMember = new LinkedHashSet<>(); @@ -9328,8 +9305,14 @@ private BType checkRecordIndexBasedAccess(BLangIndexBasedAccess accessExpr, BRec } } }); + if (!finiteTypes.isEmpty()) { - BFiniteType finiteType = createFiniteTypeFromFiniteTypeList(finiteTypes); + List newValueSpace = new ArrayList<>(); + for (BFiniteType ft : finiteTypes) { + newValueSpace.addAll(Arrays.asList(ft.valueSpace)); + } + + BFiniteType finiteType = new BFiniteType(null, newValueSpace.toArray(SemNamedType[]::new)); BType possibleType = checkRecordIndexBasedAccess(accessExpr, record, finiteType, data); if (possibleType.tag == TypeTags.UNION) { possibleTypesByMember.addAll(((BUnionType) possibleType).getMemberTypes()); @@ -9337,15 +9320,53 @@ private BType checkRecordIndexBasedAccess(BLangIndexBasedAccess accessExpr, BRec possibleTypesByMember.add(possibleType); } } + if (possibleTypesByMember.contains(symTable.semanticError)) { return symTable.semanticError; } actualType = possibleTypesByMember.size() == 1 ? possibleTypesByMember.iterator().next() : - BUnionType.create(null, possibleTypesByMember); + BUnionType.create(typeEnv, null, possibleTypesByMember); } return actualType; } + private Optional getProperSubtypeData(SemType t, BasicTypeCode u) { + if (t instanceof BasicTypeBitSet) { + return Optional.empty(); + } + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, u); + if (sd instanceof AllOrNothingSubtype) { + return Optional.empty(); + } + return Optional.of(sd); + } + + /** + * Returns the set of values belongs to a given StringSubtype. + *

+ * Note: We assume StringSubtype does not contain any diff. i.e. contains only a finite set of values + * + * @param stringSubtype string subtype data + * @return set of string values + */ + private static Set getStringValueSpace(StringSubtype stringSubtype) { + Set values = new HashSet<>(); + CharStringSubtype charStringSubtype = stringSubtype.getChar(); + assert charStringSubtype.allowed; + for (EnumerableType enumerableType : charStringSubtype.values()) { + EnumerableCharString s = (EnumerableCharString) enumerableType; + values.add(s.value); + } + + NonCharStringSubtype nonCharStringSubtype = stringSubtype.getNonChar(); + assert nonCharStringSubtype.allowed; + for (EnumerableType enumerableType : nonCharStringSubtype.values()) { + EnumerableString s = (EnumerableString) enumerableType; + values.add(s.value); + } + return values; + } + private boolean isConstExpr(BLangExpression expression) { switch (expression.getKind()) { case LITERAL: @@ -9405,7 +9426,7 @@ private BType getRepresentativeBroadType(List inferredTypeList) { return inferredTypeList.get(0); } - return BUnionType.create(null, inferredTypeList.toArray(new BType[0])); + return BUnionType.create(typeEnv, null, inferredTypeList.toArray(new BType[0])); } public BType defineInferredRecordType(BLangRecordLiteral recordLiteral, BType expType, AnalyzerData data) { @@ -9425,7 +9446,7 @@ public BType defineInferredRecordType(BLangRecordLiteral recordLiteral, BType ex if (key.computedKey) { checkExpr(keyExpr, symTable.stringType, data); BType exprType = checkExpr(expression, expType, data); - if (isUniqueType(restFieldTypes, exprType)) { + if (types.isUniqueType(restFieldTypes, exprType)) { restFieldTypes.add(exprType); } } else { @@ -9442,7 +9463,7 @@ public BType defineInferredRecordType(BLangRecordLiteral recordLiteral, BType ex if (type.tag == TypeTags.MAP) { BType constraintType = ((BMapType) type).constraint; - if (isUniqueType(restFieldTypes, constraintType)) { + if (types.isUniqueType(restFieldTypes, constraintType)) { restFieldTypes.add(constraintType); } } @@ -9459,7 +9480,7 @@ public BType defineInferredRecordType(BLangRecordLiteral recordLiteral, BType ex if (!recordType.sealed) { BType restFieldType = recordType.restFieldType; - if (isUniqueType(restFieldTypes, restFieldType)) { + if (types.isUniqueType(restFieldTypes, restFieldType)) { restFieldTypes.add(restFieldType); } } @@ -9485,7 +9506,8 @@ public BType defineInferredRecordType(BLangRecordLiteral recordLiteral, BType ex String key = entry.getKey(); Name fieldName = Names.fromString(key); - BType type = types.size() == 1 ? types.get(0) : BUnionType.create(null, types.toArray(new BType[0])); + BType type = types.size() == 1 ? types.get(0) : + BUnionType.create(typeEnv, null, types.toArray(new BType[0])); Set flags = new HashSet<>(); @@ -9507,7 +9529,7 @@ public BType defineInferredRecordType(BLangRecordLiteral recordLiteral, BType ex recordSymbol.scope.define(fieldName, fieldSymbol); } - BRecordType recordType = new BRecordType(recordSymbol); + BRecordType recordType = new BRecordType(typeEnv, recordSymbol); recordType.fields = fields; if (restFieldTypes.contains(symTable.semanticError)) { @@ -9520,13 +9542,14 @@ public BType defineInferredRecordType(BLangRecordLiteral recordLiteral, BType ex } else if (restFieldTypes.size() == 1) { recordType.restFieldType = restFieldTypes.get(0); } else { - recordType.restFieldType = BUnionType.create(null, restFieldTypes.toArray(new BType[0])); + recordType.restFieldType = + BUnionType.create(typeEnv, null, restFieldTypes.toArray(new BType[0])); } recordSymbol.type = recordType; recordType.tsymbol = recordSymbol; if (expType == symTable.readonlyType || (recordType.sealed && allReadOnlyNonRestFields)) { - recordType.flags |= Flags.READONLY; + recordType.addFlags(Flags.READONLY); recordSymbol.flags |= Flags.READONLY; } @@ -9542,7 +9565,7 @@ private BRecordTypeSymbol createRecordTypeSymbol(PackageID pkgID, Location locat SymbolEnv env = data.env; BRecordTypeSymbol recordSymbol = Symbols.createRecordSymbol(Flags.ANONYMOUS, - Names.fromString(anonymousModelHelper.getNextAnonymousTypeKey(pkgID)), + Names.fromString(anonymousModelHelper.getNextAnonymousTypeKey(pkgID)), pkgID, null, env.scope.owner, location, origin); recordSymbol.scope = new Scope(recordSymbol); return recordSymbol; @@ -9564,7 +9587,7 @@ private void addToNonRestFieldTypes(Map nonRestFieldTypes, St FieldInfo fieldInfo = nonRestFieldTypes.get(keyString); List typeList = fieldInfo.types; - if (isUniqueType(typeList, exprType)) { + if (types.isUniqueType(typeList, exprType)) { typeList.add(exprType); } @@ -9573,23 +9596,6 @@ private void addToNonRestFieldTypes(Map nonRestFieldTypes, St } } - private boolean isUniqueType(Iterable typeList, BType type) { - type = Types.getImpliedType(type); - boolean isRecord = type.tag == TypeTags.RECORD; - - for (BType bType : typeList) { - bType = Types.getImpliedType(bType); - if (isRecord) { - if (type == bType) { - return false; - } - } else if (types.isSameType(type, bType)) { - return false; - } - } - return true; - } - private BType checkXmlSubTypeLiteralCompatibility(Location location, BXMLSubType mutableXmlSubType, BType expType, AnalyzerData data) { if (expType == symTable.semanticError) { @@ -9661,7 +9667,7 @@ private BType checkXmlSubTypeLiteralCompatibility(Location location, BXMLSubType private void markChildrenAsImmutable(BLangXMLElementLiteral bLangXMLElementLiteral, AnalyzerData data) { for (BLangExpression modifiedChild : bLangXMLElementLiteral.modifiedChildren) { BType childType = modifiedChild.getBType(); - if (Symbols.isFlagOn(childType.flags, Flags.READONLY) || + if (Symbols.isFlagOn(childType.getFlags(), Flags.READONLY) || !types.isSelectivelyImmutableType(childType, data.env.enclPkg.packageID)) { continue; } @@ -9681,7 +9687,7 @@ public void logUndefinedSymbolError(Location pos, String name) { } private void markTypeAsIsolated(BType actualType) { - actualType.flags |= Flags.ISOLATED; + actualType.addFlags(Flags.ISOLATED); actualType.tsymbol.flags |= Flags.ISOLATED; } @@ -9714,7 +9720,7 @@ private void handleObjectConstrExprForReadOnly( } classDefForConstructor.flagSet.add(Flag.READONLY); - actualObjectType.flags |= Flags.READONLY; + actualObjectType.addFlags(Flags.READONLY); actualObjectType.tsymbol.flags |= Flags.READONLY; ImmutableTypeCloner.markFieldsAsImmutable(classDefForConstructor, env, actualObjectType, types, @@ -9727,7 +9733,7 @@ private void markConstructedObjectIsolatedness(BObjectType actualObjectType) { if (actualObjectType.markedIsolatedness) { return; } - if (Symbols.isFlagOn(actualObjectType.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(actualObjectType.getFlags(), Flags.READONLY)) { markTypeAsIsolated(actualObjectType); return; } @@ -9786,7 +9792,7 @@ private BType validateElvisExprLhsExpr(BLangElvisExpr elvisExpr, BType lhsType) } else if (size == 1) { actualType = memberTypes.iterator().next(); } else { - actualType = BUnionType.create(null, memberTypes); + actualType = BUnionType.create(typeEnv, null, memberTypes); } } else { // We should get here only for `any` and nil. We use the type as is since we don't have a way to @@ -9805,25 +9811,22 @@ private BType validateElvisExprLhsExpr(BLangElvisExpr elvisExpr, BType lhsType) private LinkedHashSet getTypeWithoutNilForNonAnyTypeWithNil(BType type) { BType referredType = Types.getImpliedType(type); if (referredType.tag == TypeTags.FINITE) { - Set valueSpace = ((BFiniteType) referredType).getValueSpace(); - LinkedHashSet nonNilValueSpace = new LinkedHashSet<>(); - for (BLangExpression expression : valueSpace) { - if (expression.getBType().tag != TypeTags.NIL) { - nonNilValueSpace.add(expression); + BFiniteType finiteType = (BFiniteType) referredType; + List newValueSpace = new ArrayList<>(finiteType.valueSpace.length); + for (SemNamedType semNamedType : finiteType.valueSpace) { + if (!PredefinedType.NIL.equals(semNamedType.semType())) { + newValueSpace.add(semNamedType);; } } - int nonNilValueSpaceSize = nonNilValueSpace.size(); - - if (nonNilValueSpaceSize == valueSpace.size()) { - return new LinkedHashSet<>(1) {{ add(referredType); }}; - } - - if (nonNilValueSpaceSize == 0) { + if (newValueSpace.isEmpty()) { return new LinkedHashSet<>(0); } - return new LinkedHashSet<>(1) {{ add(new BFiniteType(null, nonNilValueSpace)); }}; + BFiniteType ft = new BFiniteType(null, newValueSpace.toArray(SemNamedType[]::new)); + return new LinkedHashSet<>(1) {{ + add(ft); + }}; } BUnionType unionType = (BUnionType) referredType; @@ -9844,16 +9847,6 @@ private LinkedHashSet getTypeWithoutNilForNonAnyTypeWithNil(BType type) { return memberTypes; } - private BFiniteType createFiniteTypeFromFiniteTypeList(List finiteTypes) { - if (finiteTypes.size() == 1) { - return finiteTypes.get(0); - } else { - Set valueSpace = new LinkedHashSet<>(); - finiteTypes.forEach(constituent -> valueSpace.addAll(constituent.getValueSpace())); - return new BFiniteType(null, valueSpace); - } - } - private static class FieldInfo { List types; boolean required; @@ -9867,6 +9860,7 @@ private FieldInfo(List types, boolean required, boolean readonly) { } private static class TypeSymbolPair { + private final BVarSymbol fieldSymbol; private final BType determinedType; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeHashVisitor.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeHashVisitor.java index 28769a48e816..5e54c8a473a3 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeHashVisitor.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeHashVisitor.java @@ -24,7 +24,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; @@ -36,7 +35,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; @@ -53,7 +51,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLSubType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -73,7 +70,7 @@ * * @since 2.0.0 */ -public class TypeHashVisitor implements UniqueTypeVisitor { +public class TypeHashVisitor extends UniqueTypeVisitor { private final Map visited; private final Set unresolvedTypes; private final Map cache; @@ -106,7 +103,7 @@ public Integer getHash(BType type) { } @Override - public Integer visit(BType type) { + public Integer visit(BType type) { // TODO: can move to the abstract class? if (type == null) { return 0; } @@ -115,7 +112,7 @@ public Integer visit(BType type) { case TypeTags.ANY: return visit((BAnyType) type); case TypeTags.NIL: - return visit((BNilType) type); + return visitNilType(type); case TypeTags.NEVER: return visit((BNeverType) type); case TypeTags.ANYDATA: @@ -202,12 +199,12 @@ public Integer visit(BArrayType type) { if (isCyclic(type)) { return 0; } - Integer hash = hash(baseHash(type), type.size, type.state.getValue(), visit(type.eType)); + Integer hash = hash(baseHash(type), type.getSize(), type.state.getValue(), visit(type.eType)); return addToVisited(type, hash); } @Override - public Integer visit(BBuiltInRefType type) { + public Integer visit(BReadonlyType type) { if (isVisited(type)) { return visited.get(type); } @@ -369,7 +366,7 @@ public Integer visit(BNeverType type) { } @Override - public Integer visit(BNilType type) { + public Integer visitNilType(BType type) { if (isVisited(type)) { return visited.get(type); } @@ -406,7 +403,7 @@ public Integer visit(BTupleType type) { return 0; } List tupleTypesHashes = getOrderedTypesHashes(type.getTupleTypes()); - Integer hash = hash(baseHash(type), tupleTypesHashes, visit(type.restType), type.flags, type.tsymbol); + Integer hash = hash(baseHash(type), tupleTypesHashes, visit(type.restType), type.getFlags(), type.tsymbol); return addToVisited(type, hash); } @@ -455,18 +452,8 @@ public Integer visit(BFiniteType type) { if (isCyclic(type)) { return 0; } - List toSort = new ArrayList<>(); - for (BLangExpression bLangExpression : type.getValueSpace()) { - String toString = bLangExpression.toString(); - toSort.add(toString); - } - toSort.sort(null); - List valueSpaceHashes = new ArrayList<>(); - for (String toString : toSort) { - Integer hashCode = toString.hashCode(); - valueSpaceHashes.add(hashCode); - } - Integer hash = hash(baseHash(type), valueSpaceHashes); + + Integer hash = hash(baseHash(type), type.toString().hashCode()); return addToVisited(type, hash); } @@ -480,7 +467,7 @@ public Integer visit(BStructureType type) { } List fieldsHashes = getFieldsHashes(type.fields); List typeInclHashes = getTypesHashes(type.typeInclusions); - Integer hash = hash(baseHash(type), type.flags, fieldsHashes, typeInclHashes); + Integer hash = hash(baseHash(type), type.getFlags(), fieldsHashes, typeInclHashes); return addToVisited(type, hash); } @@ -495,7 +482,7 @@ public Integer visit(BObjectType type) { List fieldsHashes = getFieldsHashes(type.fields); List typeInclHashes = getTypesHashes(type.typeInclusions); List attachedFunctionsHashes = getFunctionsHashes(((BObjectTypeSymbol) type.tsymbol).attachedFuncs); - Integer hash = hash(baseHash(type), type.flags, fieldsHashes, typeInclHashes, + Integer hash = hash(baseHash(type), type.getFlags(), fieldsHashes, typeInclHashes, attachedFunctionsHashes, type.typeIdSet); return addToVisited(type, hash); } @@ -510,7 +497,7 @@ public Integer visit(BRecordType type) { } List fieldsHashes = getFieldsHashes(type.fields); List typeInclHashes = getTypesHashes(type.typeInclusions); - Integer hash = hash(baseHash(type), type.flags, type.sealed, fieldsHashes, typeInclHashes, + Integer hash = hash(baseHash(type), type.getFlags(), type.sealed, fieldsHashes, typeInclHashes, visit(type.restFieldType)); return addToVisited(type, hash); } @@ -523,7 +510,7 @@ public Integer visit(BUnionType type) { if (isCyclic(type)) { return 0; } - Integer hash = hash(baseHash(type), type.isCyclic, getTypesHashes(type.getMemberTypes()), type.flags); + Integer hash = hash(baseHash(type), type.isCyclic, getTypesHashes(type.getMemberTypes()), type.getFlags()); return addToVisited(type, hash); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeNarrower.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeNarrower.java index d2ee81e5d206..35ec2e2e95c7 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeNarrower.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeNarrower.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.semantics.analyzer; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.SemType; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.symbols.SymbolKind; import org.ballerinalang.model.tree.NodeKind; @@ -39,6 +40,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangGroupExpr; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangSimpleVarRef; import org.wso2.ballerinalang.compiler.tree.expressions.BLangTypeTestExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangUnaryExpr; @@ -391,7 +393,7 @@ private BType getTypeUnion(BType currentType, BType targetType) { } else if (union.size() == 1) { return union.toArray(new BType[1])[0]; } - return BUnionType.create(null, union); + return BUnionType.create(symTable.typeEnv(), null, union); } BVarSymbol getOriginalVarSymbol(BVarSymbol varSymbol) { @@ -419,15 +421,17 @@ private BFiniteType createFiniteType(BLangExpression expr) { Flags.asMask(EnumSet.noneOf(Flag.class)), Names.EMPTY, env.enclPkg.symbol.pkgID, null, env.scope.owner, expr.pos, SOURCE); - BFiniteType finiteType = new BFiniteType(finiteTypeSymbol); + SemType semType; if (expr.getKind() == NodeKind.UNARY_EXPR) { - finiteType.addValue(Types.constructNumericLiteralFromUnaryExpr((BLangUnaryExpr) expr)); + semType = SemTypeHelper.resolveSingletonType(Types.constructNumericLiteralFromUnaryExpr( + (BLangUnaryExpr) expr)); } else { expr.setBType(symTable.getTypeFromTag(expr.getBType().tag)); - finiteType.addValue(expr); + semType = SemTypeHelper.resolveSingletonType((BLangLiteral) expr); } - finiteTypeSymbol.type = finiteType; + BFiniteType finiteType = BFiniteType.newSingletonBFiniteType(finiteTypeSymbol, semType); + finiteTypeSymbol.type = finiteType; return finiteType; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeParamAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeParamAnalyzer.java index 731978cb27fc..7b3eda99d419 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeParamAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeParamAnalyzer.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.semantics.analyzer; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.PredefinedType; import org.ballerinalang.model.Name; import org.ballerinalang.model.elements.PackageID; import org.ballerinalang.model.tree.NodeKind; @@ -130,7 +131,7 @@ private TypeParamAnalyzer(CompilerContext context) { static boolean isTypeParam(BType expType) { - return Symbols.isFlagOn(expType.flags, Flags.TYPE_PARAM) + return Symbols.isFlagOn(expType.getFlags(), Flags.TYPE_PARAM) || (expType.tsymbol != null && Symbols.isFlagOn(expType.tsymbol.flags, Flags.TYPE_PARAM)); } @@ -173,7 +174,7 @@ public BType getNominalType(BType type, Name name, long flag) { BType createTypeParam(BSymbol symbol) { BType type = symbol.type; - var flag = type.flags | Flags.TYPE_PARAM; + var flag = type.getFlags() | Flags.TYPE_PARAM; return createTypeParamType(symbol, type, symbol.name, flag); } @@ -222,7 +223,7 @@ private static boolean containsTypeParam(BType type, HashSet resolvedType return false; case TypeTags.INVOKABLE: BInvokableType invokableType = (BInvokableType) type; - if (Symbols.isFlagOn(invokableType.flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(invokableType.getFlags(), Flags.ANY_FUNCTION)) { return false; } for (BType paramType : invokableType.paramTypes) { @@ -277,9 +278,9 @@ private BType createBuiltInType(BType type, Name name, long flags) { TypeTags.DECIMAL, TypeTags.STRING, TypeTags.BOOLEAN -> new BType(tag, null, name, flags); - case TypeTags.ANY -> new BAnyType(tag, null, name, flags); + case TypeTags.ANY -> new BAnyType(name, flags); case TypeTags.ANYDATA -> createAnydataType((BUnionType) referredType, name, flags); - case TypeTags.READONLY -> new BReadonlyType(tag, null, name, flags); + case TypeTags.READONLY -> new BReadonlyType(flags); // For others, we will use TSymbol. default -> type; }; @@ -297,16 +298,17 @@ private BType createTypeParamType(BSymbol symbol, BType type, Name name, long fl case TypeTags.BOOLEAN: return new BType(type.tag, null, name, flags); case TypeTags.ANY: - return new BAnyType(type.tag, null, name, flags); + return new BAnyType(name, flags); case TypeTags.ANYDATA: return createAnydataType((BUnionType) type, name, flags); case TypeTags.READONLY: - return new BReadonlyType(type.tag, null, name, flags); + return new BReadonlyType(flags); case TypeTags.UNION: if (types.isCloneableType((BUnionType) refType)) { - BUnionType cloneableType = BUnionType.create(null, symTable.readonlyType, symTable.xmlType); + BUnionType cloneableType = + BUnionType.create(symTable.typeEnv(), null, symTable.readonlyType, symTable.xmlType); addCyclicArrayMapTableOfMapMembers(cloneableType); - cloneableType.flags = flags; + cloneableType.setFlags(flags); cloneableType.tsymbol = new BTypeSymbol(SymTag.TYPE, refType.tsymbol.flags, symbol.name, refType.tsymbol.pkgID, cloneableType, refType.tsymbol.owner, type.tsymbol.pos, @@ -323,20 +325,20 @@ private BType createTypeParamType(BSymbol symbol, BType type, Name name, long fl } private BAnydataType createAnydataType(BUnionType unionType, Name name, long flags) { - BAnydataType anydataType = new BAnydataType(unionType); + BAnydataType anydataType = new BAnydataType(types.typeCtx(), unionType); Optional immutableType = Types.getImmutableType(symTable, PackageID.ANNOTATIONS, unionType); immutableType.ifPresent(bIntersectionType -> Types.addImmutableType(symTable, PackageID.ANNOTATIONS, anydataType, bIntersectionType)); anydataType.name = name; - anydataType.flags |= flags; + anydataType.addFlags(flags); return anydataType; } private void addCyclicArrayMapTableOfMapMembers(BUnionType unionType) { - BArrayType arrayCloneableType = new BArrayType(unionType); - BMapType mapCloneableType = new BMapType(TypeTags.MAP, unionType, null); - BType tableMapCloneableType = new BTableType(TypeTags.TABLE, mapCloneableType, null); + BArrayType arrayCloneableType = new BArrayType(symTable.typeEnv(), unionType); + BMapType mapCloneableType = new BMapType(symTable.typeEnv(), TypeTags.MAP, unionType, null); + BType tableMapCloneableType = new BTableType(symTable.typeEnv(), mapCloneableType, null); unionType.add(arrayCloneableType); unionType.add(mapCloneableType); unionType.add(tableMapCloneableType); @@ -590,10 +592,10 @@ private void findTypeParamInStreamForUnion(Location loc, BStreamType expType, BU } } - BUnionType cUnionType = BUnionType.create(null, constraintTypes); + BUnionType cUnionType = BUnionType.create(symTable.typeEnv(), null, constraintTypes); findTypeParam(loc, expType.constraint, cUnionType, env, resolvedTypes, result); if (!completionTypes.isEmpty()) { - BUnionType eUnionType = BUnionType.create(null, completionTypes); + BUnionType eUnionType = BUnionType.create(symTable.typeEnv(), null, completionTypes); findTypeParam(loc, expType.completionType, eUnionType, env, resolvedTypes, result); } else { findTypeParam(loc, expType.completionType, symTable.nilType, env, resolvedTypes, result); @@ -615,7 +617,7 @@ private void findTypeParamInTable(Location loc, BTableType expType, BTableType a if (members.size() == 1) { findTypeParam(loc, expType.keyTypeConstraint, members.get(0).type, env, resolvedTypes, result); } else { - BTupleType tupleType = new BTupleType(members); + BTupleType tupleType = new BTupleType(symTable.typeEnv(), members); findTypeParam(loc, expType.keyTypeConstraint, tupleType, env, resolvedTypes, result); } } @@ -632,7 +634,7 @@ private void findTypeParamInTupleForArray(Location loc, BArrayType expType, BTup int size = tupleTypes.size(); BType type = size == 0 ? symTable.neverType : - (size == 1 ? tupleTypes.iterator().next() : BUnionType.create(null, tupleTypes)); + (size == 1 ? tupleTypes.iterator().next() : BUnionType.create(symTable.typeEnv(), null, tupleTypes)); findTypeParam(loc, expType.eType, type, env, resolvedTypes, result); } @@ -663,7 +665,7 @@ private void findTypeParamInUnion(Location loc, BType expType, BUnionType actual ((BTupleType) referredType).getTupleTypes().forEach(member -> members.add(member)); } } - BUnionType tupleElementType = BUnionType.create(null, members); + BUnionType tupleElementType = BUnionType.create(symTable.typeEnv(), null, members); findTypeParam(loc, expType, tupleElementType, env, resolvedTypes, result); } @@ -703,7 +705,7 @@ private void findTypeParamInMapForRecord(Location loc, BMapType expType, BRecord if (reducedTypeSet.size() == 1) { commonFieldType = reducedTypeSet.iterator().next(); } else { - commonFieldType = BUnionType.create(null, reducedTypeSet); + commonFieldType = BUnionType.create(symTable.typeEnv(), null, reducedTypeSet); } findTypeParam(loc, expType.constraint, commonFieldType, env, resolvedTypes, result); @@ -712,7 +714,7 @@ private void findTypeParamInMapForRecord(Location loc, BMapType expType, BRecord private void findTypeParamInInvokableType(Location loc, BInvokableType expType, BInvokableType actualType, SymbolEnv env, HashSet resolvedTypes, FindTypeParamResult result) { - if (Symbols.isFlagOn(expType.flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(expType.getFlags(), Flags.ANY_FUNCTION)) { return; } for (int i = 0; i < expType.paramTypes.size() && i < actualType.paramTypes.size(); i++) { @@ -768,14 +770,14 @@ private void findTypeParamInError(Location loc, BErrorType expType, BType actual findTypeParam(loc, expType.detailType, ((BErrorType) actualType).detailType, env, resolvedTypes, result); } - if (actualType.tag == TypeTags.UNION && types.isSubTypeOfBaseType(actualType, TypeTags.ERROR)) { + if (actualType.tag == TypeTags.UNION && types.isSubTypeOfBaseType(actualType, PredefinedType.ERROR)) { BUnionType errorUnion = (BUnionType) actualType; LinkedHashSet errorDetailTypes = new LinkedHashSet<>(); for (BType errorType : errorUnion.getMemberTypes()) { BType member = Types.getImpliedType(errorType); errorDetailTypes.add(((BErrorType) member).detailType); } - BUnionType errorDetailUnionType = BUnionType.create(null, errorDetailTypes); + BUnionType errorDetailUnionType = BUnionType.create(symTable.typeEnv(), null, errorDetailTypes); findTypeParam(loc, expType.detailType, errorDetailUnionType, env, resolvedTypes, result); } } @@ -802,14 +804,15 @@ private BType getMatchingBoundType(BType expType, SymbolEnv env, HashSet if (!isDifferentTypes(elementType, matchingBoundElementType)) { return expType; } - return new BArrayType(matchingBoundElementType); + return new BArrayType(symTable.typeEnv(), matchingBoundElementType); case TypeTags.MAP: BType constraint = ((BMapType) expType).constraint; BType matchingBoundMapConstraintType = getMatchingBoundType(constraint, env, resolvedTypes); if (!isDifferentTypes(constraint, matchingBoundMapConstraintType)) { return expType; } - return new BMapType(TypeTags.MAP, matchingBoundMapConstraintType, symTable.mapType.tsymbol); + return new BMapType(symTable.typeEnv(), TypeTags.MAP, matchingBoundMapConstraintType, + symTable.mapType.tsymbol); case TypeTags.STREAM: BStreamType expStreamType = (BStreamType) expType; BType expStreamConstraint = expStreamType.constraint; @@ -826,7 +829,8 @@ private BType getMatchingBoundType(BType expType, SymbolEnv env, HashSet return expStreamType; } - return new BStreamType(TypeTags.STREAM, constraintType, completionType, symTable.streamType.tsymbol); + return new BStreamType(symTable.typeEnv(), TypeTags.STREAM, constraintType, completionType, + symTable.streamType.tsymbol); case TypeTags.TABLE: BTableType expTableType = (BTableType) expType; BType expTableConstraint = expTableType.constraint; @@ -844,7 +848,8 @@ private BType getMatchingBoundType(BType expType, SymbolEnv env, HashSet return expTableType; } - BTableType tableType = new BTableType(TypeTags.TABLE, tableConstraint, symTable.tableType.tsymbol); + BTableType tableType = new BTableType(symTable.typeEnv(), tableConstraint, + symTable.tableType.tsymbol); if (expTableKeyTypeConstraint != null) { tableType.keyTypeConstraint = keyTypeConstraint; } @@ -868,7 +873,7 @@ private BType getMatchingBoundType(BType expType, SymbolEnv env, HashSet return expType; } - return new BTypedescType(matchingBoundType, symTable.typeDesc.tsymbol); + return new BTypedescType(symTable.typeEnv(), matchingBoundType, symTable.typeDesc.tsymbol); case TypeTags.INTERSECTION: return getMatchingReadonlyIntersectionBoundType((BIntersectionType) expType, env, resolvedTypes); case TypeTags.TYPEREFDESC: @@ -910,7 +915,7 @@ private BType getMatchingReadonlyIntersectionBoundType(BIntersectionType interse } if (types.isInherentlyImmutableType(matchingBoundNonReadOnlyType) || - Symbols.isFlagOn(matchingBoundNonReadOnlyType.flags, Flags.READONLY)) { + Symbols.isFlagOn(matchingBoundNonReadOnlyType.getFlags(), Flags.READONLY)) { return matchingBoundNonReadOnlyType; } @@ -930,7 +935,7 @@ private BTupleType getMatchingTupleBoundType(BTupleType expType, SymbolEnv env, if (!hasDifferentType && isDifferentTypes(type, matchingBoundType)) { hasDifferentType = true; } - BVarSymbol varSymbol = new BVarSymbol(matchingBoundType.flags, null, null, matchingBoundType, + BVarSymbol varSymbol = new BVarSymbol(matchingBoundType.getFlags(), null, null, matchingBoundType, null, null, null); members.add(new BTupleMember(matchingBoundType, varSymbol)); } @@ -947,7 +952,7 @@ private BTupleType getMatchingTupleBoundType(BTupleType expType, SymbolEnv env, return expType; } - return new BTupleType(members); + return new BTupleType(symTable.typeEnv(), members); } private BRecordType getMatchingRecordBoundType(BRecordType expType, SymbolEnv env, HashSet resolvedTypes) { @@ -976,10 +981,10 @@ private BRecordType getMatchingRecordBoundType(BRecordType expType, SymbolEnv en recordSymbol.scope.define(expField.name, field.symbol); } - BRecordType bRecordType = new BRecordType(recordSymbol); + BRecordType bRecordType = new BRecordType(symTable.typeEnv(), recordSymbol); bRecordType.fields = fields; recordSymbol.type = bRecordType; - bRecordType.flags = expType.flags; + bRecordType.setFlags(expType.getFlags()); if (expType.sealed) { bRecordType.sealed = true; @@ -1010,7 +1015,7 @@ private BInvokableType getMatchingFunctionBoundType(BInvokableType expType, Symb } BType restType = expType.restType; - var flags = expType.flags; + var flags = expType.getFlags(); BInvokableTypeSymbol invokableTypeSymbol = Symbols.createInvokableTypeSymbol(SymTag.FUNCTION_TYPE, flags, env.enclPkg.symbol.pkgID, expType, env.scope.owner, expType.tsymbol.pos, VIRTUAL); @@ -1027,7 +1032,7 @@ private BInvokableType getMatchingFunctionBoundType(BInvokableType expType, Symb return expType; } - BInvokableType invokableType = new BInvokableType(paramTypes, restType, + BInvokableType invokableType = new BInvokableType(symTable.typeEnv(), paramTypes, restType, matchingBoundType, invokableTypeSymbol); invokableTypeSymbol.returnType = invokableType.retType; @@ -1035,7 +1040,7 @@ private BInvokableType getMatchingFunctionBoundType(BInvokableType expType, Symb invokableType.tsymbol.isTypeParamResolved = true; invokableType.tsymbol.typeParamTSymbol = expType.tsymbol; if (Symbols.isFlagOn(flags, Flags.ISOLATED)) { - invokableType.flags |= Flags.ISOLATED; + invokableType.addFlags(Flags.ISOLATED); } return invokableType; @@ -1052,7 +1057,7 @@ private BType getMatchingObjectBoundType(BObjectType expType, SymbolEnv env, Has actObjectSymbol.isTypeParamResolved = true; actObjectSymbol.typeParamTSymbol = expType.tsymbol; - BObjectType objectType = new BObjectType(actObjectSymbol); + BObjectType objectType = new BObjectType(symTable.typeEnv(), actObjectSymbol); actObjectSymbol.type = objectType; actObjectSymbol.scope = new Scope(actObjectSymbol); @@ -1134,7 +1139,7 @@ private BType getMatchingOptionalBoundType(BUnionType expType, SymbolEnv env, Ha return expType; } - return BUnionType.create(null, members); + return BUnionType.create(symTable.typeEnv(), null, members); } private BType getMatchingErrorBoundType(BErrorType expType, SymbolEnv env, HashSet resolvedTypes) { @@ -1156,7 +1161,7 @@ private BType getMatchingErrorBoundType(BErrorType expType, SymbolEnv env, HashS null, null, symTable.builtinPos, VIRTUAL); typeSymbol.isTypeParamResolved = true; typeSymbol.typeParamTSymbol = expType.tsymbol; - BErrorType errorType = new BErrorType(typeSymbol, detailType); + BErrorType errorType = new BErrorType(symTable.typeEnv(), typeSymbol, detailType); typeSymbol.type = errorType; return errorType; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeResolver.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeResolver.java index 189a1504468d..9812114fd3fb 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeResolver.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeResolver.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.semantics.analyzer; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.SemType; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; import org.ballerinalang.model.symbols.SymbolKind; @@ -68,6 +69,7 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BTypedescType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.compiler.tree.BLangClassDefinition; import org.wso2.ballerinalang.compiler.tree.BLangConstantValue; import org.wso2.ballerinalang.compiler.tree.BLangFunction; @@ -129,6 +131,7 @@ import static org.ballerinalang.model.symbols.SymbolOrigin.BUILTIN; import static org.ballerinalang.model.symbols.SymbolOrigin.SOURCE; import static org.ballerinalang.model.symbols.SymbolOrigin.VIRTUAL; +import static org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper.singleShapeBroadType; import static org.wso2.ballerinalang.compiler.util.Constants.INFERRED_ARRAY_INDICATOR; import static org.wso2.ballerinalang.compiler.util.Constants.OPEN_ARRAY_INDICATOR; @@ -377,7 +380,7 @@ public void defineClassDef(BLangClassDefinition classDefinition, SymbolEnv env) typeFlags |= Flags.OBJECT_CTOR; } - BObjectType objectType = new BObjectType(tSymbol, typeFlags); + BObjectType objectType = new BObjectType(symTable.typeEnv(), tSymbol, typeFlags); resolvingStructureTypes.add(objectType); if (classDefinition.isObjectContructorDecl || flags.contains(Flag.OBJECT_CTOR)) { classDefinition.oceEnvData.objectType = objectType; @@ -389,7 +392,7 @@ public void defineClassDef(BLangClassDefinition classDefinition, SymbolEnv env) } if (flags.contains(Flag.CLIENT)) { - objectType.flags |= Flags.CLIENT; + objectType.addFlags(Flags.CLIENT); } tSymbol.type = objectType; @@ -492,7 +495,7 @@ private void handleDistinctDefinitionOfErrorIntersection(BLangTypeDefinition typ } if (!effectiveType.typeIdSet.isEmpty()) { - definedType.flags |= Flags.DISTINCT; + definedType.addFlags(Flags.DISTINCT); } } } @@ -574,7 +577,7 @@ private void updateIsCyclicFlag(BType type) { ((BUnionType) type).isCyclic = true; break; case INTERSECTION: - updateIsCyclicFlag(((BIntersectionType) type).getEffectiveType()); + updateIsCyclicFlag(((BIntersectionType) type).effectiveType); break; } } @@ -646,8 +649,8 @@ public BType validateModuleLevelDef(String name, Name pkgAlias, Name typeName, B td.symbol = symbol; if (symbol.kind == SymbolKind.TYPE_DEF && !Symbols.isFlagOn(symbol.flags, Flags.ANONYMOUS)) { BType referenceType = ((BTypeDefinitionSymbol) symbol).referenceType; - referenceType.flags |= symbol.type.flags; - referenceType.tsymbol.flags |= symbol.type.flags; + referenceType.addFlags(symbol.type.getFlags()); + referenceType.tsymbol.flags |= symbol.type.getFlags(); return referenceType; } return resolvedType; @@ -761,7 +764,7 @@ private BType resolveTypedescTypeDesc(BLangConstrainedType td, ResolverData data SymbolEnv symEnv = data.env; BType type = resolveTypeDesc(symEnv, data.typeDefinition, data.depth + 1, td.type, data); - BTypedescType constrainedType = new BTypedescType(symTable.empty, null); + BTypedescType constrainedType = new BTypedescType(symTable.typeEnv(), symTable.empty, null); BTypeSymbol typeSymbol = type.tsymbol; constrainedType.tsymbol = Symbols.createTypeSymbol(typeSymbol.tag, typeSymbol.flags, typeSymbol.name, typeSymbol.originalName, symEnv.enclPkg.symbol.pkgID, constrainedType, typeSymbol.owner, @@ -780,7 +783,7 @@ private BType resolveFutureTypeDesc(BLangConstrainedType td, ResolverData data) SymbolEnv symEnv = data.env; BType type = resolveTypeDesc(symEnv, data.typeDefinition, data.depth + 1, td.type, data); - BFutureType constrainedType = new BFutureType(TypeTags.FUTURE, symTable.empty, null); + BFutureType constrainedType = new BFutureType(symTable.typeEnv(), symTable.empty, null); BTypeSymbol typeSymbol = type.tsymbol; constrainedType.tsymbol = Symbols.createTypeSymbol(typeSymbol.tag, typeSymbol.flags, typeSymbol.name, typeSymbol.originalName, symEnv.enclPkg.symbol.pkgID, constrainedType, typeSymbol.owner, @@ -835,7 +838,7 @@ private BType resolveMapTypeDesc(BLangConstrainedType td, ResolverData data) { BTypeSymbol tSymbol = Symbols.createTypeSymbol(SymTag.TYPE, typeSymbol.flags, Names.EMPTY, typeSymbol.originalName, symEnv.enclPkg.symbol.pkgID, null, symEnv.scope.owner, td.pos, BUILTIN); - BMapType constrainedType = new BMapType(TypeTags.MAP, symTable.empty, tSymbol); + BMapType constrainedType = new BMapType(symTable.typeEnv(), TypeTags.MAP, symTable.empty, tSymbol); td.setBType(constrainedType); tSymbol.type = type; resolvingTypes.push(constrainedType); @@ -864,7 +867,7 @@ private BType resolveTypeDesc(BLangArrayType td, ResolverData data) { symEnv.enclPkg.symbol.pkgID, null, symEnv.scope.owner, td.pos, BUILTIN); BArrayType arrType; if (td.sizes.isEmpty()) { - arrType = new BArrayType(resultType, arrayTypeSymbol); + arrType = new BArrayType(symTable.typeEnv(), resultType, arrayTypeSymbol); } else { BLangExpression size = td.sizes.get(i); if (size.getKind() == NodeKind.LITERAL || size.getKind() == NodeKind.NUMERIC_LITERAL) { @@ -877,7 +880,8 @@ private BType resolveTypeDesc(BLangArrayType td, ResolverData data) { } else { arrayState = BArrayState.CLOSED; } - arrType = new BArrayType(resultType, arrayTypeSymbol, sizeIndicator, arrayState); + arrType = + new BArrayType(symTable.typeEnv(), resultType, arrayTypeSymbol, sizeIndicator, arrayState); } else { if (size.getKind() != NodeKind.SIMPLE_VARIABLE_REF) { dlog.error(size.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, symTable.intType, @@ -928,7 +932,8 @@ private BType resolveTypeDesc(BLangArrayType td, ResolverData data) { } else { length = (int) lengthCheck; } - arrType = new BArrayType(resultType, arrayTypeSymbol, length, BArrayState.CLOSED); + arrType = + new BArrayType(symTable.typeEnv(), resultType, arrayTypeSymbol, length, BArrayState.CLOSED); } } arrayTypeSymbol.type = arrType; @@ -965,7 +970,7 @@ private BType resolveTypeDesc(BLangTupleTypeNode td, ResolverData data) { Names.EMPTY, symEnv.enclPkg.symbol.pkgID, null, symEnv.scope.owner, td.pos, BUILTIN); List memberTypes = new ArrayList<>(); - BTupleType tupleType = new BTupleType(tupleTypeSymbol, memberTypes); + BTupleType tupleType = new BTupleType(symTable.typeEnv(), tupleTypeSymbol, memberTypes); tupleTypeSymbol.type = tupleType; td.setBType(tupleType); resolvingTypes.push(tupleType); @@ -974,7 +979,7 @@ private BType resolveTypeDesc(BLangTupleTypeNode td, ResolverData data) { BType type = resolveTypeDesc(symEnv, data.typeDefinition, data.depth + 1, memberNode.typeNode, data); SymbolEnv tupleEnv = SymbolEnv.createTypeEnv(td, new Scope(tupleTypeSymbol), symEnv); symEnter.defineNode(memberNode, tupleEnv); - BVarSymbol varSymbol = new BVarSymbol(memberNode.getBType().flags, memberNode.symbol.name, + BVarSymbol varSymbol = new BVarSymbol(memberNode.getBType().getFlags(), memberNode.symbol.name, memberNode.symbol.pkgID, memberNode.getBType(), memberNode.symbol.owner, memberNode.pos, SOURCE); memberTypes.add(new BTupleMember(type, varSymbol)); } @@ -1009,7 +1014,7 @@ private BType resolveTypeDesc(BLangRecordTypeNode td, ResolverData data) { symEnv.enclPkg.symbol.pkgID, null, symEnv.scope.owner, td.pos, td.isAnonymous ? VIRTUAL : BUILTIN); - BRecordType recordType = new BRecordType(recordSymbol); + BRecordType recordType = new BRecordType(symTable.typeEnv(), recordSymbol); resolvingStructureTypes.add(recordType); recordSymbol.type = recordType; td.symbol = recordSymbol; @@ -1076,7 +1081,7 @@ private BType resolveTypeDesc(BLangObjectTypeNode td, ResolverData data) { BTypeSymbol objectSymbol = Symbols.createObjectSymbol(Flags.asMask(flags), Names.EMPTY, symEnv.enclPkg.symbol.pkgID, null, symEnv.scope.owner, td.pos, BUILTIN); - BObjectType objectType = new BObjectType(objectSymbol, typeFlags); + BObjectType objectType = new BObjectType(symTable.typeEnv(), objectSymbol, typeFlags); resolvingStructureTypes.add(objectType); objectSymbol.type = objectType; td.symbol = objectSymbol; @@ -1113,7 +1118,7 @@ private BType resolveTypeDesc(BLangFunctionTypeNode td, ResolverData data) { SymbolEnv symEnv = data.env; Location pos = td.pos; - BInvokableType bInvokableType = new BInvokableType(null, null, null, null); + BInvokableType bInvokableType = new BInvokableType(symTable.typeEnv(), List.of(), null, null, null); BInvokableTypeSymbol tsymbol = Symbols.createInvokableTypeSymbol(SymTag.FUNCTION_TYPE, Flags.asMask(td.flagSet), symEnv.enclPkg.symbol.pkgID, bInvokableType, symEnv.scope.owner, pos, BUILTIN); @@ -1146,7 +1151,7 @@ public BType createInvokableType(List paramVars, List paramNames = new ArrayList<>(); BInvokableTypeSymbol tsymbol = (BInvokableTypeSymbol) bInvokableType.tsymbol; if (Symbols.isFlagOn(flags, Flags.ANY_FUNCTION)) { - bInvokableType.flags = flags; + bInvokableType.setFlags(flags); tsymbol.params = null; tsymbol.restParam = null; tsymbol.returnType = null; @@ -1202,7 +1207,7 @@ public BType createInvokableType(List paramVars, bInvokableType.paramTypes = paramTypes; bInvokableType.restType = restType; bInvokableType.retType = retType; - bInvokableType.flags |= flags; + bInvokableType.addFlags(flags); tsymbol.params = params; tsymbol.restParam = restParam; tsymbol.returnType = retType; @@ -1222,7 +1227,7 @@ private BType resolveTypeDesc(BLangErrorType td, ResolverData data) { } if (td.detailType == null) { - BType errorType = new BErrorType(null, symTable.detailType); + BType errorType = new BErrorType(symTable.typeEnv(), null, symTable.detailType); errorType.tsymbol = new BErrorTypeSymbol(SymTag.ERROR, Flags.PUBLIC, Names.ERROR, symTable.rootPkgSymbol.pkgID, errorType, symTable.rootPkgSymbol, symTable.builtinPos, BUILTIN); return errorType; @@ -1231,7 +1236,7 @@ private BType resolveTypeDesc(BLangErrorType td, ResolverData data) { // Define user define error type. BErrorTypeSymbol errorTypeSymbol = Symbols.createErrorSymbol(Flags.asMask(td.flagSet), Names.EMPTY, data.env.enclPkg.packageID, null, data.env.scope.owner, td.pos, BUILTIN); - BErrorType errorType = new BErrorType(errorTypeSymbol, symTable.empty); + BErrorType errorType = new BErrorType(symTable.typeEnv(), errorTypeSymbol, symTable.empty); td.setBType(errorType); resolvingTypes.push(errorType); @@ -1262,7 +1267,7 @@ private BType resolveTypeDesc(BLangErrorType td, ResolverData data) { symEnter.defineSymbol(td.pos, errorTypeSymbol, data.env); } - errorType.flags |= errorTypeSymbol.flags; + errorType.addFlags(errorTypeSymbol.flags); errorTypeSymbol.type = errorType; symResolver.markParameterizedType(errorType, detailType); @@ -1286,7 +1291,7 @@ private BType resolveTypeDesc(BLangUnionTypeNode td, ResolverData data) { BTypeSymbol unionTypeSymbol = Symbols.createTypeSymbol(SymTag.UNION_TYPE, Flags.asMask(EnumSet.of(Flag.PUBLIC)), Names.EMPTY, symEnv.enclPkg.symbol.pkgID, null, symEnv.scope.owner, td.pos, BUILTIN); - BUnionType unionType = new BUnionType(unionTypeSymbol, memberTypes, false, false); + BUnionType unionType = new BUnionType(types.typeEnv(), unionTypeSymbol, memberTypes, false); unionTypeSymbol.type = unionType; td.setBType(unionType); resolvingTypes.push(unionType); @@ -1298,63 +1303,37 @@ private BType resolveTypeDesc(BLangUnionTypeNode td, ResolverData data) { continue; } - if (resolvedType.isNullable()) { - unionType.setNullable(true); - } memberTypes.add(resolvedType); } - updateReadOnlyAndNullableFlag(unionType); + updateReadOnlyFlag(unionType); symResolver.markParameterizedType(unionType, memberTypes); resolvingTypes.pop(); return unionType; } - private void updateReadOnlyAndNullableFlag(BUnionType type) { + private void updateReadOnlyFlag(BUnionType type) { LinkedHashSet memberTypes = type.getMemberTypes(); LinkedHashSet flattenMemberTypes = new LinkedHashSet<>(memberTypes.size()); boolean isImmutable = true; - boolean hasNilableType = false; for (BType memBType : BUnionType.toFlatTypeSet(memberTypes)) { if (Types.getImpliedType(memBType).tag != TypeTags.NEVER) { flattenMemberTypes.add(memBType); } - if (isImmutable && !Symbols.isFlagOn(memBType.flags, Flags.READONLY)) { + if (isImmutable && !Symbols.isFlagOn(memBType.getFlags(), Flags.READONLY)) { isImmutable = false; } } if (isImmutable) { - type.flags |= Flags.READONLY; + type.addFlags(Flags.READONLY); if (type.tsymbol != null) { type.tsymbol.flags |= Flags.READONLY; } } - for (BType memberType : flattenMemberTypes) { - if (memberType.isNullable() && memberType.tag != TypeTags.NIL) { - hasNilableType = true; - break; - } - } - - if (hasNilableType) { - LinkedHashSet bTypes = new LinkedHashSet<>(flattenMemberTypes.size()); - for (BType t : flattenMemberTypes) { - if (t.tag != TypeTags.NIL) { - bTypes.add(t); - } - } - flattenMemberTypes = bTypes; - } - - for (BType memberType : flattenMemberTypes) { - if (memberType.isNullable()) { - type.setNullable(true); - } - } type.setOriginalMemberTypes(memberTypes); memberTypes.clear(); memberTypes.addAll(flattenMemberTypes); @@ -1396,7 +1375,7 @@ private BType resolveTypeDesc(BLangIntersectionTypeNode td, ResolverData data, b intersectionType.setConstituentTypes(constituentTypes); if (hasReadonly) { - intersectionType.flags |= Flags.READONLY; + intersectionType.addFlags(Flags.READONLY); } // Differ cyclic intersection between more than 2 non-readonly types. @@ -1427,7 +1406,7 @@ private void fillEffectiveType(BIntersectionType intersectionType, while (iterator.hasNext()) { BType bLangEffectiveImpliedType = Types.getImpliedType(effectiveType); if (bLangEffectiveImpliedType.tag == TypeTags.READONLY) { - intersectionType.flags = intersectionType.flags | TypeTags.READONLY; + intersectionType.addFlags(TypeTags.READONLY); effectiveType = iterator.next(); bLangEffectiveType = bLangTypeItr.next(); continue; @@ -1436,7 +1415,7 @@ private void fillEffectiveType(BIntersectionType intersectionType, BLangType bLangType = bLangTypeItr.next(); BType typeReferenceType = Types.getImpliedType(type); if (typeReferenceType.tag == TypeTags.READONLY) { - intersectionType.flags = intersectionType.flags | TypeTags.READONLY; + intersectionType.addFlags(TypeTags.READONLY); continue; } effectiveType = calculateEffectiveType(td, bLangEffectiveType, bLangType, effectiveType, type, @@ -1447,9 +1426,9 @@ private void fillEffectiveType(BIntersectionType intersectionType, } } intersectionType.effectiveType = effectiveType; - intersectionType.flags |= effectiveType.flags; + intersectionType.addFlags(effectiveType.getFlags()); - if ((intersectionType.flags & Flags.READONLY) == Flags.READONLY) { + if ((intersectionType.getFlags() & Flags.READONLY) == Flags.READONLY) { if (types.isInherentlyImmutableType(effectiveType)) { return; } @@ -1544,7 +1523,7 @@ private BType resolveTypeDesc(BLangUserDefinedType td, ResolverData data) { null, func.symbol, tempSymbol.pos, VIRTUAL); tSymbol.type = new BParameterizedType(paramValType, (BVarSymbol) tempSymbol, tSymbol, tempSymbol.name, parameterizedTypeInfo.index); - tSymbol.type.flags |= Flags.PARAMETERIZED; + tSymbol.type.addFlags(Flags.PARAMETERIZED); td.symbol = tSymbol; return tSymbol.type; @@ -1570,8 +1549,8 @@ private BType resolveTypeDesc(BLangUserDefinedType td, ResolverData data) { if (symbol.kind == SymbolKind.TYPE_DEF && !Symbols.isFlagOn(symbol.flags, Flags.ANONYMOUS)) { BType referenceType = ((BTypeDefinitionSymbol) symbol).referenceType; - referenceType.flags |= symbol.type.flags; - referenceType.tsymbol.flags |= symbol.type.flags; + referenceType.addFlags(symbol.type.getFlags()); + referenceType.tsymbol.flags |= symbol.type.getFlags(); return referenceType; } @@ -1605,8 +1584,8 @@ private BType resolveTypeDesc(BLangUserDefinedType td, ResolverData data) { td.symbol = symbol; if (symbol.kind == SymbolKind.TYPE_DEF && !Symbols.isFlagOn(symbol.flags, Flags.ANONYMOUS)) { BType referenceType = ((BTypeDefinitionSymbol) symbol).referenceType; - referenceType.flags |= symbol.type.flags; - referenceType.tsymbol.flags |= symbol.type.flags; + referenceType.addFlags(symbol.type.getFlags()); + referenceType.tsymbol.flags |= symbol.type.getFlags(); return referenceType; } return resolvedType; @@ -1624,48 +1603,41 @@ private BType resolveTypeDesc(BLangBuiltInRefTypeNode td, SymbolEnv symEnv) { return visitBuiltInTypeNode(td, data, td.typeKind); } - private BType resolveSingletonType(BLangFiniteTypeNode td, SymbolEnv symEnv) { + protected BType resolveSingletonType(BLangFiniteTypeNode td, SymbolEnv symEnv) { BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, (Flags.asMask(EnumSet.of(Flag.PUBLIC))), Names.EMPTY, symEnv.enclPkg.symbol.pkgID, null, symEnv.scope.owner, td.pos, BUILTIN); - // In case we encounter unary expressions in finite type, we will be replacing them with numeric literals. - replaceUnaryExprWithNumericLiteral(td); - - BFiniteType finiteType = new BFiniteType(finiteTypeSymbol); - for (BLangExpression literal : td.valueSpace) { - BType type = blangTypeUpdate(literal); + List vs = td.valueSpace; + SemNamedType[] valueSpace = new SemNamedType[vs.size()]; + for (int i = 0; i < vs.size(); i++) { + BLangExpression exprOrLiteral = vs.get(i); + BType type = blangTypeUpdate(exprOrLiteral); if (type != null && type.tag == TypeTags.SEMANTIC_ERROR) { return type; } if (type != null) { - literal.setBType(symTable.getTypeFromTag(type.tag)); + exprOrLiteral.setBType(symTable.getTypeFromTag(type.tag)); } - finiteType.addValue(literal); - } - finiteTypeSymbol.type = finiteType; - td.setBType(finiteType); - return finiteType; - } - private void replaceUnaryExprWithNumericLiteral(BLangFiniteTypeNode finiteTypeNode) { - List valueSpace = finiteTypeNode.valueSpace; - for (int i = 0; i < valueSpace.size(); i++) { - BLangExpression value; - NodeKind valueKind; - value = valueSpace.get(i); - valueKind = value.getKind(); - - if (valueKind == NodeKind.UNARY_EXPR) { - BLangUnaryExpr unaryExpr = (BLangUnaryExpr) value; - if (unaryExpr.expr.getKind() == NodeKind.NUMERIC_LITERAL) { - // Replacing unary expression with numeric literal type for + and - numeric values. - BLangNumericLiteral newNumericLiteral = - Types.constructNumericLiteralFromUnaryExpr(unaryExpr); - valueSpace.set(i, newNumericLiteral); + if (SemTypeHelper.isSimpleOrString(exprOrLiteral.getBType().getKind())) { + if (exprOrLiteral.getKind() == NodeKind.UNARY_EXPR) { + exprOrLiteral = Types.constructNumericLiteralFromUnaryExpr((BLangUnaryExpr) exprOrLiteral); + // Replacing here as Semantic Analyzer BLangFiniteTypeNode visit may not invoke for all finite nodes + td.valueSpace.set(i, exprOrLiteral); } + + SemType s = SemTypeHelper.resolveSingletonType((BLangLiteral) exprOrLiteral); + valueSpace[i] = new SemNamedType(s, Optional.ofNullable(exprOrLiteral.toString())); + } else { + throw new IllegalStateException("non-sem value found in BLangFiniteType!"); } } + + BFiniteType finiteType = new BFiniteType(finiteTypeSymbol, valueSpace); + finiteTypeSymbol.type = finiteType; + td.setBType(finiteType); + return finiteType; } private BType blangTypeUpdate(BLangExpression expression) { @@ -1707,7 +1679,7 @@ private BType resolveTypeDesc(BLangTableTypeNode td, ResolverData data) { SymbolEnv symEnv = data.env; BType type = resolveTypeDesc(symEnv, data.typeDefinition, data.depth + 1, td.type, data); - BTableType tableType = new BTableType(TypeTags.TABLE, symTable.empty, null); + BTableType tableType = new BTableType(symTable.typeEnv(), symTable.empty, null); BTypeSymbol typeSymbol = type.tsymbol; tableType.tsymbol = Symbols.createTypeSymbol(SymTag.TYPE, Flags.asMask(EnumSet.noneOf(Flag.class)), typeSymbol.name, typeSymbol.originalName, symEnv.enclPkg.symbol.pkgID, @@ -1763,7 +1735,7 @@ private BType resolveTypeDesc(BLangStreamType td, ResolverData data) { BType error = td.error != null ? resolveTypeDesc(symEnv, data.typeDefinition, data.depth + 1, td.error, data) : symTable.nilType; - BStreamType streamType = new BStreamType(TypeTags.STREAM, symTable.empty, error, null); + BStreamType streamType = new BStreamType(symTable.typeEnv(), TypeTags.STREAM, symTable.empty, error, null); BTypeSymbol typeSymbol = type.tsymbol; streamType.tsymbol = Symbols.createTypeSymbol(typeSymbol.tag, typeSymbol.flags, typeSymbol.name, typeSymbol.originalName, symEnv.enclPkg.symbol.pkgID, streamType, @@ -1822,7 +1794,7 @@ public BType defineTypeDefinition(BLangTypeDefinition typeDefinition, BType reso typeDefSymbol.pkgID, typeDefSymbol.type, typeDefSymbol.owner, typeDefSymbol.pos, typeDefSymbol.origin); typeSymbol.markdownDocumentation = typeDefSymbol.markdownDocumentation; ((BTypeDefinitionSymbol) typeDefSymbol).referenceType = new BTypeReferenceType(resolvedType, typeSymbol, - typeDefSymbol.type.flags); + typeDefSymbol.type.getFlags()); if (resolvedType == symTable.semanticError && resolvedType.tsymbol == null) { typeDefinition.symbol = typeDefSymbol; @@ -1894,7 +1866,7 @@ public BType defineTypeDefinition(BLangTypeDefinition typeDefinition, BType reso dlog.error(typeDefinition.pos, DiagnosticErrorCode.TYPE_PARAM_OUTSIDE_LANG_MODULE); } } - resolvedType.flags |= typeDefSymbol.flags; + resolvedType.addFlags(typeDefSymbol.flags); typeDefinition.symbol = typeDefSymbol; @@ -2011,12 +1983,6 @@ private void defineConstant(SymbolEnv symEnv, Map modTable, B BConstantSymbol constantSymbol = symEnter.getConstantSymbol(constant); constant.symbol = constantSymbol; BLangTypeDefinition typeDef = constant.associatedTypeDefinition; - NodeKind nodeKind = constant.expr.getKind(); - boolean isLiteral = nodeKind == NodeKind.LITERAL || nodeKind == NodeKind.NUMERIC_LITERAL - || nodeKind == NodeKind.UNARY_EXPR; - if (typeDef != null && isLiteral) { - resolveTypeDefinition(symEnv, modTable, typeDef, 0); - } if (constant.typeNode != null) { // Type node is available. ResolverData data = new ResolverData(); @@ -2036,6 +2002,17 @@ private void defineConstant(SymbolEnv symEnv, Map modTable, B BType resolvedType = constantTypeChecker.checkConstExpr(constant.expr, staticType, data); data.anonTypeNameSuffixes.pop(); + NodeKind nodeKind = constant.expr.getKind(); + boolean isLiteral = nodeKind == NodeKind.LITERAL || nodeKind == NodeKind.NUMERIC_LITERAL + || nodeKind == NodeKind.UNARY_EXPR; + if (typeDef != null && isLiteral) { + typeDef.typeNode.setBType(resolvedType); + // Define the typeDefinition. Add symbol, flags etc. + resolvedType = defineTypeDefinition(typeDef, resolvedType, symEnv); + typeDef.setBType(resolvedType); + typeDef.cycleDepth = -1; + } + if (resolvedType == symTable.semanticError) { // Constant expression contains errors. constant.setBType(symTable.semanticError); @@ -2059,7 +2036,7 @@ private void defineConstant(SymbolEnv symEnv, Map modTable, B // Update the final type in necessary fields. constantSymbol.type = intersectionType; if (intersectionType.tag == TypeTags.FINITE) { - constantSymbol.literalType = ((BFiniteType) intersectionType).getValueSpace().iterator().next().getBType(); + constantSymbol.literalType = singleShapeBroadType(intersectionType.semType(), symTable).get(); } else { constantSymbol.literalType = intersectionType; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/Types.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/Types.java index de443d69b5be..308347df0d2b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/Types.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/Types.java @@ -20,7 +20,25 @@ import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.tools.diagnostics.DiagnosticCode; import io.ballerina.tools.diagnostics.Location; -import org.ballerinalang.model.Name; +import io.ballerina.types.Atom; +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.Bdd; +import io.ballerina.types.CombinedRange; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.ListMemberTypes; +import io.ballerina.types.MappingAtomicType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypePair; +import io.ballerina.types.SemTypes; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.Range; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; @@ -41,10 +59,7 @@ import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BObjectTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BRecordTypeSymbol; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BResourceFunction; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BResourcePathSegmentSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BStructureTypeSymbol; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BVarSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.SymTag; @@ -52,20 +67,16 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; import org.wso2.ballerinalang.compiler.semantics.model.types.BIntersectionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BParameterizedType; import org.wso2.ballerinalang.compiler.semantics.model.types.BReadonlyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BRecordType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BSequenceType; import org.wso2.ballerinalang.compiler.semantics.model.types.BStreamType; import org.wso2.ballerinalang.compiler.semantics.model.types.BTableType; import org.wso2.ballerinalang.compiler.semantics.model.types.BTupleMember; @@ -73,10 +84,9 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.semantics.model.types.BTypeIdSet; import org.wso2.ballerinalang.compiler.semantics.model.types.BTypeReferenceType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTypeVisitor; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTypedescType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.compiler.tree.BLangFunction; import org.wso2.ballerinalang.compiler.tree.BLangNode; import org.wso2.ballerinalang.compiler.tree.BLangTypeDefinition; @@ -102,13 +112,12 @@ import org.wso2.ballerinalang.compiler.tree.types.BLangRecordTypeNode; import org.wso2.ballerinalang.compiler.util.BArrayState; import org.wso2.ballerinalang.compiler.util.CompilerContext; -import org.wso2.ballerinalang.compiler.util.CompilerUtils; import org.wso2.ballerinalang.compiler.util.ImmutableTypeCloner; +import org.wso2.ballerinalang.compiler.util.Name; import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.NumericLiteralSupport; import org.wso2.ballerinalang.compiler.util.TypeDefBuilderHelper; import org.wso2.ballerinalang.compiler.util.TypeTags; -import org.wso2.ballerinalang.compiler.util.Unifier; import org.wso2.ballerinalang.util.Flags; import org.wso2.ballerinalang.util.Lists; @@ -133,6 +142,11 @@ import java.util.Set; import static io.ballerina.runtime.api.constants.RuntimeConstants.UNDERSCORE; +import static io.ballerina.types.BasicTypeCode.BT_OBJECT; +import static io.ballerina.types.Core.combineRanges; +import static io.ballerina.types.Core.createIsolatedObject; +import static io.ballerina.types.Core.createServiceObject; +import static io.ballerina.types.Core.isSubtypeSimple; import static org.ballerinalang.model.symbols.SymbolOrigin.SOURCE; import static org.ballerinalang.model.symbols.SymbolOrigin.VIRTUAL; import static org.wso2.ballerinalang.compiler.semantics.model.SymbolTable.BBYTE_MAX_VALUE; @@ -146,11 +160,9 @@ import static org.wso2.ballerinalang.compiler.semantics.model.SymbolTable.UNSIGNED16_MAX_VALUE; import static org.wso2.ballerinalang.compiler.semantics.model.SymbolTable.UNSIGNED32_MAX_VALUE; import static org.wso2.ballerinalang.compiler.semantics.model.SymbolTable.UNSIGNED8_MAX_VALUE; -import static org.wso2.ballerinalang.compiler.util.TypeTags.NEVER; import static org.wso2.ballerinalang.compiler.util.TypeTags.OBJECT; import static org.wso2.ballerinalang.compiler.util.TypeTags.RECORD; import static org.wso2.ballerinalang.compiler.util.TypeTags.UNION; -import static org.wso2.ballerinalang.compiler.util.TypeTags.isSimpleBasicType; /** * This class consists of utility methods which operate on types. @@ -163,17 +175,15 @@ public class Types { private static final CompilerContext.Key TYPES_KEY = new CompilerContext.Key<>(); - private final Unifier unifier; private final SymbolTable symTable; private final SymbolResolver symResolver; private final BLangDiagnosticLog dlog; private final Names names; private int finiteTypeCount = 0; - private final BUnionType expandedXMLBuiltinSubtypes; private final BLangAnonymousModelHelper anonymousModelHelper; - private final int recordCount = 0; private SymbolEnv env; - private boolean ignoreObjectTypeIds = false; + protected final Context semTypeCtx; + private static final String BASE_16 = "base16"; private static final BigDecimal DECIMAL_MAX = @@ -185,6 +195,8 @@ public class Types { private static final BigDecimal MIN_DECIMAL_MAGNITUDE = new BigDecimal("1.000000000000000000000000000000000e-6143", MathContext.DECIMAL128); + public static final String AND_READONLY_SUFFIX = " & readonly"; + public static Types getInstance(CompilerContext context) { Types types = context.get(TYPES_KEY); if (types == null) { @@ -195,29 +207,20 @@ public static Types getInstance(CompilerContext context) { } public Types(CompilerContext context) { + this(context, new Env()); + } + + public Types(CompilerContext context, Env typeEnv) { context.put(TYPES_KEY, this); + this.semTypeCtx = Context.from(typeEnv); this.symTable = SymbolTable.getInstance(context); this.symResolver = SymbolResolver.getInstance(context); this.dlog = BLangDiagnosticLog.getInstance(context); this.names = Names.getInstance(context); - this.expandedXMLBuiltinSubtypes = BUnionType.create(null, - symTable.xmlElementType, symTable.xmlCommentType, - symTable.xmlPIType, symTable.xmlTextType); - this.unifier = new Unifier(); this.anonymousModelHelper = BLangAnonymousModelHelper.getInstance(context); } - public List checkTypes(BLangExpression node, - List actualTypes, - List expTypes) { - List resTypes = new ArrayList<>(); - for (int i = 0; i < actualTypes.size(); i++) { - resTypes.add(checkType(node, actualTypes.get(i), expTypes.size() > i ? expTypes.get(i) : symTable.noType)); - } - return resTypes; - } - public BType checkType(BLangExpression node, BType actualType, BType expType) { @@ -230,7 +233,7 @@ public BType addNilForNillableAccessType(BType actualType) { return actualType; } - return BUnionType.create(null, actualType, symTable.nilType); + return BUnionType.create(typeEnv(), null, actualType, symTable.nilType); } public BType checkType(BLangExpression expr, @@ -254,6 +257,10 @@ public boolean typeIncompatible(Location pos, BType actualType, BType expType) { return checkType(pos, actualType, expType, DiagnosticErrorCode.INCOMPATIBLE_TYPES) == symTable.semanticError; } + public SemType getErrorIntersection(SemType t) { + return SemTypes.intersect(t, PredefinedType.ERROR); + } + public BType getErrorTypes(BType bType) { bType = Types.getImpliedType(bType); if (bType == null) { @@ -289,7 +296,8 @@ public BType getErrorTypes(BType bType) { return errorType; } - return errTypes.size() == 1 ? errTypes.iterator().next() : BUnionType.create(null, errTypes); + return errTypes.size() == 1 ? errTypes.iterator().next() : + BUnionType.create(typeEnv(), null, errTypes); } public BType checkType(Location pos, @@ -312,131 +320,100 @@ public BType checkType(Location pos, } public boolean isLaxFieldAccessAllowed(BType type) { - type = Types.getImpliedType(type); - Set visited = new HashSet<>(); - return isLaxType(type, visited) == 1 || isAssignable(type, symTable.xmlType); + if (type.tag == TypeTags.SEMANTIC_ERROR) { + return false; + } + return isLaxFieldAccessAllowed(type.semType()); } - // TODO : clean - public int isLaxType(BType type, Set visited) { - type = getImpliedType(type); - if (!visited.add(type)) { - return -1; - } - switch (type.tag) { - case TypeTags.JSON: - return 1; - case TypeTags.MAP: - return isLaxType(((BMapType) type).constraint, visited); - case TypeTags.UNION: - if (isSameType(type, symTable.jsonType)) { - visited.add(type); - return 1; - } - boolean atleastOneLaxType = false; - for (BType member : ((BUnionType) type).getMemberTypes()) { - int result = isLaxType(member, visited); - if (result == -1) { - continue; - } - if (result == 0) { - return 0; - } - atleastOneLaxType = true; - } - return atleastOneLaxType ? 1 : 0; + public boolean isLaxFieldAccessAllowed(SemType t) { + if (Core.isNever(t)) { + return false; } - return 0; + return isSubtypeSimple(t, PredefinedType.XML) || isLaxType(t); } - public boolean isLaxType(BType type, Map visited) { - type = getImpliedType(type); - if (visited.containsKey(type)) { - return visited.get(type); + /** + * Checks if the type is a lax type. + *

+ * Rules: + *

    + *
  • json and readonly-json are lax
  • + *
  • map<T> is lax if T is lax
  • + *
  • U = T1|T2...|Tn is lax, if Ti is lax for all i.
  • + *
+ * + * @param t type to be checked + * @return true if t is lax + */ + private boolean isLaxType(SemType t) { + SemType json = Core.createJson(semTypeCtx); + if (SemTypes.isSameType(semTypeCtx, t, json) || + SemTypes.isSameType(semTypeCtx, t, SemTypes.intersect(json, PredefinedType.VAL_READONLY))) { + return true; } - switch (type.tag) { - case TypeTags.JSON: - visited.put(type, true); - return true; - case TypeTags.MAP: - boolean result = isLaxType(((BMapType) type).constraint, visited); - visited.put(type, result); - return result; - case TypeTags.UNION: - // TODO: remove - if (type == symTable.jsonType || isSameType(type, symTable.jsonType)) { - visited.put(type, true); - return true; - } - for (BType member : ((BUnionType) type).getMemberTypes()) { - if (!isLaxType(member, visited)) { - visited.put(type, false); - return false; - } - } - visited.put(type, true); - return true; - } - visited.put(type, false); - return false; - } - public boolean isSameType(BType source, BType target) { - return isSameType(source, target, new HashSet<>()); - } + Optional> optMatList = Core.mappingAtomicTypesInUnion(semTypeCtx, t); + if (optMatList.isEmpty()) { + return false; + } - public boolean isSameOrderedType(BType source, BType target) { - return isSameOrderedType(source, target, new HashSet<>()); + List matList = optMatList.get(); + return matList.stream().allMatch(mat -> mat.names().length == 0 && isLaxType(Core.cellInnerVal(mat.rest()))); } - private boolean isSameOrderedType(BType source, BType target, Set unresolvedTypes) { - source = getImpliedType(source); - target = getImpliedType(target); - if (isNil(source) || isNil(target)) { - // If type T is ordered, then type T? Is also ordered. - // Both source and target are ordered types since they were checked in previous stage. - // Ex. Let take target -> T, source -> (). T? is ordered type where the static type of both operands belong. - return true; - } - if (!unresolvedTypes.add(new TypePair(source, target))) { - return true; + boolean isUniqueType(Iterable typeList, BType type) { + type = Types.getImpliedType(type); + boolean isRecord = type.tag == TypeTags.RECORD; + + for (BType bType : typeList) { + bType = Types.getImpliedType(bType); + if (isRecord) { + // Seems defaultable values too are considered when checking uniqueness. + if (type == bType) { + return false; + } + } else if (isSameType(type, bType)) { + return false; + } } - BTypeVisitor orderedTypeVisitor = new BOrderedTypeVisitor(unresolvedTypes); - return target.accept(orderedTypeVisitor, source); + return true; } - public boolean isPureType(BType type) { - IsPureTypeUniqueVisitor visitor = new IsPureTypeUniqueVisitor(); - return visitor.visit(type); + public boolean isSameType(BType source, BType target) { + return isSameType(source.semType(), target.semType()); } - public boolean isAnydata(BType type) { - IsAnydataUniqueVisitor visitor = new IsAnydataUniqueVisitor(); - return visitor.visit(type); - } + public boolean isSameTypeIncludingTags(BType source, BType target) { + if (source.tag != target.tag) { + return false; + } - private boolean isSameType(BType source, BType target, Set unresolvedTypes) { - // If we encounter two types that we are still resolving, then skip it. - // This is done to avoid recursive checking of the same type. - TypePair pair = null; - if (!isValueType(source) && !isValueType(target)) { - pair = new TypePair(source, target); - if (!unresolvedTypes.add(pair)) { - return true; + if (source.tag == UNION) { + boolean notSameType = ((BUnionType) source).getMemberTypes() + .stream() + .map(sT -> ((BUnionType) target).getMemberTypes() + .stream() + .anyMatch(it -> Types.getReferredType(it).tag == Types.getReferredType(sT).tag)) + .anyMatch(foundSameType -> !foundSameType); + if (notSameType) { + return false; } } - BTypeVisitor sameTypeVisitor = new BSameTypeVisitor(unresolvedTypes, this::isSameType); + return isSameType(source.semType(), target.semType()); + } - if (target.accept(sameTypeVisitor, source)) { - return true; - } + public boolean isSameType(SemType source, SemType target) { + return SemTypes.isSameType(semTypeCtx, source, target); + } - if (pair != null) { - unresolvedTypes.remove(pair); - } + public SemType anydata() { + return Core.createAnydata(semTypeCtx); + } - return false; + public boolean isAnydata(SemType t) { + return isSubtype(t, anydata()); } public boolean isValueType(BType type) { @@ -463,49 +440,16 @@ boolean isBasicNumericType(BType bType) { return type.tag < TypeTags.STRING || TypeTags.isIntegerTypeTag(type.tag); } - boolean finiteTypeContainsNumericTypeValues(BFiniteType finiteType) { - return finiteType.getValueSpace().stream().anyMatch(valueExpr -> isBasicNumericType(valueExpr.getBType())); - } - public boolean containsErrorType(BType bType) { - BType type = getImpliedType(bType); - if (type.tag == TypeTags.UNION) { - return ((BUnionType) type).getMemberTypes().stream() - .anyMatch(this::containsErrorType); - } - - if (type.tag == TypeTags.READONLY) { - return true; - } - - return type.tag == TypeTags.ERROR; + return SemTypeHelper.containsBasicType(bType, PredefinedType.ERROR); } public boolean containsNilType(BType bType) { - BType type = getImpliedType(bType); - if (type.tag == TypeTags.UNION) { - for (BType memberType : ((BUnionType) type).getMemberTypes()) { - if (containsNilType(memberType)) { - return true; - } - } - return false; - } - - if (type.tag == TypeTags.READONLY) { - return true; - } - - return type.tag == TypeTags.NIL; + return SemTypeHelper.containsBasicType(bType, PredefinedType.NIL); } public boolean isSubTypeOfList(BType bType) { - BType type = getImpliedType(bType); - if (type.tag != TypeTags.UNION) { - return isSubTypeOfBaseType(type, TypeTags.ARRAY) || isSubTypeOfBaseType(type, TypeTags.TUPLE); - } - - return ((BUnionType) type).getMemberTypes().stream().allMatch(this::isSubTypeOfList); + return SemTypeHelper.isSubtypeSimpleNotNever(bType, PredefinedType.LIST); } BType resolvePatternTypeFromMatchExpr(BLangErrorBindingPattern errorBindingPattern, BLangExpression matchExpr, @@ -736,31 +680,11 @@ public BType resolvePatternTypeFromMatchExpr(BLangMappingBindingPattern mappingB } private boolean containsAnyType(BType type) { - type = getImpliedType(type); - if (type.tag != TypeTags.UNION) { - return type.tag == TypeTags.ANY; - } - - for (BType memberTypes : ((BUnionType) type).getMemberTypes()) { - if (getImpliedType(memberTypes).tag == TypeTags.ANY) { - return true; - } - } - return false; + return SemTypeHelper.containsType(semTypeCtx, type, PredefinedType.ANY); } private boolean containsAnyDataType(BType type) { - type = getImpliedType(type); - if (type.tag != TypeTags.UNION) { - return type.tag == TypeTags.ANYDATA; - } - - for (BType memberTypes : ((BUnionType) type).getMemberTypes()) { - if (getImpliedType(memberTypes).tag == TypeTags.ANYDATA) { - return true; - } - } - return false; + return SemTypeHelper.containsType(semTypeCtx, type, Core.createAnydata(semTypeCtx)); } BType mergeTypes(BType typeFirst, BType typeSecond) { @@ -779,17 +703,21 @@ BType mergeTypes(BType typeFirst, BType typeSecond) { if (isSameBasicType(typeFirst, typeSecond)) { return typeFirst; } - return BUnionType.create(null, typeFirst, typeSecond); + return BUnionType.create(typeEnv(), null, typeFirst, typeSecond); } - public boolean isSubTypeOfMapping(BType bType) { - BType type = getImpliedType(bType); - if (type.tag != TypeTags.UNION) { - return isSubTypeOfBaseType(type, TypeTags.MAP) || isSubTypeOfBaseType(type, TypeTags.RECORD); - } - return ((BUnionType) type).getMemberTypes().stream().allMatch(this::isSubTypeOfMapping); + public boolean isSubTypeOfMapping(SemType s) { + return SemTypes.isSubtypeSimpleNotNever(s, PredefinedType.MAPPING); + } + + public boolean isSubTypeOfBaseType(BType bType, BasicTypeBitSet bbs) { + return SemTypeHelper.isSubtypeSimpleNotNever(bType, bbs); } + /** + * @deprecated Use {@link #isSubTypeOfBaseType(BType, BasicTypeBitSet)} instead. + */ + @Deprecated public boolean isSubTypeOfBaseType(BType bType, int baseTypeTag) { BType type = getImpliedType(bType); @@ -828,3032 +756,1027 @@ private boolean isUnionMemberTypesSubTypeOfBaseType(LinkedHashSet memberT /** * Checks whether source type is assignable to the target type. - *

- * Source type is assignable to the target type if, - * 1) the target type is any and the source type is not a value type. - * 2) there exists an implicit cast symbol from source to target. - * 3) both types are JSON and the target constraint is no type. - * 4) both types are array type and both array types are assignable. - * 5) both types are MAP and the target constraint is any type or constraints are structurally equivalent. * * @param source type. * @param target type. * @return true if source type is assignable to the target type. */ public boolean isAssignable(BType source, BType target) { - return isAssignable(source, target, new HashSet<>()); + return isSubtype(source.semType(), target.semType()); } public boolean isAssignableIgnoreObjectTypeIds(BType source, BType target) { - this.ignoreObjectTypeIds = true; - boolean result = isAssignable(source, target); - this.ignoreObjectTypeIds = false; - return result; + SemType s = typeIgnoringObjectTypeIds(source.semType()); + SemType t = typeIgnoringObjectTypeIds(target.semType()); + return isSubtype(s, t); } - private boolean isAssignable(BType source, BType target, Set unresolvedTypes) { - - if (isSameType(source, target)) { - return true; - } - - int sourceTag = source.tag; - int targetTag = target.tag; - - if (sourceTag == TypeTags.TYPEREFDESC || targetTag == TypeTags.TYPEREFDESC) { - return isAssignable(getImpliedType(source), getImpliedType(target), - unresolvedTypes); + public SemType typeIgnoringObjectTypeIds(SemType t) { + SubtypeData objSubTypeData = Core.subtypeData(t, BT_OBJECT); + if (!(objSubTypeData instanceof Bdd b)) { + return t; } + Bdd bdd = replaceObjectDistinctAtoms(b); + SemType newObjSemType = Core.createBasicSemType(BT_OBJECT, bdd); + SemType diff = Core.diff(t, PredefinedType.OBJECT); + return Core.union(diff, newObjSemType); + } - if (isNeverTypeOrStructureTypeWithARequiredNeverMember(source)) { - return true; + /** + * Replaces all distinct atoms in object type's bdd with full object equivalent atom. + * ({@link PredefinedType#ATOM_MAPPING_OBJECT}). + *
+ * This is to suppress effect coming from distinct atoms. + * The return bdd will be equivalent to object bdd with no distinct atoms. + * + * @param b a bdd belong to object type + * @return bdd with no distinct atoms + */ + private Bdd replaceObjectDistinctAtoms(Bdd b) { + if (b instanceof BddAllOrNothing) { + return b; } - if (sourceTag == TypeTags.SEQUENCE) { - BSequenceType seqType = (BSequenceType) source; - if (targetTag == TypeTags.ARRAY) { - return isAssignable(seqType.elementType, ((BArrayType) target).eType, unresolvedTypes); - } + BddNode bn = (BddNode) b; + Atom atom = bn.atom(); + if (bn.atom().kind() == Atom.Kind.DISTINCT_ATOM) { + atom = PredefinedType.ATOM_MAPPING_OBJECT; } + Bdd left = replaceObjectDistinctAtoms(bn.left()); + Bdd middle = replaceObjectDistinctAtoms(bn.middle()); + Bdd right = replaceObjectDistinctAtoms(bn.right()); + return BddNode.create(atom, left, middle, right); + } - if (!Symbols.isFlagOn(source.flags, Flags.PARAMETERIZED) && - !isInherentlyImmutableType(target) && Symbols.isFlagOn(target.flags, Flags.READONLY) && - !isInherentlyImmutableType(source) && isMutable(source)) { - return false; - } + public boolean isSubtype(SemType t1, SemType t2) { + return SemTypes.isSubtype(semTypeCtx, t1, t2); + } - if (sourceTag == TypeTags.INTERSECTION) { - return isAssignable(((BIntersectionType) source).effectiveType, - targetTag != TypeTags.INTERSECTION ? target : - ((BIntersectionType) target).effectiveType, unresolvedTypes); - } + public boolean isSubtype(BType t1, SemType t2) { + return SemTypeHelper.isSubtype(semTypeCtx, t1, t2); + } - if (targetTag == TypeTags.INTERSECTION) { - return isAssignable(source, ((BIntersectionType) target).effectiveType, unresolvedTypes); - } + BField getTableConstraintField(BType constraintType, String fieldName) { + constraintType = getImpliedType(constraintType); + switch (constraintType.tag) { + case TypeTags.RECORD: + Map fieldList = ((BRecordType) constraintType).getFields(); + return fieldList.get(fieldName); + case TypeTags.UNION: + BUnionType unionType = (BUnionType) constraintType; + Set memTypes = unionType.getMemberTypes(); + List fields = memTypes.stream().map(type -> getTableConstraintField(type, fieldName)) + .filter(Objects::nonNull).toList(); - if (sourceTag == TypeTags.PARAMETERIZED_TYPE) { - return isParameterizedTypeAssignable(source, target, unresolvedTypes); - } + if (fields.size() != memTypes.size()) { + return null; + } - if (sourceTag == TypeTags.BYTE && targetTag == TypeTags.INT) { - return true; + if (fields.stream().allMatch(field -> isAssignable(field.type, fields.get(0).type) && + isAssignable(fields.get(0).type, field.type))) { + return fields.get(0); + } } - if (TypeTags.isXMLTypeTag(sourceTag) && TypeTags.isXMLTypeTag(targetTag)) { - return isXMLTypeAssignable(source, target, unresolvedTypes); - } + return null; + } - if (sourceTag == TypeTags.CHAR_STRING && targetTag == TypeTags.STRING) { + public boolean isInherentlyImmutableType(BType type) { + type = getImpliedType(type); + if (isValueType(type)) { return true; } - if (sourceTag == TypeTags.ERROR && targetTag == TypeTags.ERROR) { - return isErrorTypeAssignable((BErrorType) source, (BErrorType) target, unresolvedTypes); - } else if (sourceTag == TypeTags.ERROR && targetTag == TypeTags.ANY) { - return false; - } - - if (sourceTag == TypeTags.NIL && (isNullable(target) || targetTag == TypeTags.JSON)) { - return true; - } + return switch (type.tag) { + case TypeTags.XML_TEXT, + TypeTags.FINITE, // Assuming a finite type will only have members from simple basic types. + TypeTags.READONLY, + TypeTags.NIL, + TypeTags.NEVER, + TypeTags.ERROR, + TypeTags.INVOKABLE, + TypeTags.TYPEDESC, + TypeTags.HANDLE, + TypeTags.REGEXP -> true; + case TypeTags.XML -> getImpliedType(((BXMLType) type).constraint).tag == TypeTags.NEVER; + default -> false; + }; + } - // TODO: Remove the isValueType() check - if (targetTag == TypeTags.ANY && !containsErrorType(source) && !isValueType(source)) { - return true; + /** + * Retrieve the referred type if a given type is a type reference type or + * retrieve the effective type if the given type is an intersection type. + * + * @param type type to retrieve the implied type + * @return the implied type if provided with a type reference type or an intersection type, + * else returns the original type + */ + public static BType getImpliedType(BType type) { + type = getReferredType(type); + if (type != null && type.tag == TypeTags.INTERSECTION) { + return getImpliedType(((BIntersectionType) type).effectiveType); } - if (targetTag == TypeTags.ANYDATA && !containsErrorType(source) && isAnydata(source)) { - return true; - } + return type; + } - if (targetTag == TypeTags.READONLY) { - if ((isInherentlyImmutableType(source) || Symbols.isFlagOn(source.flags, Flags.READONLY))) { - return true; - } - if (isAssignable(source, symTable.anyAndReadonlyOrError, unresolvedTypes)) { - return true; - } + public static BType getReferredType(BType type) { + if (type != null && type.tag == TypeTags.TYPEREFDESC) { + return getReferredType(((BTypeReferenceType) type).referredType); } - if (sourceTag == TypeTags.READONLY && isAssignable(symTable.anyAndReadonlyOrError, target, unresolvedTypes)) { - return true; - } + return type; + } - if (targetTag == TypeTags.MAP && sourceTag == TypeTags.RECORD) { - BRecordType recordType = (BRecordType) source; - return isAssignableRecordType(recordType, target, unresolvedTypes); + public BLangExpression addConversionExprIfRequired(BLangExpression expr, BType lhsType) { + if (lhsType.tag == TypeTags.NONE) { + return expr; } - if (targetTag == TypeTags.RECORD && sourceTag == TypeTags.MAP) { - return isAssignableMapType((BMapType) source, (BRecordType) target); - } + BType rhsType = expr.getBType(); - if (targetTag == TypeTags.TYPEDESC && sourceTag == TypeTags.TYPEDESC) { - return isAssignable(((BTypedescType) source).constraint, (((BTypedescType) target).constraint), - unresolvedTypes); + if (lhsType.tag == TypeTags.TYPEREFDESC && rhsType.tag != TypeTags.TYPEREFDESC) { + return addConversionExprIfRequired(expr, Types.getReferredType(lhsType)); } - if (targetTag == TypeTags.TABLE && sourceTag == TypeTags.TABLE) { - return isAssignableTableType((BTableType) source, (BTableType) target, unresolvedTypes); + if (rhsType.tag == lhsType.tag && isSameType(rhsType, lhsType)) { + return expr; } - if (targetTag == TypeTags.STREAM && sourceTag == TypeTags.STREAM) { - return isAssignableStreamType((BStreamType) source, (BStreamType) target, unresolvedTypes); + setImplicitCastExpr(expr, rhsType, lhsType); + if (expr.impConversionExpr != null) { + BLangExpression impConversionExpr = expr.impConversionExpr; + expr.impConversionExpr = null; + return impConversionExpr; } - if (isBuiltInTypeWidenPossible(source, target) == TypeTestResult.TRUE) { - return true; + if (lhsType.tag == TypeTags.JSON && rhsType.tag == TypeTags.NIL) { + return expr; } - if (sourceTag == TypeTags.FINITE) { - return isFiniteTypeAssignable((BFiniteType) source, target, unresolvedTypes); + if (lhsType.tag == TypeTags.NIL && rhsType.isNullable()) { + return expr; } - if ((targetTag == TypeTags.UNION || sourceTag == TypeTags.UNION) && - isAssignableToUnionType(source, target, unresolvedTypes)) { - return true; + if (lhsType.tag == TypeTags.ARRAY && rhsType.tag == TypeTags.TUPLE) { + return expr; } - if (targetTag == TypeTags.JSON) { - if (sourceTag == TypeTags.JSON) { - return true; - } + // Create a type cast expression + BLangTypeConversionExpr conversionExpr = (BLangTypeConversionExpr) + TreeBuilder.createTypeConversionNode(); + conversionExpr.expr = expr; + conversionExpr.targetType = lhsType; + conversionExpr.setBType(lhsType); + conversionExpr.pos = expr.pos; + conversionExpr.checkTypes = false; + conversionExpr.internal = true; + return conversionExpr; + } - if (sourceTag == TypeTags.TUPLE) { - return isTupleTypeAssignable(source, target, unresolvedTypes); - } + boolean isSelectivelyImmutableType(BType type, PackageID packageID) { + return isSelectivelyImmutableType(type, new HashSet<>(), false, packageID); + } - if (sourceTag == TypeTags.ARRAY) { - return isArrayTypesAssignable((BArrayType) source, target, unresolvedTypes); - } - - if (sourceTag == TypeTags.MAP) { - return isAssignable(((BMapType) source).constraint, target, unresolvedTypes); - } - - if (sourceTag == TypeTags.RECORD) { - return isAssignableRecordType((BRecordType) source, target, unresolvedTypes); - } - } - - if (targetTag == TypeTags.FUTURE && sourceTag == TypeTags.FUTURE) { - if (((BFutureType) target).constraint.tag == TypeTags.NONE) { - return true; - } - return isAssignable(((BFutureType) source).constraint, ((BFutureType) target).constraint, unresolvedTypes); - } - - if (targetTag == TypeTags.MAP && sourceTag == TypeTags.MAP) { - // Here source condition is added for prevent assigning map union constrained - // to map any constrained. - if (((BMapType) target).constraint.tag == TypeTags.ANY && - ((BMapType) source).constraint.tag != TypeTags.UNION) { - return true; - } - - return isAssignable(((BMapType) source).constraint, ((BMapType) target).constraint, unresolvedTypes); - } - - if ((sourceTag == TypeTags.OBJECT || sourceTag == TypeTags.RECORD) - && (targetTag == TypeTags.OBJECT || targetTag == TypeTags.RECORD)) { - return checkStructEquivalency(source, target, unresolvedTypes); - } - - if (sourceTag == TypeTags.TUPLE && targetTag == TypeTags.ARRAY) { - return isTupleTypeAssignableToArrayType((BTupleType) source, (BArrayType) target, unresolvedTypes); - } - - if (sourceTag == TypeTags.ARRAY && targetTag == TypeTags.TUPLE) { - return isArrayTypeAssignableToTupleType((BArrayType) source, (BTupleType) target, unresolvedTypes); - } - - if (sourceTag == TypeTags.TUPLE || targetTag == TypeTags.TUPLE) { - return isTupleTypeAssignable(source, target, unresolvedTypes); - } - - if (sourceTag == TypeTags.INVOKABLE && targetTag == TypeTags.INVOKABLE) { - return isFunctionTypeAssignable((BInvokableType) source, (BInvokableType) target, new HashSet<>()); - } - - return (sourceTag == TypeTags.ARRAY || sourceTag == TypeTags.BYTE_ARRAY) - && targetTag == TypeTags.ARRAY - && isArrayTypesAssignable((BArrayType) source, target, unresolvedTypes); + boolean isSelectivelyImmutableType(BType type, boolean forceCheck, PackageID packageID) { + return isSelectivelyImmutableType(type, new HashSet<>(), forceCheck, packageID); } - private boolean isMutable(BType type) { - if (Symbols.isFlagOn(type.flags, Flags.READONLY)) { - return false; - } - - type = getImpliedType(type); - if (type.tag != TypeTags.UNION) { - return true; - } - - BUnionType unionType = (BUnionType) type; - for (BType memberType : unionType.getMemberTypes()) { - if (!Symbols.isFlagOn(memberType.flags, Flags.READONLY)) { - return true; - } - } - - unionType.flags |= Flags.READONLY; - BTypeSymbol tsymbol = unionType.tsymbol; - if (tsymbol != null) { - tsymbol.flags |= Flags.READONLY; - } - return false; + public boolean isSelectivelyImmutableType(BType type, Set unresolvedTypes, PackageID packageID) { + return isSelectivelyImmutableType(type, unresolvedTypes, false, packageID); } - private boolean isParameterizedTypeAssignable(BType source, BType target, Set unresolvedTypes) { - BType resolvedSourceType = unifier.build(source); - target = getImpliedType(target); - if (target.tag != TypeTags.PARAMETERIZED_TYPE) { - return isAssignable(resolvedSourceType, target, unresolvedTypes); - } + private boolean isSelectivelyImmutableType(BType input, Set unresolvedTypes, boolean forceCheck, + PackageID packageID) { + BType type = getImpliedType(input); - if (((BParameterizedType) source).paramIndex != ((BParameterizedType) target).paramIndex) { + if (isInherentlyImmutableType(type) || !(type instanceof SelectivelyImmutableReferenceType)) { + // Always immutable. return false; } - return isAssignable(resolvedSourceType, unifier.build(target), unresolvedTypes); - } - - private boolean isAssignableRecordType(BRecordType recordType, BType type, Set unresolvedTypes) { - TypePair pair = new TypePair(recordType, type); - if (!unresolvedTypes.add(pair)) { + if (!unresolvedTypes.add(type)) { return true; } - type = getImpliedType(type); - BType targetType = switch (type.tag) { - case TypeTags.MAP -> ((BMapType) type).constraint; - case TypeTags.JSON -> type; - default -> throw new IllegalArgumentException("Incompatible target type: " + type); - }; - return recordFieldsAssignableToType(recordType, targetType, unresolvedTypes); - } - - private boolean isAssignableStreamType(BStreamType sourceStreamType, BStreamType targetStreamType, - Set unresolvedTypes) { - return isAssignable(sourceStreamType.constraint, targetStreamType.constraint, unresolvedTypes) - && isAssignable(sourceStreamType.completionType, targetStreamType.completionType, unresolvedTypes); - } - - private boolean recordFieldsAssignableToType(BRecordType recordType, BType targetType, - Set unresolvedTypes) { - for (BField field : recordType.fields.values()) { - if (!isAssignable(field.type, targetType, unresolvedTypes)) { - return false; - } - } - - if (!recordType.sealed) { - return isAssignable(recordType.restFieldType, targetType, unresolvedTypes); - } - - return true; - } - - private boolean isAssignableTableType(BTableType sourceTableType, BTableType targetTableType, - Set unresolvedTypes) { - if (!isAssignable(sourceTableType.constraint, targetTableType.constraint, unresolvedTypes)) { - return false; - } - - if (targetTableType.keyTypeConstraint == null && targetTableType.fieldNameList.isEmpty()) { + if (!forceCheck && + getImmutableType(symTable, packageID, (SelectivelyImmutableReferenceType) type).isPresent()) { return true; } - if (targetTableType.keyTypeConstraint != null) { - if (sourceTableType.keyTypeConstraint != null && - (isAssignable(sourceTableType.keyTypeConstraint, targetTableType.keyTypeConstraint, - unresolvedTypes))) { + switch (type.tag) { + case TypeTags.ANY: + case TypeTags.ANYDATA: + case TypeTags.JSON: + case TypeTags.XML: + case TypeTags.XML_COMMENT: + case TypeTags.XML_ELEMENT: + case TypeTags.XML_PI: + case TypeTags.BYTE_ARRAY: return true; - } - - if (sourceTableType.fieldNameList.isEmpty()) { - return false; - } - - List fieldTypes = new ArrayList<>(); - sourceTableType.fieldNameList.stream() - .map(f -> getTableConstraintField(sourceTableType.constraint, f)) - .filter(Objects::nonNull).map(f -> new BTupleMember(f.type, - Symbols.createVarSymbolForTupleMember(f.type))).forEach(fieldTypes::add); - if (fieldTypes.size() == 1) { - return isAssignable(fieldTypes.get(0).type, targetTableType.keyTypeConstraint, unresolvedTypes); - } - - BTupleType tupleType = new BTupleType(fieldTypes); - return isAssignable(tupleType, targetTableType.keyTypeConstraint, unresolvedTypes); - } - - return targetTableType.fieldNameList.equals(sourceTableType.fieldNameList); - } + case TypeTags.ARRAY: + BArrayType arrayType = (BArrayType) type; + BType elementType = arrayType.eType; + if (elementType == symTable.semanticError && arrayType.mutableType != null) { + elementType = arrayType.mutableType.eType; + } + return isInherentlyImmutableType(elementType) || + isSelectivelyImmutableType(elementType, unresolvedTypes, forceCheck, packageID); + case TypeTags.TUPLE: + BTupleType tupleType = (BTupleType) type; + List tupleMemberTypes = tupleType.getTupleTypes(); + if (tupleMemberTypes.isEmpty() && tupleType.mutableType != null) { + tupleMemberTypes = tupleType.mutableType.getTupleTypes(); + } + for (BType memberType : tupleMemberTypes) { + if (!isInherentlyImmutableType(memberType) && + !isSelectivelyImmutableType(memberType, unresolvedTypes, forceCheck, packageID)) { + return false; + } + } + BType tupRestType = tupleType.restType; + if (tupRestType == null) { + return true; + } - BField getTableConstraintField(BType constraintType, String fieldName) { - constraintType = getImpliedType(constraintType); - switch (constraintType.tag) { + return isInherentlyImmutableType(tupRestType) || + isSelectivelyImmutableType(tupRestType, unresolvedTypes, forceCheck, packageID); case TypeTags.RECORD: - Map fieldList = ((BRecordType) constraintType).getFields(); - return fieldList.get(fieldName); - case TypeTags.UNION: - BUnionType unionType = (BUnionType) constraintType; - Set memTypes = unionType.getMemberTypes(); - List fields = memTypes.stream().map(type -> getTableConstraintField(type, fieldName)) - .filter(Objects::nonNull).toList(); - - if (fields.size() != memTypes.size()) { - return null; + BRecordType recordType = (BRecordType) type; + LinkedHashMap recordFields = recordType.fields; + if (recordFields.isEmpty() && recordType.mutableType != null) { + recordFields = recordType.mutableType.fields; } - - if (fields.stream().allMatch(field -> isAssignable(field.type, fields.get(0).type) && - isAssignable(fields.get(0).type, field.type))) { - return fields.get(0); + for (BField field : recordFields.values()) { + BType fieldType = field.type; + if (!Symbols.isFlagOn(field.symbol.flags, Flags.OPTIONAL) && + !isInherentlyImmutableType(fieldType) && + !isSelectivelyImmutableType(fieldType, unresolvedTypes, forceCheck, packageID)) { + return false; + } } - } - - return null; - } - - private boolean isAssignableMapType(BMapType sourceMapType, BRecordType targetRecType) { - if (targetRecType.sealed) { - return false; - } - - for (BField field : targetRecType.fields.values()) { - if (!Symbols.isFlagOn(field.symbol.flags, Flags.OPTIONAL)) { - return false; - } - - if (hasIncompatibleReadOnlyFlags(field.symbol.flags, sourceMapType.flags)) { - return false; - } - - if (!isAssignable(sourceMapType.constraint, field.type)) { - return false; - } - } - - return isAssignable(sourceMapType.constraint, targetRecType.restFieldType); - } - - private boolean hasIncompatibleReadOnlyFlags(long targetFlags, long sourceFlags) { - return Symbols.isFlagOn(targetFlags, Flags.READONLY) && !Symbols.isFlagOn(sourceFlags, Flags.READONLY); - } - - private boolean isErrorTypeAssignable(BErrorType source, BErrorType target, Set unresolvedTypes) { - if (target == symTable.errorType) { - return true; - } - TypePair pair = new TypePair(source, target); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - return isAssignable(source.detailType, target.detailType, unresolvedTypes) - && target.typeIdSet.isAssignableFrom(source.typeIdSet); - } - - private boolean isXMLTypeAssignable(BType sourceT, BType targetT, Set unresolvedTypes) { - BType sourceType = getImpliedType(sourceT); - BType targetType = getImpliedType(targetT); - int sourceTag = sourceType.tag; - int targetTag = targetType.tag; - - if (targetTag == TypeTags.XML) { - BXMLType target = (BXMLType) targetType; - if (target.constraint != null) { - if (TypeTags.isXMLNonSequenceType(sourceTag)) { - return isAssignable(sourceType, target.constraint, unresolvedTypes); + return true; + case TypeTags.MAP: + BMapType mapType = (BMapType) type; + BType constraintType = mapType.constraint; + if (constraintType == symTable.semanticError && mapType.mutableType != null) { + constraintType = mapType.mutableType.constraint; } - BXMLType source = (BXMLType) sourceType; - if (getImpliedType(source.constraint).tag == TypeTags.NEVER) { - if (sourceTag == targetTag) { - return true; + return isInherentlyImmutableType(constraintType) || + isSelectivelyImmutableType(constraintType, unresolvedTypes, forceCheck, packageID); + case TypeTags.OBJECT: + BObjectType objectType = (BObjectType) type; + LinkedHashMap objectFields = objectType.fields; + if (objectFields.isEmpty() && objectType.mutableType != null) { + objectFields = objectType.mutableType.fields; + } + for (BField field : objectFields.values()) { + BType fieldType = field.type; + if (!isInherentlyImmutableType(fieldType) && + !isSelectivelyImmutableType(fieldType, unresolvedTypes, forceCheck, packageID)) { + return false; } - return isAssignable(source, target.constraint, unresolvedTypes); } - return isAssignable(source.constraint, target, unresolvedTypes); - } - return true; - } - if (sourceTag == TypeTags.XML) { - BXMLType source = (BXMLType) sourceType; - if (targetTag == TypeTags.XML_TEXT) { - if (source.constraint != null) { - int constraintTag = getImpliedType(source.constraint).tag; - if (constraintTag == TypeTags.NEVER || constraintTag == TypeTags.XML_TEXT) { - return true; - } else { - return isAssignable(source.constraint, targetType, unresolvedTypes); + return true; + case TypeTags.TABLE: + BTableType tableType = (BTableType) type; + BType tableConstraintType = tableType.constraint; + if (tableConstraintType == symTable.semanticError && tableType.mutableType != null) { + tableConstraintType = tableType.mutableType.constraint; + } + return isInherentlyImmutableType(tableConstraintType) || + isSelectivelyImmutableType(tableConstraintType, unresolvedTypes, forceCheck, packageID); + case TypeTags.UNION: + boolean readonlyIntersectionExists = false; + BUnionType unionType = (BUnionType) type; + LinkedHashSet memberTypes = unionType.getMemberTypes(); + for (BType memberType : memberTypes) { + if (isInherentlyImmutableType(memberType) || + isSelectivelyImmutableType(memberType, unresolvedTypes, forceCheck, packageID)) { + readonlyIntersectionExists = true; } } - return false; - } + return readonlyIntersectionExists; } - return sourceTag == targetTag; + return false; } - private boolean isTupleTypeAssignable(BType source, BType target, Set unresolvedTypes) { - TypePair pair = new TypePair(source, target); - if (unresolvedTypes.contains(pair)) { - return true; - } - - source = getImpliedType(source); - if (source.tag == TypeTags.TUPLE && ((BTupleType) source).isCyclic) { - // add cyclic source to target pair to avoid recursive calls - unresolvedTypes.add(pair); - } + public static boolean containsTypeParams(BInvokableType type) { + boolean hasParameterizedTypes = type.paramTypes.stream() + .anyMatch(t -> { + t = getImpliedType(t); + if (t.tag == TypeTags.FUNCTION_POINTER) { + return containsTypeParams((BInvokableType) t); + } + return TypeParamAnalyzer.isTypeParam(t); + }); - target = getImpliedType(target); - if (target.tag == TypeTags.JSON && source.tag == TypeTags.TUPLE) { - BTupleType rhsTupleType = (BTupleType) source; - for (BType tupleType : rhsTupleType.getTupleTypes()) { - if (!isAssignable(tupleType, target, unresolvedTypes)) { - return false; - } - } - if (rhsTupleType.restType != null) { - return isAssignable(rhsTupleType.restType, target, unresolvedTypes); - } + if (hasParameterizedTypes) { return true; } - if (source.tag != TypeTags.TUPLE || target.tag != TypeTags.TUPLE) { - return false; - } - - BTupleType lhsTupleType = (BTupleType) target; - BTupleType rhsTupleType = (BTupleType) source; - - if (lhsTupleType.restType == null && rhsTupleType.restType != null) { - return false; + BType retType = getImpliedType(type.retType); + if (retType.tag == TypeTags.FUNCTION_POINTER) { + return containsTypeParams((BInvokableType) retType); } - if (lhsTupleType.restType == null && - lhsTupleType.getMembers().size() != rhsTupleType.getMembers().size()) { - return false; - } + return TypeParamAnalyzer.isTypeParam(type.retType); + } - if (lhsTupleType.restType != null && rhsTupleType.restType != null) { - if (!isAssignable(rhsTupleType.restType, lhsTupleType.restType, unresolvedTypes)) { - return false; - } - } - List lhsTupleMemberTypes = lhsTupleType.getTupleTypes(); - List rhsTupleMemberTypes = rhsTupleType.getTupleTypes(); - - if (lhsTupleMemberTypes.size() > rhsTupleMemberTypes.size()) { - return false; - } - - for (int i = 0; i < rhsTupleMemberTypes.size(); i++) { - BType lhsType = (lhsTupleMemberTypes.size() > i) - ? lhsTupleMemberTypes.get(i) : lhsTupleType.restType; - if (!isAssignable(rhsTupleMemberTypes.get(i), lhsType, unresolvedTypes)) { - return false; - } - } - return true; - } - - private boolean checkAllTupleMembersBelongNoType(List tupleTypes) { - boolean isNoType = false; - for (BType type : tupleTypes) { - type = getImpliedType(type); - switch (type.tag) { - case TypeTags.NONE: - isNoType = true; - break; - case TypeTags.TUPLE: - isNoType = checkAllTupleMembersBelongNoType(((BTupleType) type).getTupleTypes()); - if (!isNoType) { - return false; - } - break; - default: - return false; - } - } - return isNoType; - } - - private boolean isTupleTypeAssignableToArrayType(BTupleType source, BArrayType target, - Set unresolvedTypes) { - if (target.state != BArrayState.OPEN - && (source.restType != null || source.getMembers().size() != target.size)) { - return false; - } - - List sourceTypes = new ArrayList<>(source.getTupleTypes()); - if (source.restType != null) { - BType type = source.restType; - sourceTypes.add(type); - } - return sourceTypes.stream() - .allMatch(tupleElemType -> isAssignable(tupleElemType, target.eType, unresolvedTypes)); - } - - private boolean isArrayTypeAssignableToTupleType(BArrayType source, BTupleType target, - Set unresolvedTypes) { - BType restType = target.restType; - List tupleTypes = target.getTupleTypes(); - if (source.state == BArrayState.OPEN) { - if (restType == null || !tupleTypes.isEmpty()) { - // [int, int] = int[] || [int, int...] = int[] - return false; - } - - return isAssignable(source.eType, restType, unresolvedTypes); - } - - int targetTupleMemberSize = tupleTypes.size(); - int sourceArraySize = source.size; - - if (targetTupleMemberSize > sourceArraySize) { - // [int, int, int...] = int[1] - return false; - } - - if (restType == null && targetTupleMemberSize < sourceArraySize) { - // [int, int] = int[3] - return false; - } - - BType sourceElementType = source.eType; - for (BType memType : tupleTypes) { - if (!isAssignable(sourceElementType, memType, unresolvedTypes)) { - return false; - } - } - - if (restType == null) { - return true; - } - - return sourceArraySize == targetTupleMemberSize || isAssignable(sourceElementType, restType, unresolvedTypes); - } - - private boolean isArrayTypesAssignable(BArrayType source, BType target, Set unresolvedTypes) { - BType sourceElementType = source.getElementType(); - target = getImpliedType(target); - if (target.tag == TypeTags.ARRAY) { - BArrayType targetArrayType = (BArrayType) target; - BType targetElementType = targetArrayType.getElementType(); - if (targetArrayType.state == BArrayState.OPEN) { - return isAssignable(sourceElementType, targetElementType, unresolvedTypes); - } - - if (targetArrayType.size != source.size) { - return false; - } - - return isAssignable(sourceElementType, targetElementType, unresolvedTypes); - } else if (target.tag == TypeTags.JSON) { - return isAssignable(sourceElementType, target, unresolvedTypes); - } else if (target.tag == TypeTags.ANYDATA) { - return isAssignable(sourceElementType, target, unresolvedTypes); - } - return false; - } - - private boolean isFunctionTypeAssignable(BInvokableType source, BInvokableType target, - Set unresolvedTypes) { - if (hasIncompatibleIsolatedFlags(source, target) || hasIncompatibleTransactionalFlags(source, target)) { - return false; - } - - if (Symbols.isFlagOn(target.flags, Flags.ANY_FUNCTION)) { - return true; - } - if (Symbols.isFlagOn(source.flags, Flags.ANY_FUNCTION) && !Symbols.isFlagOn(target.flags, Flags.ANY_FUNCTION)) { - return false; - } - - // For invokable types with typeParam parameters, we have to check whether the source param types are - // covariant with the target param types. - if (containsTypeParams(target)) { - // TODO: 7/4/19 See if the below code can be generalized to avoid code duplication - if (source.paramTypes.size() != target.paramTypes.size()) { - return false; - } - - for (int i = 0; i < source.paramTypes.size(); i++) { - BType sourceParam = source.paramTypes.get(i); - BType targetParam = target.paramTypes.get(i); - boolean isTypeParam = TypeParamAnalyzer.isTypeParam(targetParam); - - if (isTypeParam) { - if (!isAssignable(sourceParam, targetParam)) { - return false; - } - } else { - if (!isAssignable(targetParam, sourceParam)) { - return false; - } - } - } - - if (source.retType == null && target.retType == null) { - return true; - } else if (source.retType == null || target.retType == null) { - return false; - } - - // Source return type should be covariant with target return type - return isAssignable(source.retType, target.retType, unresolvedTypes); - } - - // Source param types should be contravariant with target param types. Hence s and t switched when checking - // assignability. - return checkFunctionTypeEquality(source, target, unresolvedTypes, (s, t, ut) -> isAssignable(t, s, ut)); - } - - public boolean isInherentlyImmutableType(BType type) { - type = getImpliedType(type); - if (isValueType(type)) { - return true; - } - - return switch (type.tag) { - case TypeTags.XML_TEXT, - TypeTags.FINITE, // Assuming a finite type will only have members from simple basic types. - TypeTags.READONLY, - TypeTags.NIL, - TypeTags.NEVER, - TypeTags.ERROR, - TypeTags.INVOKABLE, - TypeTags.TYPEDESC, - TypeTags.HANDLE, - TypeTags.REGEXP -> true; - case TypeTags.XML -> getImpliedType(((BXMLType) type).constraint).tag == TypeTags.NEVER; - default -> false; - }; - } - - /** - * Retrieve the referred type if a given type is a type reference type or - * retrieve the effective type if the given type is an intersection type. - * - * @param type type to retrieve the implied type - * @return the implied type if provided with a type reference type or an intersection type, - * else returns the original type - */ - public static BType getImpliedType(BType type) { - type = getReferredType(type); - if (type != null && type.tag == TypeTags.INTERSECTION) { - return getImpliedType(((BIntersectionType) type).effectiveType); - } - - return type; - } - - public static BType getReferredType(BType type) { - if (type != null && type.tag == TypeTags.TYPEREFDESC) { - return getReferredType(((BTypeReferenceType) type).referredType); - } - - return type; - } - - public BLangExpression addConversionExprIfRequired(BLangExpression expr, BType lhsType) { - if (lhsType.tag == TypeTags.NONE) { - return expr; - } - - BType rhsType = expr.getBType(); - - if (lhsType.tag == TypeTags.TYPEREFDESC && rhsType.tag != TypeTags.TYPEREFDESC) { - return addConversionExprIfRequired(expr, Types.getReferredType(lhsType)); - } - - if (isSameType(rhsType, lhsType)) { - return expr; - } - - setImplicitCastExpr(expr, rhsType, lhsType); - if (expr.impConversionExpr != null) { - BLangExpression impConversionExpr = expr.impConversionExpr; - expr.impConversionExpr = null; - return impConversionExpr; - } - - if (lhsType.tag == TypeTags.JSON && rhsType.tag == TypeTags.NIL) { - return expr; - } - - if (lhsType.tag == TypeTags.NIL && rhsType.isNullable()) { - return expr; - } - - if (lhsType.tag == TypeTags.ARRAY && rhsType.tag == TypeTags.TUPLE) { - return expr; - } - - // Create a type cast expression - BLangTypeConversionExpr conversionExpr = (BLangTypeConversionExpr) - TreeBuilder.createTypeConversionNode(); - conversionExpr.expr = expr; - conversionExpr.targetType = lhsType; - conversionExpr.setBType(lhsType); - conversionExpr.pos = expr.pos; - conversionExpr.checkTypes = false; - conversionExpr.internal = true; - return conversionExpr; - } - - boolean isSelectivelyImmutableType(BType type, PackageID packageID) { - return isSelectivelyImmutableType(type, new HashSet<>(), false, packageID); - } - - boolean isSelectivelyImmutableType(BType type, boolean forceCheck, PackageID packageID) { - return isSelectivelyImmutableType(type, new HashSet<>(), forceCheck, packageID); - } - - public boolean isSelectivelyImmutableType(BType type, Set unresolvedTypes, PackageID packageID) { - return isSelectivelyImmutableType(type, unresolvedTypes, false, packageID); - } - - private boolean isSelectivelyImmutableType(BType type, Set unresolvedTypes, boolean forceCheck, - PackageID packageID) { - return isSelectivelyImmutableType(type, false, unresolvedTypes, forceCheck, packageID); - } - - private boolean isSelectivelyImmutableType(BType input, boolean disallowReadOnlyObjects, Set unresolvedTypes, - boolean forceCheck, PackageID packageID) { - BType type = getImpliedType(input); - - if (isInherentlyImmutableType(type) || !(type instanceof SelectivelyImmutableReferenceType)) { - // Always immutable. - return false; - } - - if (!unresolvedTypes.add(type)) { - return true; - } - - if (!forceCheck && - getImmutableType(symTable, packageID, (SelectivelyImmutableReferenceType) type).isPresent()) { - return true; - } - - switch (type.tag) { - case TypeTags.ANY: - case TypeTags.ANYDATA: - case TypeTags.JSON: - case TypeTags.XML: - case TypeTags.XML_COMMENT: - case TypeTags.XML_ELEMENT: - case TypeTags.XML_PI: - case TypeTags.BYTE_ARRAY: - return true; - case TypeTags.ARRAY: - BArrayType arrayType = (BArrayType) type; - BType elementType = arrayType.eType; - if (elementType == symTable.semanticError && arrayType.mutableType != null) { - elementType = arrayType.mutableType.eType; - } - return isInherentlyImmutableType(elementType) || - isSelectivelyImmutableType(elementType, unresolvedTypes, forceCheck, packageID); - case TypeTags.TUPLE: - BTupleType tupleType = (BTupleType) type; - List tupleMemberTypes = tupleType.getTupleTypes(); - if (tupleMemberTypes.isEmpty() && tupleType.mutableType != null) { - tupleMemberTypes = tupleType.mutableType.getTupleTypes(); - } - for (BType memberType : tupleMemberTypes) { - if (!isInherentlyImmutableType(memberType) && - !isSelectivelyImmutableType(memberType, unresolvedTypes, forceCheck, packageID)) { - return false; - } - } - - BType tupRestType = tupleType.restType; - if (tupRestType == null) { - return true; - } - - return isInherentlyImmutableType(tupRestType) || - isSelectivelyImmutableType(tupRestType, unresolvedTypes, forceCheck, packageID); - case TypeTags.RECORD: - BRecordType recordType = (BRecordType) type; - LinkedHashMap recordFields = recordType.fields; - if (recordFields.isEmpty() && recordType.mutableType != null) { - recordFields = recordType.mutableType.fields; - } - for (BField field : recordFields.values()) { - BType fieldType = field.type; - if (!Symbols.isFlagOn(field.symbol.flags, Flags.OPTIONAL) && - !isInherentlyImmutableType(fieldType) && - !isSelectivelyImmutableType(fieldType, unresolvedTypes, forceCheck, packageID)) { - return false; - } - } - return true; - case TypeTags.MAP: - BMapType mapType = (BMapType) type; - BType constraintType = mapType.constraint; - if (constraintType == symTable.semanticError && mapType.mutableType != null) { - constraintType = mapType.mutableType.constraint; - } - return isInherentlyImmutableType(constraintType) || - isSelectivelyImmutableType(constraintType, unresolvedTypes, forceCheck, packageID); - case TypeTags.OBJECT: - BObjectType objectType = (BObjectType) type; - LinkedHashMap objectFields = objectType.fields; - if (objectFields.isEmpty() && objectType.mutableType != null) { - objectFields = objectType.mutableType.fields; - } - for (BField field : objectFields.values()) { - BType fieldType = field.type; - if (!isInherentlyImmutableType(fieldType) && - !isSelectivelyImmutableType(fieldType, unresolvedTypes, forceCheck, packageID)) { - return false; - } - } - return true; - case TypeTags.TABLE: - BTableType tableType = (BTableType) type; - BType tableConstraintType = tableType.constraint; - if (tableConstraintType == symTable.semanticError && tableType.mutableType != null) { - tableConstraintType = tableType.mutableType.constraint; - } - return isInherentlyImmutableType(tableConstraintType) || - isSelectivelyImmutableType(tableConstraintType, unresolvedTypes, forceCheck, packageID); - case TypeTags.UNION: - boolean readonlyIntersectionExists = false; - BUnionType unionType = (BUnionType) type; - LinkedHashSet memberTypes = unionType.getMemberTypes(); - for (BType memberType : memberTypes) { - if (isInherentlyImmutableType(memberType) || - isSelectivelyImmutableType(memberType, unresolvedTypes, forceCheck, packageID)) { - readonlyIntersectionExists = true; - } - } - return readonlyIntersectionExists; - } - return false; - } - - private boolean containsTypeParams(BInvokableType type) { - boolean hasParameterizedTypes = type.paramTypes.stream() - .anyMatch(t -> { - t = getImpliedType(t); - if (t.tag == TypeTags.FUNCTION_POINTER) { - return containsTypeParams((BInvokableType) t); - } - return TypeParamAnalyzer.isTypeParam(t); - }); - - if (hasParameterizedTypes) { - return true; - } - - BType retType = getImpliedType(type.retType); - if (retType.tag == TypeTags.FUNCTION_POINTER) { - return containsTypeParams((BInvokableType) retType); - } - - return TypeParamAnalyzer.isTypeParam(type.retType); - } - - private boolean checkFunctionTypeEquality(BInvokableType source, BInvokableType target, - Set unresolvedTypes, TypeEqualityPredicate equality) { - if (hasIncompatibleIsolatedFlags(source, target) || hasIncompatibleTransactionalFlags(source, target)) { - return false; - } - - if (Symbols.isFlagOn(target.flags, Flags.ANY_FUNCTION) && Symbols.isFlagOn(source.flags, Flags.ANY_FUNCTION)) { - return true; - } - - if (Symbols.isFlagOn(target.flags, Flags.ANY_FUNCTION) || Symbols.isFlagOn(source.flags, Flags.ANY_FUNCTION)) { - return false; - } - - if (source.paramTypes.size() != target.paramTypes.size()) { - return false; - } - - for (int i = 0; i < source.paramTypes.size(); i++) { - if (!equality.test(source.paramTypes.get(i), target.paramTypes.get(i), unresolvedTypes)) { - return false; - } - } - - if ((source.restType != null && target.restType == null) || - target.restType != null && source.restType == null) { - return false; - } else if (source.restType != null && !equality.test(source.restType, target.restType, unresolvedTypes)) { - return false; - } - - if (source.retType == null && target.retType == null) { - return true; - } else if (source.retType == null || target.retType == null) { - return false; - } - - // Source return type should be covariant with target return type - return isAssignable(source.retType, target.retType, unresolvedTypes); - } - - private boolean hasIncompatibleIsolatedFlags(BInvokableType source, BInvokableType target) { - return Symbols.isFlagOn(target.flags, Flags.ISOLATED) && !Symbols.isFlagOn(source.flags, Flags.ISOLATED); - } - - private boolean hasIncompatibleTransactionalFlags(BInvokableType source, BInvokableType target) { - return Symbols.isFlagOn(source.flags, Flags.TRANSACTIONAL) && - !Symbols.isFlagOn(target.flags, Flags.TRANSACTIONAL); - } - - public boolean isSameArrayType(BType source, BType target, Set unresolvedTypes) { - target = getImpliedType(target); - source = getImpliedType(source); - if (target.tag != TypeTags.ARRAY || source.tag != TypeTags.ARRAY) { - return false; - } - - BArrayType lhsArrayType = (BArrayType) target; - BArrayType rhsArrayType = (BArrayType) source; - boolean hasSameTypeElements = isSameType(lhsArrayType.eType, rhsArrayType.eType, unresolvedTypes); - if (lhsArrayType.state == BArrayState.OPEN) { - return (rhsArrayType.state == BArrayState.OPEN) && hasSameTypeElements; - } - - return checkSealedArraySizeEquality(rhsArrayType, lhsArrayType) && hasSameTypeElements; - } - - public boolean checkSealedArraySizeEquality(BArrayType rhsArrayType, BArrayType lhsArrayType) { - return lhsArrayType.size == rhsArrayType.size; - } - - public boolean checkStructEquivalency(BType rhsType, BType lhsType) { - return checkStructEquivalency(rhsType, lhsType, new HashSet<>()); - } - - private boolean checkStructEquivalency(BType rhsType, BType lhsType, Set unresolvedTypes) { - // If we encounter two types that we are still resolving, then skip it. - // This is done to avoid recursive checking of the same type. - TypePair pair = new TypePair(rhsType, lhsType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - - rhsType = getImpliedType(rhsType); - lhsType = getImpliedType(lhsType); - if (rhsType.tag == TypeTags.OBJECT && lhsType.tag == TypeTags.OBJECT) { - return checkObjectEquivalency((BObjectType) rhsType, (BObjectType) lhsType, unresolvedTypes); - } - - if (rhsType.tag == TypeTags.RECORD && lhsType.tag == TypeTags.RECORD) { - return checkRecordEquivalency((BRecordType) rhsType, (BRecordType) lhsType, unresolvedTypes); - } - - return false; - } - - public boolean checkObjectEquivalency(BObjectType rhsType, BObjectType lhsType, Set unresolvedTypes) { - if (Symbols.isFlagOn(lhsType.flags, Flags.ISOLATED) && !Symbols.isFlagOn(rhsType.flags, Flags.ISOLATED)) { - return false; - } - - BObjectTypeSymbol lhsStructSymbol = (BObjectTypeSymbol) lhsType.tsymbol; - BObjectTypeSymbol rhsStructSymbol = (BObjectTypeSymbol) rhsType.tsymbol; - List lhsFuncs = lhsStructSymbol.attachedFuncs; - List rhsFuncs = ((BObjectTypeSymbol) rhsType.tsymbol).attachedFuncs; - int lhsAttachedFuncCount = getObjectFuncCount(lhsStructSymbol); - int rhsAttachedFuncCount = getObjectFuncCount(rhsStructSymbol); - - // If LHS is a service obj, then RHS must be a service object in order to assignable - boolean isLhsAService = Symbols.isService(lhsStructSymbol); - if (isLhsAService && !Symbols.isService(rhsStructSymbol)) { - return false; - } - - // RHS type should have at least all the fields as well attached functions of LHS type. - if (lhsType.fields.size() > rhsType.fields.size() || lhsAttachedFuncCount > rhsAttachedFuncCount) { - return false; - } - - // The LHS type cannot have any private members. - for (BField bField : lhsType.fields.values()) { - if (Symbols.isPrivate(bField.symbol)) { - return false; - } - } - - for (BAttachedFunction func : lhsFuncs) { - if (Symbols.isPrivate(func.symbol)) { - return false; - } - } - - for (BField lhsField : lhsType.fields.values()) { - BField rhsField = rhsType.fields.get(lhsField.name.value); - if (rhsField == null || - !isInSameVisibilityRegion(lhsField.symbol, rhsField.symbol) || - !isAssignable(rhsField.type, lhsField.type, unresolvedTypes)) { - return false; - } - } - - for (BAttachedFunction lhsFunc : lhsFuncs) { - if (lhsFunc == lhsStructSymbol.initializerFunc) { - continue; - } - - Optional rhsFunction = getMatchingInvokableType(rhsFuncs, lhsFunc, unresolvedTypes); - if (rhsFunction.isEmpty()) { - return false; - } - - BAttachedFunction rhsFunc = rhsFunction.get(); - if (!isInSameVisibilityRegion(lhsFunc.symbol, rhsFunc.symbol)) { - return false; - } - - if (Symbols.isRemote(lhsFunc.symbol) != Symbols.isRemote(rhsFunc.symbol)) { - return false; - } - } - - return lhsType.typeIdSet.isAssignableFrom(rhsType.typeIdSet) || this.ignoreObjectTypeIds; - } - - private int getObjectFuncCount(BObjectTypeSymbol sym) { - int count = sym.attachedFuncs.size(); - // If an explicit initializer is available, it could mean, - // 1) User explicitly defined an initializer - // 2) The object type is coming from an already compiled source, hence the initializer is already set. - // If it's coming from a compiled binary, the attached functions list of the symbol would already contain - // the initializer in it. - if (sym.initializerFunc != null && sym.attachedFuncs.contains(sym.initializerFunc)) { - return count - 1; - } - return count; - } - - public boolean checkRecordEquivalency(BRecordType rhsType, BRecordType lhsType, Set unresolvedTypes) { - // If the LHS record is closed and the RHS record is open and the rest field type of RHS is not a 'never' - // type, the records aren't equivalent - if (lhsType.sealed && !rhsType.sealed && getImpliedType(rhsType.restFieldType).tag != TypeTags.NEVER) { - return false; - } - - // If both are open records, the rest field type of the RHS record should be assignable to the rest field - // type of the LHS type. - if (!rhsType.sealed && !isAssignable(rhsType.restFieldType, lhsType.restFieldType, unresolvedTypes)) { - return false; - } - - return checkFieldEquivalency(lhsType, rhsType, unresolvedTypes); - } - - public void setForeachTypedBindingPatternType(BLangForeach foreachNode) { - BType collectionType = getImpliedType(foreachNode.collection.getBType()); - BType varType; - switch (collectionType.tag) { - case TypeTags.STRING: - varType = symTable.charStringType; - break; - case TypeTags.ARRAY: - BArrayType arrayType = (BArrayType) collectionType; - varType = arrayType.eType; - break; - case TypeTags.TUPLE: - varType = getTupleMemberType((BTupleType) collectionType); - break; - case TypeTags.MAP: - BMapType bMapType = (BMapType) collectionType; - varType = bMapType.constraint; - break; - case TypeTags.RECORD: - BRecordType recordType = (BRecordType) collectionType; - varType = inferRecordFieldType(recordType); - break; - case TypeTags.XML: - BType typedBindingPatternType = getTypedBindingPatternTypeForXmlCollection(collectionType); - if (typedBindingPatternType == null) { - foreachNode.varType = symTable.semanticError; - foreachNode.resultType = symTable.semanticError; - foreachNode.nillableResultType = symTable.semanticError; - return; - } - varType = typedBindingPatternType; - break; - case TypeTags.XML_TEXT: - varType = symTable.xmlTextType; - break; - case TypeTags.TABLE: - BTableType tableType = (BTableType) collectionType; - varType = tableType.constraint; - break; - case TypeTags.STREAM: - BStreamType streamType = (BStreamType) collectionType; - if (streamType.constraint.tag == TypeTags.NONE) { - varType = symTable.anydataType; - break; - } - varType = streamType.constraint; - List completionType = getAllTypes(streamType.completionType, true); - if (completionType.stream().anyMatch(type -> getImpliedType(type).tag != TypeTags.NIL)) { - BType actualType = BUnionType.create(null, varType, streamType.completionType); - dlog.error(foreachNode.collection.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, - varType, actualType); - } - break; - case TypeTags.OBJECT: - // check for iterable objects - BUnionType nextMethodReturnType = getVarTypeFromIterableObject((BObjectType) collectionType); - if (nextMethodReturnType != null) { - foreachNode.resultType = getRecordType(nextMethodReturnType); - BType valueType = (foreachNode.resultType != null) - ? ((BRecordType) foreachNode.resultType).fields.get("value").type : null; - BType errorType = getErrorType(nextMethodReturnType); - if (errorType != null) { - BType actualType = BUnionType.create(null, valueType, errorType); - dlog.error(foreachNode.collection.pos, - DiagnosticErrorCode.INVALID_ITERABLE_COMPLETION_TYPE_IN_FOREACH_NEXT_FUNCTION, - actualType, errorType); - } - foreachNode.nillableResultType = nextMethodReturnType; - foreachNode.varType = valueType; - return; - } - // fallthrough - case TypeTags.SEMANTIC_ERROR: - foreachNode.varType = symTable.semanticError; - foreachNode.resultType = symTable.semanticError; - foreachNode.nillableResultType = symTable.semanticError; - return; - default: - foreachNode.varType = symTable.semanticError; - foreachNode.resultType = symTable.semanticError; - foreachNode.nillableResultType = symTable.semanticError; - dlog.error(foreachNode.collection.pos, DiagnosticErrorCode.ITERABLE_NOT_SUPPORTED_COLLECTION, - collectionType); - return; - } - - BInvokableSymbol iteratorSymbol = (BInvokableSymbol) symResolver.lookupLangLibMethod(collectionType, - Names.fromString(BLangCompilerConstants.ITERABLE_COLLECTION_ITERATOR_FUNC), env); - BObjectType objectType = (BObjectType) getImpliedType(iteratorSymbol.retType); - BUnionType nextMethodReturnType = - (BUnionType) getResultTypeOfNextInvocation(objectType); - foreachNode.varType = varType; - foreachNode.resultType = getRecordType(nextMethodReturnType); - foreachNode.nillableResultType = nextMethodReturnType; - } - - public void setInputClauseTypedBindingPatternType(BLangInputClause bLangInputClause) { - if (bLangInputClause.collection == null) { - //not-possible - return; - } - - BType collectionType = bLangInputClause.collection.getBType(); - bLangInputClause.varType = visitCollectionType(bLangInputClause, collectionType); - if (bLangInputClause.varType.tag == TypeTags.SEMANTIC_ERROR || - getImpliedType(collectionType).tag == OBJECT) { - return; - } - - BInvokableSymbol iteratorSymbol = (BInvokableSymbol) symResolver.lookupLangLibMethod(collectionType, - Names.fromString(BLangCompilerConstants.ITERABLE_COLLECTION_ITERATOR_FUNC), env); - BUnionType nextMethodReturnType = - (BUnionType) getResultTypeOfNextInvocation((BObjectType) getImpliedType(iteratorSymbol.retType)); - bLangInputClause.resultType = getRecordType(nextMethodReturnType); - bLangInputClause.nillableResultType = nextMethodReturnType; - } - - private BType getTypedBindingPatternTypeForXmlCollection(BType collectionType) { - BType constraint = getImpliedType(((BXMLType) collectionType).constraint); - while (constraint.tag == TypeTags.XML) { - collectionType = constraint; - constraint = getImpliedType(((BXMLType) collectionType).constraint); - } - - switch (constraint.tag) { - case TypeTags.XML_ELEMENT: - case TypeTags.XML_COMMENT: - case TypeTags.XML_TEXT: - case TypeTags.XML_PI: - case TypeTags.NEVER: - return constraint; - case TypeTags.UNION: - Set collectionTypes = getEffectiveMemberTypes((BUnionType) constraint); - Set builtinXMLConstraintTypes = getEffectiveMemberTypes - ((BUnionType) ((BXMLType) symTable.xmlType).constraint); - return collectionTypes.size() == 4 && builtinXMLConstraintTypes.equals(collectionTypes) ? - collectionType : BUnionType.create(null, (LinkedHashSet) collectionTypes); - default: - return null; - } - } - - private BType visitCollectionType(BLangInputClause bLangInputClause, BType collectionType) { - collectionType = getImpliedType(collectionType); - switch (collectionType.tag) { - case TypeTags.STRING: - return symTable.stringType; - case TypeTags.ARRAY: - BArrayType arrayType = (BArrayType) collectionType; - return arrayType.eType; - case TypeTags.TUPLE: - return getTupleMemberType((BTupleType) collectionType); - case TypeTags.MAP: - BMapType bMapType = (BMapType) collectionType; - return bMapType.constraint; - case TypeTags.RECORD: - BRecordType recordType = (BRecordType) collectionType; - return inferRecordFieldType(recordType); - case TypeTags.XML: - BType bindingPatternType = getTypedBindingPatternTypeForXmlCollection(collectionType); - return bindingPatternType == null ? symTable.semanticError : bindingPatternType; - case TypeTags.XML_TEXT: - return symTable.xmlTextType; - case TypeTags.TABLE: - BTableType tableType = (BTableType) collectionType; - return tableType.constraint; - case TypeTags.STREAM: - BStreamType streamType = (BStreamType) collectionType; - if (streamType.constraint.tag == TypeTags.NONE) { - return symTable.anydataType; - } - return streamType.constraint; - case TypeTags.OBJECT: - // check for iterable objects - if (!isAssignable(collectionType, symTable.iterableType)) { - dlog.error(bLangInputClause.collection.pos, DiagnosticErrorCode.INVALID_ITERABLE_OBJECT_TYPE, - bLangInputClause.collection.getBType(), symTable.iterableType); - bLangInputClause.varType = symTable.semanticError; - bLangInputClause.resultType = symTable.semanticError; - bLangInputClause.nillableResultType = symTable.semanticError; - break; - } - - BUnionType nextMethodReturnType = getVarTypeFromIterableObject((BObjectType) collectionType); - if (nextMethodReturnType != null) { - bLangInputClause.resultType = getRecordType(nextMethodReturnType); - bLangInputClause.nillableResultType = nextMethodReturnType; - bLangInputClause.varType = ((BRecordType) bLangInputClause.resultType).fields.get("value").type; - return bLangInputClause.varType; - } - // fallthrough - case TypeTags.SEMANTIC_ERROR: - bLangInputClause.varType = symTable.semanticError; - bLangInputClause.resultType = symTable.semanticError; - bLangInputClause.nillableResultType = symTable.semanticError; - break; - default: - bLangInputClause.varType = symTable.semanticError; - bLangInputClause.resultType = symTable.semanticError; - bLangInputClause.nillableResultType = symTable.semanticError; - dlog.error(bLangInputClause.collection.pos, DiagnosticErrorCode.ITERABLE_NOT_SUPPORTED_COLLECTION, - collectionType); - } - return symTable.semanticError; - } - - private BType getTupleMemberType(BTupleType tupleType) { - LinkedHashSet tupleTypes = new LinkedHashSet<>(tupleType.getTupleTypes()); - if (tupleType.restType != null) { - tupleTypes.add(tupleType.restType); - } - int tupleTypesSize = tupleTypes.size(); - if (tupleTypesSize == 0) { - return symTable.neverType; - } - return tupleTypesSize == 1 ? - tupleTypes.iterator().next() : BUnionType.create(null, tupleTypes); - } - - public BUnionType getVarTypeFromIterableObject(BObjectType collectionType) { - BObjectTypeSymbol objectTypeSymbol = (BObjectTypeSymbol) collectionType.tsymbol; - for (BAttachedFunction func : objectTypeSymbol.attachedFuncs) { - if (func.funcName.value.equals(BLangCompilerConstants.ITERABLE_COLLECTION_ITERATOR_FUNC)) { - return getVarTypeFromIteratorFunc(func); - } - } - - return null; - } - - private BUnionType getVarTypeFromIteratorFunc(BAttachedFunction candidateIteratorFunc) { - if (!candidateIteratorFunc.type.paramTypes.isEmpty()) { - return null; - } - - BType returnType = candidateIteratorFunc.type.retType; - // abstract object {public function next() returns record {|int value;|}?;} - return getVarTypeFromIteratorFuncReturnType(returnType); - } - - public BUnionType getVarTypeFromIteratorFuncReturnType(BType type) { - BObjectTypeSymbol objectTypeSymbol; - BType returnType = getImpliedType(type); - if (returnType.tag != TypeTags.OBJECT) { - return null; - } - - objectTypeSymbol = (BObjectTypeSymbol) returnType.tsymbol; - for (BAttachedFunction func : objectTypeSymbol.attachedFuncs) { - if (func.funcName.value.equals(BLangCompilerConstants.NEXT_FUNC)) { - return getVarTypeFromNextFunc(func); - } - } - - return null; - } - - private BUnionType getVarTypeFromNextFunc(BAttachedFunction nextFunc) { - BType returnType; - if (!nextFunc.type.paramTypes.isEmpty()) { - return null; - } - - returnType = nextFunc.type.retType; - // Check if the next function return type has the union type, - // record {|int value;|}|error|(); - if (checkNextFuncReturnType(returnType)) { - return (BUnionType) returnType; - } - - return null; - } - - private boolean checkNextFuncReturnType(BType returnType) { - if (getImpliedType(returnType).tag != TypeTags.UNION) { - return false; - } - - List types = getAllTypes(returnType, true); - boolean containsCompletionType = types.removeIf(type -> type.tag == TypeTags.NIL); - containsCompletionType = types.removeIf(type -> type.tag == TypeTags.ERROR) || containsCompletionType; - if (!containsCompletionType) { - return false; - } - - if (types.size() != 1) { - //TODO: print error - return false; - } - - if (types.get(0).tag != TypeTags.RECORD) { - return false; - } - - BRecordType recordType = (BRecordType) types.get(0); - // Check if the union type has the record type, - // record {|int value;|}; - return checkRecordTypeInNextFuncReturnType(recordType); - } - - private boolean checkRecordTypeInNextFuncReturnType(BRecordType recordType) { - if (!recordType.sealed) { - return false; - } - - if (recordType.fields.size() != 1) { - return false; - } - - return recordType.fields.containsKey(BLangCompilerConstants.VALUE_FIELD); - } - - private BRecordType getRecordType(BUnionType type) { - for (BType member : type.getMemberTypes()) { - BType referredRecordType = getImpliedType(member); - if (referredRecordType.tag == TypeTags.RECORD) { - return (BRecordType) referredRecordType; - } - } - return null; - } - - public BErrorType getErrorType(BUnionType type) { - for (BType member : type.getMemberTypes()) { - member = getImpliedType(member); - - if (member.tag == TypeTags.ERROR) { - return (BErrorType) member; - } else if (member.tag == TypeTags.UNION) { - BErrorType e = getErrorType((BUnionType) member); - if (e != null) { - return e; - } - } - } - return null; - } - - public BType getResultTypeOfNextInvocation(BObjectType iteratorType) { - BAttachedFunction nextFunc = getAttachedFuncFromObject(iteratorType, BLangCompilerConstants.NEXT_FUNC); - return Objects.requireNonNull(nextFunc).type.retType; - } - - public BAttachedFunction getAttachedFuncFromObject(BObjectType objectType, String funcName) { - BObjectTypeSymbol iteratorSymbol = (BObjectTypeSymbol) objectType.tsymbol; - for (BAttachedFunction bAttachedFunction : iteratorSymbol.attachedFuncs) { - if (funcName.equals(bAttachedFunction.funcName.value)) { - return bAttachedFunction; - } - } - return null; - } - - public BType inferRecordFieldType(BRecordType recordType) { - Map fields = recordType.fields; - BUnionType unionType = BUnionType.create(null); - - if (!recordType.sealed) { - unionType.add(recordType.restFieldType); - } else if (fields.isEmpty()) { - unionType.add(symTable.neverType); - } - - for (BField field : fields.values()) { - if (isAssignable(field.type, unionType)) { - continue; - } - - if (isAssignable(unionType, field.type)) { - unionType = BUnionType.create(null); - } - - unionType.add(field.type); - } - - if (unionType.getMemberTypes().size() > 1) { - unionType.tsymbol = Symbols.createTypeSymbol(SymTag.UNION_TYPE, Flags.asMask(EnumSet.of(Flag.PUBLIC)), - Names.EMPTY, recordType.tsymbol.pkgID, null, - recordType.tsymbol.owner, symTable.builtinPos, VIRTUAL); - return unionType; - } - - return unionType.getMemberTypes().iterator().next(); - } - - - public BType getTypeWithEffectiveIntersectionTypes(BType bType) { - // TODO Can remove this method since this unwraps the referred type and intersection type. #40958 - BType type = getReferredType(bType); - BType effectiveType = null; - if (type.tag == TypeTags.INTERSECTION) { - effectiveType = ((BIntersectionType) type).effectiveType; - type = effectiveType; - } - - if (type.tag != TypeTags.UNION) { - return Objects.requireNonNullElse(effectiveType, bType); - } - - LinkedHashSet members = new LinkedHashSet<>(); - boolean hasDifferentMember = false; - - for (BType memberType : ((BUnionType) type).getMemberTypes()) { - effectiveType = getTypeWithEffectiveIntersectionTypes(memberType); - effectiveType = getImpliedType(effectiveType); - if (effectiveType != memberType) { - hasDifferentMember = true; - } - members.add(effectiveType); - } - - if (hasDifferentMember) { - return BUnionType.create(null, members); - } - return bType; - } - - /** - * Enum to represent type test result. - * - * @since 1.2.0 - */ - enum TypeTestResult { - NOT_FOUND, - TRUE, - FALSE - } - - TypeTestResult isBuiltInTypeWidenPossible(BType actualType, BType targetType) { - int targetTag = getImpliedType(targetType).tag; - int actualTag = getImpliedType(actualType).tag; - - if (actualTag < TypeTags.JSON && targetTag < TypeTags.JSON) { - // Fail Fast for value types. - switch (actualTag) { - case TypeTags.INT: - case TypeTags.BYTE: - case TypeTags.FLOAT: - case TypeTags.DECIMAL: - if (targetTag == TypeTags.BOOLEAN || targetTag == TypeTags.STRING) { - return TypeTestResult.FALSE; - } - break; - case TypeTags.BOOLEAN: - if (targetTag == TypeTags.INT || targetTag == TypeTags.BYTE || targetTag == TypeTags.FLOAT - || targetTag == TypeTags.DECIMAL || targetTag == TypeTags.STRING) { - return TypeTestResult.FALSE; - } - break; - case TypeTags.STRING: - if (targetTag == TypeTags.INT || targetTag == TypeTags.BYTE || targetTag == TypeTags.FLOAT - || targetTag == TypeTags.DECIMAL || targetTag == TypeTags.BOOLEAN) { - return TypeTestResult.FALSE; - } - break; - } - } - switch (actualTag) { - case TypeTags.INT: - case TypeTags.BYTE: - case TypeTags.FLOAT: - case TypeTags.DECIMAL: - case TypeTags.BOOLEAN: + public void setForeachTypedBindingPatternType(BLangForeach foreachNode) { + BType collectionType = getImpliedType(foreachNode.collection.getBType()); + BType varType; + switch (collectionType.tag) { case TypeTags.STRING: - case TypeTags.SIGNED32_INT: - case TypeTags.SIGNED16_INT: - case TypeTags.SIGNED8_INT: - case TypeTags.UNSIGNED32_INT: - case TypeTags.UNSIGNED16_INT: - case TypeTags.UNSIGNED8_INT: - case TypeTags.CHAR_STRING: - if (targetTag == TypeTags.JSON || targetTag == TypeTags.ANYDATA || targetTag == TypeTags.ANY || - targetTag == TypeTags.READONLY) { - return TypeTestResult.TRUE; - } + varType = symTable.charStringType; break; - case TypeTags.ANYDATA: - case TypeTags.TYPEDESC: - if (targetTag == TypeTags.ANY) { - return TypeTestResult.TRUE; - } + case TypeTags.ARRAY: + BArrayType arrayType = (BArrayType) collectionType; + varType = arrayType.eType; break; - default: - } - - if (TypeTags.isIntegerTypeTag(targetTag) && actualTag == targetTag) { - return TypeTestResult.FALSE; // No widening. - } - - // Validate for Integers subtypes. - if ((TypeTags.isIntegerTypeTag(actualTag) || actualTag == TypeTags.BYTE) - && (TypeTags.isIntegerTypeTag(targetTag) || targetTag == TypeTags.BYTE)) { - return checkBuiltInIntSubtypeWidenPossible(actualType, targetType); - } - - if (actualTag == TypeTags.CHAR_STRING && TypeTags.STRING == targetTag) { - return TypeTestResult.TRUE; - } - return TypeTestResult.NOT_FOUND; - } - - private TypeTestResult checkBuiltInIntSubtypeWidenPossible(BType actualType, BType targetType) { - int actualTag = getImpliedType(actualType).tag; - targetType = getImpliedType(targetType); - switch (targetType.tag) { - case TypeTags.INT: - if (actualTag == TypeTags.BYTE || TypeTags.isIntegerTypeTag(actualTag)) { - return TypeTestResult.TRUE; - } + case TypeTags.TUPLE: + varType = getTupleMemberType((BTupleType) collectionType); break; - case TypeTags.SIGNED32_INT: - if (actualTag == TypeTags.SIGNED16_INT || actualTag == TypeTags.SIGNED8_INT || - actualTag == TypeTags.UNSIGNED16_INT || actualTag == TypeTags.UNSIGNED8_INT || - actualTag == TypeTags.BYTE) { - return TypeTestResult.TRUE; - } + case TypeTags.MAP: + BMapType bMapType = (BMapType) collectionType; + varType = bMapType.constraint; break; - case TypeTags.SIGNED16_INT: - if (actualTag == TypeTags.SIGNED8_INT || actualTag == TypeTags.UNSIGNED8_INT || - actualTag == TypeTags.BYTE) { - return TypeTestResult.TRUE; - } + case TypeTags.RECORD: + BRecordType recordType = (BRecordType) collectionType; + varType = inferRecordFieldType(recordType); break; - case TypeTags.UNSIGNED32_INT: - if (actualTag == TypeTags.UNSIGNED16_INT || actualTag == TypeTags.UNSIGNED8_INT || - actualTag == TypeTags.BYTE) { - return TypeTestResult.TRUE; + case TypeTags.XML: + BType typedBindingPatternType = getTypedBindingPatternTypeForXmlCollection(collectionType); + if (typedBindingPatternType == null) { + foreachNode.varType = symTable.semanticError; + foreachNode.resultType = symTable.semanticError; + foreachNode.nillableResultType = symTable.semanticError; + return; } + varType = typedBindingPatternType; break; - case TypeTags.UNSIGNED16_INT: - if (actualTag == TypeTags.UNSIGNED8_INT || actualTag == TypeTags.BYTE) { - return TypeTestResult.TRUE; - } + case TypeTags.XML_TEXT: + varType = symTable.xmlTextType; break; - case TypeTags.BYTE: - if (actualTag == TypeTags.UNSIGNED8_INT) { - return TypeTestResult.TRUE; - } + case TypeTags.TABLE: + BTableType tableType = (BTableType) collectionType; + varType = tableType.constraint; break; - case TypeTags.UNSIGNED8_INT: - if (actualTag == TypeTags.BYTE) { - return TypeTestResult.TRUE; - } - } - return TypeTestResult.NOT_FOUND; - } - - public boolean isImplicitlyCastable(BType actual, BType target) { - /* The word Builtin refers for Compiler known types. */ - - BType targetType = getImpliedType(target); - BType actualType = getImpliedType(actual); - BType newTargetType = targetType; - int targetTypeTag = targetType.tag; - if ((targetTypeTag == TypeTags.UNION || targetTypeTag == TypeTags.FINITE) && isValueType(actualType)) { - newTargetType = symTable.anyType; // TODO : Check for correctness. - } - - TypeTestResult result = isBuiltInTypeWidenPossible(actualType, newTargetType); - if (result != TypeTestResult.NOT_FOUND) { - return result == TypeTestResult.TRUE; - } - - if (isValueType(targetType) && - (actualType.tag == TypeTags.FINITE || - (actualType.tag == TypeTags.UNION && ((BUnionType) actualType).getMemberTypes().stream() - .anyMatch(type -> getImpliedType(type).tag == TypeTags.FINITE && - isAssignable(type, targetType))))) { - // for nil, no cast is required - return TypeTags.isIntegerTypeTag(targetTypeTag) || targetType.tag == TypeTags.BYTE || - targetTypeTag == TypeTags.FLOAT || - targetTypeTag == TypeTags.DECIMAL || - TypeTags.isStringTypeTag(targetTypeTag) || - targetTypeTag == TypeTags.BOOLEAN; - - } else if (isValueType(targetType) && actualType.tag == TypeTags.UNION && - ((BUnionType) actualType).getMemberTypes().stream().allMatch(type -> isAssignable(type, targetType))) { - return true; - - } else if (targetTypeTag == TypeTags.ERROR - && (actualType.tag == TypeTags.UNION - && isAllErrorMembers((BUnionType) actualType))) { - return true; - } - return false; - } - - public boolean isTypeCastable(BLangExpression expr, BType source, BType target, SymbolEnv env) { - BType sourceType = getImpliedType(source); - BType targetType = getImpliedType(target); - if (sourceType.tag == TypeTags.SEMANTIC_ERROR || targetType.tag == TypeTags.SEMANTIC_ERROR || - sourceType == targetType) { - return true; - } - - // Disallow casting away error, this forces user to handle the error via type-test, check, or checkpanic - IntersectionContext intersectionContext = IntersectionContext.compilerInternalIntersectionTestContext(); - BType errorIntersection = getTypeIntersection(intersectionContext, sourceType, symTable.errorType, env); - if (errorIntersection != symTable.semanticError && - getTypeIntersection(intersectionContext, symTable.errorType, targetType, env) - == symTable.semanticError) { - return false; - } - - if (isAssignable(sourceType, targetType) || isAssignable(targetType, sourceType)) { - return true; - } - if (isNumericConversionPossible(expr, sourceType, targetType)) { - return true; - } - if (sourceType.tag == TypeTags.ANY && targetType.tag == TypeTags.READONLY) { - return true; - } - - boolean validTypeCast = false; - - // Use instanceof to check for anydata and json. - if (sourceType instanceof BUnionType) { - if (getTypeForUnionTypeMembersAssignableToType((BUnionType) sourceType, targetType, env, - intersectionContext, new LinkedHashSet<>()) - != symTable.semanticError) { - // string|typedesc v1 = "hello world"; - // json|table v2 = > v1; - validTypeCast = true; - } - } - - // Use instanceof to check for anydata and json. - if (targetType instanceof BUnionType) { - if (getTypeForUnionTypeMembersAssignableToType((BUnionType) targetType, sourceType, env, - intersectionContext, new LinkedHashSet<>()) - != symTable.semanticError) { - // string|int v1 = "hello world"; - // string|boolean v2 = v1; - validTypeCast = true; - } - } - - if (sourceType.tag == TypeTags.FINITE) { - if (getTypeForFiniteTypeValuesAssignableToType((BFiniteType) sourceType, targetType) - != symTable.semanticError) { - validTypeCast = true; - } - } - - if (targetType.tag == TypeTags.FINITE) { - if (getTypeForFiniteTypeValuesAssignableToType((BFiniteType) targetType, sourceType) - != symTable.semanticError) { - validTypeCast = true; - } - } - - if (validTypeCast) { - if (isValueType(sourceType)) { - setImplicitCastExpr(expr, sourceType, symTable.anyType); - } - return true; - } - - return false; - } - - boolean isNumericConversionPossible(BLangExpression expr, BType sourceType, - BType targetType) { - - final boolean isSourceNumericType = isBasicNumericType(sourceType); - final boolean isTargetNumericType = isBasicNumericType(targetType); - if (isSourceNumericType && isTargetNumericType) { - // We only reach here for different numeric types. - // 2019R3 Spec defines numeric conversion between each type. - return true; - } - if (targetType.tag == TypeTags.UNION) { - HashSet typeTags = new HashSet<>(); - for (BType bType : ((BUnionType) targetType).getMemberTypes()) { - if (isBasicNumericType(bType)) { - typeTags.add(getImpliedType(bType).tag); - if (typeTags.size() > 1) { - // Multiple Basic numeric types found in the union. - return false; - } + case TypeTags.STREAM: + BStreamType streamType = (BStreamType) collectionType; + if (streamType.constraint.tag == TypeTags.NONE) { + varType = symTable.anydataType; + break; } - } - } - - if (!isTargetNumericType && targetType.tag != TypeTags.UNION) { - return false; - } - - // Target type has at least one numeric type member. - - if (isSourceNumericType) { - // i.e., a conversion from a numeric type to another numeric type in a union. - // int|string u1 = 1.0; - // TODO : Fix me. This doesn't belong here. - setImplicitCastExpr(expr, sourceType, symTable.anyType); - return true; - } - - // TODO : Do we need this? This doesn't belong here. - switch (sourceType.tag) { - case TypeTags.ANY: - case TypeTags.ANYDATA: - case TypeTags.JSON: - // This - return true; - case TypeTags.UNION: - for (BType memType : ((BUnionType) sourceType).getMemberTypes()) { - BType referredType = getImpliedType(memType); - if (isBasicNumericType(referredType) || - (referredType.tag == TypeTags.FINITE && - finiteTypeContainsNumericTypeValues((BFiniteType) referredType))) { - return true; - } + varType = streamType.constraint; + List completionType = getAllTypes(streamType.completionType, true); + if (completionType.stream().anyMatch(type -> getImpliedType(type).tag != TypeTags.NIL)) { + BType actualType = BUnionType.create(typeEnv(), null, varType, streamType.completionType); + dlog.error(foreachNode.collection.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, + varType, actualType); } break; - case TypeTags.FINITE: - if (finiteTypeContainsNumericTypeValues((BFiniteType) sourceType)) { - return true; + case TypeTags.OBJECT: + // check for iterable objects + BUnionType nextMethodReturnType = getVarTypeFromIterableObject((BObjectType) collectionType); + if (nextMethodReturnType != null) { + foreachNode.resultType = getRecordType(nextMethodReturnType); + BType valueType = (foreachNode.resultType != null) + ? ((BRecordType) foreachNode.resultType).fields.get("value").type : null; + BType errorType = getErrorType(nextMethodReturnType); + if (errorType != null) { + BType actualType = BUnionType.create(typeEnv(), null, valueType, errorType); + dlog.error(foreachNode.collection.pos, + DiagnosticErrorCode.INVALID_ITERABLE_COMPLETION_TYPE_IN_FOREACH_NEXT_FUNCTION, + actualType, errorType); + } + foreachNode.nillableResultType = nextMethodReturnType; + foreachNode.varType = valueType; + return; } - break; + // fallthrough + case TypeTags.SEMANTIC_ERROR: + foreachNode.varType = symTable.semanticError; + foreachNode.resultType = symTable.semanticError; + foreachNode.nillableResultType = symTable.semanticError; + return; + default: + foreachNode.varType = symTable.semanticError; + foreachNode.resultType = symTable.semanticError; + foreachNode.nillableResultType = symTable.semanticError; + dlog.error(foreachNode.collection.pos, DiagnosticErrorCode.ITERABLE_NOT_SUPPORTED_COLLECTION, + collectionType); + return; } - return false; - } - public boolean isAllErrorMembers(BUnionType actualType) { - return actualType.getMemberTypes().stream().allMatch(t -> isAssignable(t, symTable.errorType)); + BInvokableSymbol iteratorSymbol = (BInvokableSymbol) symResolver.lookupLangLibMethod(collectionType, + Names.fromString(BLangCompilerConstants.ITERABLE_COLLECTION_ITERATOR_FUNC), env); + BObjectType objectType = (BObjectType) getImpliedType(iteratorSymbol.retType); + BUnionType nextMethodReturnType = + (BUnionType) getResultTypeOfNextInvocation(objectType); + foreachNode.varType = varType; + foreachNode.resultType = getRecordType(nextMethodReturnType); + foreachNode.nillableResultType = nextMethodReturnType; } - public void setImplicitCastExpr(BLangExpression expr, BType actualType, BType targetType) { - BType expType = getImpliedType(targetType); - if (!isImplicitlyCastable(actualType, expType)) { + public void setInputClauseTypedBindingPatternType(BLangInputClause bLangInputClause) { + if (bLangInputClause.collection == null) { + //not-possible return; } - BLangTypeConversionExpr implicitConversionExpr = - (BLangTypeConversionExpr) TreeBuilder.createTypeConversionNode(); - implicitConversionExpr.pos = expr.pos; - implicitConversionExpr.expr = expr.impConversionExpr == null ? expr : expr.impConversionExpr; - implicitConversionExpr.setBType(expType); - implicitConversionExpr.targetType = expType; - implicitConversionExpr.internal = true; - expr.impConversionExpr = implicitConversionExpr; - } - public boolean checkListenerCompatibilityAtServiceDecl(BType type) { - if (getImpliedType(type).tag == TypeTags.UNION) { - // There should be at least one listener compatible type and all the member types, except error type - // should be listener compatible. - int listenerCompatibleTypeCount = 0; - for (BType memberType : getAllTypes(type, true)) { - if (memberType.tag != TypeTags.ERROR) { - if (!checkListenerCompatibility(memberType)) { - return false; - } - listenerCompatibleTypeCount++; - } - } - return listenerCompatibleTypeCount > 0; + BType collectionType = bLangInputClause.collection.getBType(); + bLangInputClause.varType = visitCollectionType(bLangInputClause, collectionType); + if (bLangInputClause.varType.tag == TypeTags.SEMANTIC_ERROR || + getImpliedType(collectionType).tag == OBJECT) { + return; } - return checkListenerCompatibility(type); + + BInvokableSymbol iteratorSymbol = (BInvokableSymbol) symResolver.lookupLangLibMethod(collectionType, + Names.fromString(BLangCompilerConstants.ITERABLE_COLLECTION_ITERATOR_FUNC), env); + BUnionType nextMethodReturnType = + (BUnionType) getResultTypeOfNextInvocation((BObjectType) getImpliedType(iteratorSymbol.retType)); + bLangInputClause.resultType = getRecordType(nextMethodReturnType); + bLangInputClause.nillableResultType = nextMethodReturnType; } - public boolean checkListenerCompatibility(BType bType) { - BType type = getImpliedType(bType); - if (type.tag == TypeTags.UNION) { - BUnionType unionType = (BUnionType) type; - for (BType memberType : unionType.getMemberTypes()) { - if (!checkListenerCompatibility(memberType)) { - return false; - } - } - return true; + private BType getTypedBindingPatternTypeForXmlCollection(BType collectionType) { + BType constraint = getImpliedType(((BXMLType) collectionType).constraint); + while (constraint.tag == TypeTags.XML) { + collectionType = constraint; + constraint = getImpliedType(((BXMLType) collectionType).constraint); } - if (type.tag != TypeTags.OBJECT) { - return false; + switch (constraint.tag) { + case TypeTags.XML_ELEMENT: + case TypeTags.XML_COMMENT: + case TypeTags.XML_TEXT: + case TypeTags.XML_PI: + case TypeTags.NEVER: + return constraint; + case TypeTags.UNION: + Set collectionTypes = getEffectiveMemberTypes((BUnionType) constraint); + Set builtinXMLConstraintTypes = getEffectiveMemberTypes + ((BUnionType) ((BXMLType) symTable.xmlType).constraint); + return collectionTypes.size() == 4 && builtinXMLConstraintTypes.equals(collectionTypes) ? + collectionType : + BUnionType.create(typeEnv(), null, (LinkedHashSet) collectionTypes); + default: + return null; } - - BObjectType rhsType = (BObjectType) type; - List rhsFuncs = ((BStructureTypeSymbol) rhsType.tsymbol).attachedFuncs; - - ListenerValidationModel listenerValidationModel = new ListenerValidationModel(this, symTable); - return listenerValidationModel.checkMethods(rhsFuncs); - - } - - public boolean isValidErrorDetailType(BType detailType) { - return switch (getImpliedType(detailType).tag) { - case TypeTags.MAP, TypeTags.RECORD -> isAssignable(detailType, symTable.detailType); - default -> false; - }; - } - - // private methods - - private boolean isSealedRecord(BType recordType) { - return recordType.getKind() == TypeKind.RECORD && ((BRecordType) recordType).sealed; - } - - private boolean isNullable(BType fieldType) { - return fieldType.isNullable(); } - private class BSameTypeVisitor implements BTypeVisitor { - - Set unresolvedTypes; - - TypeEqualityPredicate equality; - - BSameTypeVisitor(Set unresolvedTypes, TypeEqualityPredicate equality) { - this.unresolvedTypes = unresolvedTypes; - this.equality = equality; - } - - @Override - public Boolean visit(BType target, BType source) { - BType t = getImpliedType(target); - BType s = getImpliedType(source); - if (t == s) { - return true; - } - - return switch (t.tag) { - case TypeTags.INT, - TypeTags.BYTE, - TypeTags.FLOAT, - TypeTags.DECIMAL, - TypeTags.STRING, - TypeTags.BOOLEAN -> t.tag == s.tag && - ((TypeParamAnalyzer.isTypeParam(t) || TypeParamAnalyzer.isTypeParam(s)) || - (t.tag == TypeTags.TYPEREFDESC || s.tag == TypeTags.TYPEREFDESC)); - case TypeTags.ANY, - TypeTags.ANYDATA -> t.tag == s.tag && hasSameReadonlyFlag(s, t) && - (TypeParamAnalyzer.isTypeParam(t) || TypeParamAnalyzer.isTypeParam(s)); - default -> false; - }; - } - - @Override - public Boolean visit(BBuiltInRefType t, BType s) { - return t == s; - } - - @Override - public Boolean visit(BAnyType t, BType s) { - return t == s; - } - - @Override - public Boolean visit(BAnydataType t, BType s) { - return t == s || t.tag == s.tag; - } - - @Override - public Boolean visit(BMapType t, BType s) { - return s.tag == TypeTags.MAP && hasSameReadonlyFlag(s, t) && - equality.test(((BMapType) s).constraint, t.constraint, this.unresolvedTypes); - } - - @Override - public Boolean visit(BFutureType t, BType s) { - return s.tag == TypeTags.FUTURE && - equality.test(((BFutureType) s).constraint, t.constraint, this.unresolvedTypes); - } - - @Override - public Boolean visit(BXMLType t, BType s) { - return visit((BBuiltInRefType) t, s); - } - - @Override - public Boolean visit(BJSONType t, BType s) { - return s.tag == TypeTags.JSON && hasSameReadonlyFlag(s, t); - } - - @Override - public Boolean visit(BArrayType t, BType s) { - return s.tag == TypeTags.ARRAY && hasSameReadonlyFlag(s, t) && isSameArrayType(s, t, this.unresolvedTypes); - } - - @Override - public Boolean visit(BObjectType t, BType s) { - return t == s || (s.tag == TypeTags.OBJECT && t.tsymbol.pkgID.equals(s.tsymbol.pkgID) && - t.tsymbol.name.equals(s.tsymbol.name)); - } - - @Override - public Boolean visit(BRecordType t, BType s) { - if (t == s) { - return true; - } - - if (s.tag != TypeTags.RECORD || !hasSameReadonlyFlag(s, t)) { - return false; - } - - BRecordType source = (BRecordType) s; - LinkedHashMap sFields = source.fields; - LinkedHashMap tFields = t.fields; - - if (sFields.size() != tFields.size()) { - return false; - } - - for (BField sourceField : sFields.values()) { - if (tFields.containsKey(sourceField.name.value)) { - BField targetField = tFields.get(sourceField.name.value); - if ((!Symbols.isFlagOn(targetField.symbol.flags, Flags.READONLY) || - Symbols.isFlagOn(sourceField.symbol.flags, Flags.READONLY)) && - hasSameOptionalFlag(sourceField.symbol, targetField.symbol) && - equality.test(sourceField.type, targetField.type, new HashSet<>(this.unresolvedTypes))) { - continue; - } - } - return false; - } - return equality.test(source.restFieldType, t.restFieldType, new HashSet<>(this.unresolvedTypes)); - } - - private boolean hasSameOptionalFlag(BVarSymbol s, BVarSymbol t) { - return ((s.flags & Flags.OPTIONAL) ^ (t.flags & Flags.OPTIONAL)) != Flags.OPTIONAL; - } - - boolean hasSameReadonlyFlag(BType source, BType target) { - return Symbols.isFlagOn(target.flags, Flags.READONLY) == Symbols.isFlagOn(source.flags, Flags.READONLY); - } - - @Override - public Boolean visit(BTupleType t, BType s) { - List tTupleTypes = t.getTupleTypes(); - if (((!tTupleTypes.isEmpty() && checkAllTupleMembersBelongNoType(tTupleTypes)) || - (t.restType != null && t.restType.tag == TypeTags.NONE)) && - !(s.tag == TypeTags.ARRAY && ((BArrayType) s).state == BArrayState.OPEN)) { - return true; - } - - if (s.tag != TypeTags.TUPLE || !hasSameReadonlyFlag(s, t)) { - return false; - } - - BTupleType source = (BTupleType) s; - List sTupleTypes = source.getTupleTypes(); - if (sTupleTypes.size() != tTupleTypes.size()) { - return false; - } - - BType sourceRestType = source.restType; - BType targetRestType = t.restType; - if ((sourceRestType == null || targetRestType == null) && sourceRestType != targetRestType) { - return false; - } - - for (int i = 0; i < sTupleTypes.size(); i++) { - if (tTupleTypes.get(i) == symTable.noType) { - continue; - } - if (!equality.test(sTupleTypes.get(i), tTupleTypes.get(i), - new HashSet<>(this.unresolvedTypes))) { - return false; + private BType visitCollectionType(BLangInputClause bLangInputClause, BType collectionType) { + collectionType = getImpliedType(collectionType); + switch (collectionType.tag) { + case TypeTags.STRING: + return symTable.stringType; + case TypeTags.ARRAY: + BArrayType arrayType = (BArrayType) collectionType; + return arrayType.eType; + case TypeTags.TUPLE: + return getTupleMemberType((BTupleType) collectionType); + case TypeTags.MAP: + BMapType bMapType = (BMapType) collectionType; + return bMapType.constraint; + case TypeTags.RECORD: + BRecordType recordType = (BRecordType) collectionType; + return inferRecordFieldType(recordType); + case TypeTags.XML: + BType bindingPatternType = getTypedBindingPatternTypeForXmlCollection(collectionType); + return bindingPatternType == null ? symTable.semanticError : bindingPatternType; + case TypeTags.XML_TEXT: + return symTable.xmlTextType; + case TypeTags.TABLE: + BTableType tableType = (BTableType) collectionType; + return tableType.constraint; + case TypeTags.STREAM: + BStreamType streamType = (BStreamType) collectionType; + if (streamType.constraint.tag == TypeTags.NONE) { + return symTable.anydataType; } - } - - if (sourceRestType == null || targetRestType == symTable.noType) { - return true; - } - - return equality.test(sourceRestType, targetRestType, new HashSet<>(this.unresolvedTypes)); - } - - @Override - public Boolean visit(BStreamType t, BType s) { - if (s.tag != TypeTags.STREAM) { - return false; - } - - BStreamType source = (BStreamType) s; - return equality.test(source.constraint, t.constraint, unresolvedTypes) - && equality.test(source.completionType, t.completionType, unresolvedTypes); - } - - @Override - public Boolean visit(BTableType t, BType s) { - return t == s; - } - - @Override - public Boolean visit(BInvokableType t, BType s) { - return s.tag == TypeTags.INVOKABLE && - checkFunctionTypeEquality((BInvokableType) s, t, this.unresolvedTypes, equality); + return streamType.constraint; + case TypeTags.OBJECT: + // check for iterable objects + if (!isAssignable(collectionType, symTable.iterableType)) { + dlog.error(bLangInputClause.collection.pos, DiagnosticErrorCode.INVALID_ITERABLE_OBJECT_TYPE, + bLangInputClause.collection.getBType(), symTable.iterableType); + bLangInputClause.varType = symTable.semanticError; + bLangInputClause.resultType = symTable.semanticError; + bLangInputClause.nillableResultType = symTable.semanticError; + break; + } + + BUnionType nextMethodReturnType = getVarTypeFromIterableObject((BObjectType) collectionType); + if (nextMethodReturnType != null) { + bLangInputClause.resultType = getRecordType(nextMethodReturnType); + bLangInputClause.nillableResultType = nextMethodReturnType; + bLangInputClause.varType = ((BRecordType) bLangInputClause.resultType).fields.get("value").type; + return bLangInputClause.varType; + } + // fallthrough + case TypeTags.SEMANTIC_ERROR: + bLangInputClause.varType = symTable.semanticError; + bLangInputClause.resultType = symTable.semanticError; + bLangInputClause.nillableResultType = symTable.semanticError; + break; + default: + bLangInputClause.varType = symTable.semanticError; + bLangInputClause.resultType = symTable.semanticError; + bLangInputClause.nillableResultType = symTable.semanticError; + dlog.error(bLangInputClause.collection.pos, DiagnosticErrorCode.ITERABLE_NOT_SUPPORTED_COLLECTION, + collectionType); } + return symTable.semanticError; + } - @Override - public Boolean visit(BUnionType tUnionType, BType s) { - if (s.tag != TypeTags.UNION || !hasSameReadonlyFlag(s, tUnionType)) { - return false; - } - - BUnionType sUnionType = (BUnionType) s; - - if (sUnionType.getMemberTypes().size() - != tUnionType.getMemberTypes().size()) { - return false; - } - - Set sourceTypes = new LinkedHashSet<>(sUnionType.getMemberTypes().size()); - Set targetTypes = new LinkedHashSet<>(tUnionType.getMemberTypes().size()); - - if (sUnionType.isCyclic) { - sourceTypes.add(sUnionType); - } - if (tUnionType.isCyclic) { - targetTypes.add(tUnionType); - } - - sourceTypes.addAll(sUnionType.getMemberTypes()); - targetTypes.addAll(tUnionType.getMemberTypes()); - - boolean notSameType = sourceTypes - .stream() - .map(sT -> targetTypes - .stream() - .anyMatch(it -> equality.test(sT, it, new HashSet<>(this.unresolvedTypes)))) - .anyMatch(foundSameType -> !foundSameType); - return !notSameType; + private BType getTupleMemberType(BTupleType tupleType) { + LinkedHashSet tupleTypes = new LinkedHashSet<>(tupleType.getTupleTypes()); + if (tupleType.restType != null) { + tupleTypes.add(tupleType.restType); } - - @Override - public Boolean visit(BIntersectionType tIntersectionType, BType s) { - if (s.tag != TypeTags.INTERSECTION) { - return false; - } - return visit(tIntersectionType.effectiveType, ((BIntersectionType) s).effectiveType); + int tupleTypesSize = tupleTypes.size(); + if (tupleTypesSize == 0) { + return symTable.neverType; } + return tupleTypesSize == 1 ? + tupleTypes.iterator().next() : BUnionType.create(typeEnv(), null, tupleTypes); + } - @Override - public Boolean visit(BErrorType t, BType s) { - if (s.tag != TypeTags.ERROR) { - return false; - } - BErrorType source = (BErrorType) s; - - if (!source.typeIdSet.equals(t.typeIdSet)) { - return false; - } - - if (source.detailType == t.detailType) { - return true; + public BUnionType getVarTypeFromIterableObject(BObjectType collectionType) { + BObjectTypeSymbol objectTypeSymbol = (BObjectTypeSymbol) collectionType.tsymbol; + for (BAttachedFunction func : objectTypeSymbol.attachedFuncs) { + if (func.funcName.value.equals(BLangCompilerConstants.ITERABLE_COLLECTION_ITERATOR_FUNC)) { + return getVarTypeFromIteratorFunc(func); } - - return equality.test(source.detailType, t.detailType, this.unresolvedTypes); } - @Override - public Boolean visit(BTypedescType t, BType s) { - if (s.tag != TypeTags.TYPEDESC) { - return false; - } - BTypedescType sType = ((BTypedescType) s); - return equality.test(sType.constraint, t.constraint, this.unresolvedTypes); - } + return null; + } - @Override - public Boolean visit(BFiniteType t, BType s) { - return s == t; + private BUnionType getVarTypeFromIteratorFunc(BAttachedFunction candidateIteratorFunc) { + if (!candidateIteratorFunc.type.paramTypes.isEmpty()) { + return null; } - @Override - public Boolean visit(BParameterizedType t, BType s) { - if (s.tag != TypeTags.PARAMETERIZED_TYPE) { - return false; - } + BType returnType = candidateIteratorFunc.type.retType; + // abstract object {public function next() returns record {|int value;|}?;} + return getVarTypeFromIteratorFuncReturnType(returnType); + } - BParameterizedType sType = (BParameterizedType) s; - return sType.paramSymbol.equals(t.paramSymbol) && - equality.test(sType.paramValueType, t.paramValueType, new HashSet<>()); + public BUnionType getVarTypeFromIteratorFuncReturnType(BType type) { + BObjectTypeSymbol objectTypeSymbol; + BType returnType = getImpliedType(type); + if (returnType.tag != TypeTags.OBJECT) { + return null; } - public Boolean visit(BTypeReferenceType t, BType s) { - BType constraint = s; - if (s.tag == TypeTags.TYPEREFDESC) { - constraint = getImpliedType(((BTypeReferenceType) s).referredType); + objectTypeSymbol = (BObjectTypeSymbol) returnType.tsymbol; + for (BAttachedFunction func : objectTypeSymbol.attachedFuncs) { + if (func.funcName.value.equals(BLangCompilerConstants.NEXT_FUNC)) { + return getVarTypeFromNextFunc(func); } - BType target = getImpliedType(t.referredType); - return equality.test(target, constraint, new HashSet<>()); } - } - @Deprecated - public boolean isSameBIRShape(BType source, BType target) { - return isSameBIRShape(source, target, new HashSet<>()); + return null; } - private boolean isSameBIRShape(BType source, BType target, Set unresolvedTypes) { - // If we encounter two types that we are still resolving, then skip it. - // This is done to avoid recursive checking of the same type. - TypePair pair = new TypePair(source, target); - if (!unresolvedTypes.add(pair)) { - return true; + private BUnionType getVarTypeFromNextFunc(BAttachedFunction nextFunc) { + BType returnType; + if (!nextFunc.type.paramTypes.isEmpty()) { + return null; } - BIRSameShapeVisitor birSameShapeVisitor = new BIRSameShapeVisitor(unresolvedTypes, this::isSameBIRShape); - - if (target.accept(birSameShapeVisitor, source)) { - return true; + returnType = nextFunc.type.retType; + // Check if the next function return type has the union type, + // record {|int value;|}|error|(); + if (checkNextFuncReturnType(returnType)) { + return (BUnionType) returnType; } - unresolvedTypes.remove(pair); - return false; + return null; } - @Deprecated - private class BIRSameShapeVisitor extends BSameTypeVisitor { - - BIRSameShapeVisitor(Set unresolvedTypes, TypeEqualityPredicate equality) { - super(unresolvedTypes, equality); + private boolean checkNextFuncReturnType(BType returnType) { + if (getImpliedType(returnType).tag != TypeTags.UNION) { + return false; } - @Override - public Boolean visit(BType target, BType source) { - if (source.tag == TypeTags.TYPEREFDESC || target.tag == TypeTags.TYPEREFDESC) { - if (source.tag != target.tag) { - return false; - } - - BTypeReferenceType sourceRefType = (BTypeReferenceType) source; - BTypeReferenceType targetRefType = (BTypeReferenceType) target; - - BTypeSymbol sourceTSymbol = sourceRefType.tsymbol; - BTypeSymbol targetTSymbol = targetRefType.tsymbol; - String sourcePkgId = CompilerUtils.getPackageIDStringWithMajorVersion(sourceTSymbol.pkgID); - String targetPkgId = CompilerUtils.getPackageIDStringWithMajorVersion(targetTSymbol.pkgID); - return sourcePkgId.equals(targetPkgId) && sourceTSymbol.name.equals(targetTSymbol.name); - } - - BType t = getImpliedType(target); - BType s = getImpliedType(source); - if (t == s) { - return true; - } - return switch (t.tag) { - case TypeTags.INT, - TypeTags.BYTE, - TypeTags.FLOAT, - TypeTags.DECIMAL, - TypeTags.STRING, - TypeTags.BOOLEAN -> t.tag == s.tag && - ((TypeParamAnalyzer.isTypeParam(t) || TypeParamAnalyzer.isTypeParam(s)) || - (t.tag == TypeTags.TYPEREFDESC || s.tag == TypeTags.TYPEREFDESC)); - case TypeTags.ANY, - TypeTags.ANYDATA -> t.tag == s.tag && hasSameReadonlyFlag(s, t) && - (TypeParamAnalyzer.isTypeParam(t) || TypeParamAnalyzer.isTypeParam(s)); - default -> false; - }; - - } - - @Override - public Boolean visit(BFiniteType t, BType s) { - s = getImpliedType(s); - if (s.tag != TypeTags.FINITE) { - return false; - } - - Set sourceValueSpace = ((BFiniteType) s).getValueSpace(); - Set targetValueSpace = t.getValueSpace(); - - if (sourceValueSpace.size() != targetValueSpace.size()) { - return false; - } - - return hasSameMembers(sourceValueSpace, targetValueSpace); + List types = getAllTypes(returnType, true); + boolean containsCompletionType = types.removeIf(type -> type.tag == TypeTags.NIL); + containsCompletionType = types.removeIf(type -> type.tag == TypeTags.ERROR) || containsCompletionType; + if (!containsCompletionType) { + return false; } - @Override - public Boolean visit(BTypeReferenceType t, BType s) { - s = getImpliedType(s); - if (s.tag != TypeTags.TYPEREFDESC) { - return false; - } - - BTypeReferenceType sTypeRefType = (BTypeReferenceType) s; - - BTypeSymbol sourceTSymbol = sTypeRefType.tsymbol; - BTypeSymbol targetTSymbol = t.tsymbol; - String sourcePkgId = CompilerUtils.getPackageIDStringWithMajorVersion(sourceTSymbol.pkgID); - String targetPkgId = CompilerUtils.getPackageIDStringWithMajorVersion(targetTSymbol.pkgID); - return sourcePkgId.equals(targetPkgId) && sourceTSymbol.name.equals(targetTSymbol.name); + if (types.size() != 1) { + //TODO: print error + return false; } - } - - private boolean hasSameMembers(Set sourceValueSpace, Set targetValueSpace) { - Set setOne = new HashSet<>(sourceValueSpace); - Set setTwo = new HashSet<>(targetValueSpace); - - Iterator setOneIterator = setOne.iterator(); - Iterator setTwoIterator = setTwo.iterator(); - - while (setOneIterator.hasNext()) { - BLangLiteral setOneMem = (BLangLiteral) setOneIterator.next(); - - if (!setTwoIterator.hasNext()) { - return false; - } - - boolean hasEqualValue = false; - while (setTwoIterator.hasNext()) { - BLangLiteral setTwoMem = (BLangLiteral) setTwoIterator.next(); - if (setOneMem.value.equals(setTwoMem.value) && setOneMem.getBType() == setTwoMem.getBType()) { - hasEqualValue = true; - setOneIterator.remove(); - setTwoIterator.remove(); - break; - } - } - if (!hasEqualValue) { - return false; - } + if (types.get(0).tag != TypeTags.RECORD) { + return false; } - return !setTwoIterator.hasNext(); + BRecordType recordType = (BRecordType) types.get(0); + // Check if the union type has the record type, + // record {|int value;|}; + return checkRecordTypeInNextFuncReturnType(recordType); } - private class BOrderedTypeVisitor implements BTypeVisitor { - - Set unresolvedTypes; - - BOrderedTypeVisitor(Set unresolvedTypes) { - this.unresolvedTypes = unresolvedTypes; - } - - @Override - public Boolean visit(BType target, BType source) { - BType sourceType = getImpliedType(source); - BType targetType = getImpliedType(target); - int sourceTag = sourceType.tag; - int targetTag = targetType.tag; - if (isSimpleBasicType(sourceTag) && isSimpleBasicType(targetTag)) { - // If type T is ordered, then type T? Is also ordered. - return (source == target) || sourceTag == TypeTags.NIL || targetTag == TypeTags.NIL || - isIntOrStringType(sourceTag, targetTag); - } - if (sourceTag == TypeTags.FINITE) { - return checkValueSpaceHasSameType(((BFiniteType) source), target); - } - return isSameOrderedType(target, source, this.unresolvedTypes); - } - - @Override - public Boolean visit(BArrayType target, BType source) { - source = getImpliedType(source); - if (source.tag != TypeTags.ARRAY) { - if (source.tag == TypeTags.TUPLE || source.tag == TypeTags.UNION) { - return isSameOrderedType(target, source); - } - return false; - } - return isSameOrderedType(target.eType, ((BArrayType) source).eType, unresolvedTypes); - } - - @Override - public Boolean visit(BTupleType target, BType source) { - source = getImpliedType(source); - if (source.tag == TypeTags.UNION) { - return isSameOrderedType(target, source); - } - if (source.tag != TypeTags.TUPLE && source.tag != TypeTags.ARRAY) { - return false; - } - List targetTupleTypes = target.getTupleTypes(); - BType targetRestType = target.restType; - - if (source.tag == TypeTags.ARRAY) { - // Check whether the element type of the source array has same ordered type with each member type in - // target tuple type. - BType eType = ((BArrayType) source).eType; - for (BType memberType : targetTupleTypes) { - if (!isSameOrderedType(eType, memberType, this.unresolvedTypes)) { - return false; - } - } - if (targetRestType == null) { - return true; - } - return isSameOrderedType(targetRestType, eType, this.unresolvedTypes); - } - - BTupleType sourceT = (BTupleType) source; - List sourceTupleTypes = sourceT.getTupleTypes(); - - BType sourceRestType = sourceT.restType; - - int sourceTupleCount = sourceTupleTypes.size(); - int targetTupleCount = targetTupleTypes.size(); - - int len = Math.min(sourceTupleCount, targetTupleCount); - for (int i = 0; i < len; i++) { - // Check whether the corresponding member types are same ordered type. - if (!isSameOrderedType(sourceTupleTypes.get(i), targetTupleTypes.get(i), - this.unresolvedTypes)) { - return false; - } - } - - if (sourceTupleCount == targetTupleCount) { - if (sourceRestType == null || targetRestType == null) { - return true; - } - return isSameOrderedType(sourceRestType, targetRestType, this.unresolvedTypes); - } else if (sourceTupleCount > targetTupleCount) { - // Source tuple has higher number of member types. - // Check whether the excess member types can be narrowed to an ordered rest type in source tuple. - // Ex. source tuple -> [string, (), float, int, byte] - // target tuple -> [string, (), float] - // here, source tuple can be represented as [string, (), float, int...] - // since [string, (), float] & [string, (), float, int...] are individually order types and - // [string, (), float, int...] can be taken as the ordered type which the static type of both - // operands belong. - if (!hasCommonOrderedTypeForTuples(sourceTupleTypes, targetTupleCount + 1)) { - return false; - } - return checkSameOrderedTypeInTuples(sourceT, sourceTupleCount, targetTupleCount, sourceRestType, - targetRestType); - } else { - // Target tuple has higher number of member types. - if (!hasCommonOrderedTypeForTuples(targetTupleTypes, sourceTupleCount + 1)) { - return false; - } - return checkSameOrderedTypeInTuples(target, targetTupleCount, sourceTupleCount, targetRestType, - sourceRestType); - } - } - - private boolean hasCommonOrderedTypeForTuples(List typeList, int startIndex) { - BType baseType = typeList.get(startIndex - 1); - for (int i = startIndex; i < typeList.size(); i++) { - if (isNil(baseType)) { - baseType = typeList.get(i); - continue; - } - if (!isSameOrderedType(baseType, typeList.get(i), this.unresolvedTypes)) { - return false; - } - } - return true; + private boolean checkRecordTypeInNextFuncReturnType(BRecordType recordType) { + if (!recordType.sealed) { + return false; } - private boolean checkSameOrderedTypeInTuples(BTupleType source, int sourceTupleCount, - int targetTupleCount, - BType sourceRestType, BType targetRestType) { - if (targetRestType == null) { - return true; - } - for (int i = targetTupleCount; i < sourceTupleCount; i++) { - if (!isSameOrderedType(source.getTupleTypes().get(i), targetRestType, this.unresolvedTypes)) { - return false; - } - } - if (sourceRestType == null) { - return true; - } - return isSameOrderedType(sourceRestType, targetRestType, this.unresolvedTypes); + if (recordType.fields.size() != 1) { + return false; } - @Override - public Boolean visit(BUnionType target, BType source) { - source = getImpliedType(source); - if (source.tag != TypeTags.UNION || !hasSameReadonlyFlag(source, target)) { - return checkUnionHasSameType(target, source); - } - - BUnionType sUnionType = (BUnionType) source; - LinkedHashSet sourceTypes = sUnionType.getMemberTypes(); - LinkedHashSet targetTypes = target.getMemberTypes(); + return recordType.fields.containsKey(BLangCompilerConstants.VALUE_FIELD); + } - if (checkUnionHasAllFiniteOrNilMembers(sourceTypes) && - checkUnionHasAllFiniteOrNilMembers(targetTypes)) { - BType type = target.getMemberTypes().iterator().next(); - return checkValueSpaceHasSameType(((BFiniteType) getImpliedType(type)), - sUnionType.getMemberTypes().iterator().next()); + private BRecordType getRecordType(BUnionType type) { + for (BType member : type.getMemberTypes()) { + BType referredRecordType = getImpliedType(member); + if (referredRecordType.tag == TypeTags.RECORD) { + return (BRecordType) referredRecordType; } - - return checkSameOrderedTypesInUnionMembers(sourceTypes, targetTypes); } + return null; + } - private boolean checkSameOrderedTypesInUnionMembers(LinkedHashSet sourceTypes, - LinkedHashSet targetTypes) { + public BErrorType getErrorType(BUnionType type) { + for (BType member : type.getMemberTypes()) { + member = getImpliedType(member); - for (BType sourceT : sourceTypes) { - boolean foundSameOrderedType = false; - if (isNil(sourceT)) { - continue; - } - for (BType targetT : targetTypes) { - if (isNil(targetT)) { - foundSameOrderedType = true; - continue; - } - if (isSameOrderedType(targetT, sourceT, this.unresolvedTypes)) { - foundSameOrderedType = true; - } else { - return false; - } - } - if (!foundSameOrderedType) { - return false; + if (member.tag == TypeTags.ERROR) { + return (BErrorType) member; + } else if (member.tag == TypeTags.UNION) { + BErrorType e = getErrorType((BUnionType) member); + if (e != null) { + return e; } } - return true; - } - - @Override - public Boolean visit(BFiniteType t, BType s) { - return checkValueSpaceHasSameType(t, s); - } - - private boolean hasSameReadonlyFlag(BType source, BType target) { - return Symbols.isFlagOn(target.flags, Flags.READONLY) == Symbols.isFlagOn(source.flags, Flags.READONLY); - } - - @Override - public Boolean visit(BBuiltInRefType t, BType s) { - return false; - } - - @Override - public Boolean visit(BAnyType t, BType s) { - return false; - } - - @Override - public Boolean visit(BAnydataType t, BType s) { - return false; } + return null; + } - @Override - public Boolean visit(BMapType t, BType s) { - return false; - } + public BType getResultTypeOfNextInvocation(BObjectType iteratorType) { + BAttachedFunction nextFunc = getAttachedFuncFromObject(iteratorType, BLangCompilerConstants.NEXT_FUNC); + return Objects.requireNonNull(nextFunc).type.retType; + } - @Override - public Boolean visit(BFutureType t, BType s) { - return false; + public BAttachedFunction getAttachedFuncFromObject(BObjectType objectType, String funcName) { + BObjectTypeSymbol iteratorSymbol = (BObjectTypeSymbol) objectType.tsymbol; + for (BAttachedFunction bAttachedFunction : iteratorSymbol.attachedFuncs) { + if (funcName.equals(bAttachedFunction.funcName.value)) { + return bAttachedFunction; + } } + return null; + } - @Override - public Boolean visit(BXMLType t, BType s) { - return false; - } + public BType inferRecordFieldType(BRecordType recordType) { + Map fields = recordType.fields; + BUnionType unionType = BUnionType.create(typeEnv(), null); - @Override - public Boolean visit(BJSONType t, BType s) { - return false; + if (!recordType.sealed) { + unionType.add(recordType.restFieldType); + } else if (fields.isEmpty()) { + unionType.add(symTable.neverType); } + for (BField field : fields.values()) { + if (isAssignable(field.type, unionType)) { + continue; + } - @Override - public Boolean visit(BObjectType t, BType s) { - return false; - } + if (isAssignable(unionType, field.type)) { + unionType = BUnionType.create(typeEnv(), null); + } - @Override - public Boolean visit(BRecordType t, BType s) { - return false; + unionType.add(field.type); } - @Override - public Boolean visit(BStreamType t, BType s) { - return false; + if (unionType.getMemberTypes().size() > 1) { + unionType.tsymbol = Symbols.createTypeSymbol(SymTag.UNION_TYPE, Flags.asMask(EnumSet.of(Flag.PUBLIC)), + Names.EMPTY, recordType.tsymbol.pkgID, null, + recordType.tsymbol.owner, symTable.builtinPos, VIRTUAL); + return unionType; } - @Override - public Boolean visit(BTableType t, BType s) { - return false; - } + return unionType.getMemberTypes().iterator().next(); + } - @Override - public Boolean visit(BInvokableType t, BType s) { - return false; - } - @Override - public Boolean visit(BIntersectionType tIntersectionType, BType s) { - return this.visit(getImpliedType(tIntersectionType), s); + public BType getTypeWithEffectiveIntersectionTypes(BType bType) { + // TODO Can remove this method since this unwraps the referred type and intersection type. #40958 + BType type = getReferredType(bType); + BType effectiveType = null; + if (type.tag == TypeTags.INTERSECTION) { + effectiveType = ((BIntersectionType) type).effectiveType; + type = effectiveType; } - @Override - public Boolean visit(BErrorType t, BType s) { - return false; + if (type.tag != TypeTags.UNION) { + return Objects.requireNonNullElse(effectiveType, bType); } - @Override - public Boolean visit(BTypedescType t, BType s) { - return false; - } + LinkedHashSet members = new LinkedHashSet<>(); + boolean hasDifferentMember = false; - public Boolean visit(BTypeReferenceType t, BType s) { - return this.visit(getImpliedType(t), t); + for (BType memberType : ((BUnionType) type).getMemberTypes()) { + effectiveType = getTypeWithEffectiveIntersectionTypes(memberType); + effectiveType = getImpliedType(effectiveType); + if (effectiveType != memberType) { + hasDifferentMember = true; + } + members.add(effectiveType); } - @Override - public Boolean visit(BParameterizedType t, BType s) { - return false; + if (hasDifferentMember) { + return BUnionType.create(typeEnv(), null, members); } + return bType; } - private boolean isNil(BType type) { - // Currently, type reference for `null` literal is taken as Finite type and type reference for `()` literal - // taken as Nil type. - BType referredType = getImpliedType(type); - TypeKind referredTypeKind = referredType.getKind(); - if (referredTypeKind == TypeKind.NIL) { - return true; - } else if (referredTypeKind == TypeKind.FINITE) { - Set valueSpace = ((BFiniteType) referredType).getValueSpace(); - return valueSpace.size() == 1 && valueSpace.iterator().next().getBType().getKind() == TypeKind.NIL; - } - return false; + /** + * Enum to represent type test result. + * + * @since 1.2.0 + */ + enum TypeTestResult { + NOT_FOUND, + TRUE, + FALSE } - private boolean checkUnionHasSameType(BUnionType unionType, BType baseType) { - LinkedHashSet memberTypes = unionType.getMemberTypes(); - for (BType type : memberTypes) { - type = getImpliedType(type); - if (type.tag == TypeTags.FINITE) { - for (BLangExpression expr : ((BFiniteType) type).getValueSpace()) { - if (!isSameOrderedType(expr.getBType(), baseType)) { - return false; + TypeTestResult isBuiltInTypeWidenPossible(BType actualType, BType targetType) { // TODO: can we remove? + int targetTag = getImpliedType(targetType).tag; + int actualTag = getImpliedType(actualType).tag; + + if (actualTag < TypeTags.JSON && targetTag < TypeTags.JSON) { + // Fail Fast for value types. + switch (actualTag) { + case TypeTags.INT: + case TypeTags.BYTE: + case TypeTags.FLOAT: + case TypeTags.DECIMAL: + if (targetTag == TypeTags.BOOLEAN || targetTag == TypeTags.STRING) { + return TypeTestResult.FALSE; } + break; + case TypeTags.BOOLEAN: + if (targetTag == TypeTags.INT || targetTag == TypeTags.BYTE || targetTag == TypeTags.FLOAT + || targetTag == TypeTags.DECIMAL || targetTag == TypeTags.STRING) { + return TypeTestResult.FALSE; + } + break; + case TypeTags.STRING: + if (targetTag == TypeTags.INT || targetTag == TypeTags.BYTE || targetTag == TypeTags.FLOAT + || targetTag == TypeTags.DECIMAL || targetTag == TypeTags.BOOLEAN) { + return TypeTestResult.FALSE; + } + break; + } + } + switch (actualTag) { + case TypeTags.INT: + case TypeTags.BYTE: + case TypeTags.FLOAT: + case TypeTags.DECIMAL: + case TypeTags.BOOLEAN: + case TypeTags.STRING: + case TypeTags.SIGNED32_INT: + case TypeTags.SIGNED16_INT: + case TypeTags.SIGNED8_INT: + case TypeTags.UNSIGNED32_INT: + case TypeTags.UNSIGNED16_INT: + case TypeTags.UNSIGNED8_INT: + case TypeTags.CHAR_STRING: + if (targetTag == TypeTags.JSON || targetTag == TypeTags.ANYDATA || targetTag == TypeTags.ANY || + targetTag == TypeTags.READONLY) { + return TypeTestResult.TRUE; } -// } else if (type.tag == TypeTags.UNION) { -// if (!checkUnionHasSameType((BUnionType) type, baseType)) { -// return false; -// } - } else if (type.tag == TypeTags.TUPLE || type.tag == TypeTags.ARRAY) { - if (!isSameOrderedType(type, baseType)) { - return false; - } - } else if (isSimpleBasicType(type.tag)) { - if (!isSameOrderedType(type, baseType) && !isNil(type)) { - return false; + break; + case TypeTags.ANYDATA: + case TypeTags.TYPEDESC: + if (targetTag == TypeTags.ANY) { + return TypeTestResult.TRUE; } - } + break; + default: } - return true; - } - private boolean checkValueSpaceHasSameType(BFiniteType finiteType, BType type) { - BType baseType = getImpliedType(type); - if (baseType.tag == TypeTags.FINITE) { - BType baseExprType = finiteType.getValueSpace().iterator().next().getBType(); - return checkValueSpaceHasSameType(((BFiniteType) baseType), baseExprType); + if (TypeTags.isIntegerTypeTag(targetTag) && actualTag == targetTag) { + return TypeTestResult.FALSE; // No widening. } - boolean isValueSpaceSameType = false; - for (BLangExpression expr : finiteType.getValueSpace()) { - isValueSpaceSameType = isSameOrderedType(expr.getBType(), baseType); - if (!isValueSpaceSameType) { - break; - } + + // Validate for Integers subtypes. + if ((TypeTags.isIntegerTypeTag(actualTag) || actualTag == TypeTags.BYTE) + && (TypeTags.isIntegerTypeTag(targetTag) || targetTag == TypeTags.BYTE)) { + return checkBuiltInIntSubtypeWidenPossible(actualType, targetType); } - return isValueSpaceSameType; - } - private boolean checkUnionHasAllFiniteOrNilMembers(LinkedHashSet memberTypes) { - for (BType bType : memberTypes) { - BType type = getImpliedType(bType); - if (type.tag != TypeTags.FINITE && !isNil(type)) { - return false; - } + if (actualTag == TypeTags.CHAR_STRING && TypeTags.STRING == targetTag) { + return TypeTestResult.TRUE; } - return true; + return TypeTestResult.NOT_FOUND; } - private boolean checkFieldEquivalency(BRecordType lhsType, BRecordType rhsType, Set unresolvedTypes) { - Map rhsFields = new LinkedHashMap<>(rhsType.fields); - - // Check if the RHS record has corresponding fields to those of the LHS record. - for (BField lhsField : lhsType.fields.values()) { - BField rhsField = rhsFields.get(lhsField.name.value); - - // If LHS field is required, there should be a corresponding RHS field - // If LHS field is never typed, RHS rest field type should include never type - if (rhsField == null) { - if (!Symbols.isOptional(lhsField.symbol) || isInvalidNeverField(lhsField, rhsType)) { - return false; + private TypeTestResult checkBuiltInIntSubtypeWidenPossible(BType actualType, BType targetType) { + int actualTag = getImpliedType(actualType).tag; + targetType = getImpliedType(targetType); + switch (targetType.tag) { + case TypeTags.INT: + if (actualTag == TypeTags.BYTE || TypeTags.isIntegerTypeTag(actualTag)) { + return TypeTestResult.TRUE; } - - if (!rhsType.sealed && !isAssignable(rhsType.restFieldType, lhsField.type, unresolvedTypes)) { - return false; + break; + case TypeTags.SIGNED32_INT: + if (actualTag == TypeTags.SIGNED16_INT || actualTag == TypeTags.SIGNED8_INT || + actualTag == TypeTags.UNSIGNED16_INT || actualTag == TypeTags.UNSIGNED8_INT || + actualTag == TypeTags.BYTE) { + return TypeTestResult.TRUE; } - - continue; - } - if (hasIncompatibleReadOnlyFlags(lhsField.symbol.flags, rhsField.symbol.flags)) { - return false; - } - - // If LHS field is required, so should the RHS field - if (!Symbols.isOptional(lhsField.symbol) && Symbols.isOptional(rhsField.symbol)) { - return false; - } - - // The corresponding RHS field should be assignable to the LHS field. - if (!isAssignable(rhsField.type, lhsField.type, unresolvedTypes)) { - return false; - } - - rhsFields.remove(lhsField.name.value); - } - - if (lhsType.sealed) { - for (BField field : rhsFields.values()) { - if (!isNeverTypeOrStructureTypeWithARequiredNeverMember(field.type)) { - return false; + break; + case TypeTags.SIGNED16_INT: + if (actualTag == TypeTags.SIGNED8_INT || actualTag == TypeTags.UNSIGNED8_INT || + actualTag == TypeTags.BYTE) { + return TypeTestResult.TRUE; + } + break; + case TypeTags.UNSIGNED32_INT: + if (actualTag == TypeTags.UNSIGNED16_INT || actualTag == TypeTags.UNSIGNED8_INT || + actualTag == TypeTags.BYTE) { + return TypeTestResult.TRUE; + } + break; + case TypeTags.UNSIGNED16_INT: + if (actualTag == TypeTags.UNSIGNED8_INT || actualTag == TypeTags.BYTE) { + return TypeTestResult.TRUE; + } + break; + case TypeTags.BYTE: + if (actualTag == TypeTags.UNSIGNED8_INT) { + return TypeTestResult.TRUE; + } + break; + case TypeTags.UNSIGNED8_INT: + if (actualTag == TypeTags.BYTE) { + return TypeTestResult.TRUE; } - } - return true; - } - - // If there are any remaining RHS fields, the types of those should be assignable to the rest field type of - // the LHS record. - BType lhsRestFieldType = lhsType.restFieldType; - for (BField field : rhsFields.values()) { - if (!isAssignable(field.type, lhsRestFieldType, unresolvedTypes)) { - return false; - } } - return true; + return TypeTestResult.NOT_FOUND; } - private boolean isInvalidNeverField(BField lhsField, BRecordType rhsType) { - if (getImpliedType(lhsField.type).tag != NEVER || rhsType.sealed) { - return false; + public boolean isImplicitlyCastable(BType actual, BType target) { + /* The word Builtin refers for Compiler known types. */ + + BType targetType = getImpliedType(target); + BType actualType = getImpliedType(actual); + BType newTargetType = targetType; + int targetTypeTag = targetType.tag; + if ((targetTypeTag == TypeTags.UNION || targetTypeTag == TypeTags.FINITE) && isValueType(actualType)) { + newTargetType = symTable.anyType; // TODO : Check for correctness. } - BType restFieldType = rhsType.restFieldType; - return switch (restFieldType.tag) { - case TypeTags.UNION -> { - for (BType member : ((BUnionType) restFieldType).getOriginalMemberTypes()) { - if (getImpliedType(member).tag == NEVER) { - yield false; - } - } - yield true; - } - case NEVER -> false; - default -> true; - }; - } + TypeTestResult result = isBuiltInTypeWidenPossible(actualType, newTargetType); + if (result != TypeTestResult.NOT_FOUND) { + return result == TypeTestResult.TRUE; + } - private Optional getMatchingInvokableType(List rhsFuncList, - BAttachedFunction lhsFunc, - Set unresolvedTypes) { - Optional matchingFunction = rhsFuncList.stream() - .filter(rhsFunc -> lhsFunc.funcName.equals(rhsFunc.funcName)) - .filter(rhsFunc -> isFunctionTypeAssignable(rhsFunc.type, lhsFunc.type, unresolvedTypes)) - .findFirst(); + if (isValueType(targetType) && + (actualType.tag == TypeTags.FINITE || + (actualType.tag == TypeTags.UNION && ((BUnionType) actualType).getMemberTypes().stream() + .anyMatch(type -> getImpliedType(type).tag == TypeTags.FINITE && + isAssignable(type, targetType))))) { + // for nil, no cast is required + return TypeTags.isIntegerTypeTag(targetTypeTag) || targetType.tag == TypeTags.BYTE || + targetTypeTag == TypeTags.FLOAT || + targetTypeTag == TypeTags.DECIMAL || + TypeTags.isStringTypeTag(targetTypeTag) || + targetTypeTag == TypeTags.BOOLEAN; - if (matchingFunction.isEmpty()) { - return matchingFunction; - } - // For resource function match, we need to check whether lhs function resource path type belongs to - // rhs function resource path type - BAttachedFunction matchingFunc = matchingFunction.get(); - // Todo: We could include this logic in `isFunctionTypeAssignable` if we have `resourcePathType` information in - // `BInvokableType` issue #37502 - boolean lhsFuncIsResource = Symbols.isResource(lhsFunc.symbol); - boolean matchingFuncIsResource = Symbols.isResource(matchingFunc.symbol); + } else if (isValueType(targetType) && actualType.tag == TypeTags.UNION && + ((BUnionType) actualType).getMemberTypes().stream().allMatch(type -> isAssignable(type, targetType))) { + return true; - if (!lhsFuncIsResource && !matchingFuncIsResource) { - return matchingFunction; + } else if (targetTypeTag == TypeTags.ERROR + && (actualType.tag == TypeTags.UNION + && isAllErrorMembers((BUnionType) actualType))) { + return true; } + return false; + } - if (!lhsFuncIsResource || !matchingFuncIsResource) { - return Optional.empty(); + public boolean isTypeCastable(BType source, BType target) { + BType sourceType = getImpliedType(source); + BType targetType = getImpliedType(target); + if (sourceType.tag == TypeTags.SEMANTIC_ERROR || targetType.tag == TypeTags.SEMANTIC_ERROR || + sourceType == targetType) { + return true; } - List lhsFuncPathTypes = ((BResourceFunction) lhsFunc).pathSegmentSymbols; - List rhsFuncPathTypes = ((BResourceFunction) matchingFunc).pathSegmentSymbols; + SemType sourceSemType = sourceType.semType(); + SemType targetSemType = targetType.semType(); - int lhsFuncResourcePathTypesSize = lhsFuncPathTypes.size(); - if (lhsFuncResourcePathTypesSize != rhsFuncPathTypes.size()) { - return Optional.empty(); + // Disallow casting away error, this forces user to handle the error via type-test, check, or checkpanic + if (containsErrorType(sourceSemType) && !containsErrorType(targetSemType)) { + return false; } - for (int i = 0; i < lhsFuncResourcePathTypesSize; i++) { - if (!isAssignable(lhsFuncPathTypes.get(i).type, rhsFuncPathTypes.get(i).type)) { - return Optional.empty(); - } + if (isNumericConversionPossible(sourceType, targetType)) { + return true; } - return matchingFunction; + return intersectionExists(sourceSemType, targetSemType); } - private boolean isInSameVisibilityRegion(BSymbol lhsSym, BSymbol rhsSym) { - if (Symbols.isPrivate(lhsSym)) { - return Symbols.isPrivate(rhsSym) && lhsSym.pkgID.equals(rhsSym.pkgID) - && lhsSym.owner.name.equals(rhsSym.owner.name); - } else if (Symbols.isPublic(lhsSym)) { - return Symbols.isPublic(rhsSym); - } - return !Symbols.isPrivate(rhsSym) && !Symbols.isPublic(rhsSym) && lhsSym.pkgID.equals(rhsSym.pkgID); + public boolean containsErrorType(SemType t) { + return SemTypes.containsBasicType(t, PredefinedType.ERROR); } - private boolean isAssignableToUnionType(BType source, BType target, Set unresolvedTypes) { - TypePair pair = new TypePair(source, target); - if (unresolvedTypes.contains(pair)) { - return true; - } - - source = getImpliedType(source); - target = getImpliedType(target); - if (isJsonAnydataOrUserDefinedUnion(source) && ((BUnionType) source).isCyclic) { - // add cyclic source to target pair to avoid recursive calls - unresolvedTypes.add(pair); + boolean isNumericConversionPossible(BType sourceType, BType targetType) { + Optional targetNumericType = Core.singleNumericType(targetType.semType()); + if (targetNumericType.isEmpty()) { + return false; } - Set sourceTypes = new LinkedHashSet<>(); - Set targetTypes = new LinkedHashSet<>(); + return !Core.isEmpty(semTypeCtx, SemTypes.intersect(sourceType.semType(), PredefinedType.NUMBER)); + } - if (isJsonAnydataOrUserDefinedUnion(source)) { - sourceTypes.addAll(getEffectiveMemberTypes((BUnionType) source)); - } else { - sourceTypes.add(source); - } + public boolean isAllErrorMembers(BUnionType actualType) { + return isSubtype(actualType, PredefinedType.ERROR); + } - boolean targetIsAUnion = false; - if (target.tag == TypeTags.UNION) { - targetIsAUnion = true; - targetTypes.addAll(getEffectiveMemberTypes((BUnionType) target)); - } else { - targetTypes.add(target); + public void setImplicitCastExpr(BLangExpression expr, BType actualType, BType targetType) { + BType expType = getImpliedType(targetType); + if (!isImplicitlyCastable(actualType, expType)) { + return; } + BLangTypeConversionExpr implicitConversionExpr = + (BLangTypeConversionExpr) TreeBuilder.createTypeConversionNode(); + implicitConversionExpr.pos = expr.pos; + implicitConversionExpr.expr = expr.impConversionExpr == null ? expr : expr.impConversionExpr; + implicitConversionExpr.setBType(expType); + implicitConversionExpr.targetType = expType; + implicitConversionExpr.internal = true; + expr.impConversionExpr = implicitConversionExpr; + } - // check if all the value types are assignable between two unions - Iterator sourceIterator = sourceTypes.iterator(); - while (sourceIterator.hasNext()) { - BType sMember = sourceIterator.next(); - if (sMember.tag == TypeTags.NEVER) { - sourceIterator.remove(); - continue; - } - if (sMember.tag == TypeTags.FINITE && isAssignable(sMember, target, unresolvedTypes)) { - sourceIterator.remove(); - continue; - } - if (sMember.tag == TypeTags.XML && - isAssignableToUnionType(expandedXMLBuiltinSubtypes, target, unresolvedTypes)) { - sourceIterator.remove(); - continue; - } - - if (!isValueType(sMember)) { - if (!targetIsAUnion) { - continue; - } - BUnionType targetUnion = (BUnionType) target; - // prevent cyclic unions being compared as individual items - if (sMember instanceof BUnionType sUnion) { - if (sUnion.isCyclic && targetUnion.isCyclic) { - unresolvedTypes.add(new TypePair(sUnion, targetUnion)); - if (isAssignable(sUnion, targetUnion, unresolvedTypes)) { - sourceIterator.remove(); - continue; - } - } - if (sMember.tag == TypeTags.JSON && isAssignable(sUnion, targetUnion, unresolvedTypes)) { - sourceIterator.remove(); - continue; + public boolean checkListenerCompatibilityAtServiceDecl(BType type) { + if (getImpliedType(type).tag == TypeTags.UNION) { + // There should be at least one listener compatible type and all the member types, except error type + // should be listener compatible. + int listenerCompatibleTypeCount = 0; + for (BType memberType : getAllTypes(type, true)) { + if (memberType.tag != TypeTags.ERROR) { + if (!checkListenerCompatibility(memberType)) { + return false; } + listenerCompatibleTypeCount++; } - // readonly can match to a union similar to any|error - if (sMember.tag == TypeTags.READONLY && isAssignable(symTable.anyAndReadonlyOrError, targetUnion)) { - sourceIterator.remove(); - continue; - } - continue; - } - - boolean sourceTypeIsNotAssignableToAnyTargetType = true; - Iterator targetIterator = targetTypes.iterator(); - while (targetIterator.hasNext()) { - BType t = targetIterator.next(); - if (isAssignable(sMember, t, unresolvedTypes)) { - sourceIterator.remove(); - sourceTypeIsNotAssignableToAnyTargetType = false; - break; - } - } - if (sourceTypeIsNotAssignableToAnyTargetType) { - unresolvedTypes.remove(pair); - return false; } + return listenerCompatibleTypeCount > 0; } + return checkListenerCompatibility(type); + } - // check the structural values for similarity - sourceIterator = sourceTypes.iterator(); - while (sourceIterator.hasNext()) { - BType sourceMember = sourceIterator.next(); - boolean sourceTypeIsNotAssignableToAnyTargetType = true; - Iterator targetIterator = targetTypes.iterator(); - - boolean selfReferencedSource = (sourceMember != source) && - isSelfReferencedStructuredType(source, sourceMember); - - while (targetIterator.hasNext()) { - BType targetMember = targetIterator.next(); - - boolean selfReferencedTarget = isSelfReferencedStructuredType(target, targetMember); - if (selfReferencedTarget && selfReferencedSource && (sourceMember.tag == targetMember.tag)) { - sourceTypeIsNotAssignableToAnyTargetType = false; - break; - } - - if (isAssignable(sourceMember, targetMember, unresolvedTypes)) { - sourceTypeIsNotAssignableToAnyTargetType = false; - break; + public boolean checkListenerCompatibility(BType bType) { + BType type = getImpliedType(bType); + if (type.tag == TypeTags.UNION) { + BUnionType unionType = (BUnionType) type; + for (BType memberType : unionType.getMemberTypes()) { + if (!checkListenerCompatibility(memberType)) { + return false; } } - if (sourceTypeIsNotAssignableToAnyTargetType) { - unresolvedTypes.remove(pair); - return false; - } + return true; } - unresolvedTypes.add(pair); - return true; - } + if (type.tag != TypeTags.OBJECT) { + return false; + } - private boolean isJsonAnydataOrUserDefinedUnion(BType type) { - int tag = type.tag; - return tag == TypeTags.UNION || tag == TypeTags.JSON || tag == TypeTags.ANYDATA; - } + BObjectType rhsType = (BObjectType) type; + List rhsFuncs = ((BStructureTypeSymbol) rhsType.tsymbol).attachedFuncs; - public boolean isSelfReferencedStructuredType(BType source, BType s) { - if (source == s) { - return true; - } + ListenerValidationModel listenerValidationModel = new ListenerValidationModel(this, symTable); + return listenerValidationModel.checkMethods(rhsFuncs); - s = getImpliedType(s); - if (s.tag == TypeTags.ARRAY) { - return isSelfReferencedStructuredType(source, ((BArrayType) s).eType); - } - if (s.tag == TypeTags.MAP) { - return isSelfReferencedStructuredType(source, ((BMapType) s).constraint); - } - if (s.tag == TypeTags.TABLE) { - return isSelfReferencedStructuredType(source, ((BTableType) s).constraint); - } - return false; } - public BType updateSelfReferencedWithNewType(BType source, BType s, BType target) { - if (s.tag == TypeTags.ARRAY) { - BArrayType arrayType = (BArrayType) s; - if (arrayType.eType == source) { - return new BArrayType(target, arrayType.tsymbol, arrayType.size, - arrayType.state, arrayType.flags); - } - } - if (s.tag == TypeTags.MAP) { - BMapType mapType = (BMapType) s; - if (mapType.constraint == source) { - return new BMapType(mapType.tag, target, mapType.tsymbol, mapType.flags); - } - } - if (s.tag == TypeTags.TABLE) { - BTableType tableType = (BTableType) s; - if (tableType.constraint == source) { - return new BTableType(tableType.tag, target, tableType.tsymbol, - tableType.flags); - } else if (tableType.constraint instanceof BMapType) { - return updateSelfReferencedWithNewType(source, tableType.constraint, target); - } - } - return s; + public boolean isValidErrorDetailType(BType detailType) { + return switch (getImpliedType(detailType).tag) { + case TypeTags.MAP, TypeTags.RECORD -> isAssignable(detailType, symTable.detailType); + default -> false; + }; } - public static void fixSelfReferencingSameUnion(BType originalMemberType, BUnionType origUnionType, - BType immutableMemberType, BUnionType newImmutableUnion, - LinkedHashSet readOnlyMemTypes) { - boolean sameMember = originalMemberType == immutableMemberType; - if (originalMemberType.tag == TypeTags.ARRAY) { - BArrayType arrayType = (BArrayType) originalMemberType; - if (origUnionType == arrayType.eType) { - if (sameMember) { - BArrayType newArrayType = new BArrayType(newImmutableUnion, arrayType.tsymbol, arrayType.size, - arrayType.state, arrayType.flags); - readOnlyMemTypes.add(newArrayType); - } else { - ((BArrayType) immutableMemberType).eType = newImmutableUnion; - readOnlyMemTypes.add(immutableMemberType); - } - } - } else if (originalMemberType.tag == TypeTags.MAP) { - BMapType mapType = (BMapType) originalMemberType; - if (origUnionType == mapType.constraint) { - if (sameMember) { - BMapType newMapType = new BMapType(mapType.tag, newImmutableUnion, mapType.tsymbol, mapType.flags); - readOnlyMemTypes.add(newMapType); - } else { - ((BMapType) immutableMemberType).constraint = newImmutableUnion; - readOnlyMemTypes.add(immutableMemberType); - } - } - } else if (originalMemberType.tag == TypeTags.TABLE) { - BTableType tableType = (BTableType) originalMemberType; - if (origUnionType == tableType.constraint) { - if (sameMember) { - BTableType newTableType = new BTableType(tableType.tag, newImmutableUnion, tableType.tsymbol, - tableType.flags); - readOnlyMemTypes.add(newTableType); - } else { - ((BTableType) immutableMemberType).constraint = newImmutableUnion; - readOnlyMemTypes.add(immutableMemberType); - } - return; - } - - BType immutableConstraint = ((BTableType) immutableMemberType).constraint; - if (tableType.constraint.tag == TypeTags.MAP) { - sameMember = tableType.constraint == immutableConstraint; - BMapType mapType = (BMapType) tableType.constraint; - if (origUnionType == mapType.constraint) { - if (sameMember) { - BMapType newMapType = new BMapType(mapType.tag, newImmutableUnion, mapType.tsymbol, - mapType.flags); - ((BTableType) immutableMemberType).constraint = newMapType; - } else { - ((BTableType) immutableMemberType).constraint = newImmutableUnion; - } - readOnlyMemTypes.add(immutableMemberType); - } - } - } else { - readOnlyMemTypes.add(immutableMemberType); - } - } + // private methods private Set getEffectiveMemberTypes(BUnionType unionType) { Set memTypes = new LinkedHashSet<>(); @@ -3880,44 +1803,6 @@ private Set getEffectiveMemberTypes(BUnionType unionType) { return memTypes; } - private boolean isFiniteTypeAssignable(BFiniteType finiteType, BType targetType, Set unresolvedTypes) { - BType expType = getImpliedType(targetType); - if (expType.tag == TypeTags.FINITE) { - for (BLangExpression expression : finiteType.getValueSpace()) { - ((BLangLiteral) expression).isFiniteContext = true; - if (!isAssignableToFiniteType(expType, (BLangLiteral) expression)) { - return false; - } - } - return true; - } - - if (expType.tag == TypeTags.UNION) { - List unionMemberTypes = getAllTypes(targetType, true); - for (BLangExpression valueExpr : finiteType.getValueSpace()) { - ((BLangLiteral) valueExpr).isFiniteContext = true; - if (unionMemberTypes.stream() - .noneMatch(targetMemType -> - getImpliedType(targetMemType).tag == TypeTags.FINITE ? - isAssignableToFiniteType(targetMemType, (BLangLiteral) valueExpr) : - isAssignable(valueExpr.getBType(), targetMemType, unresolvedTypes) || - isLiteralCompatibleWithBuiltinTypeWithSubTypes( - (BLangLiteral) valueExpr, targetMemType))) { - return false; - } - } - return true; - } - - for (BLangExpression expression : finiteType.getValueSpace()) { - if (!isLiteralCompatibleWithBuiltinTypeWithSubTypes((BLangLiteral) expression, targetType) && - !isAssignable(expression.getBType(), expType, unresolvedTypes)) { - return false; - } - } - return true; - } - boolean isAssignableToFiniteType(BType type, BLangLiteral literalExpr) { type = getImpliedType(type); if (type.tag != TypeTags.FINITE) { @@ -3925,20 +1810,7 @@ boolean isAssignableToFiniteType(BType type, BLangLiteral literalExpr) { } BFiniteType expType = (BFiniteType) type; - return expType.getValueSpace().stream().anyMatch(memberLiteral -> { - if (((BLangLiteral) memberLiteral).value == null) { - return literalExpr.value == null; - } - - // If the literal which needs to be tested is from finite type and the type of the any member literal - // is not the same type, the literal cannot be assignable to finite type. - if (literalExpr.isFiniteContext && memberLiteral.getBType().tag != literalExpr.getBType().tag) { - return false; - } - // Check whether the literal that needs to be tested is assignable to any of the member literal in the - // value space. - return checkLiteralAssignabilityBasedOnType((BLangLiteral) memberLiteral, literalExpr); - }); + return checkLiteralAssignabilityBasedOnType(literalExpr, expType, literalExpr.getBType().tag); } /** @@ -3947,18 +1819,14 @@ boolean isAssignableToFiniteType(BType type, BLangLiteral literalExpr) { * literal or a constant. In case of a constant, it is assignable to the base literal if and only if both * literals have same type and equivalent values. * - * @param baseLiteral Literal based on which we check the assignability. - * @param candidateLiteral Literal to be tested whether it is assignable to the base literal or not. + * @param literal Literal to be tested whether it is assignable to the base literal or not. + * @param finiteType + * @param targetTypeTag * @return true if assignable; false otherwise. */ - boolean checkLiteralAssignabilityBasedOnType(BLangLiteral baseLiteral, BLangLiteral candidateLiteral) { - // Different literal kinds. - if (baseLiteral.getKind() != candidateLiteral.getKind()) { - return false; - } - Object baseValue = baseLiteral.value; - Object candidateValue = candidateLiteral.value; - int candidateTypeTag = candidateLiteral.getBType().tag; + boolean checkLiteralAssignabilityBasedOnType(BLangLiteral literal, BFiniteType finiteType, int targetTypeTag) { + Object value = literal.value; + int literalTypeTag = literal.getBType().tag; // Numeric literal assignability is based on assignable type and numeric equivalency of values. // If the base numeric literal is, @@ -3967,104 +1835,64 @@ boolean checkLiteralAssignabilityBasedOnType(BLangLiteral baseLiteral, BLangLite // (3) float: we can assign int simple literal(Not an int constant) or a float literal/constant with same value. // (4) decimal: we can assign int simple literal or float simple literal (Not int/float constants) or decimal // with the same value. - switch (baseLiteral.getBType().tag) { - case TypeTags.BYTE: - if (candidateTypeTag == TypeTags.BYTE || (candidateTypeTag == TypeTags.INT && - !candidateLiteral.isConstant && isByteLiteralValue((Long) candidateValue))) { - return ((Number) baseValue).longValue() == ((Number) candidateValue).longValue(); - } - break; + SemType t = finiteType.semType(); + switch (targetTypeTag) { case TypeTags.INT: - if (candidateTypeTag == TypeTags.INT) { - return ((Number) baseValue).longValue() == ((Number) candidateValue).longValue(); - } - break; - case TypeTags.SIGNED32_INT: - if (candidateTypeTag == TypeTags.INT && isSigned32LiteralValue((Long) candidateValue)) { - return ((Number) baseValue).longValue() == ((Number) candidateValue).longValue(); - } - break; - case TypeTags.SIGNED16_INT: - if (candidateTypeTag == TypeTags.INT && isSigned16LiteralValue((Long) candidateValue)) { - return ((Number) baseValue).longValue() == ((Number) candidateValue).longValue(); - } - break; - case TypeTags.SIGNED8_INT: - if (candidateTypeTag == TypeTags.INT && isSigned8LiteralValue((Long) candidateValue)) { - return ((Number) baseValue).longValue() == ((Number) candidateValue).longValue(); - } - break; - case TypeTags.UNSIGNED32_INT: - if (candidateTypeTag == TypeTags.INT && isUnsigned32LiteralValue((Long) candidateValue)) { - return ((Number) baseValue).longValue() == ((Number) candidateValue).longValue(); - } - break; - case TypeTags.UNSIGNED16_INT: - if (candidateTypeTag == TypeTags.INT && isUnsigned16LiteralValue((Long) candidateValue)) { - return ((Number) baseValue).longValue() == ((Number) candidateValue).longValue(); - } - break; - case TypeTags.UNSIGNED8_INT: - if (candidateTypeTag == TypeTags.INT && isUnsigned8LiteralValue((Long) candidateValue)) { - return ((Number) baseValue).longValue() == ((Number) candidateValue).longValue(); + if (literalTypeTag == TypeTags.INT) { + if (value instanceof String) { + return false; + } + return Core.containsConstInt(t, ((Number) value).longValue()); } break; case TypeTags.FLOAT: - String baseValueStr = String.valueOf(baseValue); - String originalValue = baseLiteral.originalValue != null ? baseLiteral.originalValue : baseValueStr; - if (NumericLiteralSupport.isDecimalDiscriminated(originalValue)) { - return false; - } - double baseDoubleVal; - try { - baseDoubleVal = Double.parseDouble(baseValueStr); - } catch (NumberFormatException e) { - // We reach here if a floating point literal has syntax diagnostics. - return false; - } - double candidateDoubleVal; - if (candidateTypeTag == TypeTags.INT && !candidateLiteral.isConstant) { - if (candidateLiteral.value instanceof Double) { + double doubleValue; + if (literalTypeTag == TypeTags.INT && !literal.isConstant) { + if (literal.value instanceof Double) { // Out of range value for int but in range for float - candidateDoubleVal = Double.parseDouble(String.valueOf(candidateValue)); - return baseDoubleVal == candidateDoubleVal; + doubleValue = Double.parseDouble(String.valueOf(value)); } else { - candidateDoubleVal = ((Long) candidateValue).doubleValue(); - return baseDoubleVal == candidateDoubleVal; + doubleValue = ((Long) value).doubleValue(); + } + return Core.containsConstFloat(t, doubleValue); + } else if (literalTypeTag == TypeTags.FLOAT) { + try { + doubleValue = Double.parseDouble(String.valueOf(value)); + return Core.containsConstFloat(t, doubleValue); + } catch (NumberFormatException e) { + return false; } - } else if (candidateTypeTag == TypeTags.FLOAT) { - candidateDoubleVal = Double.parseDouble(String.valueOf(candidateValue)); - return baseDoubleVal == candidateDoubleVal; } break; case TypeTags.DECIMAL: - BigDecimal baseDecimalVal = NumericLiteralSupport.parseBigDecimal(baseValue); - BigDecimal candidateDecimalVal; - if (candidateTypeTag == TypeTags.INT && !candidateLiteral.isConstant) { - if (candidateLiteral.value instanceof String) { + BigDecimal decimalValue; + if (literalTypeTag == TypeTags.INT && !literal.isConstant) { + if (literal.value instanceof String) { // out of range value for float but in range for decimal - candidateDecimalVal = NumericLiteralSupport.parseBigDecimal(candidateValue); - return baseDecimalVal.compareTo(candidateDecimalVal) == 0; - } else if (candidateLiteral.value instanceof Double) { + decimalValue = NumericLiteralSupport.parseBigDecimal(value); + } else if (literal.value instanceof Double) { // out of range value for int in range for decimal and float - candidateDecimalVal = new BigDecimal((Double) candidateValue, MathContext.DECIMAL128); - return baseDecimalVal.compareTo(candidateDecimalVal) == 0; + decimalValue = new BigDecimal((Double) value, MathContext.DECIMAL128); } else { - candidateDecimalVal = new BigDecimal((long) candidateValue, MathContext.DECIMAL128); - return baseDecimalVal.compareTo(candidateDecimalVal) == 0; + decimalValue = new BigDecimal((long) value, MathContext.DECIMAL128); } - } else if (candidateTypeTag == TypeTags.FLOAT && !candidateLiteral.isConstant || - candidateTypeTag == TypeTags.DECIMAL) { - if (NumericLiteralSupport.isFloatDiscriminated(String.valueOf(candidateValue))) { + return Core.containsConstDecimal(t, decimalValue); + } else if (literalTypeTag == TypeTags.FLOAT && !literal.isConstant || + literalTypeTag == TypeTags.DECIMAL) { + if (NumericLiteralSupport.isFloatDiscriminated(String.valueOf(value))) { + return false; + } + try { + decimalValue = NumericLiteralSupport.parseBigDecimal(value); + return Core.containsConstDecimal(t, decimalValue); + } catch (NumberFormatException e) { return false; } - candidateDecimalVal = NumericLiteralSupport.parseBigDecimal(candidateValue); - return baseDecimalVal.compareTo(candidateDecimalVal) == 0; } break; default: // Non-numeric literal kind. - return baseValue.equals(candidateValue); + return Core.containsConst(t, value); } return false; } @@ -4174,102 +2002,34 @@ boolean isCharLiteralValue(String literal) { * Method to retrieve a type representing all the values in the value space of a finite type that are assignable to * the target type. * - * @param finiteType the finite type - * @param targetType the target type + * @param finiteType finite type + * @param targetType target type * @return a new finite type if at least one value in the value space of the specified finiteType is * assignable to targetType (the same if all are assignable), else semanticError */ - BType getTypeForFiniteTypeValuesAssignableToType(BFiniteType finiteType, BType targetType) { - // finiteType - type Foo "foo"; - // targetType - type FooBar "foo"|"bar"; - if (isAssignable(finiteType, targetType)) { - return finiteType; - } - - // Identify all the values from the value space of the finite type that are assignable to the target type. - // e.g., finiteType - type Foo "foo"|1 ; - Set matchingValues = new HashSet<>(); - for (BLangExpression expr : finiteType.getValueSpace()) { - // case I: targetType - string ("foo" is assignable to string) - BLangLiteral literal = (BLangLiteral) expr; - if (isAssignable(expr.getBType(), targetType) || - // case II: targetType - type Bar "foo"|"baz" ; ("foo" is assignable to Bar) - isAssignableToFiniteType(targetType, literal) || - // type FooVal "foo"; - // case III: targetType - boolean|FooVal ("foo" is assignable to FooVal) - isAssignableToFiniteTypeMemberInUnion(literal, targetType) || - // case IV: targetType - int:Signed16 (1 is assignable to int:Signed16) - isAssignableToBuiltinSubtypeInTargetType(literal, targetType)) { - matchingValues.add(expr); - } - } - - if (matchingValues.isEmpty()) { - return symTable.semanticError; - } - - // Create a new finite type representing the assignable values. - BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, finiteType.tsymbol.flags, - Names.fromString("$anonType$" + UNDERSCORE + finiteTypeCount++), - finiteType.tsymbol.pkgID, null, - finiteType.tsymbol.owner, finiteType.tsymbol.pos, - VIRTUAL); - BFiniteType intersectingFiniteType = new BFiniteType(finiteTypeSymbol, matchingValues); - finiteTypeSymbol.type = intersectingFiniteType; - return intersectingFiniteType; - } - - private boolean isAssignableToFiniteTypeMemberInUnion(BLangLiteral expr, BType targetType) { - targetType = getImpliedType(targetType); - if (targetType.tag != TypeTags.UNION) { - return false; - } - - for (BType memType : ((BUnionType) targetType).getMemberTypes()) { - if (isAssignableToFiniteType(memType, expr)) { - return true; - } - } - return false; - } + private Optional getFiniteTypeForAssignableValues(BType finiteType, BType targetType) { + BFiniteType bFiniteType = (BFiniteType) finiteType; + List newValueSpace = new ArrayList<>(bFiniteType.valueSpace.length); - private boolean isAssignableToBuiltinSubtypeInTargetType(BLangLiteral literal, BType targetType) { - targetType = getImpliedType(targetType); - if (targetType.tag == TypeTags.UNION) { - for (BType memberType : ((BUnionType) targetType).getMemberTypes()) { - if (isLiteralCompatibleWithBuiltinTypeWithSubTypes(literal, memberType)) { - return true; - } + for (SemNamedType semNamedType : bFiniteType.valueSpace) { + if (SemTypes.isSubtype(semTypeCtx, semNamedType.semType(), targetType.semType())) { + newValueSpace.add(semNamedType); } - } - - return isLiteralCompatibleWithBuiltinTypeWithSubTypes(literal, targetType); - } - - public boolean isLiteralCompatibleWithBuiltinTypeWithSubTypes(BLangLiteral literal, BType targetType) { - BType literalType = literal.getBType(); - targetType = getImpliedType(targetType); - if (literalType.tag == targetType.tag) { - return true; - } - - return switch (targetType.tag) { - case TypeTags.BYTE -> literalType.tag == TypeTags.INT && isByteLiteralValue((Long) literal.value); - case TypeTags.SIGNED32_INT -> - literalType.tag == TypeTags.INT && isSigned32LiteralValue((Long) literal.value); - case TypeTags.SIGNED16_INT -> - literalType.tag == TypeTags.INT && isSigned16LiteralValue((Long) literal.value); - case TypeTags.SIGNED8_INT -> literalType.tag == TypeTags.INT && isSigned8LiteralValue((Long) literal.value); - case TypeTags.UNSIGNED32_INT -> - literalType.tag == TypeTags.INT && isUnsigned32LiteralValue((Long) literal.value); - case TypeTags.UNSIGNED16_INT -> - literalType.tag == TypeTags.INT && isUnsigned16LiteralValue((Long) literal.value); - case TypeTags.UNSIGNED8_INT -> - literalType.tag == TypeTags.INT && isUnsigned8LiteralValue((Long) literal.value); - case TypeTags.CHAR_STRING -> - literalType.tag == TypeTags.STRING && isCharLiteralValue((String) literal.value); - default -> false; - }; + } + + if (newValueSpace.isEmpty()) { + return Optional.empty(); + } + + // Create a new finite type representing the assignable values. + BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, finiteType.tsymbol.flags, + Names.fromString("$anonType$" + UNDERSCORE + finiteTypeCount++), + finiteType.tsymbol.pkgID, null, + finiteType.tsymbol.owner, finiteType.tsymbol.pos, + VIRTUAL); + BFiniteType ft = new BFiniteType(finiteTypeSymbol, newValueSpace.toArray(SemNamedType[]::new)); + finiteTypeSymbol.type = ft; + return Optional.of(ft); } /** @@ -4306,201 +2066,71 @@ BType getTypeForUnionTypeMembersAssignableToType(BUnionType unionType, BType tar if (intersection.size() == 1) { return intersection.get(0); } else { - return BUnionType.create(null, new LinkedHashSet<>(intersection)); + return BUnionType.create(typeEnv(), null, new LinkedHashSet<>(intersection)); } } boolean validEqualityIntersectionExists(BType lhsType, BType rhsType) { - if (!isAnydata(lhsType) && !isAnydata(rhsType)) { + SemType intersect = Core.intersect(lhsType.semType(), rhsType.semType()); + if (Core.isEmpty(semTypeCtx, intersect)) { return false; } - if (isAssignable(lhsType, rhsType) || isAssignable(rhsType, lhsType)) { - return true; - } - - Set lhsTypes = expandAndGetMemberTypesRecursive(lhsType); - Set rhsTypes = expandAndGetMemberTypesRecursive(rhsType); - return equalityIntersectionExists(lhsTypes, rhsTypes); + return isAnydata(intersect); } - private boolean equalityIntersectionExists(Set lhsTypes, Set rhsTypes) { - if ((lhsTypes.contains(symTable.anydataType) && - rhsTypes.stream().anyMatch(type -> getImpliedType(type).tag != TypeTags.ERROR)) || - (rhsTypes.contains(symTable.anydataType) && - lhsTypes.stream().anyMatch(type -> getImpliedType(type).tag != TypeTags.ERROR))) { - return true; - } - - boolean matchFound = false; - for (BType lhsType : lhsTypes) { - for (BType rhsType : rhsTypes) { - if (isAssignable(lhsType, rhsType) || isAssignable(rhsType, lhsType)) { - matchFound = true; - break; - } - } - if (matchFound) { - break; - } - } - - if (!matchFound) { - matchFound = equalityIntersectionExistsForComplexTypes(lhsTypes, rhsTypes); - } - - return matchFound; + /** + * Checks where a type is subtype of either string or xml. + * + * @param type type to be checked + * @return a boolean + */ + boolean validStringOrXmlTypeExists(BType type) { + return isStringSubtype(type) || isXmlSubType(type); } - boolean validNumericStringOrXmlTypeExists(BType type, TypeExistenceValidationFunction validationFunction) { - switch (type.tag) { - case TypeTags.UNION: - BUnionType unionType = (BUnionType) type; - Set memberTypes = unionType.getMemberTypes(); - BType firstTypeInUnion = getBasicTypeOfBuiltinSubtype(getImpliedType(memberTypes.iterator().next())); - if (!validationFunction.validate(firstTypeInUnion)) { - return false; - } - if (firstTypeInUnion.tag == TypeTags.FINITE) { - Set valSpace = ((BFiniteType) firstTypeInUnion).getValueSpace(); - BType baseExprType = valSpace.iterator().next().getBType(); - for (BType memType : memberTypes) { - memType = getImpliedType(memType); - if (memType.tag == TypeTags.FINITE) { - if (!checkValueSpaceHasSameType((BFiniteType) memType, baseExprType)) { - return false; - } - continue; - } - - if (!isSubTypeOfBaseType(memType, baseExprType.tag)) { - return false; - } - } - } else { - for (BType memType : memberTypes) { - memType = getImpliedType(memType); - if (memType.tag == TypeTags.FINITE) { - if (!checkValueSpaceHasSameType((BFiniteType) memType, firstTypeInUnion)) { - return false; - } - continue; - } - if (!isSubTypeOfBaseType(memType, firstTypeInUnion.tag)) { - return false; - } - } - } - return true; - case TypeTags.FINITE: - Set valSpace = ((BFiniteType) type).getValueSpace(); - BType baseExprType = valSpace.iterator().next().getBType(); - for (BLangExpression expr : valSpace) { - if (!checkValueSpaceHasSameType((BFiniteType) type, baseExprType)) { - return false; - } - if (!validationFunction.validate(expr.getBType())) { - return false; - } - } - return true; - case TypeTags.TYPEREFDESC: - return validationFunction.validate(getImpliedType(type)); - case TypeTags.INTERSECTION: - return validationFunction.validate(((BIntersectionType) type).effectiveType); - default: - return false; - } + /** + * Checks whether a type is a subtype of xml. + * + * @param type type to be checked + * @return a boolean + */ + boolean isXmlSubType(BType type) { + return SemTypeHelper.isSubtypeSimple(type, PredefinedType.XML); } - boolean validNumericTypeExists(BType type) { - if (type.isNullable() && getImpliedType(type).tag != TypeTags.NIL) { - type = getSafeType(type, true, false); - } - if (isBasicNumericType(type)) { - return true; - } - return validNumericStringOrXmlTypeExists(type, this::validNumericTypeExists); + /** + * Checks whether a type is a subtype of string. + * + * @param type type to be checked + * @return a boolean + */ + boolean isStringSubtype(BType type) { + return SemTypeHelper.isSubtypeSimple(type, PredefinedType.STRING); } - boolean validStringOrXmlTypeExists(BType type) { - BType refType = getImpliedType(type); - if (TypeTags.isStringTypeTag(refType.tag) || TypeTags.isXMLTypeTag(refType.tag)) { - return true; - } - return validNumericStringOrXmlTypeExists(type, this::validStringOrXmlTypeExists); + /** + * Checks whether a type is a subtype of one of int?, float? or decimal?. + * + * @param type type to be checked + * @return a boolean + */ + boolean validNumericTypeExists(BType type) { + SemType tButNil = Core.diff(type.semType(), PredefinedType.NIL); // nil lift + BasicTypeBitSet basicTypeBitSet = Core.widenToBasicTypes(tButNil); + return basicTypeBitSet.equals(PredefinedType.INT) || + basicTypeBitSet.equals(PredefinedType.FLOAT) || + basicTypeBitSet.equals(PredefinedType.DECIMAL); } boolean validIntegerTypeExists(BType bType) { - BType type = getImpliedType(bType); - if (type.isNullable() && type.tag != TypeTags.NIL) { - type = getSafeType(type, true, false); - } - if (TypeTags.isIntegerTypeTag(type.tag)) { - return true; - } - switch (type.tag) { - case TypeTags.BYTE: - return true; - case TypeTags.UNION: - LinkedHashSet memberTypes = ((BUnionType) type).getMemberTypes(); - for (BType memberType : memberTypes) { - memberType = getImpliedType(memberType); - if (!validIntegerTypeExists(memberType)) { - return false; - } - } - return true; - case TypeTags.FINITE: - Set valueSpace = ((BFiniteType) type).getValueSpace(); - for (BLangExpression expr : valueSpace) { - if (!validIntegerTypeExists(expr.getBType())) { - return false; - } - } - return true; - default: - return false; - } - } - - public BType getBasicTypeOfBuiltinSubtype(BType type) { - if (TypeTags.isIntegerTypeTag(type.tag) || type.tag == TypeTags.BYTE) { - return symTable.intType; - } - if (TypeTags.isStringTypeTag(type.tag)) { - return symTable.stringType; - } - if (TypeTags.isXMLTypeTag(type.tag)) { - return symTable.xmlType; - } - return type; + SemType s = bType.semType(); + s = Core.diff(s, PredefinedType.NIL); // nil lift + return SemTypes.isSubtypeSimpleNotNever(s, PredefinedType.INT); } - public boolean checkTypeContainString(BType type) { - type = getImpliedType(type); - if (TypeTags.isStringTypeTag(type.tag)) { - return true; - } - switch (type.tag) { - case TypeTags.UNION: - for (BType memType : ((BUnionType) type).getMemberTypes()) { - if (!checkTypeContainString(memType)) { - return false; - } - } - return true; - case TypeTags.FINITE: - Set valSpace = ((BFiniteType) type).getValueSpace(); - for (BLangExpression expr : valSpace) { - if (!checkTypeContainString(expr.getBType())) { - return false; - } - } - return true; - default: - return false; - } + public boolean isStringSubType(BType type) { + return SemTypeHelper.isSubtypeSimpleNotNever(type, PredefinedType.STRING); } /** @@ -4528,8 +2158,8 @@ private Set expandAndGetMemberTypesRecursiveHelper(BType bType, memberTypes.add(symTable.byteType); break; case TypeTags.FINITE: - BFiniteType expType = (BFiniteType) referredType; - expType.getValueSpace().forEach(value -> memberTypes.add(value.getBType())); + Set broadTypes = SemTypeHelper.broadTypes((BFiniteType) referredType, symTable); + memberTypes.addAll(broadTypes); break; case TypeTags.UNION: BUnionType unionType = (BUnionType) referredType; @@ -4546,12 +2176,13 @@ private Set expandAndGetMemberTypesRecursiveHelper(BType bType, // add an unsealed array to allow comparison between closed and open arrays // TODO: 10/16/18 improve this, since it will allow comparison between sealed arrays of different sizes if (((BArrayType) referredType).getSize() != -1) { - memberTypes.add(new BArrayType(arrayElementType)); + memberTypes.add(new BArrayType(typeEnv(), arrayElementType)); } if (getImpliedType(arrayElementType).tag == TypeTags.UNION) { Set elementUnionTypes = expandAndGetMemberTypesRecursiveHelper(arrayElementType, visited); - elementUnionTypes.forEach(elementUnionType -> memberTypes.add(new BArrayType(elementUnionType))); + elementUnionTypes.forEach( + elementUnionType -> memberTypes.add(new BArrayType(typeEnv(), elementUnionType))); } memberTypes.add(bType); break; @@ -4560,8 +2191,9 @@ private Set expandAndGetMemberTypesRecursiveHelper(BType bType, if (getImpliedType(mapConstraintType).tag == TypeTags.UNION) { Set constraintUnionTypes = expandAndGetMemberTypesRecursiveHelper(mapConstraintType, visited); - constraintUnionTypes.forEach(constraintUnionType -> - memberTypes.add(new BMapType(TypeTags.MAP, constraintUnionType, symTable.mapType.tsymbol))); + constraintUnionTypes.forEach(constraintUnionType -> memberTypes.add( + new BMapType(symTable.typeEnv(), TypeTags.MAP, constraintUnionType, + symTable.mapType.tsymbol))); } memberTypes.add(bType); break; @@ -4571,235 +2203,6 @@ private Set expandAndGetMemberTypesRecursiveHelper(BType bType, return memberTypes; } - private boolean tupleIntersectionExists(BTupleType lhsType, BTupleType rhsType) { - if (lhsType.getTupleTypes().size() != rhsType.getTupleTypes().size()) { - return false; - } - - List lhsMemberTypes = lhsType.getTupleTypes(); - List rhsMemberTypes = rhsType.getTupleTypes(); - - for (int i = 0; i < lhsType.getTupleTypes().size(); i++) { - if (!equalityIntersectionExists(expandAndGetMemberTypesRecursive(lhsMemberTypes.get(i)), - expandAndGetMemberTypesRecursive(rhsMemberTypes.get(i)))) { - return false; - } - } - return true; - } - - private boolean equalityIntersectionExistsForComplexTypes(Set lhsTypes, Set rhsTypes) { - for (BType lhsMemberType : lhsTypes) { - if (isEqualityIntersectionExistsForMemberType(lhsMemberType, rhsTypes)) { - return true; - } - } - return false; - } - - private boolean isEqualityIntersectionExistsForMemberType(BType lhsMemberType, Set rhsTypes) { - switch (lhsMemberType.tag) { - case TypeTags.INT: - case TypeTags.STRING: - case TypeTags.FLOAT: - case TypeTags.DECIMAL: - case TypeTags.BOOLEAN: - case TypeTags.NIL: - if (rhsTypes.stream().map(Types::getImpliedType) - .anyMatch(rhsMemberType -> rhsMemberType.tag == TypeTags.JSON)) { - return true; - } - break; - case TypeTags.JSON: - if (jsonEqualityIntersectionExists(rhsTypes)) { - return true; - } - break; - // When expanding members for tuples, arrays and maps, set isValueDeepEquality to true, to allow - // comparison between JSON lists/maps and primitive lists/maps since they are all reference types - case TypeTags.TUPLE: - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> rhsMemberType.tag == TypeTags.TUPLE && - tupleIntersectionExists((BTupleType) lhsMemberType, (BTupleType) rhsMemberType))) { - return true; - } - - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> rhsMemberType.tag == TypeTags.ARRAY && - arrayTupleEqualityIntersectionExists((BArrayType) rhsMemberType, - (BTupleType) lhsMemberType))) { - return true; - } - break; - case TypeTags.ARRAY: - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> rhsMemberType.tag == TypeTags.ARRAY && - equalityIntersectionExists( - expandAndGetMemberTypesRecursive(((BArrayType) lhsMemberType).eType), - expandAndGetMemberTypesRecursive(((BArrayType) rhsMemberType).eType)))) { - return true; - } - - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> rhsMemberType.tag == TypeTags.TUPLE && - arrayTupleEqualityIntersectionExists((BArrayType) lhsMemberType, - (BTupleType) rhsMemberType))) { - return true; - } - break; - case TypeTags.MAP: - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> rhsMemberType.tag == TypeTags.MAP && - equalityIntersectionExists( - expandAndGetMemberTypesRecursive(((BMapType) lhsMemberType).constraint), - expandAndGetMemberTypesRecursive(((BMapType) rhsMemberType).constraint)))) { - return true; - } - - if (!isAssignable(((BMapType) lhsMemberType).constraint, symTable.errorType) && - rhsTypes.stream().map(Types::getImpliedType).anyMatch(rhsMemberType - -> rhsMemberType.tag == TypeTags.JSON)) { - // at this point it is guaranteed that the map is anydata - return true; - } - - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> rhsMemberType.tag == TypeTags.RECORD && - mapRecordEqualityIntersectionExists((BMapType) lhsMemberType, - (BRecordType) rhsMemberType))) { - return true; - } - break; - case TypeTags.OBJECT: - case TypeTags.RECORD: - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> checkStructEquivalency(rhsMemberType, lhsMemberType) || - checkStructEquivalency(lhsMemberType, rhsMemberType))) { - return true; - } - - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> rhsMemberType.tag == TypeTags.RECORD && - recordEqualityIntersectionExists((BRecordType) lhsMemberType, - (BRecordType) rhsMemberType))) { - return true; - } - - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch(rhsMemberType - -> rhsMemberType.tag == TypeTags.JSON) && - jsonEqualityIntersectionExists(expandAndGetMemberTypesRecursive(lhsMemberType))) { - return true; - } - - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> rhsMemberType.tag == TypeTags.MAP && - mapRecordEqualityIntersectionExists((BMapType) rhsMemberType, - (BRecordType) lhsMemberType))) { - return true; - } - break; - case TypeTags.TYPEREFDESC: - case TypeTags.INTERSECTION: - return isEqualityIntersectionExistsForMemberType(getImpliedType(lhsMemberType), rhsTypes); - } - return false; - } - - private boolean arrayTupleEqualityIntersectionExists(BArrayType arrayType, BTupleType tupleType) { - Set elementTypes = expandAndGetMemberTypesRecursive(arrayType.eType); - - return tupleType.getTupleTypes().stream().allMatch(tupleMemType -> - equalityIntersectionExists(elementTypes, expandAndGetMemberTypesRecursive(tupleMemType))); - } - - private boolean recordEqualityIntersectionExists(BRecordType lhsType, BRecordType rhsType) { - Map lhsFields = lhsType.fields; - Map rhsFields = rhsType.fields; - - List matchedFieldNames = new ArrayList<>(); - for (BField lhsField : lhsFields.values()) { - if (rhsFields.containsKey(lhsField.name.value)) { - if (!equalityIntersectionExists(expandAndGetMemberTypesRecursive(lhsField.type), - expandAndGetMemberTypesRecursive( - rhsFields.get(lhsField.name.value).type))) { - return false; - } - matchedFieldNames.add(lhsField.getName()); - } else { - if (Symbols.isFlagOn(lhsField.symbol.flags, Flags.OPTIONAL)) { - break; - } - - if (rhsType.sealed) { - return false; - } - - if (!equalityIntersectionExists(expandAndGetMemberTypesRecursive(lhsField.type), - expandAndGetMemberTypesRecursive(rhsType.restFieldType))) { - return false; - } - } - } - - for (BField rhsField : rhsFields.values()) { - if (matchedFieldNames.contains(rhsField.getName())) { - continue; - } - - if (!Symbols.isFlagOn(rhsField.symbol.flags, Flags.OPTIONAL)) { - if (lhsType.sealed) { - return false; - } - - if (!equalityIntersectionExists(expandAndGetMemberTypesRecursive(rhsField.type), - expandAndGetMemberTypesRecursive(lhsType.restFieldType))) { - return false; - } - } - } - - return true; - } - - private boolean mapRecordEqualityIntersectionExists(BMapType mapType, BRecordType recordType) { - Set mapConstrTypes = expandAndGetMemberTypesRecursive(mapType.getConstraint()); - - for (BField field : recordType.fields.values()) { - if (!Symbols.isFlagOn(field.symbol.flags, Flags.OPTIONAL) && - !equalityIntersectionExists(mapConstrTypes, expandAndGetMemberTypesRecursive(field.type))) { - return false; - } - } - - return true; - } - - private boolean jsonEqualityIntersectionExists(Set typeSet) { - for (BType type : typeSet) { - type = getImpliedType(type); - switch (type.tag) { - case TypeTags.MAP: - if (!isAssignable(((BMapType) type).constraint, symTable.errorType)) { - return true; - } - break; - case TypeTags.RECORD: - BRecordType recordType = (BRecordType) type; - if (recordType.fields.values().stream() - .allMatch(field -> Symbols.isFlagOn(field.symbol.flags, Flags.OPTIONAL) || - !isAssignable(field.type, symTable.errorType))) { - return true; - } - break; - default: - if (isAssignable(type, symTable.jsonType)) { - return true; - } - } - } - return false; - } - public BType getRemainingMatchExprType(BType originalType, BType typeToRemove, SymbolEnv env) { originalType = getImpliedType(originalType); return switch (originalType.tag) { @@ -4832,11 +2235,11 @@ private BType getRemainingType(BTupleType originalType, BTupleType typeToRemove, List tupleTypes = new ArrayList<>(); for (int i = 0; i < originalTupleTypes.size(); i++) { BType type = getRemainingMatchExprType(originalTupleTypes.get(i), typesToRemove.get(i), env); - BVarSymbol varSymbol = new BVarSymbol(type.flags, null, null, type, null, null, null); + BVarSymbol varSymbol = new BVarSymbol(type.getFlags(), null, null, type, null, null, null); tupleTypes.add(new BTupleMember(type, varSymbol)); } if (typeToRemove.restType == null) { - return new BTupleType(tupleTypes); + return new BTupleType(typeEnv(), tupleTypes); } if (originalTupleTypes.size() == typesToRemove.size()) { return originalType; @@ -4846,7 +2249,7 @@ private BType getRemainingType(BTupleType originalType, BTupleType typeToRemove, BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(type); tupleTypes.add(new BTupleMember(type, varSymbol)); } - return new BTupleType(tupleTypes); + return new BTupleType(typeEnv(), tupleTypes); } private BType getRemainingType(BTupleType originalType, BArrayType typeToRemove, SymbolEnv env) { @@ -4857,7 +2260,7 @@ private BType getRemainingType(BTupleType originalType, BArrayType typeToRemove, BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(type); tupleTypes.add(new BTupleMember(type, varSymbol)); } - BTupleType remainingType = new BTupleType(tupleTypes); + BTupleType remainingType = new BTupleType(typeEnv(), tupleTypes); if (originalType.restType != null) { remainingType.restType = getRemainingMatchExprType(originalType.restType, eType, env); } @@ -4882,8 +2285,8 @@ public BType getRemainingType(BType originalType, BType typeToRemove, SymbolEnv getAllTypes(remainingType, true))); if (typeRemovedFromOriginalUnionType == symTable.nullSet || - isSubTypeOfReadOnly(typeRemovedFromOriginalUnionType, env) || - isSubTypeOfReadOnly(remainingType, env) || + isSubTypeOfReadOnly(typeRemovedFromOriginalUnionType) || + isSubTypeOfReadOnly(remainingType) || narrowsToUnionOfImmutableTypesOrDistinctBasicTypes(remainingType, typeToRemove, env)) { return remainingType; } @@ -4902,7 +2305,7 @@ public BType getRemainingType(BType originalType, BType typeToRemove, SymbolEnv return getRemainingType(refType, typeToRemove, env); } - if (Symbols.isFlagOn(getImpliedType(originalType).flags, Flags.READONLY)) { + if (Symbols.isFlagOn(getImpliedType(originalType).getFlags(), Flags.READONLY)) { return remainingType; } @@ -4928,10 +2331,12 @@ public BType getRemainingType(BType originalType, BType typeToRemove, SymbolEnv return originalType; } - public boolean isSubTypeOfReadOnly(BType type, SymbolEnv env) { - return isInherentlyImmutableType(type) || - (isSelectivelyImmutableType(type, env.enclPkg.packageID) && - Symbols.isFlagOn(type.flags, Flags.READONLY)); + public boolean isSubTypeOfReadOnly(SemType t) { + return isSubtype(t, PredefinedType.VAL_READONLY); + } + + public boolean isSubTypeOfReadOnly(BType type) { + return isSubTypeOfReadOnly(type.semType()); } private boolean isClosedRecordTypes(BType type) { @@ -5050,7 +2455,7 @@ private boolean narrowsToUnionOfImmutableTypesOrDistinctBasicTypes(BType remaini LinkedHashSet mutableRemainingTypes = filterMutableMembers(((BUnionType) referredRemainingType).getMemberTypes(), env); remainingType = mutableRemainingTypes.size() == 1 ? mutableRemainingTypes.iterator().next() : - BUnionType.create(null, mutableRemainingTypes); + BUnionType.create(typeEnv(), null, mutableRemainingTypes); BType referredTypeToRemove = getImpliedType(typeToRemove); @@ -5058,7 +2463,7 @@ private boolean narrowsToUnionOfImmutableTypesOrDistinctBasicTypes(BType remaini LinkedHashSet mutableTypesToRemove = filterMutableMembers(((BUnionType) referredTypeToRemove).getMemberTypes(), env); typeToRemove = mutableTypesToRemove.size() == 1 ? mutableTypesToRemove.iterator().next() : - BUnionType.create(null, mutableTypesToRemove); + BUnionType.create(typeEnv(), null, mutableTypesToRemove); } else { typeToRemove = referredTypeToRemove; } @@ -5071,7 +2476,7 @@ private LinkedHashSet filterMutableMembers(LinkedHashSet types, Sy for (BType type : types) { BType referredType = getImpliedType(type); - if (!isSubTypeOfReadOnly(referredType, env)) { + if (!isSubTypeOfReadOnly(referredType)) { remainingMemberTypes.add(referredType); } } @@ -5088,6 +2493,10 @@ private BType getRemainingType(BReadonlyType originalType, BType removeType) { return originalType; } + public boolean intersectionExists(SemType t1, SemType t2) { + return !Core.isEmpty(semTypeCtx, Core.intersect(t1, t2)); + } + public BType getTypeIntersection(IntersectionContext intersectionContext, BType lhsType, BType rhsType, SymbolEnv env) { return getTypeIntersection(intersectionContext, lhsType, rhsType, env, new LinkedHashSet<>()); @@ -5116,7 +2525,7 @@ private BType getTypeIntersection(IntersectionContext intersectionContext, BType if (intersection.size() == 1) { return intersection.toArray(new BType[0])[0]; } else { - return BUnionType.create(null, intersection); + return BUnionType.create(typeEnv(), null, intersection); } } @@ -5139,8 +2548,8 @@ private BType getIntersection(IntersectionContext intersectionContext, BType lhs // implementation, we cannot easily find the intersection between (A & readonly) and B. Instead, what we // do here is, first find the intersection between A and B then re-create the immutable type out of it. - if (Symbols.isFlagOn(referredLhsType.flags, Flags.READONLY) && referredLhsType.tag == TypeTags.INTERSECTION && - getImpliedType(((BIntersectionType) referredLhsType).effectiveType).tag == TypeTags.UNION) { + if (Symbols.isFlagOn(referredLhsType.getFlags(), Flags.READONLY) && referredLhsType.tag == TypeTags.INTERSECTION + && getImpliedType(((BIntersectionType) referredLhsType).effectiveType).tag == TypeTags.UNION) { BIntersectionType intersectionType = (BIntersectionType) referredLhsType; BType finalType = type; List types = intersectionType.getConstituentTypes().stream() @@ -5151,7 +2560,7 @@ private BType getIntersection(IntersectionContext intersectionContext, BType lhs if (types.size() == 1) { BType bType = types.get(0); - if (isInherentlyImmutableType(bType) || Symbols.isFlagOn(bType.flags, Flags.READONLY)) { + if (isInherentlyImmutableType(bType) || Symbols.isFlagOn(bType.getFlags(), Flags.READONLY)) { return bType; } @@ -5197,15 +2606,15 @@ private BType getIntersection(IntersectionContext intersectionContext, BType lhs } else if (!intersectionContext.preferNonGenerativeIntersection && isAssignable(referredLhsType, referredType)) { return lhsType; - } else if (referredLhsType.tag == TypeTags.FINITE) { - BType intersectionType = getTypeForFiniteTypeValuesAssignableToType((BFiniteType) referredLhsType, type); - if (intersectionType != symTable.semanticError) { - return intersectionType; + } else if (referredLhsType.tag == TypeTags.FINITE) { + Optional intersectionType = getFiniteTypeForAssignableValues(referredLhsType, type); + if (intersectionType.isPresent()) { + return intersectionType.get(); } } else if (referredType.tag == TypeTags.FINITE) { - BType intersectionType = getTypeForFiniteTypeValuesAssignableToType((BFiniteType) referredType, lhsType); - if (intersectionType != symTable.semanticError) { - return intersectionType; + Optional intersectionType = getFiniteTypeForAssignableValues(referredType, lhsType); + if (intersectionType.isPresent()) { + return intersectionType.get(); } } else if (referredLhsType.tag == TypeTags.UNION) { BType intersectionType = getTypeForUnionTypeMembersAssignableToType((BUnionType) referredLhsType, type, env, @@ -5225,7 +2634,7 @@ private BType getIntersection(IntersectionContext intersectionContext, BType lhs if (intersectionConstraintTypeType == null || intersectionConstraintTypeType == symTable.semanticError) { return null; } - return new BMapType(TypeTags.MAP, intersectionConstraintTypeType, null); + return new BMapType(symTable.typeEnv(), TypeTags.MAP, intersectionConstraintTypeType, null); } else if (referredType.tag == TypeTags.ARRAY && referredLhsType.tag == TypeTags.TUPLE) { BType intersectionType = createArrayAndTupleIntersection(intersectionContext, (BArrayType) referredType, (BTupleType) referredLhsType, env, visitedTypes); @@ -5281,14 +2690,14 @@ private BType getIntersection(IntersectionContext intersectionContext, BType lhs if (elementIntersection == null) { return null; } - return new BArrayType(elementIntersection); + return new BArrayType(typeEnv(), elementIntersection); } else if (referredType.tag == TypeTags.ARRAY && isAnydataOrJson(referredLhsType)) { BType elementIntersection = getIntersection(intersectionContext, lhsType, env, ((BArrayType) referredType).eType, visitedTypes); if (elementIntersection == null) { return null; } - return new BArrayType(elementIntersection); + return new BArrayType(typeEnv(), elementIntersection); } else if (referredType.tag == TypeTags.NULL_SET) { return type; } @@ -5331,8 +2740,8 @@ private BType createArrayAndTupleIntersection(IntersectionContext intersectionCo return tupleType; } List tupleTypes = tupleType.getTupleTypes(); - if (arrayType.state == BArrayState.CLOSED && tupleTypes.size() != arrayType.size) { - if (tupleTypes.size() > arrayType.size) { + if (arrayType.state == BArrayState.CLOSED && tupleTypes.size() != arrayType.getSize()) { + if (tupleTypes.size() > arrayType.getSize()) { return symTable.semanticError; } @@ -5354,15 +2763,15 @@ private BType createArrayAndTupleIntersection(IntersectionContext intersectionCo } if (tupleType.restType == null) { - return new BTupleType(null, tupleMemberTypes); + return new BTupleType(typeEnv(), tupleMemberTypes); } BType restIntersectionType = getTypeIntersection(intersectionContext, tupleType.restType, eType, env, visitedTypes); if (restIntersectionType == symTable.semanticError) { - return new BTupleType(null, tupleMemberTypes); + return new BTupleType(typeEnv(), tupleMemberTypes); } - return new BTupleType(null, tupleMemberTypes, restIntersectionType, 0); + return new BTupleType(typeEnv(), null, tupleMemberTypes, restIntersectionType, 0); } private BType createTupleAndTupleIntersection(IntersectionContext intersectionContext, @@ -5392,7 +2801,7 @@ private BType createTupleAndTupleIntersection(IntersectionContext intersectionCo if (intersectionType == symTable.semanticError) { return symTable.semanticError; } - BVarSymbol varSymbol = new BVarSymbol(intersectionType.flags, null, null, intersectionType, + BVarSymbol varSymbol = new BVarSymbol(intersectionType.getFlags(), null, null, intersectionType, null, null, null); tupleMemberTypes.add(new BTupleMember(intersectionType, varSymbol)); } @@ -5401,12 +2810,12 @@ private BType createTupleAndTupleIntersection(IntersectionContext intersectionCo BType restIntersectionType = getTypeIntersection(intersectionContext, tupleType.restType, lhsTupleType.restType, env, visitedTypes); if (restIntersectionType == symTable.semanticError) { - return new BTupleType(null, tupleMemberTypes); + return new BTupleType(typeEnv(), tupleMemberTypes); } - return new BTupleType(null, tupleMemberTypes, restIntersectionType, 0); + return new BTupleType(typeEnv(), null, tupleMemberTypes, restIntersectionType, 0); } - return new BTupleType(null, tupleMemberTypes); + return new BTupleType(typeEnv(), tupleMemberTypes); } private BType getIntersectionForErrorTypes(IntersectionContext intersectionContext, @@ -5477,7 +2886,7 @@ private BType createRecordIntersection(IntersectionContext intersectionContext, if ((newType.sealed || newType.restFieldType == symTable.neverType) && (newTypeFields.isEmpty() || allReadOnlyFields(newTypeFields))) { - newType.flags |= Flags.READONLY; + newType.addFlags(Flags.READONLY); newTypeSymbol.flags |= Flags.READONLY; } @@ -5630,14 +3039,14 @@ private BRecordType createAnonymousRecord(SymbolEnv env) { env.scope.owner, null, VIRTUAL); recordSymbol.name = Names.fromString( anonymousModelHelper.getNextAnonymousTypeKey(env.enclPkg.packageID)); - BInvokableType bInvokableType = new BInvokableType(new ArrayList<>(), symTable.nilType, null); + BInvokableType bInvokableType = new BInvokableType(typeEnv(), List.of(), symTable.nilType, null); BInvokableSymbol initFuncSymbol = Symbols.createFunctionSymbol( Flags.PUBLIC, Names.EMPTY, Names.EMPTY, env.enclPkg.symbol.pkgID, bInvokableType, env.scope.owner, false, symTable.builtinPos, VIRTUAL); initFuncSymbol.retType = symTable.nilType; recordSymbol.scope = new Scope(recordSymbol); - BRecordType recordType = new BRecordType(recordSymbol); + BRecordType recordType = new BRecordType(symTable.typeEnv(), recordSymbol); recordType.tsymbol = recordSymbol; recordSymbol.type = recordType; @@ -5645,7 +3054,7 @@ private BRecordType createAnonymousRecord(SymbolEnv env) { } private BRecordType getEquivalentRecordType(BMapType mapType) { - BRecordType equivalentRecordType = new BRecordType(null); + BRecordType equivalentRecordType = new BRecordType(symTable.typeEnv(), null); equivalentRecordType.sealed = false; equivalentRecordType.restFieldType = mapType.constraint; return equivalentRecordType; @@ -5655,11 +3064,19 @@ private BErrorType createErrorType(BType lhsType, BType rhsType, BType detailTyp BErrorType lhsErrorType = (BErrorType) lhsType; BErrorType rhsErrorType = (BErrorType) rhsType; - long flags = lhsType.flags | rhsType.flags | Flags.PUBLIC; // Anonymous (generated) types are marked as public. - BErrorType errorType = createErrorType(detailType, flags, env); + // Anonymous (generated) types are marked as public. + BErrorType errorType = createErrorType(detailType, lhsType.getFlags() | rhsType.getFlags() | Flags.PUBLIC, env); - errorType.typeIdSet = BTypeIdSet.getIntersection(lhsErrorType.typeIdSet, rhsErrorType.typeIdSet); + // This is to propagate same distinctId to effective type + lhsErrorType.setDistinctId(); + rhsErrorType.setDistinctId(); + if (lhsErrorType.distinctId != -1) { + errorType.distinctId = lhsErrorType.distinctId; + } else if (rhsErrorType.distinctId != -1) { + errorType.distinctId = rhsErrorType.distinctId; + } + errorType.typeIdSet = BTypeIdSet.getIntersection(lhsErrorType.typeIdSet, rhsErrorType.typeIdSet); return errorType; } @@ -5669,62 +3086,14 @@ public BErrorType createErrorType(BType detailType, long flags, SymbolEnv env) { env.enclPkg.symbol.pkgID, null, env.scope.owner, symTable.builtinPos, VIRTUAL); errorTypeSymbol.scope = new Scope(errorTypeSymbol); - BErrorType errorType = new BErrorType(errorTypeSymbol, detailType); - errorType.flags |= errorTypeSymbol.flags; + BErrorType errorType = new BErrorType(symTable.typeEnv(), errorTypeSymbol, detailType); + errorType.addFlags(errorTypeSymbol.flags); errorTypeSymbol.type = errorType; errorType.typeIdSet = BTypeIdSet.emptySet(); return errorType; } - private boolean populateRecordFields(IntersectionContext diagnosticContext, BRecordType newType, - BType originalType, SymbolEnv env, BType constraint) { - BTypeSymbol intersectionRecordSymbol = newType.tsymbol; - // If the detail type is BMapType simply ignore since the resulting detail type has `anydata` as rest type. - if (originalType.getKind() != TypeKind.RECORD) { - return true; - } - BRecordType originalRecordType = (BRecordType) originalType; - LinkedHashMap fields = new LinkedHashMap<>(); - for (BField origField : originalRecordType.fields.values()) { - org.wso2.ballerinalang.compiler.util.Name origFieldName = origField.name; - String nameString = origFieldName.value; - - if (!validateRecordFieldDefaultValueForIntersection(diagnosticContext, origField, originalRecordType)) { - return false; - } - - BType recordFieldType = validateRecordField(diagnosticContext, newType, origField, constraint, env); - if (recordFieldType == symTable.semanticError) { - return false; - } - - BVarSymbol recordFieldSymbol = new BVarSymbol(origField.symbol.flags, origFieldName, - env.enclPkg.packageID, recordFieldType, - intersectionRecordSymbol, origField.pos, SOURCE); - - if (recordFieldType == symTable.neverType && Symbols.isFlagOn(recordFieldSymbol.flags, Flags.OPTIONAL)) { - recordFieldSymbol.flags &= (~Flags.REQUIRED); - recordFieldSymbol.flags |= Flags.OPTIONAL; - } - - if (getImpliedType(recordFieldType).tag == TypeTags.INVOKABLE && recordFieldType.tsymbol != null) { - BInvokableTypeSymbol tsymbol = (BInvokableTypeSymbol) recordFieldType.tsymbol; - BInvokableSymbol invokableSymbol = (BInvokableSymbol) recordFieldSymbol; - invokableSymbol.params = tsymbol.params == null ? null : new ArrayList<>(tsymbol.params); - invokableSymbol.restParam = tsymbol.restParam; - invokableSymbol.retType = tsymbol.returnType; - invokableSymbol.flags = tsymbol.flags; - } - - fields.put(nameString, new BField(origFieldName, null, recordFieldSymbol)); - intersectionRecordSymbol.scope.define(origFieldName, recordFieldSymbol); - } - newType.fields.putAll(fields); - - return true; - } - private boolean validateRecordFieldDefaultValueForIntersection(IntersectionContext diagnosticContext, BField field, BRecordType recordType) { @@ -5735,52 +3104,6 @@ private boolean validateRecordFieldDefaultValueForIntersection(IntersectionConte return true; } - private BType validateRecordField(IntersectionContext intersectionContext, - BRecordType newType, BField origField, BType constraint, SymbolEnv env) { - if (hasField(newType, origField)) { - return validateOverlappingFields(newType, origField); - } - - if (constraint == null) { - return origField.type; - } - - BType fieldType = getTypeIntersection(intersectionContext, origField.type, constraint, env); - if (fieldType.tag == TypeTags.NEVER && !Symbols.isOptional(origField.symbol)) { - return symTable.semanticError; - } - - if (fieldType != symTable.semanticError) { - return fieldType; - } - - if (Symbols.isOptional(origField.symbol)) { - return symTable.neverType; - } - - return symTable.semanticError; - } - - private boolean hasField(BRecordType recordType, BField origField) { - return recordType.fields.containsKey(origField.name.value); - } - - private BType validateOverlappingFields(BRecordType newType, BField origField) { - if (!hasField(newType, origField)) { - return origField.type; - } - - BField overlappingField = newType.fields.get(origField.name.value); - if (isAssignable(overlappingField.type, origField.type)) { - return overlappingField.type; - } - - if (isAssignable(origField.type, overlappingField.type)) { - return origField.type; - } - return symTable.semanticError; - } - private void removeErrorFromReadonlyType(List remainingTypes) { Iterator remainingIterator = remainingTypes.listIterator(); boolean addAnyAndReadOnly = false; @@ -5834,28 +3157,23 @@ private BType getRemainingType(BUnionType originalType, List removeTypes) return symTable.nullSet; } - return BUnionType.create(null, new LinkedHashSet<>(remainingTypes)); + return BUnionType.create(typeEnv(), null, new LinkedHashSet<>(remainingTypes)); } private BType getRemainingType(BFiniteType originalType, List removeTypes) { - Set remainingValueSpace = new LinkedHashSet<>(); - - for (BLangExpression valueExpr : originalType.getValueSpace()) { - boolean matchExists = false; - for (BType remType : removeTypes) { - if (isAssignable(valueExpr.getBType(), remType) || - isAssignableToFiniteType(remType, (BLangLiteral) valueExpr)) { - matchExists = true; - break; - } - } + SemType removeSemType = PredefinedType.NEVER; + for (BType removeType : removeTypes) { + removeSemType = SemTypes.union(removeSemType, removeType.semType()); + } - if (!matchExists) { - remainingValueSpace.add(valueExpr); + List newValueSpace = new ArrayList<>(); + for (SemNamedType semNamedType : originalType.valueSpace) { + if (!SemTypes.isSubtype(semTypeCtx, semNamedType.semType(), removeSemType)) { + newValueSpace.add(semNamedType); } } - if (remainingValueSpace.isEmpty()) { + if (newValueSpace.isEmpty()) { return symTable.semanticError; } @@ -5864,9 +3182,21 @@ private BType getRemainingType(BFiniteType originalType, List removeTypes originalType.tsymbol.pkgID, null, originalType.tsymbol.owner, originalType.tsymbol.pos, VIRTUAL); - BFiniteType intersectingFiniteType = new BFiniteType(finiteTypeSymbol, remainingValueSpace); - finiteTypeSymbol.type = intersectingFiniteType; - return intersectingFiniteType; + BFiniteType ft = new BFiniteType(finiteTypeSymbol, newValueSpace.toArray(SemNamedType[]::new)); + finiteTypeSymbol.type = ft; + return ft; + } + + public SemType getNilLiftType(SemType t) { + return Core.diff(t, PredefinedType.NIL); + } + + public SemType getErrorLiftType(SemType t) { + return Core.diff(t, PredefinedType.ERROR); + } + + public SemType getNilAndErrorLiftType(SemType t) { + return Core.diff(t, Core.union(PredefinedType.NIL, PredefinedType.ERROR)); } public BType getSafeType(BType bType, boolean liftNil, boolean liftError) { @@ -5876,16 +3206,16 @@ public BType getSafeType(BType bType, boolean liftNil, boolean liftError) { if (liftNil) { switch (type.tag) { case TypeTags.JSON: - return new BJSONType((BJSONType) type, false); + return BJSONType.newNilLiftedBJSONType((BJSONType) type); case TypeTags.ANY: - return new BAnyType(type.tag, type.tsymbol, false); + return BAnyType.newNilLiftedBAnyType(); case TypeTags.ANYDATA: - return new BAnydataType((BAnydataType) type, false); + return BAnydataType.newNilLiftedBAnydataType((BAnydataType) type); case TypeTags.READONLY: if (liftError) { return symTable.anyAndReadonly; } - return new BReadonlyType(type.tag, type.tsymbol, false); + return BReadonlyType.newNilLiftedBReadonlyType(); } } @@ -5895,7 +3225,7 @@ public BType getSafeType(BType bType, boolean liftNil, boolean liftError) { BUnionType unionType = (BUnionType) type; LinkedHashSet memTypes = new LinkedHashSet<>(unionType.getMemberTypes()); - BUnionType errorLiftedType = BUnionType.create(null, memTypes); + BUnionType errorLiftedType = BUnionType.create(typeEnv(), null, memTypes); if (liftNil) { errorLiftedType.remove(symTable.nilType); @@ -5909,7 +3239,7 @@ public BType getSafeType(BType bType, boolean liftNil, boolean liftError) { } } memTypes = bTypes; - errorLiftedType = BUnionType.create(null, memTypes); + errorLiftedType = BUnionType.create(typeEnv(), null, memTypes); } if (errorLiftedType.getMemberTypes().size() == 1) { @@ -5980,8 +3310,7 @@ public boolean isAllowedConstantType(BType type) { yield true; } case TypeTags.FINITE -> { - BLangExpression finiteValue = ((BFiniteType) type).getValueSpace().toArray(new BLangExpression[0])[0]; - yield isAllowedConstantType(finiteValue.getBType()); + yield isAllowedConstantType(SemTypeHelper.broadTypes(type.semType(), symTable).iterator().next()); } default -> false; }; @@ -6037,61 +3366,8 @@ public boolean isSubTypeOfErrorOrNilContainingNil(BUnionType type) { return false; } - for (BType memType : type.getMemberTypes()) { - BType referredMemType = getImpliedType(memType); - if (referredMemType.tag != TypeTags.NIL && referredMemType.tag != TypeTags.ERROR) { - return false; - } - } - return true; - } - - /** - * Type vector of size two, to hold the source and the target types. - * - * @since 0.982.0 - */ - private static class TypePair { - BType sourceType; - BType targetType; - - public TypePair(BType sourceType, BType targetType) { - this.sourceType = sourceType; - this.targetType = targetType; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof TypePair other)) { - return false; - } - - return this.sourceType.equals(other.sourceType) && this.targetType.equals(other.targetType); - } - - @Override - public int hashCode() { - return Objects.hash(sourceType, targetType); - } - } - - /** - * A functional interface for parameterizing the type of type checking that needs to be done on the source and - * target types. - * - * @since 0.995.0 - */ - private interface TypeEqualityPredicate { - boolean test(BType source, BType target, Set unresolvedTypes); - } - - /** - * A functional interface to validate numeric, string or xml type existence. - * - * @since 2201.1.0 - */ - private interface TypeExistenceValidationFunction { - boolean validate(BType type); + BasicTypeBitSet nilOrError = (BasicTypeBitSet) Core.union(PredefinedType.NIL, PredefinedType.ERROR); + return SemTypeHelper.isSubtypeSimpleNotNever(type, nilOrError); } public boolean hasFillerValue(BType type) { @@ -6115,7 +3391,7 @@ public boolean hasFillerValue(BType type) { case TypeTags.ARRAY: return checkFillerValue((BArrayType) type); case TypeTags.FINITE: - return checkFillerValue((BFiniteType) type); + return hasFiller(type.semType()); case TypeTags.UNION: return checkFillerValue((BUnionType) type); case TypeTags.OBJECT: @@ -6156,45 +3432,42 @@ private boolean checkFillerValue(BObjectType type) { } /** - * This will handle two types. Singleton : As singleton can have one value that value should it self be a valid fill - * value Union : 1. if nil is a member it is the fill values 2. else all the values should belong to same type and - * the default value for that type should be a member of the union precondition : value space should have at least - * one element + * Checks whether a SemType has a filler value. + *

+ * Note: this is similar to computeFiller() in nBallerina + *

+ * 1. if type contains nil, nil is the filler value.
+ * 2. if all values belong to a single basic type B, and the filler value for B also included in the values.
+ * 3. if type is a singleton, it is the filler value. + *

* - * @param type BFiniteType union or finite - * @return boolean whether type has a valid filler value or not + * @param t SemType to be checked + * @return whether there is a filler value */ - private boolean checkFillerValue(BFiniteType type) { - if (type.isNullable()) { - return true; - } - if (type.getValueSpace().size() == 1) { // For singleton types, that value is the implicit initial value + private boolean hasFiller(SemType t) { + if (Core.containsNil(t)) { return true; } - Iterator iterator = type.getValueSpace().iterator(); - BLangExpression firstElement = iterator.next(); - boolean defaultFillValuePresent = isImplicitDefaultValue(firstElement); - - while (iterator.hasNext()) { - BLangExpression value = iterator.next(); - if (!isSameBasicType(value.getBType(), firstElement.getBType())) { - return false; - } - if (!defaultFillValuePresent && isImplicitDefaultValue(value)) { - defaultFillValuePresent = true; - } - } - return defaultFillValuePresent; + return hasImplicitDefaultValue(t) || Core.singleShape(t).isPresent(); } - private boolean hasImplicitDefaultValue(Set valueSpace) { - for (BLangExpression expression : valueSpace) { - if (isImplicitDefaultValue(expression)) { - return true; - } + private boolean hasImplicitDefaultValue(SemType t) { + BasicTypeBitSet bitSet = Core.widenToBasicTypes(t); + Object value = null; + if (bitSet.equals(PredefinedType.BOOLEAN)) { + value = false; + } else if (bitSet.equals(PredefinedType.INT)) { + value = (long) 0; + } else if (bitSet.equals(PredefinedType.DECIMAL)) { + value = BigDecimal.valueOf(0); + } else if (bitSet.equals(PredefinedType.FLOAT)) { + value = (double) 0; + } else if (bitSet.equals(PredefinedType.STRING)) { + value = ""; } - return false; + + return value != null && (t instanceof BasicTypeBitSet || Core.containsConst(t, value)); } private boolean checkFillerValue(BUnionType type) { @@ -6211,9 +3484,9 @@ private boolean checkFillerValue(BUnionType type) { for (BType member : getAllTypes(type, true)) { if (member.tag == TypeTags.FINITE) { - Set uniqueValues = getValueTypes(((BFiniteType) member).getValueSpace()); - memberTypes.addAll(uniqueValues); - if (!hasFillerValue && hasImplicitDefaultValue(((BFiniteType) member).getValueSpace())) { + Set broadTypes = SemTypeHelper.broadTypes((BFiniteType) member, symTable); + memberTypes.addAll(broadTypes); + if (!hasFillerValue && hasImplicitDefaultValue(member.semType())) { hasFillerValue = true; } } else { @@ -6256,32 +3529,6 @@ private boolean isIntegerSubTypeTag(int typeTag) { return TypeTags.isIntegerTypeTag(typeTag) || typeTag == TypeTags.BYTE; } - private Set getValueTypes(Set valueSpace) { - Set uniqueType = new HashSet<>(); - for (BLangExpression expression : valueSpace) { - uniqueType.add(expression.getBType()); - } - return uniqueType; - } - - private boolean isImplicitDefaultValue(BLangExpression expression) { - if ((expression.getKind() == NodeKind.LITERAL) || (expression.getKind() == NodeKind.NUMERIC_LITERAL)) { - BLangLiteral literalExpression = (BLangLiteral) expression; - BType literalExprType = literalExpression.getBType(); - Object value = literalExpression.getValue(); - return switch (literalExprType.getKind()) { - case INT, BYTE -> value.equals(0L); - case STRING -> value == null || value.equals(""); - case DECIMAL -> value.equals(String.valueOf(0)) || value.equals(0L); - case FLOAT -> value.equals(String.valueOf(0.0)); - case BOOLEAN -> value.equals(Boolean.FALSE); - case NIL -> true; - default -> false; - }; - } - return false; - } - private boolean checkFillerValue(BRecordType type) { for (BField field : type.fields.values()) { if (Symbols.isFlagOn(field.symbol.flags, Flags.OPTIONAL)) { @@ -6295,138 +3542,146 @@ private boolean checkFillerValue(BRecordType type) { } private boolean checkFillerValue(BArrayType type) { - if (type.size == -1) { + if (type.getSize() == -1) { return true; } return hasFillerValue(type.eType); } /** - * Get result type of the query output. + * Check whether a type is an ordered type. * - * @param type type of query expression. - * @return result type. + * @param type type to be checked + * @return boolean whether the type is an ordered type or not */ - public BType resolveExprType(BType type) { - switch (type.tag) { - case TypeTags.STREAM: - return ((BStreamType) type).constraint; - case TypeTags.TABLE: - return ((BTableType) type).constraint; - case TypeTags.ARRAY: - return ((BArrayType) type).eType; - case TypeTags.UNION: - List exprTypes = new ArrayList<>(((BUnionType) type).getMemberTypes()); - for (BType returnType : exprTypes) { - switch (returnType.tag) { - case TypeTags.STREAM: - return ((BStreamType) returnType).constraint; - case TypeTags.TABLE: - return ((BTableType) returnType).constraint; - case TypeTags.ARRAY: - return ((BArrayType) returnType).eType; - case TypeTags.STRING: - case TypeTags.XML: - return returnType; - } + public boolean isOrderedType(BType type) { + return isOrderedType(type.semType()); + } + + /** + * Checks whether a SemType is an ordered type. + *
+ *

+ * A type is an ordered type if all values belong to one of (), int?, boolean?, decimal?, float?, string? types. + * Additionally, + *

    + *
  • [T...] is ordered, if T is ordered;
  • + *
  • [] is ordered;
  • + *
  • [T, rest] is ordered if T is ordered and [rest] is ordered.
  • + *
+ * + * @param t SemType to be checked + * @return boolean + */ + public boolean isOrderedType(SemType t) { + assert !Core.isNever(t); + SemType tButNil = Core.diff(t, PredefinedType.NIL); + BasicTypeBitSet basicTypeBitSet = Core.widenToBasicTypes(tButNil); + if (SemTypes.isSubtypeSimple(basicTypeBitSet, PredefinedType.SIMPLE_OR_STRING)) { + int bitCount = SemTypeHelper.bitCount(basicTypeBitSet.bitset); + return bitCount <= 1; + } + + if (SemTypes.isSubtypeSimple(tButNil, PredefinedType.LIST)) { + ListMemberTypes lmTypes = Core.listAllMemberTypesInner(typeCtx(), t); + for (SemType lmType : lmTypes.semTypes()) { + if (!isOrderedType(lmType)) { + return false; } - default: - return type; + } + return true; } + + return false; + } + + boolean comparable(BType t1, BType t2) { + return comparable(t1.semType(), t2.semType()); } /** - * Check whether a type is an ordered type. + * Checks whether a SemType pair is comparable. + *
+ *

+ * Note: this is similar to comparable() in nBallerina. However, nBallerina API does not have + * "There must be an ordered type to which the static type of both operands belong" part from spec, implemented + *

* - * @param type type. - * @param hasCycle whether there is a cycle. - * @return boolean whether the type is an ordered type or not. + * @param t1 first semType + * @param t2 second semType + * @return boolean */ - public boolean isOrderedType(BType type, boolean hasCycle) { - type = getImpliedType(type); - switch (type.tag) { - case TypeTags.UNION: - BUnionType unionType = (BUnionType) type; - if (hasCycle) { - return true; - } - if (unionType.isCyclic) { - hasCycle = true; - } - Set memberTypes = unionType.getMemberTypes(); - boolean allMembersOrdered = false; - BType firstTypeInUnion = getTypeWithEffectiveIntersectionTypes(getImpliedType( - memberTypes.stream().filter(m -> !isNil(m)).findFirst().orElse(memberTypes.iterator().next()))); - if (isNil(firstTypeInUnion)) { - // Union contains only the nil type. - return true; - } - boolean isFirstTypeInUnionFinite = firstTypeInUnion.tag == TypeTags.FINITE; - for (BType memType : memberTypes) { - memType = getImpliedType(memType); - if (isFirstTypeInUnionFinite && memType.tag == TypeTags.FINITE && !isNil(memType)) { - Set valSpace = ((BFiniteType) firstTypeInUnion).getValueSpace(); - BType baseExprType = valSpace.iterator().next().getBType(); - if (!checkValueSpaceHasSameType((BFiniteType) memType, baseExprType)) { - return false; - } - } else if (memType.tag == TypeTags.UNION || memType.tag == TypeTags.ARRAY || - memType.tag == TypeTags.TUPLE) { - if (isSameOrderedType(memType, firstTypeInUnion)) { - allMembersOrdered = true; - continue; - } - return false; - } else if (memType.tag != firstTypeInUnion.tag && !isNil(memType) && - !isIntOrStringType(memType.tag, firstTypeInUnion.tag)) { - return false; - } - allMembersOrdered = isOrderedType(memType, hasCycle); - if (!allMembersOrdered) { - break; - } - } - return allMembersOrdered; - case TypeTags.ARRAY: - BType elementType = ((BArrayType) type).eType; - return isOrderedType(elementType, hasCycle); - case TypeTags.TUPLE: - List tupleMemberTypes = ((BTupleType) type).getTupleTypes(); - for (BType memType : tupleMemberTypes) { - if (!isOrderedType(memType, hasCycle)) { - return false; - } - } - BType restType = ((BTupleType) type).restType; - return restType == null || isOrderedType(restType, hasCycle); - case TypeTags.FINITE: - boolean isValueSpaceOrdered = false; - Set valSpace = ((BFiniteType) type).getValueSpace(); - BType baseExprType = valSpace.iterator().next().getBType(); - for (BLangExpression expr : valSpace) { - if (!checkValueSpaceHasSameType((BFiniteType) type, baseExprType)) { - return false; - } - isValueSpaceOrdered = isOrderedType(expr.getBType(), hasCycle); - if (!isValueSpaceOrdered) { - break; - } - } - return isValueSpaceOrdered; - default: - return isSimpleBasicType(type.tag); + boolean comparable(SemType t1, SemType t2) { + assert !Core.isNever(t1) && !Core.isNever(t2); + if (PredefinedType.NIL.equals(t1)) { + return isOrderedType(t2); } + + if (PredefinedType.NIL.equals(t2)) { + return isOrderedType(t1); + } + + SemType tButNil = Core.diff(Core.union(t1, t2), PredefinedType.NIL); + BasicTypeBitSet basicTypeBitSet = Core.widenToBasicTypes(tButNil); + if (SemTypes.isSubtypeSimple(basicTypeBitSet, PredefinedType.SIMPLE_OR_STRING)) { + int bitCount = SemTypeHelper.bitCount(basicTypeBitSet.bitset); + return bitCount <= 1; + } + if (SemTypes.isSubtypeSimple(tButNil, PredefinedType.LIST)) { + return comparableNillableList(typeCtx(), t1, t2); + } + return false; } - private boolean isIntOrStringType(int firstTypeTag, int secondTypeTag) { - return ((TypeTags.isIntegerTypeTag(firstTypeTag) || firstTypeTag == TypeTags.BYTE) && - (TypeTags.isIntegerTypeTag(secondTypeTag) || secondTypeTag == TypeTags.BYTE)) || - ((TypeTags.isStringTypeTag(firstTypeTag)) && (TypeTags.isStringTypeTag(secondTypeTag))); + private boolean comparableNillableList(Context cx, SemType t1, SemType t2) { + SemTypePair semPair = SemTypePair.from(t1, t2); + Boolean b = cx.comparableMemo.get(semPair); + if (b != null) { + return b; + } + + ListMemberTypes lmTypes1 = Core.listAllMemberTypesInner(cx, t1); + ListMemberTypes lmTypes2 = Core.listAllMemberTypesInner(cx, t2); + CombinedRange[] combinedRanges = combineRanges( + lmTypes1.ranges().toArray(Range[]::new), + lmTypes2.ranges().toArray(Range[]::new) + ); + SemType accum = PredefinedType.NIL; + for (CombinedRange combinedRange : combinedRanges) { + Long i1 = combinedRange.i1(); + Long i2 = combinedRange.i2(); + if (i1 == null) { + SemType lmType = lmTypes2.semTypes().get(Math.toIntExact(i2)); + if (!comparable(accum, lmType)) { + return false; + } + accum = Core.union(accum, lmType); + continue; + } + + if (i2 == null) { + SemType lmType = lmTypes1.semTypes().get(Math.toIntExact(i1)); + if (!comparable(accum, lmType)) { + return false; + } + accum = Core.union(accum, lmType); + continue; + } + + + if (!comparable(lmTypes1.semTypes().get(Math.toIntExact(i1)), + lmTypes2.semTypes().get(Math.toIntExact(i2)))) { + cx.comparableMemo.put(semPair, false); + return false; + } + } + cx.comparableMemo.put(semPair, true); + return true; } public boolean isSubTypeOfSimpleBasicTypeOrString(BType bType) { return isAssignable(getImpliedType(bType), - BUnionType.create(null, symTable.nilType, symTable.booleanType, symTable.intType, + BUnionType.create(typeEnv(), null, symTable.nilType, symTable.booleanType, symTable.intType, symTable.floatType, symTable.decimalType, symTable.stringType)); } @@ -6455,69 +3710,25 @@ public BType findCompatibleType(BType type) { yield findCompatibleType(memberTypes.iterator().next()); } default -> { - Set valueSpace = ((BFiniteType) type).getValueSpace(); - yield findCompatibleType(valueSpace.iterator().next().getBType()); + Set broadTypes = SemTypeHelper.broadTypes(type.semType(), symTable); + assert broadTypes.size() == 1; // all values should belong to a single basic type + yield broadTypes.iterator().next(); } }; } public boolean isNonNilSimpleBasicTypeOrString(BType bType) { - BType type = getImpliedType(bType); - if (type.tag == TypeTags.UNION) { - Set memberTypes = ((BUnionType) type).getMemberTypes(); - for (BType member : memberTypes) { - BType memType = getImpliedType(member); - if (memType.tag == TypeTags.FINITE || memType.tag == TypeTags.UNION) { - isNonNilSimpleBasicTypeOrString(memType); - continue; - } - if (memType.tag == TypeTags.NIL || !isSimpleBasicType(memType.tag)) { - return false; - } - } - return true; - } else if (type.tag == TypeTags.FINITE) { - for (BLangExpression expression: ((BFiniteType) type).getValueSpace()) { - BType exprType = getImpliedType(expression.getBType()); - if (exprType.tag == TypeTags.NIL || !isSimpleBasicType(exprType.tag)) { - return false; - } - } - return true; - } - return type.tag != TypeTags.NIL && isSimpleBasicType(type.tag); + return SemTypeHelper.isSubtypeSimpleNotNever(bType, + (BasicTypeBitSet) Core.diff(PredefinedType.SIMPLE_OR_STRING, PredefinedType.NIL)); } public boolean isSubTypeOfReadOnlyOrIsolatedObjectUnion(BType bType) { - BType type = getImpliedType(bType); - if (isInherentlyImmutableType(type) || Symbols.isFlagOn(type.flags, Flags.READONLY)) { - return true; - } - - int tag = type.tag; - - if (tag == TypeTags.OBJECT) { - return isIsolated(type); - } - - if (tag != TypeTags.UNION) { - return false; - } - - for (BType memberType : ((BUnionType) type).getMemberTypes()) { - if (!isSubTypeOfReadOnlyOrIsolatedObjectUnion(memberType)) { - return false; - } - } - return true; - } - - private boolean isIsolated(BType type) { - return Symbols.isFlagOn(type.flags, Flags.ISOLATED); + return SemTypes.isSubtype(semTypeCtx, bType.semType(), + SemTypes.union(PredefinedType.VAL_READONLY, createIsolatedObject(semTypeCtx))); } private boolean isImmutable(BType type) { - return Symbols.isFlagOn(type.flags, Flags.READONLY); + return Symbols.isFlagOn(type.getFlags(), Flags.READONLY); } BType getTypeWithoutNil(BType type) { @@ -6542,11 +3753,19 @@ BType getTypeWithoutNil(BType type) { return nonNilTypes.get(0); } - return BUnionType.create(null, new LinkedHashSet<>(nonNilTypes)); + return BUnionType.create(typeEnv(), null, new LinkedHashSet<>(nonNilTypes)); } public boolean isFixedLengthTuple(BTupleType bTupleType) { - return bTupleType.restType == null || isNeverTypeOrStructureTypeWithARequiredNeverMember(bTupleType.restType); + return isFixedLengthList(bTupleType); + } + + public boolean isFixedLengthList(BType type) { + // Using int:MIN_VALUE to project the rest type. + // This checks the type of effectively infinite list member, which should be the rest type. + SemType rest = Core.listMemberTypeInnerVal(semTypeCtx, type.semType(), + IntSubtype.intConst(Long.MAX_VALUE)); + return Core.isNever(rest); } public boolean isNeverTypeOrStructureTypeWithARequiredNeverMember(BType type) { @@ -6611,25 +3830,18 @@ boolean isNeverTypeOrStructureTypeWithARequiredNeverMember(BType type, Set memberTypes = ((BUnionType) type).getMemberTypes(); - return memberTypes.stream().allMatch(this::isNeverType); - } - return false; + return Core.isNever(type.semType()); } boolean isSingletonType(BType bType) { BType type = getImpliedType(bType); - return type.tag == TypeTags.FINITE && ((BFiniteType) type).getValueSpace().size() == 1; + return type.tag == TypeTags.FINITE && Core.singleShape(type.semType()).isPresent(); } boolean isSameSingletonType(BFiniteType type1, BFiniteType type2) { - BLangLiteral expr1 = (BLangLiteral) type1.getValueSpace().iterator().next(); - BLangLiteral expr2 = (BLangLiteral) type2.getValueSpace().iterator().next(); - return expr1.value.equals(expr2.value); + SemType t1 = type1.semType(); + SemType t2 = type2.semType(); + return SemTypes.isSameType(semTypeCtx, t1, t2); } public static void addImmutableType(SymbolTable symTable, PackageID packageId, @@ -6672,11 +3884,19 @@ public static Optional getImmutableType(SymbolTable symTable, return Optional.empty(); } + public static Name getImmutableTypeName(String origName) { + if (origName.isEmpty()) { + return Names.EMPTY; + } + + return Names.fromString("(".concat(origName).concat(AND_READONLY_SUFFIX).concat(")")); + } + public static String getPackageIdString(PackageID packageID) { return packageID.isTestPkg ? packageID.toString() + "_testable" : packageID.toString(); } - private static class ListenerValidationModel { + private class ListenerValidationModel { private final Types types; private final SymbolTable symtable; private final BType serviceNameType; @@ -6690,7 +3910,8 @@ public ListenerValidationModel(Types types, SymbolTable symTable) { this.types = types; this.symtable = symTable; this.serviceNameType = - BUnionType.create(null, symtable.stringType, symtable.arrayStringType, symtable.nilType); + BUnionType.create(symTable.typeEnv(), null, symtable.stringType, symtable.arrayStringType, + symtable.nilType); } boolean isValidListener() { @@ -6795,21 +4016,7 @@ private boolean checkAttachMethod(BAttachedFunction func) { } private boolean isServiceObject(BType bType) { - BType type = getImpliedType(bType); - if (type.tag == TypeTags.UNION) { - for (BType memberType : ((BUnionType) type).getMemberTypes()) { - if (!isServiceObject(memberType)) { - return false; - } - } - return true; - } - - if (type.tag != TypeTags.OBJECT) { - return false; - } - - return Symbols.isService(type.tsymbol); + return types.isSubtype(bType, createServiceObject(semTypeCtx)); } } @@ -7068,9 +4275,8 @@ private void populateBasicTypes(BType type, Set basicTypes) { basicTypes.add(BasicTypes.OBJECT); return; case TypeTags.FINITE: - for (BLangExpression expression : ((BFiniteType) type).getValueSpace()) { - populateBasicTypes(expression.getBType(), basicTypes); - } + SemType semType = type.semType(); + populateBasicTypes(semType, basicTypes); return; case TypeTags.HANDLE: basicTypes.add(BasicTypes.HANDLE); @@ -7083,6 +4289,35 @@ private void populateBasicTypes(BType type, Set basicTypes) { } } + private void populateBasicTypes(SemType t, Set basicTypes) { + int bitset; + if (t instanceof BasicTypeBitSet b) { + bitset = b.bitset; + } else { + ComplexSemType cst = (ComplexSemType) t; + bitset = cst.all() | cst.some(); + } + + if ((bitset & PredefinedType.NIL.bitset) != 0) { + basicTypes.add(BasicTypes.NIL); + } + if ((bitset & PredefinedType.BOOLEAN.bitset) != 0) { + basicTypes.add(BasicTypes.BOOLEAN); + } + if ((bitset & PredefinedType.INT.bitset) != 0) { + basicTypes.add(BasicTypes.INT); + } + if ((bitset & PredefinedType.FLOAT.bitset) != 0) { + basicTypes.add(BasicTypes.FLOAT); + } + if ((bitset & PredefinedType.DECIMAL.bitset) != 0) { + basicTypes.add(BasicTypes.DECIMAL); + } + if ((bitset & PredefinedType.STRING.bitset) != 0) { + basicTypes.add(BasicTypes.STRING); + } + } + private enum BasicTypes { NIL, BOOLEAN, @@ -7214,4 +4449,14 @@ public boolean isMappingConstructorCompatibleType(BType type) { int tag = getImpliedType(type).tag; return tag == TypeTags.RECORD || tag == TypeTags.MAP; } + + // Maybe it is a better idea to directly make Env accessible via the CompilerContext but that means SemType module + // will have a dependency on compiler + public Env typeEnv() { + return semTypeCtx.env; + } + + public Context typeCtx() { + return semTypeCtx; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/SymbolTable.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/SymbolTable.java index 5d3bfba21165..a1109b10a97c 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/SymbolTable.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/SymbolTable.java @@ -18,6 +18,9 @@ package org.wso2.ballerinalang.compiler.semantics.model; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.PackageID; import org.ballerinalang.model.symbols.SymbolOrigin; @@ -26,7 +29,6 @@ import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.diagnostic.BLangDiagnosticLocation; import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BConstructorSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BErrorTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BOperatorSymbol; @@ -48,8 +50,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BReadonlyType; @@ -76,7 +76,6 @@ import org.wso2.ballerinalang.util.Lists; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -114,49 +113,45 @@ public class SymbolTable { public final Scope rootScope; public final BType noType = new BNoType(TypeTags.NONE); - public final BType nilType = new BNilType(); - public final BType neverType = new BNeverType(); - public final BType intType = new BType(TypeTags.INT, null, Flags.READONLY); - public final BType byteType = new BType(TypeTags.BYTE, null, Flags.READONLY); - public final BType floatType = new BType(TypeTags.FLOAT, null, Flags.READONLY); - public final BType decimalType = new BType(TypeTags.DECIMAL, null, Flags.READONLY); - public final BType stringType = new BType(TypeTags.STRING, null, Flags.READONLY); - public final BType booleanType = new BType(TypeTags.BOOLEAN, null, Flags.READONLY); - - public final BType anyType = new BAnyType(TypeTags.ANY, null); - public final BMapType mapType = new BMapType(TypeTags.MAP, anyType, null); - public final BMapType mapStringType = new BMapType(TypeTags.MAP, stringType, null); - - public final BFutureType futureType = new BFutureType(TypeTags.FUTURE, nilType, null); - public final BArrayType arrayType = new BArrayType(anyType); - public final BArrayType byteArrayType = new BArrayType(byteType); - public final BArrayType arrayStringType = new BArrayType(stringType); + public final BType nilType = BType.createNilType(); + public final BType neverType = BType.createNeverType(); + public final BType intType = new BType(TypeTags.INT, null, Flags.READONLY, PredefinedType.INT); + public final BType byteType = new BType(TypeTags.BYTE, null, Flags.READONLY, PredefinedType.BYTE); + public final BType floatType = new BType(TypeTags.FLOAT, null, Flags.READONLY, PredefinedType.FLOAT); + public final BType decimalType = new BType(TypeTags.DECIMAL, null, Flags.READONLY, PredefinedType.DECIMAL); + public final BType stringType = new BType(TypeTags.STRING, null, Flags.READONLY, PredefinedType.STRING); + public final BType booleanType = new BType(TypeTags.BOOLEAN, null, Flags.READONLY, PredefinedType.BOOLEAN); + + public final BType anyType = new BAnyType(); + public final BMapType mapType; + public final BMapType mapStringType; + + public final BFutureType futureType; + public final BArrayType arrayType; + public final BArrayType byteArrayType; + public final BArrayType arrayStringType; BVarSymbol varSymbol = new BVarSymbol(0, null, null, noType, null, null, SymbolOrigin.VIRTUAL); - public final BType tupleType = new BTupleType(Lists.of(new BTupleMember(noType, varSymbol))); - public final BType recordType = new BRecordType(null); - public final BType stringArrayType = new BArrayType(stringType); - public final BType handleType = new BHandleType(TypeTags.HANDLE, null); - public final BTypedescType typeDesc = new BTypedescType(this.anyType, null); - public final BType readonlyType = new BReadonlyType(TypeTags.READONLY, null); - public final BType pathParamAllowedType = BUnionType.create(null, - intType, stringType, floatType, booleanType, decimalType); + public final BType tupleType; + public final BType recordType; + public final BType stringArrayType; + public final BType handleType = new BHandleType(); + public final BTypedescType typeDesc; + public final BType readonlyType = new BReadonlyType(); + public final BType pathParamAllowedType; public final BIntersectionType anyAndReadonly; public BUnionType anyAndReadonlyOrError; - public final BType errorIntersectionType = new BErrorType(null, null); - - public final BType semanticError = new BType(TypeTags.SEMANTIC_ERROR, null); - public final BType nullSet = new BType(TypeTags.NULL_SET, null); - public final BType invokableType = new BInvokableType(null, null, null, null); + public final BType semanticError = new BType(TypeTags.SEMANTIC_ERROR, null, PredefinedType.NEVER); + public final BType nullSet = new BType(TypeTags.NULL_SET, null, PredefinedType.NEVER); + public final BType invokableType; public final BType empty = new BType(TypeTags.EMPTY, null); - public BConstructorSymbol errorConstructor; public BUnionType anyOrErrorType; public BUnionType pureType; public BUnionType errorOrNilType; - public BFiniteType trueType; - public BFiniteType falseType; + public BType trueType; + public BType falseType; public BObjectType intRangeType; public BMapType mapAllType; public BArrayType arrayAllType; @@ -164,23 +159,25 @@ public class SymbolTable { public BObjectType iterableType; // builtin subtypes - public final BIntSubType signed32IntType = new BIntSubType(TypeTags.SIGNED32_INT, Names.SIGNED32); - public final BIntSubType signed16IntType = new BIntSubType(TypeTags.SIGNED16_INT, Names.SIGNED16); - public final BIntSubType signed8IntType = new BIntSubType(TypeTags.SIGNED8_INT, Names.SIGNED8); - public final BIntSubType unsigned32IntType = new BIntSubType(TypeTags.UNSIGNED32_INT, Names.UNSIGNED32); - public final BIntSubType unsigned16IntType = new BIntSubType(TypeTags.UNSIGNED16_INT, Names.UNSIGNED16); - public final BIntSubType unsigned8IntType = new BIntSubType(TypeTags.UNSIGNED8_INT, Names.UNSIGNED8); - public final BStringSubType charStringType = new BStringSubType(TypeTags.CHAR_STRING, Names.CHAR); - public final BXMLSubType xmlElementType = new BXMLSubType(TypeTags.XML_ELEMENT, Names.XML_ELEMENT); - public final BXMLSubType xmlPIType = new BXMLSubType(TypeTags.XML_PI, Names.XML_PI); - public final BXMLSubType xmlCommentType = new BXMLSubType(TypeTags.XML_COMMENT, Names.XML_COMMENT); - public final BXMLSubType xmlTextType = new BXMLSubType(TypeTags.XML_TEXT, Names.XML_TEXT, Flags.READONLY); - public final BRegexpType regExpType = new BRegexpType(TypeTags.REGEXP, Names.REGEXP_TYPE); + public final BIntSubType signed32IntType = BIntSubType.SIGNED32; + public final BIntSubType signed16IntType = BIntSubType.SIGNED16; + public final BIntSubType signed8IntType = BIntSubType.SIGNED8; + public final BIntSubType unsigned32IntType = BIntSubType.UNSIGNED32; + public final BIntSubType unsigned16IntType = BIntSubType.UNSIGNED16; + public final BIntSubType unsigned8IntType = BIntSubType.UNSIGNED8; + + public final BStringSubType charStringType = BStringSubType.CHAR; + + public final BXMLSubType xmlElementType = BXMLSubType.XML_ELEMENT; + public final BXMLSubType xmlPIType = BXMLSubType.XML_PI; + public final BXMLSubType xmlCommentType = BXMLSubType.XML_COMMENT; + public final BXMLSubType xmlTextType = BXMLSubType.XML_TEXT; + + public final BRegexpType regExpType = new BRegexpType(); public final BType xmlNeverType = new BXMLType(neverType, null); - public final BType xmlElementSeqType = new BXMLType(xmlElementType, null); - public final BType xmlType = new BXMLType(BUnionType.create(null, xmlElementType, xmlCommentType, - xmlPIType, xmlTextType), null); + public final BType xmlType; + public final BType xmlElementSeqType = new BXMLType(xmlElementType, null); public BAnydataType anydataType; public BArrayType arrayAnydataType; @@ -225,6 +222,7 @@ public class SymbolTable { private final Names names; private final Types types; + public Map pkgEnvMap = new HashMap<>(); public Map predeclaredModules = new HashMap<>(); public Map> immutableTypeMaps = new HashMap<>(); @@ -244,7 +242,7 @@ private SymbolTable(CompilerContext context) { this.names = Names.getInstance(context); this.types = Types.getInstance(context); - this.rootPkgNode = (BLangPackage) TreeBuilder.createPackageNode(); + this.rootPkgNode = (BLangPackage) TreeBuilder.createPackageNode(types.typeEnv()); this.rootPkgSymbol = new BPackageSymbol(PackageID.ANNOTATIONS, null, null, BUILTIN); this.builtinPos = new BLangDiagnosticLocation(Names.EMPTY.value, -1, -1, -1, -1); @@ -263,15 +261,10 @@ private SymbolTable(CompilerContext context) { initializeType(decimalType, TypeKind.DECIMAL.typeName(), BUILTIN); initializeType(stringType, TypeKind.STRING.typeName(), BUILTIN); initializeType(booleanType, TypeKind.BOOLEAN.typeName(), BUILTIN); - initializeType(xmlType, TypeKind.XML.typeName(), BUILTIN); - initializeType(mapType, TypeKind.MAP.typeName(), VIRTUAL); - initializeType(mapStringType, TypeKind.MAP.typeName(), VIRTUAL); - initializeType(futureType, TypeKind.FUTURE.typeName(), BUILTIN); initializeType(anyType, TypeKind.ANY.typeName(), BUILTIN); initializeType(nilType, TypeKind.NIL.typeName(), BUILTIN); initializeType(neverType, TypeKind.NEVER.typeName(), BUILTIN); initializeType(handleType, TypeKind.HANDLE.typeName(), BUILTIN); - initializeType(typeDesc, TypeKind.TYPEDESC.typeName(), BUILTIN); initializeType(readonlyType, TypeKind.READONLY.typeName(), BUILTIN); // Define subtypes @@ -296,22 +289,41 @@ private SymbolTable(CompilerContext context) { falseLiteral.setBType(this.booleanType); falseLiteral.value = Boolean.FALSE; + arrayType = new BArrayType(types.typeEnv(), anyType); + byteArrayType = new BArrayType(types.typeEnv(), byteType); + arrayStringType = new BArrayType(types.typeEnv(), stringType); + stringArrayType = new BArrayType(types.typeEnv(), stringType); + + mapType = new BMapType(typeEnv(), TypeTags.MAP, anyType, null); + mapStringType = new BMapType(typeEnv(), TypeTags.MAP, stringType, null); + initializeType(mapType, TypeKind.MAP.typeName(), VIRTUAL); + initializeType(mapStringType, TypeKind.MAP.typeName(), VIRTUAL); + + pathParamAllowedType = BUnionType.create(types.typeEnv(), null, + intType, stringType, floatType, booleanType, decimalType); + tupleType = new BTupleType(types.typeEnv(), Lists.of(new BTupleMember(noType, varSymbol))); + recordType = new BRecordType(typeEnv(), null); + invokableType = new BInvokableType(types.typeEnv(), List.of(), null, null, null); + + xmlType = new BXMLType(BUnionType.create(types.typeEnv(), null, xmlElementType, xmlCommentType, + xmlPIType, xmlTextType), null); + futureType = new BFutureType(types.typeEnv(), nilType, null, PredefinedType.FUTURE); + typeDesc = new BTypedescType(types.typeEnv(), this.anyType, null, PredefinedType.TYPEDESC); + initializeType(xmlType, TypeKind.XML.typeName(), BUILTIN); + initializeType(futureType, TypeKind.FUTURE.typeName(), BUILTIN); + initializeType(typeDesc, TypeKind.TYPEDESC.typeName(), BUILTIN); + defineCyclicUnionBasedInternalTypes(); - BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, Flags.PUBLIC, - Names.fromString("$anonType$TRUE"), - rootPkgNode.packageID, null, rootPkgNode.symbol.owner, - this.builtinPos, VIRTUAL); - this.trueType = new BFiniteType(finiteTypeSymbol, new HashSet<>() {{ - add(trueLiteral); - }}); + BTypeSymbol trueFiniteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, Flags.PUBLIC, + Names.fromString("$anonType$TRUE"), rootPkgNode.packageID, null, rootPkgNode.symbol.owner, + this.builtinPos, VIRTUAL); + this.trueType = BFiniteType.newSingletonBFiniteType(trueFiniteTypeSymbol, SemTypes.booleanConst(true)); BTypeSymbol falseFiniteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, Flags.PUBLIC, Names.fromString("$anonType$FALSE"), rootPkgNode.packageID, null, rootPkgNode.symbol.owner, this.builtinPos, VIRTUAL); - this.falseType = new BFiniteType(falseFiniteTypeSymbol, new HashSet<>() {{ - add(falseLiteral); - }}); + this.falseType = BFiniteType.newSingletonBFiniteType(falseFiniteTypeSymbol, SemTypes.booleanConst(false)); this.anyAndReadonly = ImmutableTypeCloner.getImmutableIntersectionType(this.anyType, this, names, this.types, @@ -319,7 +331,7 @@ private SymbolTable(CompilerContext context) { initializeType(this.anyAndReadonly, this.anyAndReadonly.effectiveType.name.getValue(), BUILTIN); // Initialize the invokable type - this.invokableType.flags = Flags.ANY_FUNCTION; + this.invokableType.setFlags(Flags.ANY_FUNCTION); BInvokableTypeSymbol tSymbol = Symbols.createInvokableTypeSymbol(SymTag.FUNCTION_TYPE, Flags.ANY_FUNCTION, rootPkgSymbol.pkgID, this.invokableType, rootPkgNode.symbol.scope.owner, builtinPos, BUILTIN); tSymbol.params = null; @@ -331,7 +343,7 @@ private SymbolTable(CompilerContext context) { } private void defineReadonlyCompoundType() { - anyAndReadonlyOrError = BUnionType.create(null, anyAndReadonly, errorType); + anyAndReadonlyOrError = BUnionType.create(typeEnv(), null, anyAndReadonly, errorType); } public BType getTypeFromTag(int tag) { @@ -639,9 +651,7 @@ private void defineIntegerUnaryOperations() { } private BUnionType getNilableBType(BType type) { - BUnionType nilableType = BUnionType.create(null, type, nilType); - nilableType.setNullable(true); - return nilableType; + return BUnionType.create(typeEnv(), null, type, nilType); } private void defineNilableIntegerUnaryOperations() { @@ -1103,7 +1113,7 @@ private void defineUnaryOperator(OperatorKind kind, private void defineOperator(Name name, List paramTypes, BType retType) { - BInvokableType opType = new BInvokableType(paramTypes, retType, null); + BInvokableType opType = new BInvokableType(typeEnv(), paramTypes, retType, null); BOperatorSymbol symbol = new BOperatorSymbol(name, rootPkgSymbol.pkgID, opType, rootPkgSymbol, this.builtinPos, BUILTIN); @@ -1123,7 +1133,7 @@ private void defineCyclicUnionBasedInternalTypes() { } private void defineCloneableCyclicTypeAndDependentTypes() { - cloneableType = BUnionType.create(null, readonlyType, xmlType); + cloneableType = BUnionType.create(typeEnv(), null, readonlyType, xmlType); addCyclicArrayMapTableOfMapMembers(cloneableType); // `cloneableType` and its symbol gets replaced by `Cloneable` type defined in lang value module. To prevent @@ -1132,31 +1142,31 @@ private void defineCloneableCyclicTypeAndDependentTypes() { cloneableType.tsymbol = new BTypeSymbol(SymTag.TYPE, Flags.PRIVATE, Names.CLONEABLE, rootPkgSymbol.pkgID, cloneableType, rootPkgSymbol, builtinPos, BUILTIN); - detailType = new BMapType(TypeTags.MAP, cloneableType, null); - errorType = new BErrorType(null, detailType); + detailType = new BMapType(typeEnv(), TypeTags.MAP, cloneableType, null); + errorType = new BErrorType(typeEnv(), null, detailType); errorType.tsymbol = new BErrorTypeSymbol(SymTag.ERROR, Flags.PUBLIC, Names.ERROR, rootPkgSymbol.pkgID, errorType, rootPkgSymbol, builtinPos, BUILTIN); - errorOrNilType = BUnionType.create(null, errorType, nilType); - anyOrErrorType = BUnionType.create(null, anyType, errorType); + errorOrNilType = BUnionType.create(typeEnv(), null, errorType, nilType); + anyOrErrorType = BUnionType.create(typeEnv(), null, anyType, errorType); - mapAllType = new BMapType(TypeTags.MAP, anyOrErrorType, null); - arrayAllType = new BArrayType(anyOrErrorType); + mapAllType = new BMapType(typeEnv(), TypeTags.MAP, anyOrErrorType, null); + arrayAllType = new BArrayType(typeEnv(), anyOrErrorType); typeDesc.constraint = anyOrErrorType; futureType.constraint = anyOrErrorType; - pureType = BUnionType.create(null, anydataType, errorType); - streamType = new BStreamType(TypeTags.STREAM, pureType, nilType, null); - tableType = new BTableType(TypeTags.TABLE, pureType, null); + pureType = BUnionType.create(typeEnv(), null, anydataType, errorType); + streamType = new BStreamType(typeEnv(), TypeTags.STREAM, pureType, nilType, null); + tableType = new BTableType(typeEnv(), pureType, null); initializeType(streamType, TypeKind.STREAM.typeName(), BUILTIN); initializeType(tableType, TypeKind.TABLE.typeName(), BUILTIN); } private void addCyclicArrayMapTableOfMapMembers(BUnionType unionType) { - BArrayType arrayCloneableType = new BArrayType(unionType); - BMapType mapCloneableType = new BMapType(TypeTags.MAP, unionType, null); - BType tableMapCloneableType = new BTableType(TypeTags.TABLE, mapCloneableType, null); + BArrayType arrayCloneableType = new BArrayType(typeEnv(), unionType); + BMapType mapCloneableType = new BMapType(typeEnv(), TypeTags.MAP, unionType, null); + BType tableMapCloneableType = new BTableType(typeEnv(), mapCloneableType, null); unionType.add(arrayCloneableType); unionType.add(mapCloneableType); unionType.add(tableMapCloneableType); @@ -1164,15 +1174,16 @@ private void addCyclicArrayMapTableOfMapMembers(BUnionType unionType) { } private void defineJsonCyclicTypeAndDependentTypes() { - BUnionType jsonInternal = BUnionType.create(null, nilType, booleanType, intType, floatType, decimalType, + BUnionType jsonInternal = + BUnionType.create(typeEnv(), null, nilType, booleanType, intType, floatType, decimalType, stringType); - BArrayType arrayJsonTypeInternal = new BArrayType(jsonInternal); - BMapType mapJsonTypeInternal = new BMapType(TypeTags.MAP, jsonInternal, null); + BArrayType arrayJsonTypeInternal = new BArrayType(typeEnv(), jsonInternal); + BMapType mapJsonTypeInternal = new BMapType(typeEnv(), TypeTags.MAP, jsonInternal, null); jsonInternal.add(arrayJsonTypeInternal); jsonInternal.add(mapJsonTypeInternal); jsonInternal.isCyclic = true; - jsonType = new BJSONType(jsonInternal); + jsonType = new BJSONType(types.typeCtx(), jsonInternal); PackageID pkgID = rootPkgSymbol.pkgID; Optional immutableType = Types.getImmutableType(this, pkgID, jsonInternal); if (immutableType.isPresent()) { @@ -1181,16 +1192,17 @@ private void defineJsonCyclicTypeAndDependentTypes() { jsonType.tsymbol = new BTypeSymbol(SymTag.TYPE, Flags.PUBLIC, Names.JSON, pkgID, jsonType, rootPkgSymbol, builtinPos, BUILTIN); - arrayJsonType = new BArrayType(jsonType); - mapJsonType = new BMapType(TypeTags.MAP, jsonType, null); + arrayJsonType = new BArrayType(typeEnv(), jsonType); + mapJsonType = new BMapType(typeEnv(), TypeTags.MAP, jsonType, null); } private void defineAnydataCyclicTypeAndDependentTypes() { - BUnionType anyDataInternal = BUnionType.create(null, nilType, booleanType, intType, floatType, decimalType, + BUnionType anyDataInternal = + BUnionType.create(typeEnv(), null, nilType, booleanType, intType, floatType, decimalType, stringType, xmlType); addCyclicArrayMapTableOfMapMembers(anyDataInternal); - anydataType = new BAnydataType(anyDataInternal); + anydataType = new BAnydataType(types.typeCtx(), anyDataInternal); PackageID pkgID = rootPkgSymbol.pkgID; Optional immutableType = Types.getImmutableType(this, pkgID, anyDataInternal); if (immutableType.isPresent()) { @@ -1199,10 +1211,15 @@ private void defineAnydataCyclicTypeAndDependentTypes() { anydataType.tsymbol = new BTypeSymbol(SymTag.TYPE, Flags.PUBLIC, Names.ANYDATA, pkgID, anydataType, rootPkgSymbol, builtinPos, BUILTIN); - arrayAnydataType = new BArrayType(anydataType); - mapAnydataType = new BMapType(TypeTags.MAP, anydataType, null); - anydataOrReadonly = BUnionType.create(null, anydataType, readonlyType); + arrayAnydataType = new BArrayType(typeEnv(), anydataType); + mapAnydataType = new BMapType(typeEnv(), TypeTags.MAP, anydataType, null); + anydataOrReadonly = BUnionType.create(typeEnv(), null, anydataType, readonlyType); initializeType(mapAnydataType, TypeKind.MAP.typeName(), VIRTUAL); } + + public Env typeEnv() { + assert types.typeEnv() != null; + return types.typeEnv(); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/TypeVisitor.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/TypeVisitor.java index 86e84538855b..9cf5163e54c2 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/TypeVisitor.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/TypeVisitor.java @@ -22,7 +22,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; @@ -32,7 +31,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; @@ -47,70 +45,77 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BTypedescType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; +import org.wso2.ballerinalang.compiler.util.TypeTags; /** * Visit ballerina types and maps them to instances T. * * @since 0.995.0 */ -public interface TypeVisitor { +public abstract class TypeVisitor { - void visit(BAnnotationType bAnnotationType); + public abstract void visit(BAnnotationType bAnnotationType); - void visit(BArrayType bArrayType); + public abstract void visit(BArrayType bArrayType); - void visit(BBuiltInRefType bBuiltInRefType); + public abstract void visit(BAnyType bAnyType); - void visit(BAnyType bAnyType); + public abstract void visit(BAnydataType bAnydataType); - void visit(BAnydataType bAnydataType); + public abstract void visit(BErrorType bErrorType); - void visit(BErrorType bErrorType); + public abstract void visit(BFiniteType bFiniteType); - void visit(BFiniteType bFiniteType); + public abstract void visit(BInvokableType bInvokableType); - void visit(BInvokableType bInvokableType); + public abstract void visit(BJSONType bjsonType); - void visit(BJSONType bjsonType); + public abstract void visit(BMapType bMapType); - void visit(BMapType bMapType); + public abstract void visit(BStreamType bStreamType); - void visit(BStreamType bStreamType); + public abstract void visit(BTypedescType bTypedescType); - void visit(BTypedescType bTypedescType); + public abstract void visit(BTypeReferenceType bTypeReferenceType); - void visit(BTypeReferenceType bTypeReferenceType); + public abstract void visit(BParameterizedType bTypedescType); - void visit(BParameterizedType bTypedescType); + public abstract void visit(BNeverType bNeverType); - void visit(BNeverType bNeverType); + public abstract void visitNilType(BType bType); - void visit(BNilType bNilType); + public abstract void visit(BNoType bNoType); - void visit(BNoType bNoType); + public abstract void visit(BPackageType bPackageType); - void visit(BPackageType bPackageType); + public abstract void visit(BStructureType bStructureType); - void visit(BStructureType bStructureType); + public abstract void visit(BTupleType bTupleType); - void visit(BTupleType bTupleType); + public abstract void visit(BUnionType bUnionType); - void visit(BUnionType bUnionType); + public abstract void visit(BIntersectionType bIntersectionType); - void visit(BIntersectionType bIntersectionType); + public abstract void visit(BXMLType bxmlType); - void visit(BXMLType bxmlType); + public abstract void visit(BTableType bTableType); - void visit(BTableType bTableType); + public abstract void visit(BRecordType bRecordType); - void visit(BRecordType bRecordType); + public abstract void visit(BObjectType bObjectType); - void visit(BObjectType bObjectType); + public void visit(BType type) { + if (type == null) { // TODO: see if we can remove + return; + } - void visit(BType bType); + switch (type.tag) { + case TypeTags.NIL: + visitNilType(type); + } + } - void visit(BFutureType bFutureType); - - void visit(BHandleType bHandleType); + public abstract void visit(BFutureType bFutureType); + public abstract void visit(BHandleType bHandleType); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/UniqueTypeVisitor.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/UniqueTypeVisitor.java index 3f574d7c81f6..273ca80f2dc6 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/UniqueTypeVisitor.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/UniqueTypeVisitor.java @@ -22,7 +22,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; @@ -33,11 +32,11 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; import org.wso2.ballerinalang.compiler.semantics.model.types.BParameterizedType; +import org.wso2.ballerinalang.compiler.semantics.model.types.BReadonlyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BRecordType; import org.wso2.ballerinalang.compiler.semantics.model.types.BStreamType; import org.wso2.ballerinalang.compiler.semantics.model.types.BStructureType; @@ -56,75 +55,75 @@ * @param return type of visit methods * @since Swan Lake */ -public interface UniqueTypeVisitor { +public abstract class UniqueTypeVisitor { - default R visit(UniqueTypeVisitor visitor) { + R visit(UniqueTypeVisitor visitor) { return visitor.visit(this); } - boolean isVisited(BType type); + public abstract boolean isVisited(BType type); - void reset(); + public abstract void reset(); - R visit(BAnnotationType bAnnotationType); + public abstract R visit(BAnnotationType bAnnotationType); - R visit(BArrayType bArrayType); + public abstract R visit(BArrayType bArrayType); - R visit(BBuiltInRefType bBuiltInRefType); + public abstract R visit(BReadonlyType bReadonlyType); - R visit(BAnyType bAnyType); + public abstract R visit(BAnyType bAnyType); - R visit(BAnydataType bAnydataType); + public abstract R visit(BAnydataType bAnydataType); - R visit(BErrorType bErrorType); + public abstract R visit(BErrorType bErrorType); - R visit(BFiniteType bFiniteType); + public abstract R visit(BFiniteType bFiniteType); - R visit(BInvokableType bInvokableType); + public abstract R visit(BInvokableType bInvokableType); - R visit(BJSONType bjsonType); + public abstract R visit(BJSONType bjsonType); - R visit(BMapType bMapType); + public abstract R visit(BMapType bMapType); - R visit(BStreamType bStreamType); + public abstract R visit(BStreamType bStreamType); - R visit(BTypedescType bTypedescType); + public abstract R visit(BTypedescType bTypedescType); - R visit(BParameterizedType bTypedescType); + public abstract R visit(BParameterizedType bTypedescType); - R visit(BNeverType bNeverType); + public abstract R visit(BNeverType bNeverType); - R visit(BNilType bNilType); + public abstract R visitNilType(BType bType); - R visit(BNoType bNoType); + public abstract R visit(BNoType bNoType); - R visit(BPackageType bPackageType); + public abstract R visit(BPackageType bPackageType); - R visit(BStructureType bStructureType); + public abstract R visit(BStructureType bStructureType); - R visit(BTupleType bTupleType); + public abstract R visit(BTupleType bTupleType); - R visit(BUnionType bUnionType); + public abstract R visit(BUnionType bUnionType); - R visit(BIntersectionType bIntersectionType); + public abstract R visit(BIntersectionType bIntersectionType); - R visit(BTypeReferenceType bTypeReferenceType); + public abstract R visit(BTypeReferenceType bTypeReferenceType); - R visit(BXMLType bxmlType); + public abstract R visit(BXMLType bxmlType); - R visit(BTableType bTableType); + public abstract R visit(BTableType bTableType); - R visit(BRecordType bRecordType); + public abstract R visit(BRecordType bRecordType); - R visit(BObjectType bObjectType); + public abstract R visit(BObjectType bObjectType); - R visit(BType bType); + public abstract R visit(BType bType); - R visit(BFutureType bFutureType); + public abstract R visit(BFutureType bFutureType); - R visit(BHandleType bHandleType); + public abstract R visit(BHandleType bHandleType); - R visit(BIntSubType intSubType); + public abstract R visit(BIntSubType intSubType); - R visit(BXMLSubType bxmlSubType); + public abstract R visit(BXMLSubType bxmlSubType); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BAttachedFunction.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BAttachedFunction.java index 4bff7d56f36b..d425df9b2822 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BAttachedFunction.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BAttachedFunction.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.semantics.model.symbols; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.SemType; import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; import org.wso2.ballerinalang.compiler.util.Name; import org.wso2.ballerinalang.util.Flags; @@ -45,13 +46,17 @@ public BAttachedFunction(Name funcName, BInvokableSymbol symbol, BInvokableType public String toString() { StringBuilder sb = new StringBuilder(); - if (Symbols.isFlagOn(type.flags, Flags.ISOLATED)) { + if (Symbols.isFlagOn(type.getFlags(), Flags.ISOLATED)) { sb.append("isolated "); } - if (Symbols.isFlagOn(type.flags, Flags.TRANSACTIONAL)) { + if (Symbols.isFlagOn(type.getFlags(), Flags.TRANSACTIONAL)) { sb.append("transactional "); } sb.append("function ").append(funcName).append(" ").append(type.getTypeSignature()); return sb.toString(); } + + public SemType semType() { + return type.semType(); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BResourceFunction.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BResourceFunction.java index 7cc5fd7eea01..7b552f438272 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BResourceFunction.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BResourceFunction.java @@ -18,6 +18,10 @@ package org.wso2.ballerinalang.compiler.semantics.model.symbols; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.definition.FunctionDefinition; import org.ballerinalang.model.symbols.SymbolKind; import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; @@ -25,6 +29,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * {@code BResourceFunction} represents a resource function in Ballerina. @@ -77,4 +82,17 @@ public String toString() { type.paramTypes = originalParamTypes; return sb.toString(); } + + @Override + public SemType semType() { + List params = new ArrayList<>(); + params.add(SemTypes.stringConst(accessor.value)); + for (var each : pathSegmentSymbols) { + params.add(Objects.requireNonNullElse(each.type.semType(), PredefinedType.NEVER)); + } + for (var param : this.type.paramTypes) { + params.add(Objects.requireNonNullElse(param.semType(), PredefinedType.NEVER)); + } + return this.type.getSemTypeWithParams(params, new FunctionDefinition()); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BAnyType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BAnyType.java index 0991d500ead1..ff9a6f4efe09 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BAnyType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BAnyType.java @@ -17,40 +17,54 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; import org.ballerinalang.model.Name; import org.ballerinalang.model.types.SelectivelyImmutableReferenceType; import org.ballerinalang.model.types.TypeKind; +import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; +import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import static io.ballerina.types.PredefinedType.ANY; +import static io.ballerina.types.PredefinedType.VAL_READONLY; + /** * @since 0.94 */ -public class BAnyType extends BBuiltInRefType implements SelectivelyImmutableReferenceType { +public class BAnyType extends BType implements SelectivelyImmutableReferenceType { + private boolean nullable = true; - public BAnyType(int tag, BTypeSymbol tsymbol) { - super(tag, tsymbol); + public BAnyType() { + this(ANY); + } + + public BAnyType(Name name, long flag) { + this(name, flag, Symbols.isFlagOn(flag, Flags.READONLY) ? Core.intersect(ANY, VAL_READONLY) : ANY); } - public BAnyType(int tag, BTypeSymbol tsymbol, Name name, long flag) { - super(tag, tsymbol); + private BAnyType(Name name, long flags, SemType semType) { + super(TypeTags.ANY, null, semType); this.name = name; - this.flags = flag; + this.setFlags(flags); } - public BAnyType(int tag, BTypeSymbol tsymbol, boolean nullable) { - super(tag, tsymbol); - this.nullable = nullable; + private BAnyType(SemType semType) { + super(TypeTags.ANY, null, semType); } - public BAnyType(int tag, BTypeSymbol tsymbol, Name name, long flags, boolean nullable) { - super(tag, tsymbol); - this.name = name; - this.flags = flags; - this.nullable = nullable; + public static BAnyType newNilLiftedBAnyType() { + BAnyType result = new BAnyType(Core.diff(ANY, PredefinedType.NIL)); + result.nullable = false; + return result; + } + + public static BAnyType newImmutableBAnyType() { + return new BAnyType(Types.getImmutableTypeName(TypeKind.ANY.typeName()), Flags.READONLY); } @Override @@ -75,7 +89,7 @@ public void accept(TypeVisitor visitor) { @Override public String toString() { - return !Symbols.isFlagOn(flags, Flags.READONLY) ? getKind().typeName() : + return !Symbols.isFlagOn(getFlags(), Flags.READONLY) ? getKind().typeName() : getKind().typeName().concat(" & readonly"); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BAnydataType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BAnydataType.java index cdfe4f488060..4c2c3b9b08ca 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BAnydataType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BAnydataType.java @@ -17,6 +17,10 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; import org.ballerinalang.model.Name; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; @@ -33,52 +37,54 @@ * @since 0.985.0 */ public class BAnydataType extends BUnionType { - + private boolean nullable; private static final int INITIAL_CAPACITY = 10; + private final Context typeCtx; - public BAnydataType(BTypeSymbol tsymbol, Name name, long flags) { - this(tsymbol, name, flags, true); - } - - public BAnydataType(BTypeSymbol tsymbol, boolean nullable) { - super(tsymbol, new LinkedHashSet<>(INITIAL_CAPACITY), nullable, false); + private BAnydataType(Context typeCtx, BTypeSymbol tsymbol, Name name, long flags, boolean nullable) { + super(typeCtx.env, tsymbol, new LinkedHashSet<>(INITIAL_CAPACITY), false); this.tag = TypeTags.ANYDATA; - this.isCyclic = true; - } - - public BAnydataType(BTypeSymbol tsymbol, Name name, long flags, boolean nullable) { - super(tsymbol, new LinkedHashSet<>(INITIAL_CAPACITY), nullable, false); - this.tag = TypeTags.ANYDATA; - this.flags = flags; + this.setFlags(flags); this.name = name; this.isCyclic = true; + this.nullable = nullable; + this.typeCtx = typeCtx; } - public BAnydataType(BUnionType type) { - super(type.tsymbol, new LinkedHashSet<>(type.memberTypes.size()), type.isNullable(), - Symbols.isFlagOn(type.flags, Flags.READONLY)); + public BAnydataType(Context typeCtx, BUnionType type) { + super(type.env, type.tsymbol, new LinkedHashSet<>(type.memberTypes.size()), + Symbols.isFlagOn(type.getFlags(), Flags.READONLY)); this.tag = TypeTags.ANYDATA; this.isCyclic = true; this.name = type.name; - this.flags = type.flags; + this.setFlags(type.getFlags()); + this.nullable = type.isNullable(); mergeUnionType(type); + this.typeCtx = typeCtx; } - public BAnydataType(BAnydataType type, boolean nullable) { - super(type.tsymbol, new LinkedHashSet<>(INITIAL_CAPACITY), nullable, - Symbols.isFlagOn(type.flags, Flags.READONLY)); - this.flags = type.flags; - this.tag = TypeTags.ANYDATA; - this.isCyclic = true; - mergeUnionType(type); + public static BAnydataType newNilLiftedBAnydataType(BAnydataType type) { + BAnydataType result = new BAnydataType(type.typeCtx, type); + result.nullable = false; + return result; + } + + public static BAnydataType newImmutableBAnydataType(BAnydataType type, BTypeSymbol typeSymbol, Name name, + boolean nullable) { + return new BAnydataType(type.typeCtx, typeSymbol, name, type.getFlags() | Flags.READONLY, nullable); } @Override public String toString() { - return !Symbols.isFlagOn(flags, Flags.READONLY) ? getKind().typeName() : + return !Symbols.isFlagOn(getFlags(), Flags.READONLY) ? getKind().typeName() : getKind().typeName().concat(" & readonly"); } + @Override + public boolean isNullable() { + return nullable; + } + @Override public TypeKind getKind() { return TypeKind.ANYDATA; @@ -94,4 +100,15 @@ public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } + @Override + public SemType semType() { + SemType anydata = Core.createAnydata(typeCtx); + if (!nullable) { + anydata = Core.diff(anydata, PredefinedType.NIL); + } + if (Symbols.isFlagOn(getFlags(), Flags.READONLY)) { + anydata = Core.intersect(anydata, PredefinedType.VAL_READONLY); + } + return anydata; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BArrayType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BArrayType.java index be1b2986bb68..855dfe0c1919 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BArrayType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BArrayType.java @@ -17,6 +17,10 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Env; +import io.ballerina.types.SemType; +import io.ballerina.types.definition.ListDefinition; import org.ballerinalang.model.types.ArrayType; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; @@ -26,40 +30,63 @@ import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import java.util.List; + +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.VAL; + /** * @since 0.94 */ public class BArrayType extends BType implements ArrayType { + + private static final int NO_FIXED_SIZE = -1; public BType eType; - public int size = -1; + private int size = NO_FIXED_SIZE; public BArrayState state = BArrayState.OPEN; public BArrayType mutableType; + private final Env env; + private ListDefinition ld = null; - public BArrayType(BType elementType) { + public BArrayType(Env env, BType elementType) { super(TypeTags.ARRAY, null); this.eType = elementType; + this.env = env; } - public BArrayType(BType elementType, BTypeSymbol tsymbol) { + public BArrayType(Env env, BType elementType, BTypeSymbol tsymbol) { super(TypeTags.ARRAY, tsymbol); this.eType = elementType; + this.env = env; } - public BArrayType(BType elementType, BTypeSymbol tsymbol, int size, BArrayState state) { + public BArrayType(Env env, BType elementType, BTypeSymbol tsymbol, int size, BArrayState state) { super(TypeTags.ARRAY, tsymbol); this.eType = elementType; this.size = size; this.state = state; + this.env = env; } - public BArrayType(BType elementType, BTypeSymbol tsymbol, int size, BArrayState state, long flags) { + public BArrayType(Env env, BType elementType, BTypeSymbol tsymbol, int size, BArrayState state, long flags) { super(TypeTags.ARRAY, tsymbol, flags); this.eType = elementType; this.size = size; this.state = state; + this.env = env; + } + + /** + * It is required to reset {@link #ld} when the type gets mutated. + * This method is used for that. e.g. When changing Flags.READONLY + */ + protected void restLd() { + ld = null; } @Override @@ -67,6 +94,17 @@ public int getSize() { return size; } + public void setSize(int size) { + if (ld != null) { + // This is dangerous since someone have already captured the SemType may use it in the future. But we have + // cases where we actually do "proper" (i.e not accidental type checks like `isNullable`) type checks and + // then update the size. One option for this may be to poison the semtype, so that using it after this + // point trigger an exception. + ld = null; + } + this.size = size; + } + @Override public BType getElementType() { return eType; @@ -104,6 +142,66 @@ public String toString() { sb.append("[]"); } } - return !Symbols.isFlagOn(flags, Flags.READONLY) ? sb.toString() : sb.append(" & readonly").toString(); + return !Symbols.isFlagOn(getFlags(), Flags.READONLY) ? sb.toString() : sb.append(" & readonly").toString(); + } + + private boolean hasTypeHoles() { + return eType instanceof BNoType; + } + + /** + * When the type is mutated we need to reset the definition used for the semType. + */ + @Override + public void resetSemType() { + ld = null; + } + + // If the element type has a semtype component then it will be represented by that component otherwise with never. + // This means we depend on properly partitioning types to semtype components. Also, we need to ensure member types + // are "ready" when we call this + @Override + public SemType semType() { + if (ld != null) { + return ld.getSemType(env); + } + ld = new ListDefinition(); + if (hasTypeHoles()) { + return ld.defineListTypeWrapped(env, VAL); + } + SemType elementTypeSemType = eType.semType(); + if (elementTypeSemType == null) { + elementTypeSemType = NEVER; + } + boolean isReadonly = Symbols.isFlagOn(getFlags(), Flags.READONLY); + CellAtomicType.CellMutability mut = isReadonly ? CELL_MUT_NONE : CELL_MUT_LIMITED; + // Not entirely sure if I understand this correctly, + // if size == -1 it means T[] + // if size < 0 && not -1 it means T[abs(size)] (and size was inferred) + // else it is the fixed size + if (size != NO_FIXED_SIZE) { + return ld.defineListTypeWrapped(env, List.of(elementTypeSemType), Math.abs(size), NEVER, mut); + } else { + return ld.defineListTypeWrapped(env, List.of(), 0, elementTypeSemType, mut); + } + } + + // This is to ensure call to isNullable won't call semType. In case this is a member of a recursive union otherwise + // this will have an invalid list type since parent union type call this while it is filling its members + @Override + public boolean isNullable() { + return false; + } + + @Override + public void setFlags(long flags) { + super.setFlags(flags); + restLd(); + } + + @Override + public void addFlags(long flags) { + super.addFlags(flags); + restLd(); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BBuiltInRefType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BBuiltInRefType.java deleted file mode 100644 index cb8965ec71e1..000000000000 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BBuiltInRefType.java +++ /dev/null @@ -1,67 +0,0 @@ -/* -* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -* -* WSO2 Inc. licenses this file to you under the Apache License, -* Version 2.0 (the "License"); you may not use this file except -* in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ -package org.wso2.ballerinalang.compiler.semantics.model.types; - -import org.ballerinalang.model.types.ReferenceType; -import org.ballerinalang.model.types.TypeKind; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; - -import static org.wso2.ballerinalang.compiler.util.TypeTags.ANY; -import static org.wso2.ballerinalang.compiler.util.TypeTags.ANYDATA; -import static org.wso2.ballerinalang.compiler.util.TypeTags.FUTURE; -import static org.wso2.ballerinalang.compiler.util.TypeTags.JSON; -import static org.wso2.ballerinalang.compiler.util.TypeTags.MAP; -import static org.wso2.ballerinalang.compiler.util.TypeTags.STREAM; -import static org.wso2.ballerinalang.compiler.util.TypeTags.TABLE; -import static org.wso2.ballerinalang.compiler.util.TypeTags.TYPEDESC; -import static org.wso2.ballerinalang.compiler.util.TypeTags.XML; - -/** - * @since 0.94 - */ -public class BBuiltInRefType extends BType implements ReferenceType { - - public BBuiltInRefType(int tag, BTypeSymbol tsymbol) { - super(tag, tsymbol); - } - - public BBuiltInRefType(int tag, BTypeSymbol tsymbol, long flags) { - super(tag, tsymbol, flags); - } - - @Override - public R accept(BTypeVisitor visitor, T t) { - return visitor.visit(this, t); - } - - @Override - public TypeKind getKind() { - return switch (tag) { - case JSON -> TypeKind.JSON; - case XML -> TypeKind.XML; - case STREAM -> TypeKind.STREAM; - case TABLE -> TypeKind.TABLE; - case ANY -> TypeKind.ANY; - case ANYDATA -> TypeKind.ANYDATA; - case MAP -> TypeKind.MAP; - case FUTURE -> TypeKind.FUTURE; - case TYPEDESC -> TypeKind.TYPEDESC; - default -> TypeKind.OTHER; - }; - } -} diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BErrorType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BErrorType.java index 89e0892f9910..972299acb5db 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BErrorType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BErrorType.java @@ -17,12 +17,25 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.ErrorType; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; +import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + /** * Represents error type in Ballerina. * @@ -36,15 +49,23 @@ public class BErrorType extends BType implements ErrorType { private static final String ERROR = "error<"; private static final String CLOSE_ERROR = ">"; - public BErrorType(BTypeSymbol tSymbol, BType detailType) { + private final Env env; + public int distinctId = -1; + private final DistinctIdSupplier distinctIdSupplier; + + public BErrorType(Env env, BTypeSymbol tSymbol, BType detailType) { super(TypeTags.ERROR, tSymbol, Flags.READONLY); this.detailType = detailType; this.typeIdSet = BTypeIdSet.emptySet(); + this.env = env; + this.distinctIdSupplier = new DistinctIdSupplier(env); } - public BErrorType(BTypeSymbol tSymbol) { + public BErrorType(Env env, BTypeSymbol tSymbol) { super(TypeTags.ERROR, tSymbol, Flags.READONLY); this.typeIdSet = BTypeIdSet.emptySet(); + this.env = env; + this.distinctIdSupplier = new DistinctIdSupplier(env); } @Override @@ -70,4 +91,58 @@ public String toString() { } return ERROR + detailType + CLOSE_ERROR; } + + public void setDistinctId() { + if (Symbols.isFlagOn(this.getFlags(), Flags.DISTINCT)) { + distinctId = env.distinctAtomCountGetAndIncrement(); + } + } + + @Override + public SemType semType() { + return distinctIdWrapper(semTypeInner()); + } + + SemType distinctIdWrapper(SemType semTypeInner) { + return distinctIdSupplier.get().stream().map(SemTypes::errorDistinct).reduce(semTypeInner, Core::intersect); + } + + private SemType semTypeInner() { + if (this.semType != null) { + return this.semType; + } + + if (detailType == null || detailType.semType() == null) { + // semtype will be null for semantic error + this.semType = PredefinedType.ERROR; + } else { + SemType detail = detailType.semType(); + this.semType = SemTypes.errorDetail(detail); + } + return this.semType; + } + + private final class DistinctIdSupplier implements Supplier> { + + private List ids = null; + private static final Map> allocatedIds = + Collections.synchronizedMap(new WeakHashMap<>()); + private final Env env; + + private DistinctIdSupplier(Env env) { + this.env = env; + allocatedIds.putIfAbsent(env, new ConcurrentHashMap<>()); + } + + public synchronized List get() { + if (ids != null) { + return ids; + } + Map envAllocatedIds = allocatedIds.get(env); + ids = typeIdSet.getAll().stream() + .map(each -> envAllocatedIds.computeIfAbsent(each, (key) -> env.distinctAtomCountGetAndIncrement())) + .toList(); + return ids; + } + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BFiniteType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BFiniteType.java index 45c5512ef81f..100134cd1fec 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BFiniteType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BFiniteType.java @@ -18,45 +18,61 @@ package org.wso2.ballerinalang.compiler.semantics.model.types; -import org.ballerinalang.model.types.FiniteType; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.StringSubtype; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; +import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.Optional; import java.util.StringJoiner; +import static io.ballerina.types.Core.getComplexSubtypeData; +import static io.ballerina.types.Core.singleShape; +import static io.ballerina.types.SemTypes.isSubtypeSimple; +import static io.ballerina.types.BasicTypeCode.BT_BOOLEAN; +import static io.ballerina.types.BasicTypeCode.BT_DECIMAL; +import static io.ballerina.types.BasicTypeCode.BT_FLOAT; +import static io.ballerina.types.BasicTypeCode.BT_INT; +import static io.ballerina.types.BasicTypeCode.BT_STRING; + /** * {@code BFiniteType} represents the finite type in Ballerina. * */ -public class BFiniteType extends BType implements FiniteType { - - private final Set valueSpace; - private boolean nullable = false; - public Boolean isAnyData = null; +public class BFiniteType extends BType { + public SemNamedType[] valueSpace; - public BFiniteType(BTypeSymbol tsymbol) { - super(TypeTags.FINITE, tsymbol); - valueSpace = new LinkedHashSet<>(); - this.flags |= Flags.READONLY; + public BFiniteType(BTypeSymbol tsymbol, SemNamedType[] valueSpace) { + super(TypeTags.FINITE, tsymbol, Flags.READONLY); + this.valueSpace = valueSpace; + assert validValueSpace(valueSpace); } - public BFiniteType(BTypeSymbol tsymbol, Set valueSpace) { - super(TypeTags.FINITE, tsymbol); - this.valueSpace = valueSpace; - this.flags |= Flags.READONLY; + public static BFiniteType newSingletonBFiniteType(BTypeSymbol tsymbol, SemType singletonSemType) { + return new BFiniteType(tsymbol, new SemNamedType[]{ + new SemNamedType(singletonSemType, Optional.empty()) + }); } - @Override - public Set getValueSpace() { - return Collections.unmodifiableSet(valueSpace); + private boolean validValueSpace(SemNamedType[] valueSpace) { + for (SemNamedType semNamedType : valueSpace) { + if (singleShape(semNamedType.semType()).isEmpty()) { + return false; + } + } + return true; } @Override @@ -64,6 +80,22 @@ public TypeKind getKind() { return TypeKind.FINITE; } + @Override + public SemType semType() { + if (this.semType == null) { + this.semType = computeResultantSemType(valueSpace); + } + return this.semType; + } + + private SemType computeResultantSemType(SemNamedType[] valueSpace) { + SemType s = PredefinedType.NEVER; + for (SemNamedType semNamedType : valueSpace) { + s = SemTypes.union(s, semNamedType.semType()); + } + return s; + } + @Override public void accept(TypeVisitor visitor) { visitor.visit(this); @@ -74,37 +106,47 @@ public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } + @SuppressWarnings("OptionalGetWithoutIsPresent") // xxxSubtypeSingleValue() are guaranteed to have a value @Override public String toString() { StringJoiner joiner = new StringJoiner("|"); - for (BLangExpression value : this.valueSpace) { - switch (value.getBType().tag) { - case TypeTags.FLOAT: - joiner.add(value + "f"); - break; - case TypeTags.DECIMAL: - joiner.add(value + "d"); - break; - case TypeTags.STRING: - case TypeTags.CHAR_STRING: - joiner.add("\"" + value + "\""); - break; - default: - joiner.add(value.toString()); - } - } - return joiner.toString(); - } + for (SemNamedType semNamedType : valueSpace) { + SemType semType = semNamedType.semType(); + Optional name = semNamedType.optName(); - @Override - public boolean isNullable() { - return nullable; - } + if (PredefinedType.NIL.equals(semType)) { + joiner.add(name.orElse(Names.NIL_VALUE.value)); + continue; + } - public void addValue(BLangExpression value) { - this.valueSpace.add(value); - if (!this.nullable && value.getBType() != null && value.getBType().isNullable()) { - this.nullable = true; + ComplexSemType cs = (ComplexSemType) semType; + if (isSubtypeSimple(semType, PredefinedType.BOOLEAN)) { + joiner.add(name.orElse( + BooleanSubtype.booleanSubtypeSingleValue(getComplexSubtypeData(cs, BT_BOOLEAN)).get() ? + Names.TRUE.value : Names.FALSE.value + )); + } else if (isSubtypeSimple(semType, PredefinedType.INT)) { + joiner.add(name.orElse( + Long.toString(IntSubtype.intSubtypeSingleValue(getComplexSubtypeData(cs, BT_INT)).get()) + )); + } else if (isSubtypeSimple(semType, PredefinedType.FLOAT)) { + joiner.add(name.orElse( + Double.toString(FloatSubtype.floatSubtypeSingleValue(getComplexSubtypeData(cs, BT_FLOAT)).get()) + ) + "f"); + } else if (isSubtypeSimple(semType, PredefinedType.DECIMAL)) { + joiner.add(name.orElse( + DecimalSubtype.decimalSubtypeSingleValue(getComplexSubtypeData(cs, BT_DECIMAL)).get() + .toPlainString() + ) + "d"); + } else if (isSubtypeSimple(semType, PredefinedType.STRING)) { + joiner.add("\"" + name.orElse( + StringSubtype.stringSubtypeSingleValue(getComplexSubtypeData(cs, BT_STRING)).get() + ) + "\""); + } else { + throw new IllegalStateException("Unexpected value space type in BFiniteType: " + semType); + } } + + return joiner.toString(); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BFutureType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BFutureType.java index 4cb0b0b72cd7..33065fa3c05b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BFutureType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BFutureType.java @@ -16,7 +16,12 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.ConstrainedType; +import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -26,31 +31,48 @@ * * @since 0.965.0 */ -public class BFutureType extends BBuiltInRefType implements ConstrainedType { +public class BFutureType extends BType implements ConstrainedType { public BType constraint; public boolean workerDerivative; + private final Env env; - public BFutureType(int tag, BType constraint, BTypeSymbol tsymbol) { - super(tag, tsymbol); + public BFutureType(Env env, BType constraint, BTypeSymbol tsymbol) { + super(TypeTags.FUTURE, tsymbol); this.constraint = constraint; + this.env = env; } - public BFutureType(int tag, BType constraint, BTypeSymbol tsymbol, boolean workerDerivative) { - this(tag, constraint, tsymbol); + public BFutureType(Env env, BType constraint, BTypeSymbol tsymbol, boolean workerDerivative) { + this(env, constraint, tsymbol); this.workerDerivative = workerDerivative; } + public BFutureType(Env env, BType constraint, BTypeSymbol tsymbol, SemType semType) { + this(env, constraint, tsymbol); + this.semType = semType; + } + @Override public BType getConstraint() { return constraint; } + public void setConstraint(BType constraint) { + this.constraint = constraint; + this.semType = null; + } + @Override public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } - + + @Override + public TypeKind getKind() { + return TypeKind.FUTURE; + } + @Override public String toString() { if (constraint.tag == TypeTags.NONE || constraint.tag == TypeTags.SEMANTIC_ERROR @@ -65,4 +87,18 @@ public String toString() { public void accept(TypeVisitor visitor) { visitor.visit(this); } + + @Override + public SemType semType() { + if (this.semType != null) { + return this.semType; + } + + if (constraint == null || constraint instanceof BNoType) { + this.semType = PredefinedType.FUTURE; + } else { + this.semType = SemTypes.futureContaining(env, constraint.semType()); + } + return this.semType; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BHandleType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BHandleType.java index 8c535433699d..dcc4540cb8e9 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BHandleType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BHandleType.java @@ -17,9 +17,10 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.PredefinedType; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; +import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; /** @@ -29,10 +30,10 @@ * * @since 1.0.0 */ -public class BHandleType extends BBuiltInRefType { +public class BHandleType extends BType { - public BHandleType(int tag, BTypeSymbol tsymbol) { - super(tag, tsymbol, Flags.READONLY); + public BHandleType() { + super(TypeTags.HANDLE, null, Flags.READONLY, PredefinedType.HANDLE); } @Override diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BIntSubType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BIntSubType.java index 13dd337a6c39..675fa600ccc2 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BIntSubType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BIntSubType.java @@ -17,10 +17,12 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.Name; import org.ballerinalang.model.types.TypeKind; -import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.util.Names; +import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; /** @@ -30,55 +32,44 @@ */ public class BIntSubType extends BType { - public BIntSubType(int tag, Name name) { + public static final BIntSubType SIGNED32 = new BIntSubType(TypeTags.SIGNED32_INT, Names.SIGNED32, SemTypes.SINT32); + public static final BIntSubType SIGNED16 = new BIntSubType(TypeTags.SIGNED16_INT, Names.SIGNED16, SemTypes.SINT16); + public static final BIntSubType SIGNED8 = new BIntSubType(TypeTags.SIGNED8_INT, Names.SIGNED8, SemTypes.SINT8); - super(tag, null, name, Flags.READONLY); + public static final BIntSubType UNSIGNED32 = new BIntSubType(TypeTags.UNSIGNED32_INT, Names.UNSIGNED32, + SemTypes.UINT32); + public static final BIntSubType UNSIGNED16 = new BIntSubType(TypeTags.UNSIGNED16_INT, Names.UNSIGNED16, + SemTypes.UINT16); + public static final BIntSubType UNSIGNED8 = new BIntSubType(TypeTags.UNSIGNED8_INT, Names.UNSIGNED8, + SemTypes.UINT8); + + private BIntSubType(int tag, Name name, SemType semType) { + super(tag, null, name, Flags.READONLY, semType); } @Override public boolean isNullable() { - return false; } @Override public R accept(BTypeVisitor visitor, T t) { - return visitor.visit(this, t); } @Override public TypeKind getKind() { - return TypeKind.INT; } - @Override - public void accept(TypeVisitor visitor) { - - visitor.visit(this); - } - @Override public String toString() { - return Names.INT.value + Names.ALIAS_SEPARATOR + name; } @Override public String getQualifiedTypeName() { - return Names.BALLERINA_ORG.value + Names.ORG_NAME_SEPARATOR.value + Names.LANG.value + Names.DOT.value + Names.INT.value + Names.ALIAS_SEPARATOR + name; } - - public boolean isAnydata() { - - return true; - } - - public boolean isPureType() { - - return true; - } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BIntersectionType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BIntersectionType.java index 6c3bba3fcfe5..bd80e473972f 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BIntersectionType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BIntersectionType.java @@ -17,8 +17,12 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.IntersectionType; import org.ballerinalang.model.types.TypeKind; +import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; @@ -45,14 +49,14 @@ public BIntersectionType(BTypeSymbol tsymbol, LinkedHashSet types, BType effectiveType) { super(TypeTags.INTERSECTION, tsymbol); this.constituentTypes = toFlatTypeSet(types); - this.effectiveType = effectiveType; for (BType constituentType : this.constituentTypes) { if (constituentType.tag == TypeTags.READONLY) { - this.flags |= Flags.READONLY; + this.addFlags(Flags.READONLY); break; } } + this.effectiveType = effectiveType; } public BIntersectionType(BTypeSymbol tsymbol) { @@ -81,18 +85,13 @@ public void accept(TypeVisitor visitor) { visitor.visit(this); } - @Override - public boolean isNullable() { - return this.effectiveType.isNullable(); - } - @Override public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } public void setConstituentTypes(LinkedHashSet constituentTypes) { - this.constituentTypes = toFlatTypeSet(constituentTypes); + this.constituentTypes = toFlatTypeSet(constituentTypes); } @Override @@ -134,4 +133,34 @@ private static LinkedHashSet toFlatTypeSet(LinkedHashSet types) { public BType getEffectiveType() { return this.effectiveType; } + + /** + * When the type is mutated we need to reset resolved semType. + */ + public void resetSemType() { + this.semType = null; + } + + @Override + public SemType semType() { + // We have to recalculate this everytime since the actual BTypes inside constituent types do mutate and we + // can't detect those mutations. + return computeResultantIntersection(); + } + + private SemType computeResultantIntersection() { + SemType t = PredefinedType.VAL; + for (BType constituentType : this.getConstituentTypes()) { + t = SemTypes.intersect(t, constituentType.semType()); + } + + // TODO: this is a temporary workaround to propagate effective typeIds + BType referredType = Types.getReferredType(this.effectiveType); + if (referredType instanceof BErrorType effErr) { + t = effErr.distinctIdWrapper(t); + } else if (referredType instanceof BObjectType effObj) { + t = effObj.distinctIdWrapper(t); + } + return t; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BInvokableType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BInvokableType.java index 3882ba40fde0..86d23257ed77 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BInvokableType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BInvokableType.java @@ -17,8 +17,17 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.definition.FunctionDefinition; +import io.ballerina.types.definition.FunctionQualifiers; +import io.ballerina.types.definition.ListDefinition; import org.ballerinalang.model.types.InvokableType; import org.ballerinalang.model.types.TypeKind; +import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; @@ -26,7 +35,10 @@ import org.wso2.ballerinalang.util.Flags; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * @since 0.94 @@ -34,23 +46,39 @@ public class BInvokableType extends BType implements InvokableType { public List paramTypes; + // TODO: make these final public BType restType; public BType retType; + private final Env env; + private FunctionDefinition defn; - public BInvokableType(List paramTypes, BType restType, BType retType, BTypeSymbol tsymbol) { + public BInvokableType(Env env, List paramTypes, BType restType, BType retType, BTypeSymbol tsymbol) { super(TypeTags.INVOKABLE, tsymbol, Flags.READONLY); - this.paramTypes = paramTypes; + this.paramTypes = Collections.unmodifiableList(paramTypes); + assert restType == null || restType instanceof BArrayType || restType.tag == TypeTags.SEMANTIC_ERROR; this.restType = restType; this.retType = retType; + this.env = env; } - public BInvokableType(List paramTypes, BType retType, BTypeSymbol tsymbol) { - this(paramTypes, null, retType, tsymbol); + public BInvokableType(Env env, List paramTypes, BType retType, BTypeSymbol tsymbol) { + this(env, paramTypes, null, retType, tsymbol); } - public BInvokableType(BTypeSymbol tSymbol) { + public BInvokableType(Env env, BTypeSymbol tSymbol) { super(TypeTags.INVOKABLE, tSymbol, Flags.READONLY); - this.paramTypes = new ArrayList<>(); + this.paramTypes = List.of(); + this.env = env; + } + + public void addParamType(BType type) { + List newParams = new ArrayList<>(paramTypes); + newParams.add(type); + paramTypes = Collections.unmodifiableList(newParams); + } + + public void setParamTypes(List paramTypes) { + this.paramTypes = Collections.unmodifiableList(paramTypes); } @Override @@ -76,13 +104,13 @@ public R accept(BTypeVisitor visitor, T t) { @Override public String toString() { String flagStr = ""; - if (Symbols.isFlagOn(flags, Flags.ISOLATED)) { + if (Symbols.isFlagOn(getFlags(), Flags.ISOLATED)) { flagStr = "isolated "; } - if (Symbols.isFlagOn(flags, Flags.TRANSACTIONAL)) { + if (Symbols.isFlagOn(getFlags(), Flags.TRANSACTIONAL)) { flagStr += "transactional "; } - if (Symbols.isFlagOn(flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(getFlags(), Flags.ANY_FUNCTION)) { return flagStr + "function"; } return flagStr + "function " + getTypeSignature(); @@ -97,7 +125,7 @@ public boolean equals(Object o) { return false; } - if (this.flags != that.flags) { + if (this.getFlags() != that.getFlags()) { return false; } @@ -155,4 +183,129 @@ private static String getBTypeListAsString(List typeNames) { public void accept(TypeVisitor visitor) { visitor.visit(this); } + + @Override + public boolean isNullable() { + return false; + } + + /** + * When the type is mutated we need to reset the definition used for the semType. + */ + @Override + public void resetSemType() { + defn = null; + } + + @Override + public SemType semType() { + if (isFunctionTop()) { + if (Symbols.isFlagOn(getFlags(), Flags.ISOLATED) || Symbols.isFlagOn(getFlags(), Flags.TRANSACTIONAL)) { + FunctionQualifiers qualifiers = + FunctionQualifiers.from(env, Symbols.isFlagOn(getFlags(), Flags.ISOLATED), + Symbols.isFlagOn(getFlags(), Flags.TRANSACTIONAL)); + FunctionDefinition definition = new FunctionDefinition(); + return definition.define(env, PredefinedType.NEVER, PredefinedType.VAL, qualifiers); + } + return PredefinedType.FUNCTION; + } + if (defn != null) { + return defn.getSemType(env); + } + FunctionDefinition fd = new FunctionDefinition(); + this.defn = fd; + List params = this.paramTypes.stream().map(BInvokableType::from).toList(); + return getSemTypeWithParams(params, fd); + } + + public SemType getSemTypeWithParams(List params, FunctionDefinition fd) { + SemType rest; + if (restType instanceof BArrayType arrayType) { + rest = from(arrayType.eType); + } else { + // Is this correct even when type is semantic error? + rest = PredefinedType.NEVER; + } + SemType returnType = resolveReturnType(); + ListDefinition paramListDefinition = new ListDefinition(); + SemType paramTypes = paramListDefinition.defineListTypeWrapped(env, params, params.size(), rest, + CellAtomicType.CellMutability.CELL_MUT_NONE); + // TODO: probably we need to move this method from Types. + boolean isGeneric = Types.containsTypeParams(this); + FunctionQualifiers qualifiers = FunctionQualifiers.from(env, Symbols.isFlagOn(getFlags(), Flags.ISOLATED), + Symbols.isFlagOn(getFlags(), Flags.TRANSACTIONAL)); + if (isGeneric) { + return fd.defineGeneric(env, paramTypes, returnType, qualifiers); + } + return fd.define(env, paramTypes, returnType, qualifiers); + } + + private SemType resolveReturnType() { + if (retType == null) { + return PredefinedType.NIL; + } + SemType innerType = from(retType); + ListDefinition ld = new ListDefinition(); + return ld.tupleTypeWrapped(env, isDependentlyTyped(retType, + new HashSet<>()) ? SemTypes.booleanConst(true) : PredefinedType.BOOLEAN, innerType); + } + + private static boolean isDependentlyTyped(BType returnType, Set visited) { + if (visited.contains(returnType)) { + return false; + } + + visited.add(returnType); + + // it doesn't seem we actually have a flag to check this, may be the correct way to do this is to have a + // method in BType for this, but given this is a temporary thing, this should be enough. + if (returnType instanceof BParameterizedType) { + return true; + } + if (returnType instanceof BUnionType unionType) { + return unionType.getMemberTypes().stream().anyMatch(returnType1 -> + isDependentlyTyped(returnType1, visited)); + } + if (returnType instanceof BMapType mapType) { + return isDependentlyTyped(mapType.constraint, visited); + } + if (returnType instanceof BRecordType recordType) { + return recordType.fields.values().stream().anyMatch(field -> isDependentlyTyped(field.type, visited)) || + isDependentlyTyped(recordType.restFieldType, visited); + } + if (returnType instanceof BArrayType arrayType) { + return isDependentlyTyped(arrayType.eType, visited); + } + if (returnType instanceof BTupleType tupleType) { + return tupleType.getTupleTypes().stream().anyMatch(returnType1 -> isDependentlyTyped(returnType1, visited)); + } + if (returnType instanceof BInvokableType invokableType) { + return invokableType.paramTypes.stream().anyMatch(returnType1 -> + isDependentlyTyped(returnType1, visited)) || + isDependentlyTyped(invokableType.retType, visited) || + isDependentlyTyped(invokableType.restType, visited); + } + if (returnType instanceof BFutureType futureType) { + return isDependentlyTyped(futureType.constraint, visited); + } + if (returnType instanceof BTableType tableType) { + return isDependentlyTyped(tableType.constraint, visited); + } + if (returnType instanceof BStreamType streamType) { + return isDependentlyTyped(streamType.constraint, visited); + } + return false; + } + + private static SemType from(BType type) { + SemType semType = type.semType(); + if (semType == null) { + semType = PredefinedType.NEVER; + } + return semType; + } + + private boolean isFunctionTop() { + return paramTypes.isEmpty() && restType == null && retType == null; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BJSONType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BJSONType.java index 46b3c81d955b..78f81599f2e3 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BJSONType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BJSONType.java @@ -17,6 +17,10 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; @@ -30,37 +34,49 @@ * @since 0.94 */ public class BJSONType extends BUnionType { - + private boolean nullable = true; private static final int INITIAL_CAPACITY = 8; + private final Context typeCtx; - public BJSONType(BJSONType type, boolean nullable) { - super(type.tsymbol, new LinkedHashSet<>(INITIAL_CAPACITY), nullable, - Symbols.isFlagOn(type.flags, Flags.READONLY)); + public BJSONType(Context typeCtx, BUnionType type) { + super(type.env, type.tsymbol, new LinkedHashSet<>(INITIAL_CAPACITY), Symbols.isFlagOn(type.getFlags(), + Flags.READONLY)); mergeUnionType(type); this.tag = TypeTags.JSON; this.isCyclic = true; + this.typeCtx = typeCtx; } - public BJSONType(BUnionType type) { - super(type.tsymbol, new LinkedHashSet<>(INITIAL_CAPACITY), type.isNullable(), Symbols.isFlagOn(type.flags, - Flags.READONLY)); - mergeUnionType(type); + private BJSONType(Context typeCtx, BTypeSymbol typeSymbol, boolean nullable, long flags) { + super(typeCtx.env, typeSymbol, new LinkedHashSet<>(INITIAL_CAPACITY), Symbols.isFlagOn(flags, Flags.READONLY)); + this.setFlags(flags); this.tag = TypeTags.JSON; + this.isCyclic = true; + this.nullable = nullable; + this.typeCtx = typeCtx; } - public BJSONType(BTypeSymbol typeSymbol, boolean nullable, long flags) { - super(typeSymbol, new LinkedHashSet<>(INITIAL_CAPACITY), nullable, Symbols.isFlagOn(flags, Flags.READONLY)); - this.flags = flags; - this.tag = TypeTags.JSON; - this.isCyclic = true; + public static BJSONType newNilLiftedBJSONType(BJSONType type) { + BJSONType result = new BJSONType(type.typeCtx, type); + result.nullable = false; + return result; + } + + public static BJSONType newImmutableBJSONType(BJSONType type, BTypeSymbol typeSymbol, boolean nullable) { + return new BJSONType(type.typeCtx, typeSymbol, nullable, type.getFlags() | Flags.READONLY); } @Override public String toString() { - return !Symbols.isFlagOn(flags, Flags.READONLY) ? getKind().typeName() : + return !Symbols.isFlagOn(getFlags(), Flags.READONLY) ? getKind().typeName() : getKind().typeName().concat(" & readonly"); } + @Override + public boolean isNullable() { + return nullable; + } + @Override public TypeKind getKind() { return TypeKind.JSON; @@ -75,4 +91,17 @@ public void accept(TypeVisitor visitor) { public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } + + @Override + public SemType semType() { + SemType json = Core.createJson(typeCtx); + // TODO: refer to https://github.com/ballerina-platform/ballerina-lang/issues/43343#issuecomment-2485247172 +// if (!nullable) { +// json = Core.diff(json, PredefinedType.NIL); +// } + if (Symbols.isFlagOn(getFlags(), Flags.READONLY)) { + json = Core.intersect(json, PredefinedType.VAL_READONLY); + } + return json; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BMapType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BMapType.java index 3112abb1d6c8..6efc4a020d33 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BMapType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BMapType.java @@ -17,29 +17,54 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Env; +import io.ballerina.types.SemType; +import io.ballerina.types.definition.MappingDefinition; import org.ballerinalang.model.types.ConstrainedType; import org.ballerinalang.model.types.SelectivelyImmutableReferenceType; +import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import java.util.List; + +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.VAL; + /** * @since 0.94 */ -public class BMapType extends BBuiltInRefType implements ConstrainedType, SelectivelyImmutableReferenceType { +public class BMapType extends BType implements ConstrainedType, SelectivelyImmutableReferenceType { public BType constraint; public BMapType mutableType; - public BMapType(int tag, BType constraint, BTypeSymbol tsymbol) { + private final Env env; + private MappingDefinition md = null; + + public BMapType(Env env, int tag, BType constraint, BTypeSymbol tsymbol) { super(tag, tsymbol); this.constraint = constraint; + this.env = env; } - public BMapType(int tag, BType constraint, BTypeSymbol tsymbol, long flags) { + public BMapType(Env env, int tag, BType constraint, BTypeSymbol tsymbol, long flags) { super(tag, tsymbol, flags); this.constraint = constraint; + this.env = env; + } + + /** + * It is required to reset {@link #md} when the type gets mutated. + * This method is used for that. e.g. When changing Flags.READONLY + */ + protected void restMd() { + md = null; } @Override @@ -52,6 +77,11 @@ public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } + @Override + public TypeKind getKind() { + return TypeKind.MAP; + } + @Override public String toString() { String stringRep; @@ -62,11 +92,63 @@ public String toString() { stringRep = super.toString() + "<" + constraint + ">"; } - return !Symbols.isFlagOn(flags, Flags.READONLY) ? stringRep : stringRep.concat(" & readonly"); + return !Symbols.isFlagOn(getFlags(), Flags.READONLY) ? stringRep : stringRep.concat(" & readonly"); } @Override public void accept(TypeVisitor visitor) { visitor.visit(this); } + + private boolean hasTypeHoles() { + return constraint instanceof BNoType; + } + + /** + * When the type is mutated we need to reset the definition used for the semType. + */ + @Override + public void resetSemType() { + md = null; + } + + // If the member has a semtype component then it will be represented by that component otherwise with never. This + // means we depend on properly partitioning types to semtype components. Also, we need to ensure member types are + // "ready" when we call this + @Override + public SemType semType() { + if (md != null) { + return md.getSemType(env); + } + md = new MappingDefinition(); + if (hasTypeHoles()) { + return md.defineMappingTypeWrapped(env, List.of(), VAL); + } + SemType elementTypeSemType = constraint.semType(); + if (elementTypeSemType == null) { + elementTypeSemType = NEVER; + } + boolean isReadonly = Symbols.isFlagOn(getFlags(), Flags.READONLY); + CellAtomicType.CellMutability mut = isReadonly ? CELL_MUT_NONE : CELL_MUT_LIMITED; + return md.defineMappingTypeWrapped(env, List.of(), elementTypeSemType, mut); + } + + // This is to ensure call to isNullable won't call semType. In case this is a member of a recursive union otherwise + // this will have an invalid map type since parent union type call this while it is filling its members + @Override + public boolean isNullable() { + return false; + } + + @Override + public void setFlags(long flags) { + super.setFlags(flags); + restMd(); + } + + @Override + public void addFlags(long flags) { + super.addFlags(flags); + restMd(); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNeverType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNeverType.java index fd43d8e3d06f..7dd18758dbfb 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNeverType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNeverType.java @@ -17,6 +17,7 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.PredefinedType; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -31,13 +32,8 @@ public class BNeverType extends BType { - public BNeverType() { - super(TypeTags.NEVER, null, Flags.READONLY); - } - - @Override - public boolean isNullable() { - return false; + protected BNeverType() { + super(TypeTags.NEVER, null, Flags.READONLY, PredefinedType.NEVER); } @Override diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNilType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNilType.java index 8abb31ab1370..0b821361a5c9 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNilType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNilType.java @@ -17,8 +17,7 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; -import org.ballerinalang.model.types.NullType; -import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; +import io.ballerina.types.PredefinedType; import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; @@ -29,24 +28,14 @@ * * @since 0.970.0 */ -public class BNilType extends BType implements NullType { +public class BNilType extends BType { - public BNilType() { - super(TypeTags.NIL, null, Flags.READONLY); - } - - @Override - public boolean isNullable() { - return true; + BNilType() { + super(TypeTags.NIL, null, Flags.READONLY, PredefinedType.NIL); } @Override public String toString() { return Names.NIL_VALUE.value; } - - @Override - public void accept(TypeVisitor visitor) { - visitor.visit(this); - } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNoType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNoType.java index 3a4abd20fde8..f3a95d660c68 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNoType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNoType.java @@ -17,6 +17,7 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.PredefinedType; import org.ballerinalang.model.types.NoType; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; @@ -26,7 +27,7 @@ public class BNoType extends BType implements NoType { public BNoType(int tag) { - super(tag, null); + super(tag, null, PredefinedType.UNDEF); } @Override diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BObjectType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BObjectType.java index 8abd29cc8474..59ac8e697a79 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BObjectType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BObjectType.java @@ -17,6 +17,14 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.definition.Member; +import io.ballerina.types.definition.ObjectDefinition; +import io.ballerina.types.definition.ObjectQualifiers; import org.ballerinalang.model.types.ObjectType; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; @@ -28,6 +36,17 @@ import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + /** * {@code BObjectType} represents object type in Ballerina. * @@ -42,6 +61,7 @@ public class BObjectType extends BStructureType implements ObjectType { private static final String RIGHT_CURL = "}"; private static final String SEMI_COLON = ";"; private static final String READONLY = "readonly"; + private final Env env; public boolean markedIsolatedness; public BObjectType mutableType = null; @@ -49,12 +69,21 @@ public class BObjectType extends BStructureType implements ObjectType { public BTypeIdSet typeIdSet = new BTypeIdSet(); - public BObjectType(BTypeSymbol tSymbol) { + private ObjectDefinition od = null; + private final DistinctIdSupplier distinctIdSupplier; + + public BObjectType(Env env, BTypeSymbol tSymbol) { super(TypeTags.OBJECT, tSymbol); + assert env != null; + this.env = env; + this.distinctIdSupplier = new DistinctIdSupplier(env); } - public BObjectType(BTypeSymbol tSymbol, long flags) { + public BObjectType(Env env, BTypeSymbol tSymbol, long flags) { super(TypeTags.OBJECT, tSymbol, flags); + assert env != null; + this.env = env; + this.distinctIdSupplier = new DistinctIdSupplier(env); } @Override @@ -72,6 +101,104 @@ public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } + private boolean hasTypeHoles() { + return fields.values().stream().anyMatch(field -> field.type instanceof BNoType); + } + + /** + * When the type is mutated we need to reset the definition used for the semType. + */ + @Override + public void resetSemType() { + od = null; + } + + @Override + public SemType semType() { + return distinctIdWrapper(semTypeInner()); + } + + SemType distinctIdWrapper(SemType semTypeInner) { + return distinctIdSupplier.get().stream().map(SemTypes::objectDistinct).reduce(semTypeInner, Core::intersect); + } + + private SemType semTypeInner() { + if (od != null) { + return od.getSemType(env); + } + od = new ObjectDefinition(); + // I don't think this is actually possible + assert !hasTypeHoles() : "unimplemented"; + List members = new ArrayList<>(fields.size()); + ObjectQualifiers qualifiers = getObjectQualifiers(); + Set memberNames = new HashSet<>(); + for (BField field : fields.values()) { + Optional member = createMember(field, qualifiers.readonly(), memberNames); + member.ifPresent(members::add); + } + + BObjectTypeSymbol objectSymbol = (BObjectTypeSymbol) this.tsymbol; + for (BAttachedFunction fun : objectSymbol.attachedFuncs) { + Optional member = createMember(fun, memberNames); + member.ifPresent(members::add); + } + return od.define(env, qualifiers, members); + } + + private static Optional createMember(BAttachedFunction func, Set visitedFields) { + String name = func.funcName.value; + if (Symbols.isFlagOn(func.symbol.flags, Flags.REMOTE)) { + name = "$remote$" + name; + } + if (visitedFields.contains(name)) { + return Optional.empty(); + } + visitedFields.add(name); + Member.Visibility visibility = Symbols.isFlagOn(func.symbol.flags, Flags.PUBLIC) ? + Member.Visibility.Public : Member.Visibility.Private; + SemType type = func.semType(); + assert type != null : "function type is fully implemented"; + assert !Core.isNever(type) : "method can't be never"; + return Optional.of(new Member(name, type, Member.Kind.Method, visibility, true)); + } + + private static Optional createMember(BField field, boolean readonlyObject, Set visitedFields) { + String name = field.name.value; + if (visitedFields.contains(name)) { + return Optional.empty(); + } + visitedFields.add(name); + Member.Visibility visibility = Symbols.isFlagOn(field.symbol.flags, Flags.PUBLIC) ? + Member.Visibility.Public : Member.Visibility.Private; + SemType type = field.type.semType(); + if (type == null) { + type = PredefinedType.NEVER; + } + boolean immutableField; + if (readonlyObject || Symbols.isFlagOn(field.symbol.flags, Flags.READONLY)) { + type = Core.intersect(type, PredefinedType.VAL_READONLY); + immutableField = true; + } else { + immutableField = false; + } + return Optional.of(new Member(name, type, Member.Kind.Field, visibility, immutableField)); + } + + private ObjectQualifiers getObjectQualifiers() { + long flags = tsymbol.flags; + boolean isolated = Symbols.isFlagOn(this.tsymbol.flags, Flags.ISOLATED); + ObjectQualifiers.NetworkQualifier networkQualifier; + if (Symbols.isFlagOn(flags, Flags.SERVICE)) { + networkQualifier = ObjectQualifiers.NetworkQualifier.Service; + } else if (Symbols.isFlagOn(flags, Flags.CLIENT)) { + networkQualifier = ObjectQualifiers.NetworkQualifier.Client; + } else { + networkQualifier = ObjectQualifiers.NetworkQualifier.None; + } + boolean readonly = Symbols.isFlagOn(this.tsymbol.flags, Flags.READONLY); + return new ObjectQualifiers(isolated, readonly, networkQualifier); + } + @Override public String toString() { if (shouldPrintShape()) { @@ -119,4 +246,35 @@ public String toString() { } return this.tsymbol.toString(); } + + // This is to ensure call to isNullable won't call semType. In case this is a member of a recursive union otherwise + // this will have an invalid object type since parent union type call this while it is filling its members + @Override + public boolean isNullable() { + return false; + } + + private final class DistinctIdSupplier implements Supplier> { + + private List ids = null; + private static final Map> allocatedIds = + Collections.synchronizedMap(new WeakHashMap<>()); + private final Env env; + + private DistinctIdSupplier(Env env) { + this.env = env; + allocatedIds.putIfAbsent(env, new ConcurrentHashMap<>()); + } + + public synchronized List get() { + if (ids != null) { + return ids; + } + Map envAllocatedIds = allocatedIds.get(env); + ids = typeIdSet.getAll().stream() + .map(each -> envAllocatedIds.computeIfAbsent(each, (key) -> env.distinctAtomCountGetAndIncrement())) + .toList(); + return ids; + } + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BParameterizedType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BParameterizedType.java index f966d588da46..b4aa3a60a095 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BParameterizedType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BParameterizedType.java @@ -17,6 +17,7 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.SemType; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BVarSymbol; @@ -45,11 +46,6 @@ public BParameterizedType(BType valueType, BVarSymbol paramSymbol, BTypeSymbol t this.paramIndex = paramIndex; } - @Override - public boolean isNullable() { - return false; - } - @Override public String toString() { return this.paramSymbol.name.toString(); @@ -64,4 +60,9 @@ public void accept(TypeVisitor visitor) { public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } + + @Override + public SemType semType() { + return this.paramValueType.semType(); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BReadonlyType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BReadonlyType.java index b59d62a41f4a..eab400866809 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BReadonlyType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BReadonlyType.java @@ -17,36 +17,40 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; -import org.ballerinalang.model.Name; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; import org.ballerinalang.model.types.TypeKind; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; +import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import static io.ballerina.types.PredefinedType.VAL_READONLY; + /** * {@code BReadonlyType} represents the shapes that have their read-only bit on. * * @since 1.3.0 */ -public class BReadonlyType extends BBuiltInRefType { +public class BReadonlyType extends BType { private boolean nullable = true; - public BReadonlyType(int tag, BTypeSymbol tsymbol) { - super(tag, tsymbol); - this.flags |= Flags.READONLY; + public BReadonlyType() { + this(VAL_READONLY); + } + + public BReadonlyType(long flag) { + super(TypeTags.READONLY, null, flag | Flags.READONLY, VAL_READONLY); } - public BReadonlyType(int tag, BTypeSymbol tsymbol, Name name, long flag) { - super(tag, tsymbol); - this.name = name; - this.flags = flag; - this.flags |= Flags.READONLY; + private BReadonlyType(SemType semType) { + super(TypeTags.READONLY, null, Flags.READONLY, semType); } - public BReadonlyType(int tag, BTypeSymbol tsymbol, boolean nullable) { - super(tag, tsymbol); - this.nullable = nullable; - this.flags |= Flags.READONLY; + public static BReadonlyType newNilLiftedBReadonlyType() { + BReadonlyType result = new BReadonlyType(Core.diff(VAL_READONLY, PredefinedType.NIL)); + result.nullable = false; + return result; } @Override diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BRecordType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BRecordType.java index a6933f7e6856..95200e5c6a57 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BRecordType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BRecordType.java @@ -17,6 +17,12 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.SemType; +import io.ballerina.types.definition.Field; +import io.ballerina.types.definition.MappingDefinition; import org.ballerinalang.model.types.RecordType; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; @@ -25,6 +31,14 @@ import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import java.util.ArrayList; +import java.util.List; + +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.VAL; + /** * {@code BRecordType} represents record type in Ballerina. * @@ -42,16 +56,28 @@ public class BRecordType extends BStructureType implements RecordType { public static final String READONLY = "readonly"; public boolean sealed; public BType restFieldType; - public Boolean isAnyData = null; public BRecordType mutableType; - public BRecordType(BTypeSymbol tSymbol) { + private final Env env; + private MappingDefinition md = null; + + public BRecordType(Env env, BTypeSymbol tSymbol) { super(TypeTags.RECORD, tSymbol); + this.env = env; } - public BRecordType(BTypeSymbol tSymbol, long flags) { + public BRecordType(Env env, BTypeSymbol tSymbol, long flags) { super(TypeTags.RECORD, tSymbol, flags); + this.env = env; + } + + /** + * It is required to reset {@link #md} when the type gets mutated. + * This method is used for that. e.g. When changing Flags.READONLY + */ + protected void restMd() { + md = null; } @Override @@ -95,12 +121,102 @@ public String toString() { } if (sealed) { sb.append(SPACE).append(CLOSE_RIGHT); - return !Symbols.isFlagOn(this.flags, Flags.READONLY) ? sb.toString() : + return !Symbols.isFlagOn(this.getFlags(), Flags.READONLY) ? sb.toString() : sb.toString().concat(" & readonly"); } sb.append(SPACE).append(restFieldType).append(REST).append(SEMI).append(SPACE).append(CLOSE_RIGHT); - return !Symbols.isFlagOn(this.flags, Flags.READONLY) ? sb.toString() : sb.toString().concat(" & readonly"); + return !Symbols.isFlagOn(this.getFlags(), Flags.READONLY) ? sb.toString() : + sb.toString().concat(" & readonly"); } return this.tsymbol.toString(); } + + private boolean hasTypeHoles() { + if (this.fields != null) { + for (BField member : this.fields.values()) { + if (member.type instanceof BNoType) { + return true; + } + } + } + + // Note: restFieldType will be null/BNoType for closed records + return false; + } + + /** + * When the type is mutated we need to reset the definition used for the semType. + */ + @Override + public void resetSemType() { + md = null; + } + + // If the member has a semtype component then it will be represented by that component otherwise with never. This + // means we depend on properly partitioning types to semtype components. Also, we need to ensure member types are + // "ready" when we call this + @Override + public SemType semType() { + if (md != null) { + return md.getSemType(env); + } + md = new MappingDefinition(); + if (hasTypeHoles()) { + return md.defineMappingTypeWrapped(env, List.of(), VAL); + } + + SemType restFieldSemType; + if (restFieldType == null || restFieldType instanceof BNoType || restFieldType.semType() == null) { + restFieldSemType = NEVER; + } else { + restFieldSemType = restFieldType.semType(); + } + + List semFields = new ArrayList<>(this.fields.size()); + for (BField field : this.fields.values()) { + boolean optional = Symbols.isOptional(field.symbol); + BType bType = field.type; + SemType ty = bType.semType(); + if (ty == null || NEVER.equals(ty)) { + if (!optional) { + // if there is a non-optional field with `never` type, it is not possible to create a value. + // Hence, the whole record type is considered as `never`. + md.setSemTypeToNever(); + return NEVER; + } + + if (Core.isNever(restFieldSemType)) { + // record { never x?; never...;} is equivalent to record { never... } + // ignore the field + continue; + } + } + Field semField = Field.from(field.name.value, ty, Symbols.isFlagOn(field.symbol.flags, Flags.READONLY), + optional); + semFields.add(semField); + } + + boolean isReadonly = Symbols.isFlagOn(getFlags(), Flags.READONLY); + CellAtomicType.CellMutability mut = isReadonly ? CELL_MUT_NONE : CELL_MUT_LIMITED; + return md.defineMappingTypeWrapped(env, semFields, restFieldSemType, mut); + } + + // This is to ensure call to isNullable won't call semType. In case this is a member of a recursive union otherwise + // this will have an invalid record type since parent union type call this while it is filling its members + @Override + public boolean isNullable() { + return false; + } + + @Override + public void setFlags(long flags) { + super.setFlags(flags); + restMd(); + } + + @Override + public void addFlags(long flags) { + super.addFlags(flags); + restMd(); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BRegexpType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BRegexpType.java index 900216e6fef0..16f005bc8d06 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BRegexpType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BRegexpType.java @@ -17,10 +17,11 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; -import org.ballerinalang.model.Name; +import io.ballerina.types.PredefinedType; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.util.Names; +import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; /** @@ -30,13 +31,8 @@ */ public class BRegexpType extends BType { - public BRegexpType(int tag, Name name) { - super(tag, null, name, Flags.READONLY); - } - - @Override - public boolean isNullable() { - return false; + public BRegexpType() { + super(TypeTags.REGEXP, null, Names.REGEXP_TYPE, Flags.READONLY, PredefinedType.REGEXP); } @Override @@ -64,12 +60,4 @@ public String getQualifiedTypeName() { return Names.BALLERINA_ORG.value + Names.ORG_NAME_SEPARATOR.value + Names.LANG.value + Names.DOT.value + Names.REGEXP.value + Names.ALIAS_SEPARATOR + name; } - - public boolean isAnydata() { - return true; - } - - public boolean isPureType() { - return true; - } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BSequenceType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BSequenceType.java index fee674386469..571887ad0c55 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BSequenceType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BSequenceType.java @@ -17,8 +17,15 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Env; +import io.ballerina.types.SemType; +import io.ballerina.types.definition.ListDefinition; import org.wso2.ballerinalang.compiler.util.TypeTags; +import java.util.List; + +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; + /** * Represents type for sequence variable. * @@ -26,13 +33,25 @@ */ public class BSequenceType extends BType { public BType elementType; - public BSequenceType(BType elementType) { + private final Env env; + + public BSequenceType(Env env, BType elementType) { super(TypeTags.SEQUENCE, null); this.elementType = elementType; + this.env = env; } @Override public String toString() { return "seq " + elementType; } + + public SemType semType() { + if (this.semType != null) { + return this.semType; + } + ListDefinition ld = new ListDefinition(); + this.semType = ld.defineListTypeWrapped(env, List.of(), 0, elementType.semType(), CELL_MUT_LIMITED); + return this.semType; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BStreamType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BStreamType.java index e71f6997b6f1..5f218cf9a3d6 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BStreamType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BStreamType.java @@ -18,8 +18,13 @@ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.definition.StreamDefinition; import org.ballerinalang.model.types.StreamType; import org.ballerinalang.model.types.Type; +import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -29,15 +34,19 @@ * * @since 1.2.0 */ -public class BStreamType extends BBuiltInRefType implements StreamType { +public class BStreamType extends BType implements StreamType { public BType constraint; public BType completionType; - public BStreamType(int tag, BType constraint, BType completionType, BTypeSymbol tsymbol) { + private final Env env; + private StreamDefinition d = null; + + public BStreamType(Env env, int tag, BType constraint, BType completionType, BTypeSymbol tsymbol) { super(tag, tsymbol); this.constraint = constraint; - this.completionType = completionType != null ? completionType : new BNilType(); + this.completionType = completionType != null ? completionType : BType.createNilType(); + this.env = env; } @Override @@ -55,6 +64,11 @@ public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } + @Override + public TypeKind getKind() { + return TypeKind.STREAM; + } + @Override public String toString() { if (constraint.tag == TypeTags.ANY) { @@ -69,4 +83,26 @@ public String toString() { public void accept(TypeVisitor visitor) { visitor.visit(this); } + + /** + * When the type is mutated we need to reset the definition used for the semType. + */ + @Override + public void resetSemType() { + d = null; + } + + @Override + public SemType semType() { + if (constraint == null || constraint instanceof BNoType) { + return PredefinedType.STREAM; + } + + if (d != null) { + return d.getSemType(env); + } + + d = new StreamDefinition(); + return d.define(env, constraint.semType(), completionType.semType()); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BStringSubType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BStringSubType.java index d63c962f96d9..af7beacd9ec3 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BStringSubType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BStringSubType.java @@ -17,10 +17,11 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; -import org.ballerinalang.model.Name; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.util.Names; +import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; /** @@ -30,55 +31,40 @@ */ public class BStringSubType extends BType { - public BStringSubType(int tag, Name name) { + public static final BStringSubType CHAR = new BStringSubType(); - super(tag, null, name, Flags.READONLY); + private BStringSubType() { + super(TypeTags.CHAR_STRING, null, Names.CHAR, Flags.READONLY, SemTypes.CHAR); } @Override public boolean isNullable() { - return false; } @Override public R accept(BTypeVisitor visitor, T t) { - return visitor.visit(this, t); } @Override public TypeKind getKind() { - return TypeKind.STRING; } @Override public void accept(TypeVisitor visitor) { - visitor.visit(this); } @Override public String toString() { - return Names.STRING.value + Names.ALIAS_SEPARATOR + name; } @Override public String getQualifiedTypeName() { - return Names.BALLERINA_ORG.value + Names.ORG_NAME_SEPARATOR.value + Names.LANG.value + Names.DOT.value + Names.STRING.value + Names.ALIAS_SEPARATOR + name; } - - public boolean isAnydata() { - - return true; - } - - public boolean isPureType() { - - return true; - } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTableType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTableType.java index d4c3ce7fea6d..f5bb090449b5 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTableType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTableType.java @@ -15,15 +15,21 @@ * specific language governing permissions and limitations * under the License. */ - package org.wso2.ballerinalang.compiler.semantics.model.types; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Context; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.TableType; import org.ballerinalang.model.types.TypeKind; +import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; +import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; import java.util.ArrayList; @@ -43,21 +49,29 @@ public class BTableType extends BType implements TableType { public Location constraintPos; public BTableType mutableType; + private final Env env; - public BTableType(int tag, BType constraint, BTypeSymbol tSymbol) { - super(tag, tSymbol); + public BTableType(Env env, BType constraint, BTypeSymbol tSymbol) { + super(TypeTags.TABLE, tSymbol); this.constraint = constraint; + this.env = env; } - public BTableType(int tag, BType constraint, BTypeSymbol tSymbol, long flags) { - super(tag, tSymbol, flags); + public BTableType(Env env, BType constraint, BTypeSymbol tSymbol, long flags) { + super(TypeTags.TABLE, tSymbol, flags); this.constraint = constraint; + this.env = env; } public BType getConstraint() { return this.constraint; } + public void setConstraint(BType constraint) { + this.constraint = constraint; + this.semType = null; + } + @Override public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); @@ -65,7 +79,7 @@ public R accept(BTypeVisitor visitor, T t) { @Override public String toString() { - boolean readonly = Symbols.isFlagOn(flags, Flags.READONLY); + boolean readonly = Symbols.isFlagOn(getFlags(), Flags.READONLY); if (constraint == null) { return readonly ? super.toString().concat(" & readonly") : super.toString(); } @@ -97,4 +111,50 @@ public TypeKind getKind() { public void accept(TypeVisitor visitor) { visitor.visit(this); } + + @Override + public SemType semType() { + if (this.semType != null) { + return this.semType; + } + + SemType s = semTypeInner(); + boolean readonly = Symbols.isFlagOn(this.getFlags(), Flags.READONLY); + this.semType = readonly ? SemTypes.intersect(PredefinedType.VAL_READONLY, s) : s; + return this.semType; + } + + private SemType semTypeInner() { + BType constraintType = Types.getReferredType(constraint); + if (constraintType.tag == TypeTags.TABLE || constraintType.tag == TypeTags.SEMANTIC_ERROR) { + // this is to handle negative table recursions. e.g. type T table + return PredefinedType.TABLE; + } else if (constraintType instanceof BIntersectionType intersectionType) { + for (BType memberType : intersectionType.getConstituentTypes()) { + if (Types.getReferredType(memberType).tag == TypeTags.TABLE) { + // Negative scenario + return PredefinedType.TABLE; + } + } + } + + SemType tableConstraint = constraintType instanceof BParameterizedType p ? p.paramValueType.semType() : + constraintType.semType(); + tableConstraint = SemTypes.intersect(tableConstraint, PredefinedType.MAPPING); + + Context cx = Context.from(env); // apis calling with 'cx' here are only accessing the env field internally + String[] fieldNames = fieldNameList.toArray(String[]::new); + if (!fieldNameList.isEmpty()) { + return SemTypes.tableContainingKeySpecifier(cx, tableConstraint, fieldNames); + } + + BType keyConstraintType = Types.getReferredType(keyTypeConstraint); + if (keyTypeConstraint != null && keyConstraintType.tag != TypeTags.NEVER && + keyTypeConstraint.tag != TypeTags.SEMANTIC_ERROR) { + SemType keyConstraint = keyTypeConstraint.semType(); + return SemTypes.tableContainingKeyConstraint(cx, tableConstraint, keyConstraint); + } + + return SemTypes.tableContaining(env, tableConstraint); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTupleType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTupleType.java index 6b647c1c2052..54c93ad0f200 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTupleType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTupleType.java @@ -16,6 +16,10 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Env; +import io.ballerina.types.SemType; +import io.ballerina.types.definition.ListDefinition; import org.ballerinalang.model.types.TupleType; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; @@ -26,8 +30,14 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.VAL; + /** * {@code {@link BTupleType }} represents the tuple type. * @@ -37,56 +47,71 @@ public class BTupleType extends BType implements TupleType { private List members; private List memberTypes; public BType restType; - public Boolean isAnyData = null; public boolean resolvingToString = false; public boolean isCyclic = false; public BTupleType mutableType; + private final Env env; + private ListDefinition ld = null; - public BTupleType(List members) { + public BTupleType(Env env, List members) { super(TypeTags.TUPLE, null); this.members = members; + this.env = env; } - public BTupleType(BTypeSymbol tsymbol, List members) { + public BTupleType(Env env, BTypeSymbol tsymbol, List members) { super(TypeTags.TUPLE, tsymbol); this.members = members; + this.env = env; } - public BTupleType(BTypeSymbol tsymbol, List members, boolean isCyclic) { + public BTupleType(Env env, BTypeSymbol tsymbol, List members, boolean isCyclic) { super(TypeTags.TUPLE, tsymbol); this.members = members; this.isCyclic = isCyclic; + this.env = env; } - public BTupleType(BTypeSymbol tsymbol, List members, BType restType, long flags) { + public BTupleType(Env env, BTypeSymbol tsymbol, List members, BType restType, long flags) { super(TypeTags.TUPLE, tsymbol, flags); this.members = members; this.restType = restType; + this.env = env; } - public BTupleType(BTypeSymbol tsymbol, List members, BType restType, long flags, + public BTupleType(Env env, BTypeSymbol tsymbol, List members, BType restType, long flags, boolean isCyclic) { super(TypeTags.TUPLE, tsymbol, flags); this.members = members; this.restType = restType; this.isCyclic = isCyclic; + this.env = env; } - public BTupleType(BTypeSymbol tsymbol) { - this(tsymbol, true); + public BTupleType(Env env, BTypeSymbol tsymbol) { + this(env, tsymbol, true); } - private BTupleType(BTypeSymbol tsymbol, boolean readonly) { + private BTupleType(Env env, BTypeSymbol tsymbol, boolean readonly) { super(TypeTags.TUPLE, tsymbol); if (readonly) { - this.flags |= Flags.READONLY; + this.addFlags(Flags.READONLY); if (tsymbol != null) { this.tsymbol.flags |= Flags.READONLY; } } + this.env = env; + } + + /** + * It is required to reset {@link #ld} when the type gets mutated. + * This method is used for that. e.g. When changing Flags.READONLY + */ + protected void restLd() { + ld = null; } @Override @@ -132,12 +157,13 @@ public String toString() { + ((restType != null) ? (!members.isEmpty() ? "," : "") + restType.toString() + "...]" : "]"); this.resolvingToString = false; - return !Symbols.isFlagOn(flags, Flags.READONLY) ? stringRep : stringRep.concat(" & readonly"); + return !Symbols.isFlagOn(getFlags(), Flags.READONLY) ? stringRep : stringRep.concat(" & readonly"); } // In the case of a cyclic tuple, this aids in //adding resolved members to a previously defined empty tuple shell in main scope public boolean addMembers(BTupleMember member) { + ld = null; // Prevent cyclic types of same type ex: type Foo [int, Foo]; if (member.type instanceof BTupleType && ((BTupleType) member.type).isCyclic && member.type.getQualifiedTypeName().equals(this.getQualifiedTypeName())) { @@ -147,8 +173,9 @@ public boolean addMembers(BTupleMember member) { if (this.memberTypes != null) { this.memberTypes.add(member.type); } - if (Symbols.isFlagOn(this.flags, Flags.READONLY) && !Symbols.isFlagOn(member.type.flags, Flags.READONLY)) { - this.flags ^= Flags.READONLY; + if (Symbols.isFlagOn(this.getFlags(), Flags.READONLY) && + !Symbols.isFlagOn(member.type.getFlags(), Flags.READONLY)) { + this.setFlags(this.getFlags() ^ Flags.READONLY); } setCyclicFlag(member.type); return true; @@ -158,13 +185,15 @@ public boolean addMembers(BTupleMember member) { // adding rest type of resolved node to a previously defined // empty tuple shell in main scope public boolean addRestType(BType restType) { + ld = null; if (restType != null && restType instanceof BTupleType && ((BTupleType) restType).isCyclic && restType.getQualifiedTypeName().equals(this.getQualifiedTypeName()) && this.members.isEmpty()) { return false; } this.restType = restType; - if (Symbols.isFlagOn(this.flags, Flags.READONLY) && !Symbols.isFlagOn(restType.flags, Flags.READONLY)) { - this.flags ^= Flags.READONLY; + if (Symbols.isFlagOn(this.getFlags(), Flags.READONLY) && + !Symbols.isFlagOn(restType.getFlags(), Flags.READONLY)) { + this.setFlags(this.getFlags() ^ Flags.READONLY); } setCyclicFlag(restType); return true; @@ -174,6 +203,7 @@ public void setMembers(List members) { assert members.isEmpty(); this.memberTypes = null; this.members = members; + ld = null; } private void setCyclicFlag(BType type) { @@ -205,4 +235,82 @@ private void setCyclicFlag(BType type) { } } } + + // This is to ensure call to isNullable won't call semType. In case this is a member of a recursive union otherwise + // this will have an invalid list type since parent union type call this while it is filling its members + @Override + public boolean isNullable() { + return false; + } + + private boolean hasTypeHoles() { + if (members != null) { + for (BTupleMember member : members) { + if (member.type instanceof BNoType) { + return true; + } + } + } + if (restType != null) { + return restType instanceof BNoType; + } + return false; + } + + /** + * When the type is mutated we need to reset the definition used for the semType. + */ + @Override + public void resetSemType() { + ld = null; + } + + // If the member has a semtype component then it will be represented by that component otherwise with never. This + // means we depend on properly partitioning types to semtype components. Also, we need to ensure member types are + // "ready" when we call this + @Override + public SemType semType() { + if (ld != null) { + return ld.getSemType(env); + } + ld = new ListDefinition(); + if (hasTypeHoles()) { + return ld.defineListTypeWrapped(env, VAL); + } + boolean isReadonly = Symbols.isFlagOn(getFlags(), Flags.READONLY); + CellAtomicType.CellMutability mut = isReadonly ? CELL_MUT_NONE : CELL_MUT_LIMITED; + if (members == null) { + if (restType == null) { + throw new IllegalStateException("Both members and rest type can't be null"); + } + SemType restSemType = restType.semType(); + return ld.defineListTypeWrapped(env, List.of(), 0, Objects.requireNonNullElse(restSemType, NEVER), mut); + } + List memberSemTypes = new ArrayList<>(members.size()); + for (BTupleMember member : members) { + BType memberType = member.type; + SemType semType = memberType.semType(); + if (semType == null) { + semType = NEVER; + } + memberSemTypes.add(semType); + } + SemType restSemType = restType != null ? restType.semType() : NEVER; + if (restSemType == null) { + restSemType = NEVER; + } + return ld.defineListTypeWrapped(env, memberSemTypes, memberSemTypes.size(), restSemType, mut); + } + + @Override + public void setFlags(long flags) { + super.setFlags(flags); + restLd(); + } + + @Override + public void addFlags(long flags) { + super.addFlags(flags); + restLd(); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BType.java index a60272975151..3704a586bb7b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BType.java @@ -17,6 +17,8 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Core; +import io.ballerina.types.SemType; import org.ballerinalang.model.Name; import org.ballerinalang.model.types.TypeKind; import org.ballerinalang.model.types.ValueType; @@ -51,27 +53,52 @@ public class BType implements ValueType { // sometimes we loose type param information down the line. which is a problem. // TODO: Refactor this after JBallerina 1.0. public Name name; - public long flags; + private long flags; + + protected SemType semType; public BType(int tag, BTypeSymbol tsymbol) { - this.tag = tag; - this.tsymbol = tsymbol; - this.name = Names.EMPTY; - this.flags = 0; + this(tag, tsymbol, Names.EMPTY, 0, null); + } + + public BType(int tag, BTypeSymbol tsymbol, SemType semType) { + this(tag, tsymbol, Names.EMPTY, 0, semType); } public BType(int tag, BTypeSymbol tsymbol, long flags) { - this.tag = tag; - this.tsymbol = tsymbol; - this.name = Names.EMPTY; - this.flags = flags; + this(tag, tsymbol, Names.EMPTY, flags, null); } public BType(int tag, BTypeSymbol tsymbol, Name name, long flags) { + this(tag, tsymbol, name, flags, null); + } + + public BType(int tag, BTypeSymbol tsymbol, long flags, SemType semType) { + this(tag, tsymbol, Names.EMPTY, flags, semType); + } + + public BType(int tag, BTypeSymbol tsymbol, Name name, long flags, SemType semType) { this.tag = tag; this.tsymbol = tsymbol; this.name = name; this.flags = flags; + this.semType = semType; + } + + public static BType createNilType() { + return new BNilType(); + } + + public static BType createNeverType() { + return new BNeverType(); + } + + public SemType semType() { + return semType; + } + + public void semType(SemType semtype) { + this.semType = semtype; } public BType getReturnType() { @@ -79,7 +106,7 @@ public BType getReturnType() { } public boolean isNullable() { - return false; + return Core.containsNil(semType()); } public R accept(BTypeVisitor visitor, T t) { @@ -119,6 +146,25 @@ public String getQualifiedTypeName() { return tsymbol.pkgID.toString() + ":" + tsymbol.name; } + public final long getFlags() { + return flags; + } + + public void setFlags(long flags) { + this.flags = flags; + } + + public void addFlags(long flags) { + this.flags |= flags; + this.resetSemType(); + } + + /** + * When the type is mutated we need to reset resolved semType. + */ + public void resetSemType() { + } + /** * A data holder to hold the type associated with an expression. */ diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypeReferenceType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypeReferenceType.java index 4914628e9a6d..91edd117538c 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypeReferenceType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypeReferenceType.java @@ -17,7 +17,7 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; -import org.ballerinalang.model.types.ReferenceType; +import io.ballerina.types.SemType; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; @@ -27,11 +27,10 @@ /** * @since 2.0.0 */ -public class BTypeReferenceType extends BType implements ReferenceType { +public class BTypeReferenceType extends BType { public BType referredType; public final String definitionName; - private Boolean nilable = null; public BTypeReferenceType(BType referredType, BTypeSymbol tsymbol, long flags) { super(TYPEREFDESC, tsymbol, flags); @@ -39,9 +38,9 @@ public BTypeReferenceType(BType referredType, BTypeSymbol tsymbol, long flags) { this.definitionName = tsymbol.getName().getValue(); } - public BTypeReferenceType(BType referredType, BTypeSymbol tsymbol, long flags, boolean nilable) { - this(referredType, tsymbol, flags); - this.nilable = nilable; + @Override + public SemType semType() { + return referredType.semType(); } @Override @@ -64,14 +63,4 @@ public void accept(TypeVisitor visitor) { public TypeKind getKind() { return TypeKind.TYPEREFDESC; } - - @Override - public boolean isNullable() { - if (this.nilable != null) { - return this.nilable; - } - - this.nilable = this.referredType.isNullable(); - return this.nilable; - } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypeVisitor.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypeVisitor.java index fa8d92c78151..ac742e46f36d 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypeVisitor.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypeVisitor.java @@ -25,8 +25,6 @@ public interface BTypeVisitor { R visit(BType t, T s); - R visit(BBuiltInRefType t, T s); - R visit(BAnyType t, T s); R visit(BAnydataType t, T s); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypedescType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypedescType.java index cdd883b5b4e5..5c92251bfe7b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypedescType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypedescType.java @@ -17,7 +17,12 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.ConstrainedType; +import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -26,31 +31,39 @@ /** * @since 0.94 */ -public class BTypedescType extends BBuiltInRefType implements ConstrainedType { +public class BTypedescType extends BType implements ConstrainedType { public BType constraint; + private final Env env; - public BTypedescType(BType constraint, BTypeSymbol tsymbol) { - + public BTypedescType(Env env, BType constraint, BTypeSymbol tsymbol) { super(TypeTags.TYPEDESC, tsymbol, Flags.READONLY); this.constraint = constraint; + this.env = env; + } + + public BTypedescType(Env env, BType constraint, BTypeSymbol tsymbol, SemType semType) { + this(env, constraint, tsymbol); + this.semType = semType; } @Override public BType getConstraint() { - return constraint; } @Override public R accept(BTypeVisitor visitor, T t) { - return visitor.visit(this, t); } @Override - public String toString() { + public TypeKind getKind() { + return TypeKind.TYPEDESC; + } + @Override + public String toString() { if (constraint.tag == TypeTags.ANY) { return super.toString(); } @@ -60,7 +73,20 @@ public String toString() { @Override public void accept(TypeVisitor visitor) { - visitor.visit(this); } + + @Override + public SemType semType() { + if (this.semType != null) { + return this.semType; + } + + if (constraint == null || constraint instanceof BNoType) { + this.semType = PredefinedType.TYPEDESC; + } else { + this.semType = SemTypes.typedescContaining(env, constraint.semType()); + } + return this.semType; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BUnionType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BUnionType.java index b73167676809..d414330d321b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BUnionType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BUnionType.java @@ -17,6 +17,10 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.TypeKind; import org.ballerinalang.model.types.UnionType; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; @@ -44,14 +48,11 @@ */ public class BUnionType extends BType implements UnionType { public boolean resolvingToString = false; - private boolean nullable; private String cachedToString; protected LinkedHashSet memberTypes; - public Boolean isAnyData = null; - public Boolean isPureType = null; - public boolean isCyclic = false; + public boolean isCyclic = false; private LinkedHashSet originalMemberTypes; private static final String INT_CLONEABLE = "__Cloneable"; @@ -59,44 +60,38 @@ public class BUnionType extends BType implements UnionType { private static final String CLONEABLE_TYPE = "CloneableType"; private static final Pattern pCloneable = Pattern.compile(INT_CLONEABLE); private static final Pattern pCloneableType = Pattern.compile(CLONEABLE_TYPE); + public final Env env; - public BUnionType(BTypeSymbol tsymbol, LinkedHashSet memberTypes, boolean nullable, boolean readonly) { - this(tsymbol, memberTypes, memberTypes, nullable, readonly); + public BUnionType(Env env, BTypeSymbol tsymbol, LinkedHashSet memberTypes, boolean readonly) { + this(env, tsymbol, memberTypes, memberTypes, readonly, false); } - private BUnionType(BTypeSymbol tsymbol, LinkedHashSet originalMemberTypes, LinkedHashSet memberTypes, - boolean nullable, boolean readonly) { - super(TypeTags.UNION, tsymbol); - - if (readonly) { - this.flags |= Flags.READONLY; - - if (tsymbol != null) { - this.tsymbol.flags |= Flags.READONLY; - } - } + private BUnionType(Env env, BTypeSymbol tsymbol, LinkedHashSet originalMemberTypes, + LinkedHashSet memberTypes, boolean readonly) { + this(env, tsymbol, originalMemberTypes, memberTypes, readonly, false); + } - this.originalMemberTypes = originalMemberTypes; - this.memberTypes = memberTypes; - this.nullable = nullable; + private BUnionType(Env env, BTypeSymbol tsymbol, LinkedHashSet memberTypes, + boolean readonly, boolean isCyclic) { + this(env, tsymbol, null, memberTypes, readonly, isCyclic); } - private BUnionType(BTypeSymbol tsymbol, LinkedHashSet memberTypes, boolean nullable, boolean readonly, - boolean isCyclic) { + private BUnionType(Env env, BTypeSymbol tsymbol, LinkedHashSet originalMemberTypes, + LinkedHashSet memberTypes, boolean readonly, boolean isCyclic) { super(TypeTags.UNION, tsymbol); if (readonly) { - this.flags |= Flags.READONLY; + this.addFlags(Flags.READONLY); if (tsymbol != null) { this.tsymbol.flags |= Flags.READONLY; } } - this.originalMemberTypes = memberTypes; + this.originalMemberTypes = originalMemberTypes; this.memberTypes = memberTypes; - this.nullable = nullable; this.isCyclic = isCyclic; + this.env = env; } @Override @@ -129,11 +124,6 @@ public void accept(TypeVisitor visitor) { visitor.visit(this); } - @Override - public boolean isNullable() { - return nullable; - } - @Override public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); @@ -150,38 +140,35 @@ public String toString() { return cachedToString; } - public void setNullable(boolean nullable) { - this.nullable = nullable; - } - /** * Creates an empty union for cyclic union types. * + * @param env The environment to be used to create the union type. * @param tsymbol Type symbol for the union. * @param types The types to be used to define the union. * @param isCyclic The cyclic indicator. * @return The created union type. */ - public static BUnionType create(BTypeSymbol tsymbol, LinkedHashSet types, boolean isCyclic) { + public static BUnionType create(Env env, BTypeSymbol tsymbol, LinkedHashSet types, boolean isCyclic) { LinkedHashSet memberTypes = new LinkedHashSet<>(types.size()); boolean isImmutable = true; - boolean hasNilableType = false; - return new BUnionType(tsymbol, memberTypes, hasNilableType, isImmutable, isCyclic); + return new BUnionType(env, tsymbol, memberTypes, isImmutable, isCyclic); } /** * Creates a union type using the types specified in the `types` set. The created union will not have union types in * its member types set. If the set contains the nil type, calling isNullable() will return true. * + * @param env The environment to be used to create the union type. * @param tsymbol Type symbol for the union. * @param types The types to be used to define the union. * @return The created union type. */ - public static BUnionType create(BTypeSymbol tsymbol, LinkedHashSet types) { + public static BUnionType create(Env env, BTypeSymbol tsymbol, LinkedHashSet types) { LinkedHashSet memberTypes = new LinkedHashSet<>(types.size()); if (types.isEmpty()) { - return new BUnionType(tsymbol, memberTypes, false, true); + return new BUnionType(env, tsymbol, memberTypes, true); } boolean isImmutable = true; @@ -190,54 +177,32 @@ public static BUnionType create(BTypeSymbol tsymbol, LinkedHashSet types) memberTypes.add(memBType); } - if (isImmutable && !Symbols.isFlagOn(memBType.flags, Flags.READONLY)) { + if (isImmutable && !Symbols.isFlagOn(memBType.getFlags(), Flags.READONLY)) { isImmutable = false; } } - if (memberTypes.isEmpty()) { - memberTypes.add(new BNeverType()); - return new BUnionType(tsymbol, memberTypes, false, isImmutable); - } - boolean hasNilableType = false; - for (BType memberType : memberTypes) { - if (memberType.isNullable() && memberType.tag != TypeTags.NIL) { - hasNilableType = true; - break; - } - } - - if (hasNilableType) { - LinkedHashSet bTypes = new LinkedHashSet<>(memberTypes.size()); - for (BType t : memberTypes) { - if (t.tag != TypeTags.NIL) { - bTypes.add(t); - } - } - memberTypes = bTypes; - } - - for (BType memberType : memberTypes) { - if (memberType.isNullable()) { - return new BUnionType(tsymbol, types, memberTypes, true, isImmutable); - } + if (memberTypes.isEmpty()) { + memberTypes.add(BType.createNeverType()); + return new BUnionType(env, tsymbol, memberTypes, isImmutable); } - return new BUnionType(tsymbol, types, memberTypes, false, isImmutable); + return new BUnionType(env, tsymbol, types, memberTypes, isImmutable); } /** * Creates a union type using the provided types. If the set contains the nil type, calling isNullable() will return * true. * + * @param env The environment to be used to create the union type. * @param tsymbol Type symbol for the union. * @param types The types to be used to define the union. * @return The created union type. */ - public static BUnionType create(BTypeSymbol tsymbol, BType... types) { + public static BUnionType create(Env env, BTypeSymbol tsymbol, BType... types) { LinkedHashSet memberTypes = new LinkedHashSet<>(types.length); memberTypes.addAll(Arrays.asList(types)); - return create(tsymbol, memberTypes); + return create(env, tsymbol, memberTypes); } /** @@ -262,13 +227,12 @@ public void add(BType type) { this.memberTypes.add(type); } - if (Symbols.isFlagOn(this.flags, Flags.READONLY) && !Symbols.isFlagOn(type.flags, Flags.READONLY)) { - this.flags ^= Flags.READONLY; + if (Symbols.isFlagOn(this.getFlags(), Flags.READONLY) && !Symbols.isFlagOn(type.getFlags(), Flags.READONLY)) { + this.setFlags(this.getFlags() ^ Flags.READONLY); } setCyclicFlag(type); - - this.nullable = this.nullable || type.isNullable(); + this.semType = null; // reset cached sem-type if exists } private void setCyclicFlag(BType type) { @@ -319,24 +283,20 @@ public void remove(BType type) { } this.originalMemberTypes.remove(type); - if (type.isNullable()) { - this.nullable = false; - } - - if (Symbols.isFlagOn(this.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(this.getFlags(), Flags.READONLY)) { return; } boolean isImmutable = true; for (BType memBType : this.memberTypes) { - if (!Symbols.isFlagOn(memBType.flags, Flags.READONLY)) { + if (!Symbols.isFlagOn(memBType.getFlags(), Flags.READONLY)) { isImmutable = false; break; } } if (isImmutable) { - this.flags |= Flags.READONLY; + this.addFlags(Flags.READONLY); } } @@ -351,28 +311,28 @@ public void mergeUnionType(BUnionType unionType) { for (BType member : unionType.getMemberTypes()) { if (member instanceof BArrayType arrayType) { if (getImpliedType(arrayType.eType) == unionType) { - BArrayType newArrayType = new BArrayType(this, arrayType.tsymbol, arrayType.size, - arrayType.state, arrayType.flags); + BArrayType newArrayType = new BArrayType(env, this, arrayType.tsymbol, arrayType.getSize(), + arrayType.state, arrayType.getFlags()); this.add(newArrayType); continue; } } else if (member instanceof BMapType mapType) { if (getImpliedType(mapType.constraint) == unionType) { - BMapType newMapType = new BMapType(mapType.tag, this, mapType.tsymbol, mapType.flags); + BMapType newMapType = new BMapType(env, mapType.tag, this, mapType.tsymbol, mapType.getFlags()); this.add(newMapType); continue; } } else if (member instanceof BTableType tableType) { if (getImpliedType(tableType.constraint) == unionType) { - BTableType newTableType = new BTableType(tableType.tag, this, tableType.tsymbol, - tableType.flags); + BTableType newTableType = new BTableType(env, this, tableType.tsymbol, + tableType.getFlags()); this.add(newTableType); continue; } else if (tableType.constraint instanceof BMapType mapType) { if (getImpliedType(mapType.constraint) == unionType) { - BMapType newMapType = new BMapType(mapType.tag, this, mapType.tsymbol, mapType.flags); - BTableType newTableType = new BTableType(tableType.tag, newMapType, tableType.tsymbol, - tableType.flags); + BMapType newMapType = new BMapType(env, mapType.tag, this, mapType.tsymbol, mapType.getFlags()); + BTableType newTableType = new BTableType(env, newMapType, tableType.tsymbol, + tableType.getFlags()); this.add(newTableType); continue; } @@ -445,7 +405,7 @@ private void computeStringRepresentation() { if (tsymbol != null && !tsymbol.getName().getValue().isEmpty()) { String typeName = tsymbol.getName().getValue(); String packageId = tsymbol.pkgID.toString(); - boolean isTypeParam = Symbols.isFlagOn(flags, Flags.TYPE_PARAM); + boolean isTypeParam = Symbols.isFlagOn(getFlags(), Flags.TYPE_PARAM); // improve readability of cyclic union types if (isCyclic && (pCloneable.matcher(typeName).matches() || (isTypeParam && pCloneableType.matcher(typeName).matches()))) { @@ -496,8 +456,8 @@ private void computeStringRepresentation() { String typeStr = numberOfNotNilTypes > 1 ? "(" + joiner + ")" : joiner.toString(); boolean hasNilType = uniqueTypes.size() > numberOfNotNilTypes; - cachedToString = (nullable && hasNilType && !hasNilableMember) ? (typeStr + Names.QUESTION_MARK.value) : - typeStr; + cachedToString = (this.isNullable() && hasNilType && !hasNilableMember) ? (typeStr + Names.QUESTION_MARK.value) + : typeStr; } private static boolean isNeverType(BType type) { @@ -515,4 +475,27 @@ private static boolean isNeverType(BType type) { } return false; } + + /** + * When the type is mutated we need to reset resolved semType. + */ + public void resetSemType() { + this.semType = null; + } + + @Override + public SemType semType() { + if (this.semType == null) { + this.semType = computeSemTypeFromMemberTypes(); + } + return this.semType; + } + + private SemType computeSemTypeFromMemberTypes() { + SemType t = PredefinedType.NEVER; + for (BType ty : this.memberTypes) { + t = SemTypes.union(t, ty.semType()); + } + return t; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BXMLSubType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BXMLSubType.java index eb62806d81ff..c51c77304eec 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BXMLSubType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BXMLSubType.java @@ -17,11 +17,18 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.Name; import org.ballerinalang.model.types.SelectivelyImmutableReferenceType; import org.ballerinalang.model.types.TypeKind; -import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.util.Names; +import org.wso2.ballerinalang.compiler.util.TypeTags; +import org.wso2.ballerinalang.util.Flags; + +import static org.wso2.ballerinalang.compiler.semantics.analyzer.Types.AND_READONLY_SUFFIX; /** * Represents Builtin Subtype of integer. @@ -29,54 +36,53 @@ * @since 1.2.0 */ public class BXMLSubType extends BType implements SelectivelyImmutableReferenceType { - public BXMLSubType(int tag, Name name) { - super(tag, null, name, 0); + public static final BXMLSubType XML_ELEMENT = new BXMLSubType(TypeTags.XML_ELEMENT, Names.XML_ELEMENT, + SemTypes.XML_ELEMENT); + public static final BXMLSubType XML_PI = new BXMLSubType(TypeTags.XML_PI, Names.XML_PI, SemTypes.XML_PI); + public static final BXMLSubType XML_COMMENT = new BXMLSubType(TypeTags.XML_COMMENT, Names.XML_COMMENT, + SemTypes.XML_COMMENT); + public static final BXMLSubType XML_TEXT = new BXMLSubType(TypeTags.XML_TEXT, Names.XML_TEXT, Flags.READONLY, + SemTypes.XML_TEXT); + + private BXMLSubType(int tag, Name name, SemType semType) { + this(tag, name, 0, semType); } - public BXMLSubType(int tag, Name name, long flags) { + private BXMLSubType(int tag, Name name, long flags, SemType semType) { + super(tag, null, name, flags, semType); + } - super(tag, null, name, flags); + public static BXMLSubType newImmutableXMLSubType(BXMLSubType xmlSubType) { + return new BXMLSubType(xmlSubType.tag, + Names.fromString(xmlSubType.name.getValue().concat(AND_READONLY_SUFFIX)), + xmlSubType.getFlags() | Flags.READONLY, + Core.intersect(xmlSubType.semType, PredefinedType.VAL_READONLY)); } @Override public boolean isNullable() { - return false; } @Override public R accept(BTypeVisitor visitor, T t) { - return visitor.visit(this, t); } @Override public TypeKind getKind() { - return TypeKind.XML; } - @Override - public void accept(TypeVisitor visitor) { - - visitor.visit(this); - } - @Override public String toString() { - return Names.XML.value + Names.ALIAS_SEPARATOR + name; } @Override public String getQualifiedTypeName() { - return Names.BALLERINA_ORG.value + Names.ORG_NAME_SEPARATOR.value + Names.LANG.value + Names.DOT.value + Names.XML.value + Names.ALIAS_SEPARATOR + name; } - - public boolean isAnydata() { - return true; - } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BXMLType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BXMLType.java index f86112006093..602c52bb28d0 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BXMLType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BXMLType.java @@ -16,7 +16,12 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.SelectivelyImmutableReferenceType; +import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; @@ -29,7 +34,7 @@ * * @since 0.961.0 */ -public class BXMLType extends BBuiltInRefType implements SelectivelyImmutableReferenceType { +public class BXMLType extends BType implements SelectivelyImmutableReferenceType { public BType constraint; public BXMLType mutableType; @@ -54,7 +59,7 @@ public String toString() { stringRep = Names.XML.value; } - return !Symbols.isFlagOn(flags, Flags.READONLY) ? stringRep : stringRep.concat(" & readonly"); + return !Symbols.isFlagOn(getFlags(), Flags.READONLY) ? stringRep : stringRep.concat(" & readonly"); } @Override @@ -62,8 +67,42 @@ public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } + @Override + public TypeKind getKind() { + return TypeKind.XML; + } + @Override public void accept(TypeVisitor visitor) { visitor.visit(this); } + + @Override + public SemType semType() { + if (this.semType != null) { + return this.semType; + } + + SemType s; + if (constraint == null) { + s = PredefinedType.XML; + } else { + SemType contraintSemtype; + if (constraint instanceof BParameterizedType parameterizedType) { + contraintSemtype = parameterizedType.paramValueType.semType(); + } else { + contraintSemtype = constraint.semType(); + } + + if (contraintSemtype == null || !Core.isSubtypeSimple(contraintSemtype, PredefinedType.XML)) { + // we reach here for negative semantics + contraintSemtype = PredefinedType.NEVER; + } + s = SemTypes.xmlSequence(contraintSemtype); + } + + boolean readonly = Symbols.isFlagOn(this.getFlags(), Flags.READONLY); + this.semType = readonly ? SemTypes.intersect(PredefinedType.VAL_READONLY, s) : s; + return this.semType; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/SemNamedType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/SemNamedType.java new file mode 100644 index 000000000000..7e7b7ed949e8 --- /dev/null +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/SemNamedType.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.ballerinalang.compiler.semantics.model.types; + +import io.ballerina.types.SemType; + +import java.util.Optional; + +/** + * Represents a sem-type and its user-specified string representation. + * + * @param semType Sem-type representation of a type + * @param optName User-specified string representation for the type, if available + */ +public record SemNamedType(SemType semType, Optional optName) { +} diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangPackage.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangPackage.java index 2b53aabcaf12..36ce13aa0db5 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangPackage.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangPackage.java @@ -20,6 +20,7 @@ import io.ballerina.projects.internal.ModuleContextDataHolder; import io.ballerina.tools.diagnostics.Diagnostic; import io.ballerina.tools.diagnostics.DiagnosticSeverity; +import io.ballerina.types.Env; import org.ballerinalang.compiler.CompilerPhase; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; @@ -92,7 +93,9 @@ public class BLangPackage extends BLangNode implements PackageNode { public ModuleContextDataHolder moduleContextDataHolder; - public BLangPackage() { + public final Env semtypeEnv; + + public BLangPackage(Env env) { this.compUnits = new ArrayList<>(); this.imports = new ArrayList<>(); this.xmlnsList = new ArrayList<>(); @@ -110,6 +113,7 @@ public BLangPackage() { this.testablePkgs = new ArrayList<>(); this.flagSet = EnumSet.noneOf(Flag.class); this.diagnostics = new TreeSet<>(new DiagnosticComparator()); + this.semtypeEnv = env; } @Override diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTestablePackage.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTestablePackage.java index 58196fac987d..4a73e3143fe0 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTestablePackage.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTestablePackage.java @@ -17,6 +17,8 @@ */ package org.wso2.ballerinalang.compiler.tree; +import io.ballerina.types.Env; + import java.util.HashMap; import java.util.Map; @@ -31,6 +33,11 @@ public class BLangTestablePackage extends BLangPackage { private final Map mockFunctionNamesMap = new HashMap<>(); private final Map isLegacyMockingMap = new HashMap<>(); + + public BLangTestablePackage(Env env) { + super(env); + } + public Map getMockFunctionNamesMap() { return mockFunctionNamesMap; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTypeDefinition.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTypeDefinition.java index d54361654d93..ff3129519fad 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTypeDefinition.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTypeDefinition.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.tree; +import io.ballerina.types.SemType; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.tree.AnnotationAttachmentNode; import org.ballerinalang.model.tree.IdentifierNode; @@ -57,6 +58,10 @@ public class BLangTypeDefinition extends BLangNode implements TypeDefinition { public int cycleDepth = -1; + // SemType Integration + public SemType semType; + public int semCycleDepth = -1; + public BLangTypeDefinition() { this.annAttachments = new ArrayList<>(); this.flagSet = EnumSet.noneOf(Flag.class); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangUnaryExpr.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangUnaryExpr.java index 0b6dfa3c4bab..4d182ae04c30 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangUnaryExpr.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangUnaryExpr.java @@ -41,8 +41,6 @@ public class BLangUnaryExpr extends BLangExpression implements UnaryExpressionNo // Semantic Data public BOperatorSymbol opSymbol; - public boolean isConstant; - @Override public BLangExpression getExpression() { return expr; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/types/BLangType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/types/BLangType.java index cd9eb532ddb9..50f3bdc78199 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/types/BLangType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/types/BLangType.java @@ -17,6 +17,7 @@ */ package org.wso2.ballerinalang.compiler.tree.types; +import io.ballerina.types.Definition; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.tree.types.TypeNode; import org.wso2.ballerinalang.compiler.tree.BLangNode; @@ -36,6 +37,7 @@ public abstract class BLangType extends BLangNode implements TypeNode { public boolean nullable; public boolean grouped; public Set flagSet = EnumSet.noneOf(Flag.class); + public Definition defn; @Override public boolean isNullable() { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/ImmutableTypeCloner.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/ImmutableTypeCloner.java index ea65959446a4..fe11dda777dd 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/ImmutableTypeCloner.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/ImmutableTypeCloner.java @@ -88,8 +88,6 @@ */ public final class ImmutableTypeCloner { - private static final String AND_READONLY_SUFFIX = " & readonly"; - private ImmutableTypeCloner() { } @@ -98,8 +96,8 @@ public static BType getEffectiveImmutableType(Location pos, Types types, SymbolTable symTable, BLangAnonymousModelHelper anonymousModelHelper, Names names) { return getImmutableIntersectionType(pos, types, type, env, env.enclPkg.packageID, env.scope.owner, - symTable, anonymousModelHelper, names, new HashSet<>(), - new HashSet<>()).effectiveType; + symTable, anonymousModelHelper, names, new HashSet<>(), + new HashSet<>()).effectiveType; } public static BType getEffectiveImmutableType(Location pos, Types types, @@ -107,8 +105,8 @@ public static BType getEffectiveImmutableType(Location pos, Types types, BSymbol owner, SymbolTable symTable, BLangAnonymousModelHelper anonymousModelHelper, Names names) { return getImmutableIntersectionType(pos, types, type, null, pkgId, owner, - symTable, anonymousModelHelper, names, new HashSet<>(), - new HashSet<>()).effectiveType; + symTable, anonymousModelHelper, names, new HashSet<>(), + new HashSet<>()).effectiveType; } public static BIntersectionType getImmutableIntersectionType(Location pos, Types types, @@ -169,7 +167,7 @@ public static BType getImmutableType(Location pos, Types types, BType type, Symb return symTable.semanticError; } - if (types.isInherentlyImmutableType(type) || Symbols.isFlagOn(type.flags, Flags.READONLY)) { + if (types.isInherentlyImmutableType(type) || Symbols.isFlagOn(type.getFlags(), Flags.READONLY)) { return type; } @@ -192,7 +190,7 @@ private static BIntersectionType getImmutableIntersectionType(Location pos, Set unresolvedTypes) { BType refType = Types.getReferredType(bType); SelectivelyImmutableReferenceType type = (SelectivelyImmutableReferenceType) refType; - if (refType.tag == TypeTags.INTERSECTION && Symbols.isFlagOn(refType.flags, Flags.READONLY)) { + if (refType.tag == TypeTags.INTERSECTION && Symbols.isFlagOn(refType.getFlags(), Flags.READONLY)) { return (BIntersectionType) refType; } @@ -219,12 +217,7 @@ private static BIntersectionType setImmutableType(Location pos, Types types, case TypeTags.XML_ELEMENT: case TypeTags.XML_PI: BXMLSubType origXmlSubType = (BXMLSubType) type; - - // TODO: 4/28/20 Check tsymbol - BXMLSubType immutableXmlSubType = - new BXMLSubType(origXmlSubType.tag, - Names.fromString(origXmlSubType.name.getValue().concat(AND_READONLY_SUFFIX)), - origXmlSubType.flags | Flags.READONLY); + BXMLSubType immutableXmlSubType = BXMLSubType.newImmutableXMLSubType(origXmlSubType); BIntersectionType immutableXmlSubTypeIntersectionType = createImmutableIntersectionType(pkgId, owner, originalType, immutableXmlSubType, symTable); @@ -256,20 +249,7 @@ private static BIntersectionType setImmutableType(Location pos, Types types, unresolvedTypes, (BTableType) type, originalType); case TypeTags.ANY: BAnyType origAnyType = (BAnyType) type; - - BTypeSymbol immutableAnyTSymbol = getReadonlyTSymbol(names, origAnyType.tsymbol, env, pkgId, owner); - - BAnyType immutableAnyType; - if (immutableAnyTSymbol != null) { - immutableAnyType = new BAnyType(origAnyType.tag, immutableAnyTSymbol, immutableAnyTSymbol.name, - origAnyType.flags | Flags.READONLY, origAnyType.isNullable()); - immutableAnyTSymbol.type = immutableAnyType; - } else { - immutableAnyType = new BAnyType(origAnyType.tag, null, - getImmutableTypeName(names, TypeKind.ANY.typeName()), - origAnyType.flags | Flags.READONLY, origAnyType.isNullable()); - } - + BAnyType immutableAnyType = BAnyType.newImmutableBAnyType(); BIntersectionType immutableAnyIntersectionType = createImmutableIntersectionType(pkgId, owner, originalType, immutableAnyType, @@ -294,14 +274,14 @@ private static BIntersectionType defineImmutableTableType(Location pos, Types ty Names names, Set unresolvedTypes, BTableType type, BType originalType) { - BTypeSymbol immutableTableTSymbol = getReadonlyTSymbol(names, type.tsymbol, env, pkgId, owner); + BTypeSymbol immutableTableTSymbol = getReadonlyTSymbol(type.tsymbol, env, pkgId, owner); Optional immutableType = Types.getImmutableType(symTable, pkgId, type); if (immutableType.isPresent()) { return immutableType.get(); } else { Types.addImmutableType(symTable, pkgId, type, createImmutableIntersectionType(pkgId, owner, - originalType, new BTableType(TypeTags.TABLE, null, immutableTableTSymbol, - type.flags | Flags.READONLY), symTable)); + originalType, new BTableType(symTable.typeEnv(), null, immutableTableTSymbol, + type.getFlags() | Flags.READONLY), symTable)); } BIntersectionType immutableTableType = Types.getImmutableType(symTable, pkgId, type).orElseThrow(); @@ -336,13 +316,13 @@ private static BIntersectionType defineImmutableXMLType(Location pos, Types type Names names, Set unresolvedTypes, BXMLType type, BType originalType) { - BTypeSymbol immutableXmlTSymbol = getReadonlyTSymbol(names, type.tsymbol, env, pkgId, owner); + BTypeSymbol immutableXmlTSymbol = getReadonlyTSymbol(type.tsymbol, env, pkgId, owner); Optional immutableType = Types.getImmutableType(symTable, pkgId, type); if (immutableType.isPresent()) { return immutableType.get(); } else { Types.addImmutableType(symTable, pkgId, type, createImmutableIntersectionType(pkgId, owner, - originalType, new BXMLType(null, immutableXmlTSymbol, type.flags | Flags.READONLY), + originalType, new BXMLType(null, immutableXmlTSymbol, type.getFlags() | Flags.READONLY), symTable)); } @@ -360,14 +340,15 @@ private static BIntersectionType defineImmutableArrayType(Location pos, Types ty Names names, Set unresolvedTypes, BArrayType type, BType originalType) { - BTypeSymbol immutableArrayTSymbol = getReadonlyTSymbol(names, type.tsymbol, env, pkgId, owner); + BTypeSymbol immutableArrayTSymbol = getReadonlyTSymbol(type.tsymbol, env, pkgId, owner); Optional immutableType = Types.getImmutableType(symTable, pkgId, type); if (immutableType.isPresent()) { return immutableType.get(); } else { Types.addImmutableType(symTable, pkgId, type, createImmutableIntersectionType(pkgId, owner, - originalType, new BArrayType(null, immutableArrayTSymbol, type.size, type.state, - type.flags | Flags.READONLY), symTable)); + originalType, + new BArrayType(symTable.typeEnv(), null, immutableArrayTSymbol, type.getSize(), type.state, + type.getFlags() | Flags.READONLY), symTable)); } BIntersectionType immutableArrayType = Types.getImmutableType(symTable, pkgId, type).orElseThrow(); @@ -384,14 +365,14 @@ private static BIntersectionType defineImmutableMapType(Location pos, Types type Names names, Set unresolvedTypes, BMapType type, BType originalType) { - BTypeSymbol immutableMapTSymbol = getReadonlyTSymbol(names, type.tsymbol, env, pkgId, owner); + BTypeSymbol immutableMapTSymbol = getReadonlyTSymbol(type.tsymbol, env, pkgId, owner); Optional immutableType = Types.getImmutableType(symTable, pkgId, type); if (immutableType.isPresent()) { return immutableType.get(); } else { Types.addImmutableType(symTable, pkgId, type, createImmutableIntersectionType(pkgId, owner, - originalType, new BMapType(TypeTags.MAP, null, immutableMapTSymbol, - type.flags | Flags.READONLY), symTable)); + originalType, new BMapType(symTable.typeEnv(), TypeTags.MAP, null, immutableMapTSymbol, + type.getFlags() | Flags.READONLY), symTable)); } BIntersectionType immutableMapType = Types.getImmutableType(symTable, pkgId, type).orElseThrow(); @@ -418,7 +399,7 @@ private static BIntersectionType defineImmutableTupleType(Location pos, Types ty return immutableType.get(); } else { Types.addImmutableType(symTable, pkgId, type, createImmutableIntersectionType(pkgId, owner, - originalType, new BTupleType(origTupleTypeSymbol), symTable)); + originalType, new BTupleType(symTable.typeEnv(), origTupleTypeSymbol), symTable)); } List immutableMemTypes = new ArrayList<>(origTupleMembers.size()); @@ -432,7 +413,7 @@ private static BIntersectionType defineImmutableTupleType(Location pos, Types ty Name origTupleTypeSymbolName = Names.EMPTY; if (!originalTypeName.isEmpty()) { origTupleTypeSymbolName = origTupleTypeSymbol.name.value.isEmpty() ? Names.EMPTY : - getImmutableTypeName(names, getSymbolFQN(origTupleTypeSymbol)); + Types.getImmutableTypeName(getSymbolFQN(origTupleTypeSymbol)); tupleEffectiveImmutableType.name = origTupleTypeSymbolName; } @@ -463,11 +444,11 @@ private static BIntersectionType defineImmutableTupleType(Location pos, Types ty BTypeSymbol immutableTupleTSymbol = getReadonlyTSymbol(origTupleTypeSymbol, env, pkgId, owner, origTupleTypeSymbolName); effectiveTypeFromType.tsymbol = immutableTupleTSymbol; - effectiveTypeFromType.flags |= (type.flags | Flags.READONLY); + effectiveTypeFromType.addFlags(type.getFlags() | Flags.READONLY); immutableTupleTSymbol.type = effectiveTypeFromType; } else { - effectiveTypeFromType.flags |= (type.flags | Flags.READONLY); + effectiveTypeFromType.addFlags(type.getFlags() | Flags.READONLY); } BType effectiveType = immutableTupleIntersectionType.effectiveType; @@ -482,7 +463,7 @@ private static BIntersectionType defineImmutableTupleType(Location pos, Types ty BLangTypeDefinition typeDefinition = TypeDefBuilderHelper.addTypeDefinition(effectiveType, effectiveType.tsymbol, tupleTypeNode, env); typeDefinition.pos = pos; - effectiveType.flags |= Flags.EFFECTIVE_TYPE_DEF; + effectiveType.addFlags(Flags.EFFECTIVE_TYPE_DEF); return immutableTupleIntersectionType; } @@ -583,10 +564,11 @@ private static BIntersectionType defineImmutableRecordType(Location pos, BRecord BTypeSymbol recordTypeSymbol = origRecordType.tsymbol; BRecordTypeSymbol recordSymbol = Symbols.createRecordSymbol(recordTypeSymbol.flags | Flags.READONLY, - getImmutableTypeName(names, getSymbolFQN(recordTypeSymbol)), + Types.getImmutableTypeName(getSymbolFQN(recordTypeSymbol)), pkgID, null, env.scope.owner, pos, VIRTUAL); - BInvokableType bInvokableType = new BInvokableType(new ArrayList<>(), symTable.nilType, null); + BInvokableType bInvokableType = + new BInvokableType(symTable.typeEnv(), List.of(), symTable.nilType, null); BInvokableSymbol initFuncSymbol = Symbols.createFunctionSymbol( Flags.PUBLIC, Names.EMPTY, Names.EMPTY, env.enclPkg.symbol.pkgID, bInvokableType, env.scope.owner, false, symTable.builtinPos, VIRTUAL); @@ -594,7 +576,8 @@ private static BIntersectionType defineImmutableRecordType(Location pos, BRecord recordSymbol.scope = new Scope(recordSymbol); - BRecordType immutableRecordType = new BRecordType(recordSymbol, origRecordType.flags | Flags.READONLY); + BRecordType immutableRecordType = new BRecordType(symTable.typeEnv(), recordSymbol, + origRecordType.getFlags() | Flags.READONLY); BIntersectionType immutableRecordIntersectionType = createImmutableIntersectionType(env, originalType, immutableRecordType, @@ -632,7 +615,7 @@ private static BIntersectionType defineImmutableObjectType(Location pos, flags &= ~Flags.CLASS; BObjectTypeSymbol objectSymbol = Symbols.createObjectSymbol(flags, - getImmutableTypeName(names, + Types.getImmutableTypeName( getSymbolFQN(origObjectTSymbol)), pkgID, null, env.scope.owner, pos, VIRTUAL); @@ -640,7 +623,8 @@ private static BIntersectionType defineImmutableObjectType(Location pos, defineObjectFunctions(objectSymbol, origObjectTSymbol, names, symTable); - BObjectType immutableObjectType = new BObjectType(objectSymbol, origObjectType.flags | Flags.READONLY); + BObjectType immutableObjectType = + new BObjectType(symTable.typeEnv(), objectSymbol, origObjectType.getFlags() | Flags.READONLY); immutableObjectType.typeIdSet = origObjectType.typeIdSet; BIntersectionType immutableObjectIntersectionType = createImmutableIntersectionType(env, originalType, @@ -684,7 +668,8 @@ public static void defineObjectFunctions(BObjectTypeSymbol immutableObjectSymbol Name funcName = Names.fromString(Symbols.getAttachedFuncSymbolName(immutableObjectSymbol.name.value, origFunc.funcName.value)); BInvokableSymbol immutableFuncSymbol = - ASTBuilderUtil.duplicateFunctionDeclarationSymbol(origFunc.symbol, immutableObjectSymbol, + ASTBuilderUtil.duplicateFunctionDeclarationSymbol(symTable.typeEnv(), origFunc.symbol, + immutableObjectSymbol, funcName, immutableObjectSymbol.pkgID, symTable.builtinPos, VIRTUAL); immutableFuncs.add(new BAttachedFunction(origFunc.funcName, immutableFuncSymbol, @@ -707,7 +692,7 @@ private static BIntersectionType defineImmutableUnionType(Location pos, Types ty if (immutableTypeOptional.isPresent()) { return immutableTypeOptional.get(); } else { - BUnionType immutableUnionType = BUnionType.create(origUnionTypeSymbol); + BUnionType immutableUnionType = BUnionType.create(symTable.typeEnv(), origUnionTypeSymbol); Types.addImmutableType(symTable, pkgId, type, createImmutableIntersectionType(pkgId, owner, originalType, immutableUnionType, symTable)); } @@ -762,27 +747,23 @@ private static BIntersectionType defineImmutableBuiltInUnionType(Location pos, T private static BAnydataType defineImmutableAnydataType(SymbolEnv env, PackageID pkgId, BSymbol owner, Names names, BAnydataType type) { - BTypeSymbol immutableAnydataTSymbol = getReadonlyTSymbol(names, type.tsymbol, env, pkgId, owner); + BTypeSymbol immutableAnydataTSymbol = getReadonlyTSymbol(type.tsymbol, env, pkgId, owner); if (immutableAnydataTSymbol != null) { - BAnydataType immutableAnydataType = - new BAnydataType(immutableAnydataTSymbol, - immutableAnydataTSymbol.name, type.flags | Flags.READONLY, + BAnydataType immutableAnydataType = BAnydataType.newImmutableBAnydataType(type, immutableAnydataTSymbol, + immutableAnydataTSymbol.name, type.isNullable()); immutableAnydataTSymbol.type = immutableAnydataType; return immutableAnydataType; } - return new BAnydataType(null, - getImmutableTypeName(names, TypeKind.ANYDATA.typeName()), - type.flags | Flags.READONLY, type.isNullable()); + return BAnydataType.newImmutableBAnydataType(type, null, + Types.getImmutableTypeName(TypeKind.ANYDATA.typeName()), type.isNullable()); } private static BJSONType defineImmutableJsonType(SymbolEnv env, PackageID pkgId, BSymbol owner, Names names, BJSONType type) { - BTypeSymbol immutableJsonTSymbol = getReadonlyTSymbol(names, type.tsymbol, env, pkgId, owner); - BJSONType immutableJsonType = new BJSONType(immutableJsonTSymbol, - type.isNullable(), - type.flags | Flags.READONLY); + BTypeSymbol immutableJsonTSymbol = getReadonlyTSymbol(type.tsymbol, env, pkgId, owner); + BJSONType immutableJsonType = BJSONType.newImmutableBJSONType(type, immutableJsonTSymbol, type.isNullable()); if (immutableJsonTSymbol != null) { immutableJsonTSymbol.type = immutableJsonType; } @@ -804,7 +785,7 @@ private static BIntersectionType handleImmutableUnionType(Location pos, Types ty String originalTypeName = origUnionTypeSymbol == null ? "" : origUnionTypeSymbol.name.getValue(); if (!originalTypeName.isEmpty()) { - unionEffectiveImmutableType.name = getImmutableTypeName(names, getSymbolFQN(origUnionTypeSymbol)); + unionEffectiveImmutableType.name = Types.getImmutableTypeName(getSymbolFQN(origUnionTypeSymbol)); } for (BType memberType : originalMemberList) { @@ -829,25 +810,25 @@ private static BIntersectionType handleImmutableUnionType(Location pos, Types ty BTypeSymbol immutableUnionTSymbol = getReadonlyTSymbol(origUnionTypeSymbol, env, pkgId, owner, origUnionTypeSymbol.name.value.isEmpty() ? Names.EMPTY : - getImmutableTypeName(names, getSymbolFQN(origUnionTypeSymbol))); + Types.getImmutableTypeName(getSymbolFQN(origUnionTypeSymbol))); immutableType.effectiveType.tsymbol = immutableUnionTSymbol; - immutableType.effectiveType.flags |= (type.flags | Flags.READONLY); + immutableType.effectiveType.addFlags(type.getFlags() | Flags.READONLY); immutableUnionTSymbol.type = immutableType.effectiveType; } else { - immutableType.effectiveType.flags |= (type.flags | Flags.READONLY); + immutableType.effectiveType.addFlags(type.getFlags() | Flags.READONLY); } return immutableType; } - private static BTypeSymbol getReadonlyTSymbol(Names names, BTypeSymbol originalTSymbol, SymbolEnv env, + private static BTypeSymbol getReadonlyTSymbol(BTypeSymbol originalTSymbol, SymbolEnv env, PackageID pkgId, BSymbol owner) { if (originalTSymbol == null) { return null; } - return getReadonlyTSymbol(originalTSymbol, env, pkgId, owner, getImmutableTypeName(names, originalTSymbol)); + return getReadonlyTSymbol(originalTSymbol, env, pkgId, owner, getImmutableTypeName(originalTSymbol)); } private static BTypeSymbol getReadonlyTSymbol(BTypeSymbol originalTSymbol, SymbolEnv env, PackageID pkgId, @@ -877,16 +858,8 @@ private static String getSymbolFQN(BTypeSymbol originalTSymbol) { getMajorVersion(pkgID.version.value) + ":" + originalTSymbol.name; } - private static Name getImmutableTypeName(Names names, BTypeSymbol originalTSymbol) { - return getImmutableTypeName(names, originalTSymbol.name.getValue()); - } - - private static Name getImmutableTypeName(Names names, String origName) { - if (origName.isEmpty()) { - return Names.EMPTY; - } - - return Names.fromString("(".concat(origName).concat(AND_READONLY_SUFFIX).concat(")")); + private static Name getImmutableTypeName(BTypeSymbol originalTSymbol) { + return Types.getImmutableTypeName(originalTSymbol.name.getValue()); } private static BIntersectionType createImmutableIntersectionType(SymbolEnv env, BType nonReadOnlyType, @@ -910,7 +883,7 @@ private static BIntersectionType createImmutableIntersectionType(PackageID pkgId }}; BIntersectionType intersectionType = new BIntersectionType(intersectionTypeSymbol, constituentTypes, - effectiveType, Flags.READONLY | effectiveType.flags); + effectiveType, Flags.READONLY | effectiveType.getFlags()); intersectionTypeSymbol.type = intersectionType; return intersectionType; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/Names.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/Names.java index a57fdf0b7d68..fc540bdfa491 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/Names.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/Names.java @@ -127,7 +127,9 @@ public class Names { public static final Name XML_COMMENT = new Name(STRING_XML_COMMENT); public static final Name XML_TEXT = new Name(STRING_XML_TEXT); public static final Name REGEXP_TYPE = new Name(STRING_REGEXP); - + public static final Name TRUE = new Name("true"); + public static final Name FALSE = new Name("false"); + // Names related to transactions. public static final Name TRANSACTION_PACKAGE = new Name("transactions"); public static final Name TRANSACTION_INFO_RECORD = new Name("Info"); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/TypeDefBuilderHelper.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/TypeDefBuilderHelper.java index 727bbc9e1cdb..380455d75304 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/TypeDefBuilderHelper.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/TypeDefBuilderHelper.java @@ -17,6 +17,7 @@ package org.wso2.ballerinalang.compiler.util; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Env; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.MarkdownDocAttachment; @@ -142,14 +143,12 @@ public static BLangFunction createInitFunctionForStructureType(BSymbol symbol, Name suffix, SymbolTable symTable, BType type) { - return createInitFunctionForStructureType(symbol, env, names, suffix, type, symTable.nilType); + return createInitFunctionForStructureType(symTable.typeEnv(), symbol, env, names, suffix, type, + symTable.nilType); } - public static BLangFunction createInitFunctionForStructureType(BSymbol symbol, - SymbolEnv env, - Names names, - Name suffix, - BType type, + public static BLangFunction createInitFunctionForStructureType(Env typeEnv, BSymbol symbol, SymbolEnv env, + Names names, Name suffix, BType type, BType returnType) { String structTypeName = type.tsymbol.name.value; BLangFunction initFunction = ASTBuilderUtil.createInitFunctionWithNilReturn(null, structTypeName, suffix); @@ -165,7 +164,7 @@ public static BLangFunction createInitFunctionForStructureType(BSymbol symbol, initFunction.flagSet.add(Flag.ATTACHED); // Create the function type - initFunction.setBType(new BInvokableType(new ArrayList<>(), returnType, null)); + initFunction.setBType(new BInvokableType(typeEnv, List.of(), returnType, null)); // Create the function symbol Name funcSymbolName = Names.fromString(Symbols.getAttachedFuncSymbolName(structTypeName, suffix.value)); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/Unifier.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/Unifier.java index 51225b103c49..1bb911853161 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/Unifier.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/Unifier.java @@ -17,6 +17,7 @@ package org.wso2.ballerinalang.compiler.util; +import io.ballerina.types.Env; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.tree.NodeKind; import org.ballerinalang.model.types.TypeKind; @@ -31,7 +32,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; @@ -89,14 +89,16 @@ public class Unifier implements BTypeVisitor { private SymbolEnv env; private Types types; private BLangDiagnosticLog dlog; + private Env typeEnv; - public BType build(BType originalType, BType expType, BLangInvocation invocation, Types types, + public BType build(Env typeEnv, BType originalType, BType expType, BLangInvocation invocation, Types types, SymbolTable symbolTable, BLangDiagnosticLog dlog) { this.isInvocation = invocation != null; if (this.isInvocation) { this.invocation = invocation; createParamMap(invocation); } + this.typeEnv = typeEnv; this.types = types; this.symbolTable = symbolTable; this.dlog = dlog; @@ -105,12 +107,13 @@ public BType build(BType originalType, BType expType, BLangInvocation invocation return newType; } - public BType build(BType originalType) { - return build(originalType, null, null, null, null, null); + public BType build(Env typeEnv, BType originalType) { + return build(typeEnv, originalType, null, null, null, null, null); } - public void validate(BType returnType, BLangFunction function, SymbolTable symbolTable, SymbolEnv env, Types types, - BLangDiagnosticLog dlog) { + public void validate(Env typeEnv, BType returnType, BLangFunction function, SymbolTable symbolTable, SymbolEnv env, + Types types, BLangDiagnosticLog dlog) { + this.typeEnv = typeEnv; this.function = function; this.symbolTable = symbolTable; this.env = env; @@ -125,11 +128,6 @@ public BType visit(BType originalType, BType expType) { return originalType; } - @Override - public BType visit(BBuiltInRefType originalType, BType expType) { - return originalType; - } - @Override public BType visit(BAnyType originalType, BType expType) { return originalType; @@ -154,8 +152,8 @@ public BType visit(BMapType originalType, BType expType) { return symbolTable.semanticError; } - BMapType newMType = new BMapType(originalType.tag, newConstraint, null); - setFlags(newMType, originalType.flags); + BMapType newMType = new BMapType(typeEnv, originalType.tag, newConstraint, null); + setFlags(newMType, originalType.getFlags()); return newMType; } @@ -174,7 +172,7 @@ public BType visit(BXMLType originalType, BType expType) { } BXMLType newXMLType = new BXMLType(newConstraint, null); - setFlags(newXMLType, originalType.flags); + setFlags(newXMLType, originalType.getFlags()); return newXMLType; } @@ -197,8 +195,9 @@ public BType visit(BArrayType originalType, BType expType) { return symbolTable.semanticError; } - BArrayType newArrayType = new BArrayType(newElemType, null, originalType.size, originalType.state); - setFlags(newArrayType, originalType.flags); + BArrayType newArrayType = new BArrayType(typeEnv, newElemType, null, originalType.getSize(), + originalType.state); + setFlags(newArrayType, originalType.getFlags()); return newArrayType; } @@ -253,7 +252,7 @@ public BType visit(BTupleType originalType, BType expType) { BType member = tupleTypes.get(i); BType expMember = expTupleTypes.get(j); BType newMem = member.accept(this, expMember); - BVarSymbol varSymbol = new BVarSymbol(newMem.flags, null, null, newMem, null, null, null); + BVarSymbol varSymbol = new BVarSymbol(newMem.getFlags(), null, null, newMem, null, null, null); members.add(new BTupleMember(newMem, varSymbol)); if (isSemanticErrorInInvocation(newMem)) { @@ -284,9 +283,9 @@ public BType visit(BTupleType originalType, BType expType) { return expType != null ? expType : originalType; } - BTupleType type = new BTupleType(null, members); + BTupleType type = new BTupleType(typeEnv, members); type.restType = newRestType; - setFlags(type, originalType.flags); + setFlags(type, originalType.getFlags()); return type; } @@ -313,8 +312,8 @@ public BType visit(BStreamType originalType, BType expType) { return symbolTable.semanticError; } - BStreamType type = new BStreamType(originalType.tag, newConstraint, newError, null); - setFlags(type, originalType.flags); + BStreamType type = new BStreamType(typeEnv, originalType.tag, newConstraint, newError, null); + setFlags(type, originalType.getFlags()); return type; } @@ -340,19 +339,19 @@ public BType visit(BTableType originalType, BType expType) { return symbolTable.semanticError; } - BTableType newTableType = new BTableType(TypeTags.TABLE, newConstraint, null); + BTableType newTableType = new BTableType(typeEnv, newConstraint, null); newTableType.keyTypeConstraint = null; newTableType.fieldNameList = originalType.fieldNameList; newTableType.constraintPos = originalType.constraintPos; newTableType.isTypeInlineDefined = originalType.isTypeInlineDefined; newTableType.keyPos = originalType.keyPos; - setFlags(newTableType, originalType.flags); + setFlags(newTableType, originalType.getFlags()); return newTableType; } @Override public BType visit(BInvokableType originalType, BType expType) { - if (Symbols.isFlagOn(originalType.flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(originalType.getFlags(), Flags.ANY_FUNCTION)) { return originalType; } @@ -412,8 +411,8 @@ public BType visit(BInvokableType originalType, BType expType) { } } - BType type = new BInvokableType(paramTypes, newRestType, retType, null); - setFlags(type, originalType.flags); + BType type = new BInvokableType(typeEnv, paramTypes, newRestType, retType, null); + setFlags(type, originalType.getFlags()); return type; } @@ -450,7 +449,7 @@ public BType visit(BUnionType originalType, BType expType) { return symbolTable.semanticError; } - if (newMember == member && Symbols.isFlagOn(member.flags, Flags.PARAMETERIZED)) { + if (newMember == member && Symbols.isFlagOn(member.getFlags(), Flags.PARAMETERIZED)) { return expType; } } @@ -466,8 +465,8 @@ public BType visit(BUnionType originalType, BType expType) { return originalType; } - BUnionType type = BUnionType.create(null, newMemberTypes); - setFlags(type, originalType.flags); + BUnionType type = BUnionType.create(originalType.env, null, newMemberTypes); + setFlags(type, originalType.getFlags()); return type; } @@ -488,7 +487,7 @@ public BType visit(BIntersectionType originalType, BType expType) { BIntersectionType type = new BIntersectionType(null, (LinkedHashSet) originalType.getConstituentTypes(), newEffectiveType); - setFlags(type, originalType.flags); + setFlags(type, originalType.getFlags()); return originalType; } @@ -511,9 +510,8 @@ public BType visit(BFutureType originalType, BType expType) { return symbolTable.semanticError; } - BFutureType newFutureType = new BFutureType(originalType.tag, newConstraint, null, - originalType.workerDerivative); - setFlags(newFutureType, originalType.flags); + BFutureType newFutureType = new BFutureType(typeEnv, newConstraint, null, originalType.workerDerivative); + setFlags(newFutureType, originalType.getFlags()); return newFutureType; } @@ -537,8 +535,8 @@ public BType visit(BTypedescType originalType, BType expType) { return symbolTable.semanticError; } - BTypedescType newTypedescType = new BTypedescType(newConstraint, null); - setFlags(newTypedescType, originalType.flags); + BTypedescType newTypedescType = new BTypedescType(typeEnv, newConstraint, null); + setFlags(newTypedescType, originalType.getFlags()); return newTypedescType; } @@ -569,7 +567,7 @@ public BType visit(BParameterizedType originalType, BType expType) { // Log an error only if the user has not explicitly passed an argument. If the passed // argument is invalid, the type checker will log the error. dlog.error(invocation.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPE_FOR_INFERRED_TYPEDESC_VALUE, - paramVarName, paramSymbolTypedescType, new BTypedescType(expType, null)); + paramVarName, paramSymbolTypedescType, new BTypedescType(typeEnv, expType, null)); return symbolTable.semanticError; } BType type = paramValueTypes.get(paramVarName); @@ -698,7 +696,7 @@ private BLangNamedArgsExpression createTypedescExprNamedArg(BType expType, Strin BLangTypedescExpr typedescExpr = (BLangTypedescExpr) TreeBuilder.createTypeAccessNode(); typedescExpr.pos = this.symbolTable.builtinPos; typedescExpr.resolvedType = expType; - typedescExpr.setBType(new BTypedescType(expType, null)); + typedescExpr.setBType(new BTypedescType(typeEnv, expType, null)); BLangNamedArgsExpression namedArgsExpression = (BLangNamedArgsExpression) TreeBuilder.createNamedArgNode(); BLangIdentifier identifierNode = (BLangIdentifier) TreeBuilder.createIdentifierNode(); @@ -829,7 +827,7 @@ private void populateParamMapFromTupleRestArg(List params, int curre } private void setFlags(BType type, long originalFlags) { - type.flags = originalFlags & (~Flags.PARAMETERIZED); + type.setFlags(originalFlags & (~Flags.PARAMETERIZED)); } private int getParamPosition(BVarSymbol sym) { @@ -960,7 +958,7 @@ private BType getMatchingTypeForInferrableType(BType originalType, BType expType BType referredOriginalType = Types.getImpliedType(originalType); if (referredOriginalType.tag == TypeTags.UNION) { for (BType memberType : ((BUnionType) referredOriginalType).getMemberTypes()) { - if (!Symbols.isFlagOn(memberType.flags, Flags.PARAMETERIZED)) { + if (!Symbols.isFlagOn(memberType.getFlags(), Flags.PARAMETERIZED)) { continue; } @@ -1051,7 +1049,7 @@ private boolean refersInferableParamName(List paramsWithInferredTypedesc } return refersInferableParamName(paramsWithInferredTypedescDefault, completionType, unresolvedTypes); case TypeTags.INVOKABLE: - if (Symbols.isFlagOn(type.flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(type.getFlags(), Flags.ANY_FUNCTION)) { return false; } BInvokableType invokableType = (BInvokableType) type; @@ -1136,7 +1134,7 @@ private List getParamsWithInferredTypedescDefault(List param // If the `expType` is `int|string|boolean` and the original type is `t|string` then the expected type for `t` // is `int|boolean`. private BType getExpectedTypeForInferredTypedescMember(BUnionType originalType, BType expType, BType member) { - if (expType == null || !this.isInvocation || !Symbols.isFlagOn(member.flags, Flags.PARAMETERIZED)) { + if (expType == null || !this.isInvocation || !Symbols.isFlagOn(member.getFlags(), Flags.PARAMETERIZED)) { return null; } @@ -1185,7 +1183,7 @@ private BType getExpectedTypeForInferredTypedescMember(BUnionType originalType, return expectedTypesSet.iterator().next(); } - return BUnionType.create(null, expectedTypesSet); + return BUnionType.create(typeEnv, null, expectedTypesSet); } private boolean isSameTypeOrError(BType newType, BType originalType) { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/programfile/ProgramFileConstants.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/programfile/ProgramFileConstants.java index e9ae324c0d33..da348731b9ec 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/programfile/ProgramFileConstants.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/programfile/ProgramFileConstants.java @@ -25,9 +25,9 @@ public final class ProgramFileConstants { public static final int MAGIC_NUMBER = 0xBA1DA4CE; public static final short VERSION_NUMBER = 50; - public static final int BIR_VERSION_NUMBER = 73; - public static final short MIN_SUPPORTED_VERSION = 73; - public static final short MAX_SUPPORTED_VERSION = 73; + public static final int BIR_VERSION_NUMBER = 74; + public static final short MIN_SUPPORTED_VERSION = 74; + public static final short MAX_SUPPORTED_VERSION = 74; // todo move this to a proper place public static final String[] SUPPORTED_PLATFORMS = {"java21", "java17", "java11"}; diff --git a/compiler/ballerina-lang/src/test/java/org/wso2/ballerinalang/compiler/diagnostic/BLangDiagnosticLogTest.java b/compiler/ballerina-lang/src/test/java/org/wso2/ballerinalang/compiler/diagnostic/BLangDiagnosticLogTest.java index 111755979b37..e2159787bbb9 100644 --- a/compiler/ballerina-lang/src/test/java/org/wso2/ballerinalang/compiler/diagnostic/BLangDiagnosticLogTest.java +++ b/compiler/ballerina-lang/src/test/java/org/wso2/ballerinalang/compiler/diagnostic/BLangDiagnosticLogTest.java @@ -28,6 +28,7 @@ import io.ballerina.tools.diagnostics.DiagnosticInfo; import io.ballerina.tools.diagnostics.DiagnosticSeverity; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Env; import org.ballerinalang.compiler.CompilerOptionName; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.PackageID; @@ -62,7 +63,7 @@ public void setup() { @Test public void testLogDiagnosticWithModuleDescriptor() { - BLangPackage pkgNode = (BLangPackage) TreeBuilder.createPackageNode(); + BLangPackage pkgNode = (BLangPackage) TreeBuilder.createPackageNode(new Env()); PackageID packageID = createPackageID("org.diagnostic.log", ".", "1.0.0"); PackageCache packageCache = PackageCache.getInstance(context); @@ -80,7 +81,7 @@ public void testLogDiagnosticWithModuleDescriptor() { @Test public void testLogDiagnosticWithPackageID() { - BLangPackage pkgNode = (BLangPackage) TreeBuilder.createPackageNode(); + BLangPackage pkgNode = (BLangPackage) TreeBuilder.createPackageNode(new Env()); PackageID packageID = createPackageID("org.diagnostic.log", ".", "1.0.0"); PackageCache packageCache = PackageCache.getInstance(context); diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/BlockStatementNode.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/BlockStatementNode.java index 16c5271dd1c4..61253a9cf1e2 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/BlockStatementNode.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/BlockStatementNode.java @@ -40,6 +40,10 @@ public NodeList statements() { return new NodeList<>(childInBucket(1)); } + public NodeAndCommentList statementsWithComments() { + return new NodeAndCommentList<>(childInBucket(1), childInBucket(2)); + } + public Token closeBraceToken() { return childInBucket(2); } diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/CommentNode.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/CommentNode.java new file mode 100644 index 000000000000..e09d25f846e1 --- /dev/null +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/CommentNode.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://wso2.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.ballerina.compiler.syntax.tree; + +import io.ballerina.compiler.internal.parser.tree.STNode; + +import java.util.List; + +/** + * Represents a comment. This is not a part of the Ballerina syntax tree. + * + * @since 2201.10.0 + */ +public class CommentNode extends NonTerminalNode { + private Node commentAttachedNode; + private Minutiae lastMinutiae; + private List commentLines; + + public CommentNode(STNode commentAttachedSTNode, int position, NonTerminalNode commentAttachedNode) { + super(commentAttachedSTNode, position, commentAttachedNode); + } + + public Node getCommentAttachedNode() { + return this.commentAttachedNode; + } + + public void setCommentAttachedNode(Node commentAttachedNode) { + this.commentAttachedNode = commentAttachedNode; + } + + public Minutiae getLastMinutiae() { + return this.lastMinutiae; + } + + public void setLastMinutiae(Minutiae lastMinutiae) { + this.lastMinutiae = lastMinutiae; + } + + public List getCommentLines() { + return this.commentLines; + } + + public void setCommentLines(List commentLines) { + this.commentLines = commentLines; + } + + @Override + protected String[] childNames() { + return new String[0]; + } + + @Override + public void accept(NodeVisitor visitor) { + visitor.visit(this); + } + + @Override + public T apply(NodeTransformer visitor) { + return visitor.transform(this); + } +} diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/FunctionBodyBlockNode.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/FunctionBodyBlockNode.java index fd9f281c50ee..7aeaef153cd1 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/FunctionBodyBlockNode.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/FunctionBodyBlockNode.java @@ -45,6 +45,10 @@ public NodeList statements() { return new NodeList<>(childInBucket(2)); } + public NodeAndCommentList statementsWithComments() { + return new NodeAndCommentList<>(childInBucket(2), childInBucket(3)); + } + public Token closeBraceToken() { return childInBucket(3); } diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeAndCommentList.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeAndCommentList.java new file mode 100644 index 000000000000..dfa391c06841 --- /dev/null +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeAndCommentList.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://wso2.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.ballerina.compiler.syntax.tree; + +import io.ballerina.compiler.internal.parser.tree.STNode; +import io.ballerina.compiler.internal.parser.tree.STNodeList; +import io.ballerina.compiler.internal.syntax.NodeListUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import static io.ballerina.compiler.internal.syntax.NodeListUtils.rangeCheck; +import static io.ballerina.compiler.internal.syntax.NodeListUtils.rangeCheckForAdd; + +/** + * Represent both nodes and attached comments to each node. + * + * @param The type of Node + */ +public class NodeAndCommentList implements Iterable { + protected final STNodeList internalListNode; + protected final NonTerminalNode nonTerminalNode; + protected final int size; + protected final Node[] nodes; + + NodeAndCommentList(NonTerminalNode nonTerminalNode, Token semicolon) { + this(nonTerminalNode, semicolon, nonTerminalNode.bucketCount() * 2 + 1); + } + + protected NodeAndCommentList(NonTerminalNode nonTerminalNode, Token semicolon, int size) { + if (!NodeListUtils.isSTNodeList(nonTerminalNode.internalNode())) { + throw new IllegalArgumentException("An STNodeList instance is expected"); + } + + this.internalListNode = (STNodeList) nonTerminalNode.internalNode(); + this.nonTerminalNode = nonTerminalNode; + this.nodes = new Node[size]; + int nodeIndex = 0; + for (int i = 0; i < nonTerminalNode.bucketCount(); i++) { + Node node = nonTerminalNode.childInBucket(i); + CommentNode commentNode = getCommentNode(node); + if (commentNode != null) { + this.nodes[nodeIndex++] = commentNode; + } + this.nodes[nodeIndex++] = node; + } + + CommentNode commentNodeBeforeEnd = getCommentNode(semicolon); + if (commentNodeBeforeEnd != null) { + this.nodes[nodeIndex++] = commentNodeBeforeEnd; + } + this.size = nodeIndex; + } + + private CommentNode getCommentNode(Node node) { + List commentLines = new ArrayList<>(); + Minutiae lastMinutiae = null; + for (Minutiae minutiae : node.leadingMinutiae()) { + String[] splits = minutiae.text().split("// "); + if (splits.length >= 2) { + commentLines.add(splits[1]); + lastMinutiae = minutiae; + } else if (splits.length == 1 && splits[0].contains("//")) { + commentLines.add(""); + lastMinutiae = minutiae; + } + } + if (commentLines.isEmpty()) { + return null; + } + CommentNode commentNode = new CommentNode(node.internalNode(), 0, null); + commentNode.setCommentAttachedNode(node); + commentNode.setLastMinutiae(lastMinutiae); + commentNode.setCommentLines(commentLines); + return commentNode; + } + + // Positional access methods + + public T get(int index) { + rangeCheck(index, size); + return (T) this.nodes[index]; + } + + // Modification methods + + public NodeAndCommentList add(T node) { + Objects.requireNonNull(node, "node should not be null"); + return new NodeAndCommentList<>(internalListNode.add(node.internalNode()).createUnlinkedFacade(), null); + } + + public NodeAndCommentList add(int index, T node) { + Objects.requireNonNull(node, "node should not be null"); + rangeCheckForAdd(index, size); + return new NodeAndCommentList<>(internalListNode.add(index, node.internalNode()).createUnlinkedFacade(), null); + } + + public NodeAndCommentList addAll(Collection c) { + if (c.isEmpty()) { + return this; + } + + List stNodesToBeAdded = c.stream() + .map(node -> Objects.requireNonNull(node, "node should not be null")) + .map(Node::internalNode) + .collect(Collectors.toList()); + return new NodeAndCommentList<>(internalListNode.addAll(stNodesToBeAdded).createUnlinkedFacade(), null); + } + + public NodeAndCommentList set(int index, T node) { + Objects.requireNonNull(node, "node should not be null"); + rangeCheck(index, size); + if (nonTerminalNode.checkForReferenceEquality(index, node)) { + return this; + } + + return new NodeAndCommentList<>(internalListNode.set(index, node.internalNode()).createUnlinkedFacade(), null); + } + + public NodeAndCommentList remove(int index) { + rangeCheck(index, size); + return new NodeAndCommentList<>(internalListNode.remove(index).createUnlinkedFacade(), null); + } + + public NodeAndCommentList remove(T node) { + Objects.requireNonNull(node, "node should not be null"); + for (int bucket = 0; bucket < nonTerminalNode.bucketCount(); bucket++) { + if (nonTerminalNode.checkForReferenceEquality(bucket, node)) { + return remove(bucket); + } + } + return this; + } + + @SuppressWarnings("SuspiciousMethodCalls") + public NodeAndCommentList removeAll(Collection c) { + if (c.isEmpty()) { + return this; + } + c.forEach(node -> Objects.requireNonNull(node, "node should not be null")); + + List toBeDeletedList = new ArrayList<>(); + for (int bucket = 0; bucket < nonTerminalNode.bucketCount(); bucket++) { + Node childNode = nonTerminalNode.childBuckets[bucket]; + if (c.contains(childNode)) { + toBeDeletedList.add(childNode.internalNode()); + } + } + + return new NodeAndCommentList<>(internalListNode.removeAll(toBeDeletedList).createUnlinkedFacade(), null); + } + + //query methods + + public int size() { + return this.size; + } + + public boolean isEmpty() { + return this.size == 0; + } + + @Override + public Iterator iterator() { + return new NodeAndCommentListIterator(); + } + + public Stream stream() { + return StreamSupport.stream(spliterator(), false); + } + + NonTerminalNode underlyingListNode() { + return this.nonTerminalNode; + } + + /** + * An iterator for this list of nodes. + * + * @since 2201.10.0 + */ + protected class NodeAndCommentListIterator implements Iterator { + private int currentIndex = 0; + + @Override + public boolean hasNext() { + return this.currentIndex < size; + } + + @Override + public T next() { + return get(currentIndex++); + } + } +} diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeTransformer.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeTransformer.java index 1f9a6b0026dc..ae474fa7c866 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeTransformer.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeTransformer.java @@ -964,6 +964,10 @@ public T transform(ReceiveFieldNode receiveFieldNode) { return transformSyntaxNode(receiveFieldNode); } + public T transform(CommentNode commentNode) { + return transformSyntaxNode(commentNode); + } + // Tokens public T transform(Token token) { diff --git a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeVisitor.java b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeVisitor.java index 4d90fe9c2010..df273637eaea 100644 --- a/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeVisitor.java +++ b/compiler/ballerina-parser/src/main/java/io/ballerina/compiler/syntax/tree/NodeVisitor.java @@ -962,6 +962,9 @@ public void visit(MemberTypeDescriptorNode memberTypeDescriptorNode) { public void visit(ReceiveFieldNode receiveFieldNode) { visitSyntaxNode(receiveFieldNode); } + public void visit(CommentNode commentNode) { + visitSyntaxNode(commentNode); + } // Tokens diff --git a/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/ParserTestUtils.java b/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/ParserTestUtils.java index 75107ac07abb..da7d00dc460e 100644 --- a/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/ParserTestUtils.java +++ b/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/ParserTestUtils.java @@ -32,6 +32,7 @@ import io.ballerina.compiler.internal.parser.tree.STNodeList; import io.ballerina.compiler.internal.parser.tree.STToken; import io.ballerina.compiler.internal.syntax.SyntaxUtils; +import io.ballerina.compiler.syntax.tree.CommentNode; import io.ballerina.compiler.syntax.tree.Node; import io.ballerina.compiler.syntax.tree.SyntaxKind; import io.ballerina.compiler.syntax.tree.SyntaxTree; @@ -46,6 +47,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; +import java.util.List; import static io.ballerina.compiler.internal.syntax.SyntaxUtils.isSTNodePresent; import static io.ballerinalang.compiler.parser.test.ParserTestConstants.CHILDREN_FIELD; @@ -987,4 +989,14 @@ private static SyntaxKind getDocumentationKind(String kind) { default -> throw new UnsupportedOperationException("cannot find syntax kind: " + kind); }; } + + public static void assertCommentNode(Node node, List comments) { + Assert.assertTrue(node instanceof CommentNode); + CommentNode commentNode = (CommentNode) node; + List commentLines = commentNode.getCommentLines(); + Assert.assertEquals(commentLines.size(), comments.size()); + for (int i = 0; i < comments.size(); i++) { + Assert.assertEquals(commentLines.get(i), comments.get(i)); + } + } } diff --git a/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/tree/nodeparser/ParseBlockStatementTest.java b/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/tree/nodeparser/ParseBlockStatementTest.java index f665793167a0..d246eff02732 100644 --- a/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/tree/nodeparser/ParseBlockStatementTest.java +++ b/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/tree/nodeparser/ParseBlockStatementTest.java @@ -18,6 +18,8 @@ package io.ballerinalang.compiler.parser.test.tree.nodeparser; import io.ballerina.compiler.syntax.tree.BlockStatementNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeAndCommentList; import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.compiler.syntax.tree.NodeParser; import io.ballerina.compiler.syntax.tree.StatementNode; @@ -28,6 +30,8 @@ import java.util.List; +import static io.ballerinalang.compiler.parser.test.ParserTestUtils.assertCommentNode; + /** * Test {@code parseBlockStatement} method. * @@ -177,4 +181,30 @@ public void testBlockStmtRecovery() { Assert.assertEquals(blockStmtNode.toString(), " INVALID[%]{ int a; INVALID[;] } INVALID[;] INVALID[;]"); } + + @Test + public void testCommentInBlockStatementBody() { + String blockStatement = """ + { + // Initialize x + int x = 1; + // Initialize y + int y = 1; + + // new comment + // another new comment + }"""; + BlockStatementNode blockStmtNode = NodeParser.parseBlockStatement(blockStatement); + Assert.assertEquals(blockStmtNode.kind(), SyntaxKind.BLOCK_STATEMENT); + Assert.assertFalse(blockStmtNode.hasDiagnostics()); + + NodeAndCommentList nodes = blockStmtNode.statementsWithComments(); + Assert.assertEquals(nodes.size(), 5); + + assertCommentNode(nodes.get(0), List.of("Initialize x")); + assertCommentNode(nodes.get(2), List.of("Initialize y")); + assertCommentNode(nodes.get(4), List.of("new comment", "another new comment")); + } + + } diff --git a/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/tree/nodeparser/ParseFunctionBodyBlock.java b/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/tree/nodeparser/ParseFunctionBodyBlock.java index 926702dab247..6d5ea710a004 100644 --- a/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/tree/nodeparser/ParseFunctionBodyBlock.java +++ b/compiler/ballerina-parser/src/test/java/io/ballerinalang/compiler/parser/test/tree/nodeparser/ParseFunctionBodyBlock.java @@ -20,6 +20,8 @@ import io.ballerina.compiler.syntax.tree.FunctionBodyBlockNode; import io.ballerina.compiler.syntax.tree.NamedWorkerDeclarationNode; import io.ballerina.compiler.syntax.tree.NamedWorkerDeclarator; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeAndCommentList; import io.ballerina.compiler.syntax.tree.NodeList; import io.ballerina.compiler.syntax.tree.NodeParser; import io.ballerina.compiler.syntax.tree.StatementNode; @@ -31,6 +33,8 @@ import java.util.List; import java.util.Optional; +import static io.ballerinalang.compiler.parser.test.ParserTestUtils.assertCommentNode; + /** * Test {@code parseFunctionBodyBlock} method. * @@ -255,4 +259,28 @@ public void testFuncBodyBlockRecovery() { Assert.assertEquals(funcBodyBlockNode.toString(), " INVALID[%]{ int a; INVALID[;] }; INVALID[;]"); } + + @Test + public void testCommentInFunctionBody() { + String funcBodyBlock = """ + { + // Initialize x + int x = 1; + // Initialize y + int y = 1; + + // new comment + // another new comment + }"""; + FunctionBodyBlockNode funcBodyBlockNode = NodeParser.parseFunctionBodyBlock(funcBodyBlock); + Assert.assertEquals(funcBodyBlockNode.kind(), SyntaxKind.FUNCTION_BODY_BLOCK); + Assert.assertFalse(funcBodyBlockNode.hasDiagnostics()); + + NodeAndCommentList nodes = funcBodyBlockNode.statementsWithComments(); + Assert.assertEquals(nodes.size(), 5); + + assertCommentNode(nodes.get(0), List.of("Initialize x")); + assertCommentNode(nodes.get(2), List.of("Initialize y")); + assertCommentNode(nodes.get(4), List.of("new comment", "another new comment")); + } } diff --git a/docs/bir-spec/src/main/resources/kaitai/bir.ksy b/docs/bir-spec/src/main/resources/kaitai/bir.ksy index c4ac5bb12572..4114e134e913 100644 --- a/docs/bir-spec/src/main/resources/kaitai/bir.ksy +++ b/docs/bir-spec/src/main/resources/kaitai/bir.ksy @@ -96,8 +96,6 @@ types: type: s4 - id: type_flag type: s8 - - id: type_special_flag - type: s4 - id: type_structure type: switch-on: type_tag @@ -122,6 +120,238 @@ types: instances: name_as_str: value: _root.constant_pool.constant_pool_entries[name_index].cp_info.as.value + sem_named_type: + seq: + - id: semtype + type: semtype_info + - id: optional_name + type: nullable_str_info + nullable_str_info: + seq: + - id: has_non_null_string + type: u1 + - id: str_cp_index + type: s4 + if: has_non_null_string == 1 + semtype_info: + seq: + - id: has_semtype + type: u1 + - id: semtype + type: semtype_internal + if: has_semtype == 1 + semtype_internal: + seq: + - id: is_uniform_type_bit_set + type: u1 + - id: uniform_type_bit_set + type: s4 + if: is_uniform_type_bit_set == 1 + - id: complex_semtype + type: semtype_complex + if: is_uniform_type_bit_set == 0 + semtype_complex: + seq: + - id: all_bit_set + type: s4 + - id: some_bit_set + type: s4 + - id: subtype_data_list_length + type: s1 + - id: proper_subtype_data + type: semtype_proper_subtype_data + repeat: expr + repeat-expr: subtype_data_list_length + semtype_proper_subtype_data: + seq: + - id: proper_subtype_data_kind + type: s1 + - id: bdd + type: semtype_bdd + if: proper_subtype_data_kind == 1 + - id: int_subtype + type: semtype_int_subtype + if: proper_subtype_data_kind == 2 + - id: boolean_subtype + type: semtype_boolean_subtype + if: proper_subtype_data_kind == 3 + - id: float_subtype + type: semtype_float_subtype + if: proper_subtype_data_kind == 4 + - id: decimal_subtype + type: semtype_decimal_subtype + if: proper_subtype_data_kind == 5 + - id: string_subtype + type: semtype_string_subtype + if: proper_subtype_data_kind == 6 + - id: xml_subtype + type: semtype_xml_subtype + if: proper_subtype_data_kind == 7 + semtype_bdd: + seq: + - id: is_bdd_node + type: u1 + - id: bdd_node + type: semtype_bdd_node + if: is_bdd_node == 1 + - id: bdd_all_or_nothing + type: u1 + if: is_bdd_node == 0 + semtype_bdd_node: + seq: + - id: is_rec_atom + type: u1 + - id: rec_atom_index + type: s4 + if: is_rec_atom == 1 + - id: target_kind + type: s4 + if: is_rec_atom == 1 and rec_atom_index > 1 + - id: type_atom + type: semtype_type_atom + if: is_rec_atom == 0 + - id: bdd_node_left + type: semtype_bdd + - id: bdd_node_middle + type: semtype_bdd + - id: bdd_node_right + type: semtype_bdd + semtype_type_atom: + seq: + - id: type_atom_index + type: s4 + - id: type_atom_kind + type: s1 + - id: mapping_atomic_type + type: semtype_mapping_atomic_type + if: type_atom_kind == 1 + - id: list_atomic_type + type: semtype_list_atomic_type + if: type_atom_kind == 2 + - id: function_atomic_type + type: semtype_function_atomic_type + if: type_atom_kind == 3 + - id: cell_atomic_type + type: semtype_cell_atomic_type + if: type_atom_kind == 4 + semtype_mapping_atomic_type: + seq: + - id: names_length + type: s4 + - id: names + type: s4 + repeat: expr + repeat-expr: names_length + - id: types_length + type: s4 + - id: types + type: semtype_info + repeat: expr + repeat-expr: types_length + - id: rest + type: semtype_info + semtype_list_atomic_type: + seq: + - id: initial_list_size + type: s4 + - id: initial + type: semtype_info + repeat: expr + repeat-expr: initial_list_size + - id: fixed_length + type: s4 + - id: rest + type: semtype_info + semtype_function_atomic_type: + seq: + - id: param_type + type: semtype_info + - id: ret_type + type: semtype_info + - id: qualifier_type + type: semtype_info + - id: is_generic + type: u1 + semtype_cell_atomic_type: + seq: + - id: ty + type: semtype_info + - id: mut + type: s1 + semtype_int_subtype: + seq: + - id: ranges_length + type: s4 + - id: x + type: semtype_range + repeat: expr + repeat-expr: ranges_length + semtype_range: + seq: + - id: min + type: s8 + - id: max + type: s8 + semtype_boolean_subtype: + seq: + - id: value + type: u1 + semtype_float_subtype: + seq: + - id: allowed + type: u1 + - id: values_length + type: s4 + - id: values + type: f8 + repeat: expr + repeat-expr: values_length + semtype_decimal_subtype: + seq: + - id: allowed + type: u1 + - id: values_length + type: s4 + - id: values + type: semtype_enumerable_decimal + repeat: expr + repeat-expr: values_length + semtype_enumerable_decimal: + seq: + - id: scale + type: s4 + - id: unscaled_value_bytes_length + type: s4 + - id: unscaled_value_bytes + size: unscaled_value_bytes_length + semtype_string_subtype: + seq: + - id: allowed + type: u1 + - id: values_length + type: s4 + - id: values + type: semtype_enumerable_string + repeat: expr + repeat-expr: values_length + - id: allowed1 + type: u1 + - id: values_length1 + type: s4 + - id: values1 + type: semtype_enumerable_string + repeat: expr + repeat-expr: values_length1 + semtype_enumerable_string: + seq: + - id: string_cp_index + type: s4 + semtype_xml_subtype: + seq: + - id: primitives + type: s4 + - id: sequence + type: semtype_bdd type_array: seq: - id: state @@ -170,18 +400,10 @@ types: type: s8 - id: value_space_size type: s4 - - id: finite_values - type: finite_value + - id: value_space + type: sem_named_type repeat: expr repeat-expr: value_space_size - finite_value: - seq: - - id : type_cp_index - type: s4 - - id: value_length - type: s4 - - id: value - size: value_length closure_symbol_body: seq: - id: name_cp_index diff --git a/docs/bir-spec/src/test/java/org/ballerinalang/birspec/BIRTestUtils.java b/docs/bir-spec/src/test/java/org/ballerinalang/birspec/BIRTestUtils.java index 636262ee19af..23e97cefd4af 100644 --- a/docs/bir-spec/src/test/java/org/ballerinalang/birspec/BIRTestUtils.java +++ b/docs/bir-spec/src/test/java/org/ballerinalang/birspec/BIRTestUtils.java @@ -649,7 +649,7 @@ private static void assertType(Bir.ConstantPoolEntry constantPoolEntry, BType ex Bir.TypeInfo typeInfo = ((Bir.ShapeCpInfo) constantPoolEntry.cpInfo()).shape(); Assert.assertEquals(typeInfo.typeTag().id(), expectedValue.tag); Assert.assertEquals(typeInfo.nameAsStr(), expectedValue.name.getValue()); - assertFlags(typeInfo.typeFlag(), expectedValue.flags); + assertFlags(typeInfo.typeFlag(), expectedValue.getFlags()); KaitaiStruct typeStructure = typeInfo.typeStructure(); if (typeStructure instanceof Bir.TypeObjectOrService objectOrService) { diff --git a/langlib/lang.__internal/src/main/java/org/ballerinalang/langlib/internal/SetNarrowType.java b/langlib/lang.__internal/src/main/java/org/ballerinalang/langlib/internal/SetNarrowType.java index 3518836a2fd0..89c3b5342375 100644 --- a/langlib/lang.__internal/src/main/java/org/ballerinalang/langlib/internal/SetNarrowType.java +++ b/langlib/lang.__internal/src/main/java/org/ballerinalang/langlib/internal/SetNarrowType.java @@ -29,6 +29,7 @@ import io.ballerina.runtime.api.values.BTypedesc; import java.util.HashMap; +import java.util.concurrent.atomic.AtomicLong; /** * Native implementation of lang.internal:setNarrowType(typedesc, (any|error)[]). @@ -37,13 +38,19 @@ */ public final class SetNarrowType { + private static final AtomicLong nextNarrowTypeId = new AtomicLong(0); + private SetNarrowType() { } + private static String getTypeName() { + return "narrowType" + nextNarrowTypeId.getAndIncrement(); + } + public static BMap setNarrowType(BTypedesc td, BMap value) { RecordType recordType = (RecordType) TypeUtils.getImpliedType(value.getType()); RecordType newRecordType = - TypeCreator.createRecordType("narrowType", recordType.getPackage(), recordType.getTypeFlags(), + TypeCreator.createRecordType(getTypeName(), recordType.getPackage(), recordType.getTypeFlags(), recordType.isSealed(), recordType.getTypeFlags()); newRecordType.setFields(new HashMap<>() {{ put("value", TypeCreator.createField(td.getDescribingType(), "value", diff --git a/langlib/lang.error/src/main/java/org/ballerinalang/langlib/error/StackTrace.java b/langlib/lang.error/src/main/java/org/ballerinalang/langlib/error/StackTrace.java index 33212bd6edd9..d0b49ffca282 100644 --- a/langlib/lang.error/src/main/java/org/ballerinalang/langlib/error/StackTrace.java +++ b/langlib/lang.error/src/main/java/org/ballerinalang/langlib/error/StackTrace.java @@ -25,6 +25,7 @@ import io.ballerina.runtime.api.types.MethodType; import io.ballerina.runtime.api.types.ObjectType; import io.ballerina.runtime.api.types.PredefinedTypes; +import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; @@ -47,6 +48,8 @@ import static io.ballerina.runtime.api.constants.RuntimeConstants.DOT; import static io.ballerina.runtime.api.constants.RuntimeConstants.EMPTY; import static io.ballerina.runtime.api.constants.RuntimeConstants.FILE_NAME_PERIOD_SEPARATOR; +import static io.ballerina.runtime.api.flags.SymbolFlags.OPTIONAL; +import static io.ballerina.runtime.api.flags.SymbolFlags.PUBLIC; import static io.ballerina.runtime.api.values.BError.CALL_STACK_ELEMENT; /** @@ -59,21 +62,34 @@ public final class StackTrace { private StackTrace() { } + private static final ObjectType CALLSTACK_TYPE = createCallStackType(); + public static BObject stackTrace(BError value) { + CallStack callStack = new CallStack(CALLSTACK_TYPE); + callStack.callStack = getCallStackArray(value.getStackTrace()); + callStack.callStack.freezeDirect(); + return callStack; + } + + private static ObjectType createCallStackType() { + Module module = new Module("ballerina", "lang.error", null); + RecordType callStackElementType = + TypeCreator.createRecordType("CallStackElement", module, 0, Map.of( + "callableName", TypeCreator.createField(PredefinedTypes.TYPE_STRING, "callableName", 0), + "moduleName", TypeCreator.createField(PredefinedTypes.TYPE_STRING, "moduleName", OPTIONAL), + "fileName", TypeCreator.createField(PredefinedTypes.TYPE_STRING, "fileName", 0), + "lineNumber", TypeCreator.createField(PredefinedTypes.TYPE_INT, "lineNumber", 0) + ), PredefinedTypes.TYPE_NEVER, false, 0); + ObjectType callStackObjType = TypeCreator - .createObjectType("CallStack", new Module("ballerina", "lang.error", null), 0); + .createObjectType("CallStack", module, 0); callStackObjType.setMethods(new MethodType[]{}); callStackObjType .setFields(Collections.singletonMap("callStack", - TypeCreator.createField(TypeCreator.createArrayType( - PredefinedTypes.TYPE_ANY), - null, 0))); - - CallStack callStack = new CallStack(callStackObjType); - callStack.callStack = getCallStackArray(value.getStackTrace()); - callStack.callStack.freezeDirect(); - return callStack; + TypeCreator.createField(TypeCreator.createArrayType(callStackElementType), "callStack", + PUBLIC))); + return callStackObjType; } private static BArray getCallStackArray(StackTraceElement[] stackTrace) { diff --git a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibMapTest.java b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibMapTest.java index 8258de8eca9c..80db405f6fff 100644 --- a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibMapTest.java +++ b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibMapTest.java @@ -20,12 +20,12 @@ import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.ArrayType; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; -import io.ballerina.runtime.internal.types.BMapType; import org.ballerinalang.test.BCompileUtil; import org.ballerinalang.test.BRunUtil; import org.ballerinalang.test.CompileResult; @@ -86,7 +86,7 @@ public void testEntries() { assertEquals(getType(returns).getTag(), TypeTags.MAP_TAG); BMap map = (BMap) returns; - assertEquals(((BMapType) map.getType()).getConstrainedType().getTag(), TypeTags.TUPLE_TAG); + assertEquals(((MapType) map.getType()).getConstrainedType().getTag(), TypeTags.TUPLE_TAG); assertEquals(map.size(), 3); assertEquals(map.get(StringUtils.fromString("lk")).toString(), "[\"lk\",\"Sri Lanka\"]"); assertEquals(map.get(StringUtils.fromString("us")).toString(), "[\"us\",\"USA\"]"); @@ -148,7 +148,7 @@ public void testMap() { assertEquals(getType(returns).getTag(), TypeTags.MAP_TAG); BMap map = (BMap) returns; - assertEquals(((BMapType) map.getType()).getConstrainedType().getTag(), TypeTags.FLOAT_TAG); + assertEquals(((MapType) map.getType()).getConstrainedType().getTag(), TypeTags.FLOAT_TAG); assertEquals(map.size(), 3); assertEquals(map.get(StringUtils.fromString("1")), 5.5d); assertEquals(map.get(StringUtils.fromString("2")), 11.0d); @@ -167,7 +167,7 @@ public void testFilter() { assertEquals(getType(returns).getTag(), TypeTags.MAP_TAG); BMap map = (BMap) returns; - assertEquals(((BMapType) map.getType()).getConstrainedType().getTag(), TypeTags.DECIMAL_TAG); + assertEquals(((MapType) map.getType()).getConstrainedType().getTag(), TypeTags.DECIMAL_TAG); assertEquals(map.size(), 2); assertEquals(map.get(StringUtils.fromString("1")), ValueCreator.createDecimalValue("12.34")); assertEquals(map.get(StringUtils.fromString("4")), ValueCreator.createDecimalValue("21.2")); diff --git a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibRecordTest.java b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibRecordTest.java index 72933bf4069d..020049525a2c 100644 --- a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibRecordTest.java +++ b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibRecordTest.java @@ -18,6 +18,7 @@ package org.ballerinalang.langlib.test; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.utils.StringUtils; @@ -25,7 +26,6 @@ import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.types.BArrayType; -import io.ballerina.runtime.internal.types.BMapType; import org.ballerinalang.test.BCompileUtil; import org.ballerinalang.test.BRunUtil; import org.ballerinalang.test.CompileResult; @@ -88,7 +88,7 @@ public void testEntries() { assertEquals(getType(returns).getTag(), TypeTags.MAP_TAG); BMap map = (BMap) returns; - assertEquals(((BMapType) map.getType()).getConstrainedType().getTag(), TypeTags.TUPLE_TAG); + assertEquals(((MapType) map.getType()).getConstrainedType().getTag(), TypeTags.TUPLE_TAG); assertEquals(map.size(), 2); assertEquals(map.get(StringUtils.fromString("name")).toString(), "[\"name\",\"John Doe\"]"); assertEquals(map.get(StringUtils.fromString("age")).toString(), "[\"age\",25]"); @@ -143,7 +143,7 @@ public void testMap() { assertEquals(getType(returns).getTag(), TypeTags.MAP_TAG); BMap map = (BMap) returns; - assertEquals(((BMapType) map.getType()).getConstrainedType().getTag(), TypeTags.INT_TAG); + assertEquals(((MapType) map.getType()).getConstrainedType().getTag(), TypeTags.INT_TAG); assertEquals(map.size(), 2); assertEquals(map.get(StringUtils.fromString("name")), 8L); assertEquals(map.get(StringUtils.fromString("age")), 25L); @@ -161,7 +161,7 @@ public void testFilter() { assertEquals(getType(returns).getTag(), TypeTags.MAP_TAG); BMap map = (BMap) returns; - assertEquals(((BMapType) map.getType()).getConstrainedType().getTag(), TypeTags.INT_TAG); + assertEquals(((MapType) map.getType()).getConstrainedType().getTag(), TypeTags.INT_TAG); assertEquals(map.size(), 2); assertEquals(map.get(StringUtils.fromString("physics")), 75L); assertEquals(map.get(StringUtils.fromString("ict")), 85L); diff --git a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibTableTest.java b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibTableTest.java index 89a71714b5d5..d7658103ba5b 100644 --- a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibTableTest.java +++ b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibTableTest.java @@ -214,9 +214,6 @@ public void testImplementationrNegativeCases() { validateError(negativeResult, index++, "missing ellipsis token", 181, 38); validateError(negativeResult, index++, "missing open brace token", 181, 38); validateError(negativeResult, index++, "missing close brace token", 181, 39); - validateError(negativeResult, index++, "incompatible types: expected " + - "'table key', " + - "found 'table key(age)'", 182, 9); validateError(negativeResult, index++, "incompatible types: expected '[]', found 'int'", 182, 20); validateError(negativeResult, index++, "table with constraint of type map cannot have key specifier " + "or key type constraint", 188, 30); diff --git a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/TypeParamTest.java b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/TypeParamTest.java index a7dcd6accd70..8b81da85bd3a 100644 --- a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/TypeParamTest.java +++ b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/TypeParamTest.java @@ -72,9 +72,6 @@ public void testTypeParamNegative() { BAssertUtil.validateError(result, err++, "incompatible types: expected '(int|string)', found 'float'", 131, 24); BAssertUtil.validateError(result, err++, "incompatible types: expected '[int,(int|float)][]', found '[int," + "(int|float|string)][]'", 137, 34); - BAssertUtil.validateError(result, err++, "incompatible types: expected 'function " + - "(ballerina/lang.table:0.0.0:MapType) returns (ballerina/lang.table:0.0.0:MapType1)', " + - "found 'function (other) returns (DataRow)'", 150, 31); BAssertUtil.validateError(result, err++, "unknown type 'dRecord'", 150, 40); BAssertUtil.validateError(result, err++, "missing identifier", 150, 47); BAssertUtil.validateError(result, err++, "unknown type 'x'", 158, 35); diff --git a/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal b/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal index d5b54572d3f2..c45a8c9515f2 100644 --- a/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal +++ b/langlib/langlib-test/src/test/resources/test-src/valuelib_test.bal @@ -883,7 +883,7 @@ function testCloneWithTypeDecimalToIntNegative() { var message = err.detail()["message"]; string messageString = message is error ? message.toString() : message.toString(); assert(err.message(), "{ballerina/lang.value}ConversionError"); - assert(messageString, "'decimal' value cannot be converted to 'int'"); + assert(messageString, "'decimal' value '9223372036854775807.5' cannot be converted to 'int'"); decimal[] a1 = [9223372036854775807.5, -9223372036854775807.6]; int[]|error a2e = a1.cloneWithType(IntArray); @@ -892,7 +892,7 @@ function testCloneWithTypeDecimalToIntNegative() { message = err.detail()["message"]; messageString = message is error ? message.toString() : message.toString(); assert(err.message(), "{ballerina/lang.value}ConversionError"); - assert(messageString, "'decimal' value cannot be converted to 'int'"); + assert(messageString, "'decimal' value '9223372036854775807.5' cannot be converted to 'int'"); } type IntSubtypeArray1 int:Signed32[]; @@ -1007,8 +1007,7 @@ function testCloneWithTypeIntArrayToUnionArray() { error err = u; var message = err.detail()["message"]; string messageString = message is error ? message.toString() : message.toString(); - string errMsg = "'int[]' value cannot be converted to '(byte|lang.int:Signed16)[]': " + - "\n\t\tarray element '[2]' should be of type '(byte|lang.int:Signed16)', found '65000'"; + string errMsg = "'int' value cannot be converted to '(byte|lang.int:Signed16)'"; assert(err.message(), "{ballerina/lang.value}ConversionError"); assert(messageString, errMsg); @@ -1739,9 +1738,7 @@ function testCloneWithTypeWithFiniteTypeArrayFromIntArrayNegative() { error err = a; var message = err.detail()["message"]; string messageString = message is error ? message.toString() : message.toString(); - string errMsg = "'int[]' value cannot be converted to 'IntTwoOrThree[]': " + - "\n\t\tarray element '[0]' should be of type 'IntTwoOrThree', found '1'" + - "\n\t\tarray element '[3]' should be of type 'IntTwoOrThree', found '4'"; + string errMsg = "'int' value cannot be converted to 'IntTwoOrThree'"; assert(messageString, errMsg); (IntTwoOrThree|IntThreeOrFour)[]|error c = x.cloneWithType(); @@ -1749,8 +1746,7 @@ function testCloneWithTypeWithFiniteTypeArrayFromIntArrayNegative() { err = c; message = err.detail()["message"]; messageString = message is error ? message.toString() : message.toString(); - errMsg = "'int[]' value cannot be converted to '(IntTwoOrThree|IntThreeOrFour)[]': " + - "\n\t\tarray element '[0]' should be of type '(IntTwoOrThree|IntThreeOrFour)', found '1'"; + errMsg = "'int' value cannot be converted to '(IntTwoOrThree|IntThreeOrFour)'"; assert(messageString, errMsg); int[] y = [3, 4]; @@ -4713,8 +4709,8 @@ function testEnsureTypeJsonToNestedRecordsWithErrors() { Factory|error val = trap clonedJsonVal.ensureType(Factory); error err = val; - string errorMsgPrefix = "incompatible types: 'map<(json & readonly)> & readonly' cannot be cast to 'Factory': "; - string errorMsg = errorMsgPrefix + errorMsgContent; + string errorMsgPrefix = "incompatible types: 'map<(json & readonly)> & readonly' cannot be cast to 'Factory'"; + string errorMsg = errorMsgPrefix; assert(checkpanic err.detail()["message"], errorMsg); assert(err.message(), "{ballerina}TypeCastError"); } diff --git a/language-server/modules/langserver-commons/src/main/java/org/ballerinalang/langserver/commons/workspace/WorkspaceManager.java b/language-server/modules/langserver-commons/src/main/java/org/ballerinalang/langserver/commons/workspace/WorkspaceManager.java index 8f5dafa67f7f..156c8e92b57c 100644 --- a/language-server/modules/langserver-commons/src/main/java/org/ballerinalang/langserver/commons/workspace/WorkspaceManager.java +++ b/language-server/modules/langserver-commons/src/main/java/org/ballerinalang/langserver/commons/workspace/WorkspaceManager.java @@ -244,10 +244,10 @@ public interface WorkspaceManager { * @throws IOException If failed to start the process. * @since 2201.6.0 */ - Optional run(Path filePath) throws IOException; + Optional run(Path filePath, List mainFuncArgs) throws IOException; /** - * Stop a running process started with {@link #run(Path)}. + * Stop a running process started with {@link #run}. * @param filePath Path that belongs to the project to be stopped. * @return {@code true} if the process was stopped successfully (or already dead), {@code false} otherwise. * @since 2201.6.0 diff --git a/language-server/modules/langserver-core/spotbugs-exclude.xml b/language-server/modules/langserver-core/spotbugs-exclude.xml index 7eda93fe6fbb..d335ac2bb0f2 100644 --- a/language-server/modules/langserver-core/spotbugs-exclude.xml +++ b/language-server/modules/langserver-core/spotbugs-exclude.xml @@ -112,6 +112,12 @@ + + + + + + diff --git a/language-server/modules/langserver-core/src/main/java/module-info.java b/language-server/modules/langserver-core/src/main/java/module-info.java index a2ad981981ed..18420edf66df 100644 --- a/language-server/modules/langserver-core/src/main/java/module-info.java +++ b/language-server/modules/langserver-core/src/main/java/module-info.java @@ -19,6 +19,7 @@ exports org.ballerinalang.langserver.codeaction.providers; exports org.ballerinalang.langserver.exception; exports org.ballerinalang.langserver.extensions; + exports org.ballerinalang.langserver.extensions.ballerina.packages; exports org.ballerinalang.langserver.config; exports org.ballerinalang.langserver.telemetry; exports org.ballerinalang.langserver.util; diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/LSPackageLoader.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/LSPackageLoader.java index bc0814c9f029..caf4b30d4ea1 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/LSPackageLoader.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/LSPackageLoader.java @@ -15,6 +15,8 @@ */ package org.ballerinalang.langserver; +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; import io.ballerina.compiler.api.ModuleID; @@ -36,6 +38,7 @@ import io.ballerina.projects.environment.ResolutionRequest; import io.ballerina.projects.internal.environment.BallerinaDistribution; import io.ballerina.projects.internal.environment.BallerinaUserHome; +import io.ballerina.projects.util.FileUtils; import org.ballerinalang.langserver.codeaction.CodeActionModuleId; import org.ballerinalang.langserver.common.utils.ModuleUtil; import org.ballerinalang.langserver.commons.DocumentServiceContext; @@ -50,19 +53,18 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.wso2.ballerinalang.compiler.util.Names; +import java.io.IOException; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; /** @@ -141,33 +143,11 @@ public void loadModules(LanguageServerContext context) { lsClientLogger.logTrace("Loading packages from Ballerina distribution"); this.distRepoPackages.addAll(checkAndResolvePackagesFromRepository(packageRepository, skippedLangLibs, Collections.emptySet())); - Set distRepoModuleIdentifiers = distRepoPackages.stream().map(ModuleInfo::packageIdentifier) - .collect(Collectors.toSet()); lsClientLogger.logTrace("Successfully loaded packages from Ballerina distribution"); - lsClientLogger.logTrace("Loading packages from Ballerina User Home"); - BallerinaUserHome ballerinaUserHome = BallerinaUserHome.from(environment); - //Load modules from local repo - PackageRepository localRepository = ballerinaUserHome.localPackageRepository(); - this.localRepoPackages.addAll(checkAndResolvePackagesFromRepository(localRepository, - Collections.emptyList(), distRepoModuleIdentifiers)); - - //Load modules from remote repo - PackageRepository remoteRepository = ballerinaUserHome.remotePackageRepository(); - Set loadedModules = new HashSet<>(); - loadedModules.addAll(distRepoModuleIdentifiers); - loadedModules.addAll(localRepoPackages.stream().map(ModuleInfo::packageIdentifier) - .collect(Collectors.toSet())); - this.remoteRepoPackages.addAll(checkAndResolvePackagesFromRepository(remoteRepository, - Collections.emptyList(), - loadedModules)); - lsClientLogger.logTrace("Successfully loaded packages from Ballerina User Home"); - this.getDistributionRepoModules().forEach(packageInfo -> packagesList.put(packageInfo.packageIdentifier(), packageInfo)); - List repoPackages = new ArrayList<>(); - repoPackages.addAll(this.getRemoteRepoModules()); - repoPackages.addAll(this.getLocalRepoModules()); + List repoPackages = new ArrayList<>(this.getLocalRepoModules()); repoPackages.stream().filter(packageInfo -> !packagesList.containsKey(packageInfo.packageIdentifier())) .forEach(packageInfo -> packagesList.put(packageInfo.packageIdentifier(), packageInfo)); }).thenRunAsync(() -> { @@ -181,10 +161,11 @@ public void loadModules(LanguageServerContext context) { progressNotification.setCancellable(false); languageClient.notifyProgress(new ProgressParams(Either.forLeft(taskId), Either.forLeft(progressNotification))); - }).thenRunAsync(() -> { try { - this.centralPackages.addAll(this.centralPackageDescriptorLoader.getCentralPackages().get()); - } catch (InterruptedException | ExecutionException e) { + String moduleInfo = FileUtils.readFileAsString("moduleInfo.json"); + this.centralPackages.addAll(new Gson().fromJson(moduleInfo, new TypeToken>() { + }.getType())); + } catch (IOException e) { throw new RuntimeException(e); } }).thenRunAsync(() -> { diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/command/executors/RunExecutor.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/command/executors/RunExecutor.java index f27abe6869e5..31222bda88df 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/command/executors/RunExecutor.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/command/executors/RunExecutor.java @@ -15,6 +15,7 @@ */ package org.ballerinalang.langserver.command.executors; +import com.google.gson.JsonArray; import com.google.gson.JsonPrimitive; import org.ballerinalang.annotation.JavaSPIService; import org.ballerinalang.langserver.commons.ExecuteCommandContext; @@ -27,6 +28,8 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; import java.util.function.Supplier; @@ -42,7 +45,8 @@ public class RunExecutor implements LSCommandExecutor { @Override public Boolean execute(ExecuteCommandContext context) throws LSCommandExecutorException { try { - Optional processOpt = context.workspace().run(extractPath(context)); + Optional processOpt = context.workspace().run(extractPath(context), + extractMainFunctionArgs(context)); if (processOpt.isEmpty()) { return false; } @@ -59,6 +63,17 @@ private static Path extractPath(ExecuteCommandContext context) { return Path.of(context.getArguments().get(0).value().getAsString()); } + private static List extractMainFunctionArgs(ExecuteCommandContext context) { + List args = new ArrayList<>(); + if (context.getArguments().size() == 1) { + return args; + } + context.getArguments().get(1).value().getAsJsonArray().iterator().forEachRemaining(arg -> { + args.add(arg.getAsString()); + }); + return args; + } + public void listenOutputAsync(ExtendedLanguageClient client, Supplier getInputStream, String channel) { Thread.startVirtualThread(() -> listenOutput(client, getInputStream, channel)); } diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/common/utils/NameUtil.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/common/utils/NameUtil.java index a9f2746d24dd..320a294871db 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/common/utils/NameUtil.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/common/utils/NameUtil.java @@ -90,11 +90,7 @@ public static String generateVariableName(Symbol symbol, TypeSymbol typeSymbol, name = typeSymbol.getName().get(); } else { TypeSymbol rawType = CommonUtil.getRawType(typeSymbol); - name = switch (rawType.typeKind()) { - case RECORD -> "mappingResult"; - case TUPLE, ARRAY -> "listResult"; - default -> rawType.typeKind().getName() + "Result"; - }; + name = generateNameForRawType(rawType.typeKind()); } return generateVariableName(1, name, names); } else { @@ -102,6 +98,23 @@ public static String generateVariableName(Symbol symbol, TypeSymbol typeSymbol, } } + /** + * Generates a variable name based on the provided signature. + * + * @param signature The type or name signature. + * @param names The set of existing names to avoid duplicates. + * @return The generated variable name. + */ + public static String generateVariableName(String signature, Set names) { + try { + TypeDescKind typeDescKind = TypeDescKind.valueOf(signature); + return generateVariableName(1, generateNameForRawType(typeDescKind), names); + } catch (IllegalArgumentException ignored) { + String camelCase = toCamelCase(signature); + return generateVariableName(1, camelCase, names); + } + } + /** * Given a prefix and visible symbols, this method will return a type name by appending a number to the end. * @@ -207,7 +220,7 @@ public static String getValidatedSymbolName(PositionedOperationContext context, * @return Signature */ public static String getModifiedTypeName(DocumentServiceContext context, TypeSymbol typeSymbol) { - String typeSignature = typeSymbol.typeKind() == + String typeSignature = typeSymbol.typeKind() == TypeDescKind.COMPILATION_ERROR ? "" : typeSymbol.signature(); return CommonUtil.getModifiedSignature(context, typeSignature); } @@ -260,7 +273,21 @@ public static List getDefinedArgumentNames(BallerinaCompletionContext co } return existingArgNames; } - + + /** + * Generates a name for a raw type. + * + * @param rawType The raw type descriptor kind. + * @return The generated name for the raw type. + */ + private static String generateNameForRawType(TypeDescKind rawType) { + return switch (rawType) { + case RECORD -> "mappingResult"; + case TUPLE, ARRAY -> "listResult"; + default -> rawType.getName() + "Result"; + }; + } + /** * Coverts a given text to camel case. * diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/diagnostic/DiagnosticsHelper.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/diagnostic/DiagnosticsHelper.java index f6a53e3b8d03..dab8630b8a21 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/diagnostic/DiagnosticsHelper.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/diagnostic/DiagnosticsHelper.java @@ -206,34 +206,7 @@ private Map> toDiagnosticsMap(Collection compileAndSendDiagnostics(client, projectRoot, pkgCompilation, workspaceManager))); } + + public static Diagnostic getLSDiagnosticsFromCompilationDiagnostics( + LineRange lineRange, io.ballerina.tools.diagnostics.Diagnostic diag) { + int startLine = lineRange.startLine().line(); + int startChar = lineRange.startLine().offset(); + int endLine = lineRange.endLine().line(); + int endChar = lineRange.endLine().offset(); + + endLine = (endLine <= 0) ? startLine : endLine; + endChar = (endChar <= 0) ? startChar + 1 : endChar; + + Range range = new Range(new Position(startLine, startChar), new Position(endLine, endChar)); + Diagnostic diagnostic = new Diagnostic(range, diag.message(), null, null, diag.diagnosticInfo().code()); + + switch (diag.diagnosticInfo().severity()) { + case ERROR: + diagnostic.setSeverity(DiagnosticSeverity.Error); + break; + case WARNING: + diagnostic.setSeverity(DiagnosticSeverity.Warning); + break; + case HINT: + diagnostic.setSeverity(DiagnosticSeverity.Hint); + break; + case INFO: + diagnostic.setSeverity(DiagnosticSeverity.Information); + break; + default: + break; + } + return diagnostic; + } } diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/BallerinaPackageService.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/BallerinaPackageService.java index 726e0a6ca478..f537e1ddc024 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/BallerinaPackageService.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/BallerinaPackageService.java @@ -157,7 +157,7 @@ public Class getRemoteInterface() { * @param project {@link Project} * @return {@link JsonObject} with package components */ - private JsonObject getPackageComponents(Project project) { + public JsonObject getPackageComponents(Project project) { Package currentPackage = project.currentPackage(); PackageObject packageObject = new PackageObject(currentPackage.packageName().value(), project.sourceRoot().toUri().toString()); diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/DocumentComponentTransformer.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/DocumentComponentTransformer.java index 9592f9828f95..4a3d11c87027 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/DocumentComponentTransformer.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/DocumentComponentTransformer.java @@ -26,6 +26,7 @@ import io.ballerina.compiler.syntax.tree.NonTerminalNode; import io.ballerina.compiler.syntax.tree.ServiceDeclarationNode; import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.compiler.syntax.tree.Token; import io.ballerina.compiler.syntax.tree.TypeDefinitionNode; import io.ballerina.tools.text.LineRange; @@ -36,6 +37,7 @@ * The node transformer class to get the list of module level components. */ public class DocumentComponentTransformer extends NodeTransformer> { + private final ModuleObject module; DocumentComponentTransformer(ModuleObject moduleObject) { @@ -63,6 +65,11 @@ public Optional transformSyntaxNode(Node node) { @Override public Optional transform(FunctionDefinitionNode functionDefinitionNode) { + if (functionDefinitionNode.functionName().text().equals(PackageServiceConstants.MAIN_FUNCTION)) { + return Optional.of(new MapperObject(PackageServiceConstants.AUTOMATIONS, + createDataObject(PackageServiceConstants.MAIN_FUNCTION, functionDefinitionNode))); + } + return Optional.of(new MapperObject(PackageServiceConstants.FUNCTIONS, createDataObject(functionDefinitionNode.functionName().text(), functionDefinitionNode))); } @@ -77,11 +84,24 @@ public Optional transform(ListenerDeclarationNode listenerDeclarat public Optional transform(ServiceDeclarationNode serviceDeclarationNode) { String name = serviceDeclarationNode.absoluteResourcePath().stream().map(node -> String.join("_", node.toString())).collect(Collectors.joining()); + if (name.isEmpty()) { + name = serviceDeclarationNode.typeDescriptor().map(typeDescriptorNode -> + typeDescriptorNode.toSourceCode().strip()).orElse(""); + } + DataObject dataObject = createDataObject(name, serviceDeclarationNode); serviceDeclarationNode.members().forEach(member -> { if (member.kind() == SyntaxKind.RESOURCE_ACCESSOR_DEFINITION) { - dataObject.addResource(createDataObject(((FunctionDefinitionNode) member).functionName().text(), - member)); + FunctionDefinitionNode functionDefinitionNode = (FunctionDefinitionNode) member; + String resourceName = functionDefinitionNode.functionName().text() + "-" + + functionDefinitionNode.relativeResourcePath().stream() + .map(Node::toSourceCode) + .collect(Collectors.joining("")); + dataObject.addResource(createDataObject(resourceName, member)); + } else if (member.kind() == SyntaxKind.OBJECT_METHOD_DEFINITION) { + FunctionDefinitionNode functionDefinitionNode = (FunctionDefinitionNode) member; + String functionName = functionDefinitionNode.functionName().text(); + dataObject.addFunction(this.createDataObject(functionName, member)); } }); return Optional.of(new MapperObject(PackageServiceConstants.SERVICES, dataObject)); @@ -115,9 +135,17 @@ public Optional transform(TypeDefinitionNode typeDefinitionNode) { @Override public Optional transform(ModuleVariableDeclarationNode moduleVariableDeclarationNode) { + Optional isConfigurable = moduleVariableDeclarationNode.qualifiers().stream() + .filter(qualifier -> qualifier.kind() == SyntaxKind.CONFIGURABLE_KEYWORD) + .findFirst(); + if (isConfigurable.isPresent()) { + return Optional.of(new MapperObject(PackageServiceConstants.CONFIGURABLE_VARIABLES, + createDataObject(moduleVariableDeclarationNode.typedBindingPattern().bindingPattern().toString(), + moduleVariableDeclarationNode))); + } return Optional.of(new MapperObject(PackageServiceConstants.MODULE_LEVEL_VARIABLE, createDataObject(moduleVariableDeclarationNode.typedBindingPattern().bindingPattern().toString(), - moduleVariableDeclarationNode))); + moduleVariableDeclarationNode))); } @Override diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/ModuleObject.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/ModuleObject.java index 4b7fedfdd0c1..d480b5606243 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/ModuleObject.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/ModuleObject.java @@ -32,6 +32,8 @@ public class ModuleObject { private final List enums = new ArrayList<>(); private final List listeners = new ArrayList<>(); private final List moduleVariables = new ArrayList<>(); + private final List configurableVariables = new ArrayList<>(); + private final List automations = new ArrayList<>(); private String name; @@ -47,64 +49,70 @@ protected void addFunction(DataObject dataObject) { this.functions.add(dataObject); } - protected void addConstant(DataObject dataObject) { + private void addConstant(DataObject dataObject) { this.constants.add(dataObject); } - protected void addService(DataObject dataObject) { + private void addService(DataObject dataObject) { this.services.add(dataObject); } - protected void addRecord(DataObject dataObject) { + private void addRecord(DataObject dataObject) { this.records.add(dataObject); } - protected void addObject(DataObject dataObject) { + private void addObject(DataObject dataObject) { this.objects.add(dataObject); } - protected void addClass(DataObject dataObject) { + private void addClass(DataObject dataObject) { this.classes.add(dataObject); } - protected void addType(DataObject dataObject) { + private void addType(DataObject dataObject) { this.types.add(dataObject); } + private void addAutomation(DataObject dataObject) { + this.automations.add(dataObject); + this.functions.add(dataObject); // The main function + } + + private void addModuleVariable(DataObject dataObject) { + this.moduleVariables.add(dataObject); + } + + private void addConfigurableVariable(DataObject dataObject) { + this.configurableVariables.add(dataObject); + this.moduleVariables.add(dataObject); // Configurable variable is also a module variable + } + + private void addListener(DataObject dataObject) { + this.listeners.add(dataObject); + } + + private void addEnum(DataObject dataObject) { + this.enums.add(dataObject); + } + protected void addDataObject(MapperObject mapperObject) { switch (mapperObject.getKey()) { - case PackageServiceConstants.FUNCTIONS: - this.addFunction(mapperObject.getDataObject()); - break; - case PackageServiceConstants.SERVICES: - this.addService(mapperObject.getDataObject()); - break; - case PackageServiceConstants.CONSTANTS: - this.addConstant(mapperObject.getDataObject()); - break; - case PackageServiceConstants.RECORDS: - this.addRecord(mapperObject.getDataObject()); - break; - case PackageServiceConstants.OBJECTS: - this.addObject(mapperObject.getDataObject()); - break; - case PackageServiceConstants.CLASSES: - this.addClass(mapperObject.getDataObject()); - break; - case PackageServiceConstants.TYPES: - this.addType(mapperObject.getDataObject()); - break; - case PackageServiceConstants.ENUMS: - this.enums.add(mapperObject.getDataObject()); - break; - case PackageServiceConstants.LISTENERS: - this.listeners.add(mapperObject.getDataObject()); - break; - case PackageServiceConstants.MODULE_LEVEL_VARIABLE: - this.moduleVariables.add(mapperObject.getDataObject()); - break; - default: - break; + case PackageServiceConstants.FUNCTIONS -> this.addFunction(mapperObject.getDataObject()); + case PackageServiceConstants.SERVICES -> this.addService(mapperObject.getDataObject()); + case PackageServiceConstants.CONSTANTS -> this.addConstant(mapperObject.getDataObject()); + case PackageServiceConstants.RECORDS -> this.addRecord(mapperObject.getDataObject()); + case PackageServiceConstants.OBJECTS -> this.addObject(mapperObject.getDataObject()); + case PackageServiceConstants.CLASSES -> this.addClass(mapperObject.getDataObject()); + case PackageServiceConstants.TYPES -> this.addType(mapperObject.getDataObject()); + case PackageServiceConstants.ENUMS -> this.addEnum(mapperObject.getDataObject()); + case PackageServiceConstants.LISTENERS -> this.addListener(mapperObject.getDataObject()); + case PackageServiceConstants.MODULE_LEVEL_VARIABLE -> + this.addModuleVariable(mapperObject.getDataObject()); + case PackageServiceConstants.CONFIGURABLE_VARIABLES -> + this.addConfigurableVariable(mapperObject.getDataObject()); + case PackageServiceConstants.AUTOMATIONS -> this.addAutomation(mapperObject.getDataObject()); + default -> { + } } } } diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/PackageServiceConstants.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/PackageServiceConstants.java index 453944781521..4a512af2e6df 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/PackageServiceConstants.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/packages/PackageServiceConstants.java @@ -23,6 +23,7 @@ public final class PackageServiceConstants { static final String CAPABILITY_NAME = "ballerinaPackage"; + static final String MAIN_FUNCTION = "main"; static final String NAME = "name"; static final String FILE_PATH = "filePath"; static final String START_LINE = "startLine"; @@ -41,6 +42,8 @@ public final class PackageServiceConstants { static final String CLASSES = "classes"; static final String LISTENERS = "listeners"; static final String MODULE_LEVEL_VARIABLE = "moduleVariables"; + static final String CONFIGURABLE_VARIABLES = "configurableVariables"; + static final String AUTOMATIONS = "automations"; private PackageServiceConstants() { } diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerService.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerService.java new file mode 100644 index 000000000000..a477df09b61a --- /dev/null +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerService.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.ballerinalang.langserver.extensions.ballerina.runner; + +import io.ballerina.compiler.syntax.tree.FunctionDefinitionNode; +import io.ballerina.compiler.syntax.tree.ModuleMemberDeclarationNode; +import io.ballerina.compiler.syntax.tree.ModulePartNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.ParameterNode; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.projects.Document; +import io.ballerina.projects.DocumentId; +import io.ballerina.projects.Package; +import io.ballerina.projects.Project; +import org.ballerinalang.annotation.JavaSPIService; +import org.ballerinalang.langserver.LSClientLogger; +import org.ballerinalang.langserver.common.utils.PathUtil; +import org.ballerinalang.langserver.commons.LSOperation; +import org.ballerinalang.langserver.commons.LanguageServerContext; +import org.ballerinalang.langserver.commons.service.spi.ExtendedLanguageServerService; +import org.ballerinalang.langserver.commons.workspace.WorkspaceManager; +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; +import org.eclipse.lsp4j.services.LanguageServer; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +/** + * Implementation of Ballerina runner extension for Language Server. + * + * @since 2201.11.0 + */ +@JavaSPIService("org.ballerinalang.langserver.commons.service.spi.ExtendedLanguageServerService") +@JsonSegment("ballerinaRunner") +public class BallerinaRunnerService implements ExtendedLanguageServerService { + + private WorkspaceManager workspaceManager; + private LSClientLogger clientLogger; + + @Override + public void init(LanguageServer langServer, WorkspaceManager workspaceManager, + LanguageServerContext serverContext) { + this.workspaceManager = workspaceManager; + this.clientLogger = LSClientLogger.getInstance(serverContext); + } + + /** + * Get all the diagnostics of the project. + * + * @param request {@link ProjectDiagnosticsRequest} + * @return {@link ProjectDiagnosticsResponse} + */ + @JsonRequest + public CompletableFuture diagnostics(ProjectDiagnosticsRequest request) { + return CompletableFuture.supplyAsync(() -> { + try { + ProjectDiagnosticsResponse projectDiagnosticsResponse = new ProjectDiagnosticsResponse(); + Optional filePath = PathUtil.getPathFromURI(request.getProjectRootIdentifier().getUri()); + if (filePath.isEmpty()) { + return projectDiagnosticsResponse; + } + Project project = this.workspaceManager.loadProject(filePath.get()); + Map> errorDiagnosticMap = + BallerinaRunnerUtil.getErrorDiagnosticMap(this.workspaceManager, project, filePath.get()); + projectDiagnosticsResponse.setErrorDiagnosticMap(errorDiagnosticMap); + return projectDiagnosticsResponse; + } catch (Throwable e) { + String msg = "Operation 'ballerinaRunner/diagnostics' failed!"; + this.clientLogger.logError(RunnerContext.RUNNER_DIAGNOSTICS, msg, e, request.getProjectRootIdentifier(), + (Position) null); + } + return new ProjectDiagnosticsResponse(); + }); + } + + /** + * Get the main function parameters. + * + * @param request {@link MainFunctionParamsRequest} + * @return {@link MainFunctionParamsResponse} + */ + @JsonRequest + public CompletableFuture mainFunctionParams(MainFunctionParamsRequest request) { + return CompletableFuture.supplyAsync(() -> { + try { + Optional filePath = PathUtil.getPathFromURI(request.getProjectRootIdentifier().getUri()); + if (filePath.isEmpty()) { + return new MainFunctionParamsResponse(false, null, null); + } + Project project = this.workspaceManager.loadProject(filePath.get()); + Package currentPackage = project.currentPackage(); + for (DocumentId documentId : currentPackage.getDefaultModule().documentIds()) { + Document document = currentPackage.getDefaultModule().document(documentId); + Node node = document.syntaxTree().rootNode(); + if (node instanceof ModulePartNode modulePartNode) { + for (ModuleMemberDeclarationNode member : modulePartNode.members()) { + if (member.kind() == SyntaxKind.FUNCTION_DEFINITION) { + FunctionDefinitionNode functionDefinitionNode = (FunctionDefinitionNode) member; + if (functionDefinitionNode.functionName().text() + .equals(BallerinaRunnerServiceConstants.MAIN_FUNCTION)) { + List params = new ArrayList<>(); + for (ParameterNode param:functionDefinitionNode.functionSignature().parameters()) { + if (param.kind() == SyntaxKind.REST_PARAM) { + return new MainFunctionParamsResponse(true, params, + BallerinaRunnerUtil.extractParamDetails(param)); + } else { + params.add(BallerinaRunnerUtil.extractParamDetails(param)); + } + } + return new MainFunctionParamsResponse(true, params, null); + } + } + } + } + } + } catch (Throwable e) { + String msg = "Operation 'ballerinaRunner/mainFunctionParams' failed!"; + this.clientLogger.logError(RunnerContext.RUNNER_MAIN_FUNCTION_PARAMS, msg, e, + request.getProjectRootIdentifier(), (Position) null); + } + return new MainFunctionParamsResponse(false, null, null); + }); + } + + @Override + public Class getRemoteInterface() { + return getClass(); + } + + public record MainFunctionParamsResponse(boolean hasMain, List params, + TypeBindingPair restParams) { + } + + public record TypeBindingPair(String type, String paramName, String defaultValue) { + } + + private enum RunnerContext implements LSOperation { + RUNNER_DIAGNOSTICS("ballerinaRunner/diagnostics"), + RUNNER_MAIN_FUNCTION_PARAMS("ballerinaRunner/mainFunctionParams"); + + private final String name; + + RunnerContext(String name) { + this.name = name; + } + + @Override + public String getName() { + return this.name; + } + } + +} diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerServiceClientCapabilities.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerServiceClientCapabilities.java new file mode 100644 index 000000000000..f14f63740cc3 --- /dev/null +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerServiceClientCapabilities.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.ballerinalang.langserver.extensions.ballerina.runner; + +import org.ballerinalang.langserver.commons.registration.BallerinaClientCapability; + +/** + * Client capabilities for the ballerina runner service. + * + * @since 2201.11.0 + */ +public class BallerinaRunnerServiceClientCapabilities extends BallerinaClientCapability { + + private boolean diagnostics; + private boolean mainFunctionParams; + + public BallerinaRunnerServiceClientCapabilities() { + super(BallerinaRunnerServiceConstants.CAPABILITY_NAME); + } + + public boolean isDiagnostics() { + return diagnostics; + } + + public void setDiagnostics(boolean diagnostics) { + this.diagnostics = diagnostics; + } + + public boolean isMainFunctionParams() { + return mainFunctionParams; + } + + public void setMainFunctionParams(boolean mainFunctionParams) { + this.mainFunctionParams = mainFunctionParams; + } +} diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerServiceClientCapabilitySetter.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerServiceClientCapabilitySetter.java new file mode 100644 index 000000000000..cd672780c44b --- /dev/null +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerServiceClientCapabilitySetter.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.ballerinalang.langserver.extensions.ballerina.runner; + +import org.ballerinalang.annotation.JavaSPIService; +import org.ballerinalang.langserver.commons.registration.BallerinaClientCapabilitySetter; + +/** + * Client capability setter for the {@link BallerinaRunnerService}. + * + * @since 2201.11.0 + */ +@JavaSPIService("org.ballerinalang.langserver.commons.registration.BallerinaClientCapabilitySetter") +public class BallerinaRunnerServiceClientCapabilitySetter extends + BallerinaClientCapabilitySetter { + + @Override + public String getCapabilityName() { + return BallerinaRunnerServiceConstants.CAPABILITY_NAME; + } + + @Override + public Class getCapability() { + return BallerinaRunnerServiceClientCapabilities.class; + } +} diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerServiceConstants.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerServiceConstants.java new file mode 100644 index 000000000000..92cbf4742076 --- /dev/null +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerServiceConstants.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.ballerinalang.langserver.extensions.ballerina.runner; + +/** + * Ballerina runner service constants. + * + * @since 2201.11.0 + */ +public class BallerinaRunnerServiceConstants { + + private BallerinaRunnerServiceConstants() { + } + + protected static final String CAPABILITY_NAME = "ballerinaRunner"; + protected static final String MAIN_FUNCTION = "main"; +} diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerServiceServerCapabilities.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerServiceServerCapabilities.java new file mode 100644 index 000000000000..47e53a0904c8 --- /dev/null +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerServiceServerCapabilities.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.ballerinalang.langserver.extensions.ballerina.runner; + +import org.ballerinalang.langserver.commons.registration.BallerinaServerCapability; + +/** + * Server capabilities for the ballerina runner service. + * + * @since 2201.11.0 + */ +public class BallerinaRunnerServiceServerCapabilities extends BallerinaServerCapability { + + private boolean diagnostics; + private boolean mainFunctionParams; + + public BallerinaRunnerServiceServerCapabilities() { + super(BallerinaRunnerServiceConstants.CAPABILITY_NAME); + } + + public boolean isDiagnostics() { + return diagnostics; + } + + public void setDiagnostics(boolean diagnostics) { + this.diagnostics = diagnostics; + } + + public boolean isMainFunctionParams() { + return mainFunctionParams; + } + + public void setMainFunctionParams(boolean mainFunctionParams) { + this.mainFunctionParams = mainFunctionParams; + } +} diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerServiceServerCapabilitySetter.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerServiceServerCapabilitySetter.java new file mode 100644 index 000000000000..8b1ec2784766 --- /dev/null +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerServiceServerCapabilitySetter.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.ballerinalang.langserver.extensions.ballerina.runner; + +import org.ballerinalang.annotation.JavaSPIService; +import org.ballerinalang.langserver.commons.registration.BallerinaServerCapabilitySetter; + +import java.util.Optional; + +/** + * Server capability setter for the {@link BallerinaRunnerService}. + * + * @since 2201.11.0 + */ +@JavaSPIService("org.ballerinalang.langserver.commons.registration.BallerinaServerCapabilitySetter") +public class BallerinaRunnerServiceServerCapabilitySetter extends + BallerinaServerCapabilitySetter { + + @Override + public Optional build() { + BallerinaRunnerServiceServerCapabilities capabilities = new BallerinaRunnerServiceServerCapabilities(); + capabilities.setDiagnostics(true); + capabilities.setMainFunctionParams(true); + return Optional.of(capabilities); + } + + @Override + public String getCapabilityName() { + return BallerinaRunnerServiceConstants.CAPABILITY_NAME; + } + + @Override + public Class getCapability() { + return BallerinaRunnerServiceServerCapabilities.class; + } +} diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerUtil.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerUtil.java new file mode 100644 index 000000000000..f1288001a1a6 --- /dev/null +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/BallerinaRunnerUtil.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.ballerinalang.langserver.extensions.ballerina.runner; + +import io.ballerina.compiler.syntax.tree.DefaultableParameterNode; +import io.ballerina.compiler.syntax.tree.IncludedRecordParameterNode; +import io.ballerina.compiler.syntax.tree.ParameterNode; +import io.ballerina.compiler.syntax.tree.RequiredParameterNode; +import io.ballerina.compiler.syntax.tree.RestParameterNode; +import io.ballerina.projects.Project; +import io.ballerina.tools.text.LineRange; +import org.ballerinalang.langserver.common.utils.PathUtil; +import org.ballerinalang.langserver.commons.workspace.WorkspaceManager; +import org.ballerinalang.langserver.diagnostic.DiagnosticsHelper; +import org.eclipse.lsp4j.Diagnostic; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Ballerina Runner Util. + * + * @since 2201.11.0 + */ +public class BallerinaRunnerUtil { + + public static Map> getErrorDiagnosticMap(WorkspaceManager workspaceManager, + Project project, Path projectRoot) { + Collection diagnostics = project.currentPackage() + .getCompilation().diagnosticResult().errors(); + Map> diagnosticsMap = new HashMap<>(); + for (io.ballerina.tools.diagnostics.Diagnostic diag : diagnostics) { + LineRange lineRange = diag.location().lineRange(); + Diagnostic result = DiagnosticsHelper.getLSDiagnosticsFromCompilationDiagnostics(lineRange, diag); + String resolvedUri = projectRoot.resolve(lineRange.fileName()).toUri().toString(); + String fileURI = PathUtil.getModifiedUri(workspaceManager, resolvedUri); + List clientDiagnostics = diagnosticsMap.computeIfAbsent(fileURI, s -> new ArrayList<>()); + clientDiagnostics.add(result); + } + return diagnosticsMap; + } + + public static BallerinaRunnerService.TypeBindingPair extractParamDetails(ParameterNode param) { + switch (param.kind()) { + case DEFAULTABLE_PARAM -> { + DefaultableParameterNode defaultableParam = (DefaultableParameterNode) param; + return new BallerinaRunnerService.TypeBindingPair( + defaultableParam.typeName().toString().strip(), + Objects.requireNonNull(defaultableParam.paramName().orElse(null)).text(), + defaultableParam.expression().toString()); + } + case REST_PARAM -> { + RestParameterNode restParam = (RestParameterNode) param; + return new BallerinaRunnerService.TypeBindingPair( + restParam.typeName().toString().strip(), + Objects.requireNonNull(restParam.paramName().orElse(null)).text(), + null); + } + case INCLUDED_RECORD_PARAM -> { + IncludedRecordParameterNode includedRecordParam = (IncludedRecordParameterNode) param; + return new BallerinaRunnerService.TypeBindingPair( + includedRecordParam.typeName().toString().strip(), + Objects.requireNonNull(includedRecordParam.paramName().orElse(null)).text(), + null); + } + default -> { + RequiredParameterNode requiredParam = (RequiredParameterNode) param; + return new BallerinaRunnerService.TypeBindingPair( + requiredParam.typeName().toString().strip(), + Objects.requireNonNull(requiredParam.paramName().orElse(null)).text(), + null); + } + } + } +} diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/MainFunctionParamsRequest.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/MainFunctionParamsRequest.java new file mode 100644 index 000000000000..509479502fcd --- /dev/null +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/MainFunctionParamsRequest.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.ballerinalang.langserver.extensions.ballerina.runner; + +import org.eclipse.lsp4j.TextDocumentIdentifier; + +/** + * {@link BallerinaRunnerService} mainFunctionParams api request. + * + * @since 2201.11.0 + */ +public class MainFunctionParamsRequest { + + private TextDocumentIdentifier projectRootIdentifier; + + protected TextDocumentIdentifier getProjectRootIdentifier() { + return projectRootIdentifier; + } + + public void setDocumentIdentifier(TextDocumentIdentifier projectRootIdentifier) { + this.projectRootIdentifier = projectRootIdentifier == null ? null : + new TextDocumentIdentifier(projectRootIdentifier.getUri()); + } +} diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/ProjectDiagnosticsRequest.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/ProjectDiagnosticsRequest.java new file mode 100644 index 000000000000..ca9e2851ca71 --- /dev/null +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/ProjectDiagnosticsRequest.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.ballerinalang.langserver.extensions.ballerina.runner; + +import org.eclipse.lsp4j.TextDocumentIdentifier; + +/** + * {@link BallerinaRunnerService} diagnostics api request. + * + * @since 2201.11.0 + */ +public class ProjectDiagnosticsRequest { + + private TextDocumentIdentifier projectRootIdentifier; + + protected TextDocumentIdentifier getProjectRootIdentifier() { + return projectRootIdentifier; + } + + public void setDocumentIdentifier(TextDocumentIdentifier projectRootIdentifier) { + this.projectRootIdentifier = projectRootIdentifier == null ? null : + new TextDocumentIdentifier(projectRootIdentifier.getUri()); + } +} diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/ProjectDiagnosticsResponse.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/ProjectDiagnosticsResponse.java new file mode 100644 index 000000000000..99a600256440 --- /dev/null +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/extensions/ballerina/runner/ProjectDiagnosticsResponse.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.ballerinalang.langserver.extensions.ballerina.runner; + +import org.eclipse.lsp4j.Diagnostic; + +import java.util.List; +import java.util.Map; + +/** + * {@link BallerinaRunnerService} diagnostics api response. + * + * @since 2201.11.0 + */ +public class ProjectDiagnosticsResponse { + + private Map> errorDiagnosticMap; + + public Map> getErrorDiagnosticMap() { + return errorDiagnosticMap; + } + + public void setErrorDiagnosticMap(Map> errorDiagnosticMap) { + this.errorDiagnosticMap = errorDiagnosticMap; + } +} diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/util/TestUtil.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/util/TestUtil.java index edc593837695..f34c85b782ee 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/util/TestUtil.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/util/TestUtil.java @@ -38,6 +38,8 @@ import org.ballerinalang.langserver.extensions.ballerina.packages.PackageComponentsRequest; import org.ballerinalang.langserver.extensions.ballerina.packages.PackageConfigSchemaRequest; import org.ballerinalang.langserver.extensions.ballerina.packages.PackageMetadataRequest; +import org.ballerinalang.langserver.extensions.ballerina.runner.MainFunctionParamsRequest; +import org.ballerinalang.langserver.extensions.ballerina.runner.ProjectDiagnosticsRequest; import org.eclipse.lsp4j.ClientCapabilities; import org.eclipse.lsp4j.CodeActionCapabilities; import org.eclipse.lsp4j.CodeActionContext; @@ -164,6 +166,9 @@ public final class TestUtil { private static final String SEMANTIC_TOKENS_FULL = "textDocument/semanticTokens/full"; + private static final String RUNNER_DIAGNOSTICS = "ballerinaRunner/diagnostics"; + private static final String RUNNER_MAIN_FUNC_PARAMS = "ballerinaRunner/mainFunctionParams"; + private static final Gson GSON = new Gson(); private TestUtil() { @@ -461,6 +466,32 @@ public static String getPackageComponentsResponse(Endpoint serviceEndpoint, Iter return getResponseString(serviceEndpoint.request(PACKAGE_COMPONENTS, packageComponentsRequest)); } + /** + * Get runner service's diagnostics response. + * + * @param serviceEndpoint Language Server Service endpoint + * @param projectDir root directory of the project + * @return {@link String} Runner diagnostics response + */ + public static String getRunnerDiagnosticsResponse(Endpoint serviceEndpoint, String projectDir) { + ProjectDiagnosticsRequest projectDiagnosticsRequest = new ProjectDiagnosticsRequest(); + projectDiagnosticsRequest.setDocumentIdentifier(getTextDocumentIdentifier(projectDir)); + return getResponseString(serviceEndpoint.request(RUNNER_DIAGNOSTICS, projectDiagnosticsRequest)); + } + + /** + * Get runner service's main function params response. + * + * @param serviceEndpoint Language Server Service endpoint + * @param projectDir root directory of the project + * @return {@link String} Runner diagnostics response + */ + public static String getRunnerMainFuncParamsResponse(Endpoint serviceEndpoint, String projectDir) { + MainFunctionParamsRequest mainFunctionParamsRequest = new MainFunctionParamsRequest(); + mainFunctionParamsRequest.setDocumentIdentifier(getTextDocumentIdentifier(projectDir)); + return getResponseString(serviceEndpoint.request(RUNNER_MAIN_FUNC_PARAMS, mainFunctionParamsRequest)); + } + /** * Get package service's config schema response. * diff --git a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/workspace/BallerinaWorkspaceManager.java b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/workspace/BallerinaWorkspaceManager.java index 4bf2649bb7e6..97a9f13575f3 100644 --- a/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/workspace/BallerinaWorkspaceManager.java +++ b/language-server/modules/langserver-core/src/main/java/org/ballerinalang/langserver/workspace/BallerinaWorkspaceManager.java @@ -588,7 +588,7 @@ public String uriScheme() { } @Override - public Optional run(Path filePath) throws IOException { + public Optional run(Path filePath, List mainFuncArgs) throws IOException { Optional projectPairOpt = projectContext(projectRoot(filePath)); if (projectPairOpt.isEmpty()) { String msg = "Run command execution aborted because project is not loaded"; @@ -632,6 +632,7 @@ public Optional run(Path filePath) throws IOException { commands.add("-cp"); commands.add(getAllClassPaths(jarResolver)); commands.add(initClassName); + commands.addAll(mainFuncArgs); ProcessBuilder pb = new ProcessBuilder(commands); Lock lock = projectContext.lockAndGet(); diff --git a/language-server/modules/langserver-core/src/main/resources/moduleInfo.json b/language-server/modules/langserver-core/src/main/resources/moduleInfo.json new file mode 100644 index 000000000000..e9da8371a3b1 --- /dev/null +++ b/language-server/modules/langserver-core/src/main/resources/moduleInfo.json @@ -0,0 +1,16274 @@ +[ + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "twilio" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 4, + "minor": 0, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/twilio", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "choreo" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 4, + "patch": 12 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/choreo", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.sheets" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 5, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.sheets", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "client.config" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/client.config", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "salesforce" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 8, + "minor": 0, + "patch": 2 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/salesforce", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "trigger.salesforce" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 10, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/trigger.salesforce", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "twitter" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 3, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/twitter", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "java.jdbc" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 11, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/java.jdbc", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "covid19" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/covid19", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "mysql" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 12, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/mysql", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "worldbank" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/worldbank", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "mysql.driver" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 6, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/mysql.driver", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "asyncapi.native.handler" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 2, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/asyncapi.native.handler", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "kafka" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 4, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/kafka", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "postgresql" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 12, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/postgresql", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "github" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 5, + "minor": 0, + "patch": 2 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/github", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "redis" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 0, + "patch": 2 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/redis", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "mssql.driver" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 6, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/mssql.driver", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "persist.sql" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/persist.sql", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "trigger.github" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 9, + "patch": 2 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/trigger.github", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "trigger.slack" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 8, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/trigger.slack", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.gmail" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 4, + "minor": 0, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.gmail", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "jaeger" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 5, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/jaeger", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure_storage_service" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 4, + "minor": 3, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure_storage_service", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.calendar" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 2, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.calendar", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "aws.s3" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 4, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/aws.s3", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sap" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sap", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.base" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 3 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.base", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.drive" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 3, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.drive", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "slack" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 3, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/slack", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "mssql" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 12, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/mssql", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.r4" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 5, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.r4", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "mongodb" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 5, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/mongodb", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "stripe" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 6, + "patch": 2 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/stripe", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "openweathermap" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/openweathermap", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "netsuite" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 3, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/netsuite", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "openai.chat" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/openai.chat", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "asb" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 8, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/asb", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "openai.text" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 5 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/openai.text", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "trigger.google.sheets" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 9, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/trigger.google.sheets", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "rabbitmq" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 0, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/rabbitmq", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.openai.text" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 3 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.openai.text", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "postgresql.driver" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/postgresql.driver", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.openai.chat" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 0, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.openai.chat", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "trigger.google.drive" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 10, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/trigger.google.drive", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.r4.international401" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.r4.international401", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure_cosmosdb" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 4, + "minor": 2, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure_cosmosdb", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "hubspot.crm.contact" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/hubspot.crm.contact", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "trigger.asgardeo" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 7, + "patch": 2 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/trigger.asgardeo", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "trigger.google.mail" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 10, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/trigger.google.mail", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "trigger.google.calendar" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 11, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/trigger.google.calendar", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.r4.parser" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 5, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.r4.parser", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "trigger.asb" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 2, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/trigger.asb", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "mailchimp" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/mailchimp", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "shopify.admin" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 5, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/shopify.admin", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "zoom" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 7, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/zoom", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "aws.sqs" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/aws.sqs", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "servicenow" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/servicenow", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "prometheus" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/prometheus", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "microsoft.onedrive" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 4, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/microsoft.onedrive", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "aws.ses" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/aws.ses", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "capsulecrm" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/capsulecrm", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "aws.dynamodb" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/aws.dynamodb", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.people" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 4, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.people", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "trello" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/trello", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "themoviedb" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/themoviedb", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "newsapi" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/newsapi", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "spotify" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/spotify", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "microsoft.excel" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 4, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/microsoft.excel", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.hl7v2" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 2, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.hl7v2", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "microsoft.teams" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 4, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/microsoft.teams", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "quickbooks.online" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/quickbooks.online", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhirr4" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhirr4", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "interzoid.convertcurrency" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/interzoid.convertcurrency", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "eventbrite" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 6, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/eventbrite", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ronin" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 2 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ronin", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "asana" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/asana", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "microsoft.outlook.mail" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 4, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/microsoft.outlook.mail", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "interzoid.weatherzip" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/interzoid.weatherzip", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "pipedrive" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/pipedrive", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "xero.accounts" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/xero.accounts", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "scim" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/scim", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "trigger.twilio" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 9, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/trigger.twilio", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "aayu.mftg.as2" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/aayu.mftg.as2", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "snowflake.driver" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 7, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/snowflake.driver", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "zoho.people" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/zoho.people", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "paypal.orders" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/paypal.orders", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "activecampaign" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/activecampaign", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "trigger.aayu.mftg.as2" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 9, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/trigger.aayu.mftg.as2", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure_eventhub" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure_eventhub", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.r4.uscore501" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 4 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.r4.uscore501", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "confluent.cregistry" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 1, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/confluent.cregistry", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.hl7v23" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 0, + "patch": 3 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.hl7v23", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "oracledb.driver" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 4, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/oracledb.driver", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "livestorm" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 6, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/livestorm", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.hl7v25" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.hl7v25", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.clients.fhir" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.clients.fhir", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.hl7v251" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.hl7v251", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.hl7v26" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.hl7v26", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.hl7v28" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.hl7v28", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.hl7v231" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.hl7v231", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.hl7v27" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.hl7v27", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.hl7v24" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.hl7v24", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "cdata.connect.driver" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 1, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/cdata.connect.driver", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.hl7v2commons" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 1, + "patch": 2 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.hl7v2commons", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "h2.driver" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/h2.driver", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.r4utils.fhirpath" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 2, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.r4utils.fhirpath", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "persist.inmemory" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/persist.inmemory", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "oracledb" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 11, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/oracledb", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.hl7v2.utils.v2tofhirr4" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 0, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.hl7v2.utils.v2tofhirr4", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "aws.redshift" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 3 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/aws.redshift", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "aws.redshift.driver" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/aws.redshift.driver", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "nats" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/nats", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.r4.validator" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 4, + "minor": 2, + "patch": 2 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.r4.validator", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "exchangerates" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/exchangerates", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.r4utils" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 2 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.r4utils", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "peoplehr" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 2, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/peoplehr", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "vonage.sms" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/vonage.sms", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "snowflake" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/snowflake", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "aws.sns" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/aws.sns", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.keyvault" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 6, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.keyvault", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sendgrid" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sendgrid", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "zipkin" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 8, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/zipkin", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.r4.aubase410" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.r4.aubase410", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.r4utils.ccdatofhir" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 4 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.r4utils.ccdatofhir", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "trigger.shopify" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 4, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/trigger.shopify", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "trigger.hubspot" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 9, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/trigger.hubspot", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.r4.terminology" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 4, + "minor": 1, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.r4.terminology", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "openai.embeddings" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 5 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/openai.embeddings", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "amadeus.flightofferssearch" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/amadeus.flightofferssearch", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "amadeus.flightoffersprice" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/amadeus.flightoffersprice", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.r4.lkcore010" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 2, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.r4.lkcore010", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "medium" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/medium", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "trigger.quickbooks" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 2, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/trigger.quickbooks", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "amadeus.flightcreateorders" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/amadeus.flightcreateorders", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "leanix.integrationapi" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/leanix.integrationapi", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "java.jms" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/java.jms", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "candid" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 1, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/candid", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "openai.images" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 5 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/openai.images", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.textanalytics" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.textanalytics", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "impala" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/impala", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.docs" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.docs", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ibm.ibmmq" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ibm.ibmmq", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "cdata.connect" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 2, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/cdata.connect", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "copybook" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 2, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/copybook", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.r4.aubase421" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.r4.aubase421", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "openai.audio" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 5 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/openai.audio", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.cloudfunctions" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.cloudfunctions", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "confluent.cavroserdes" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/confluent.cavroserdes", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "edifact.d03a.supplychain" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 9, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/edifact.d03a.supplychain", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "dayforce" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/dayforce", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.functions" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 4, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.functions", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "persist.googlesheets" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/persist.googlesheets", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "aws.lambda" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 2, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/aws.lambda", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "zendesk.support" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 6, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/zendesk.support", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "edifact.d03a.retail" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 9, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/edifact.d03a.retail", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "pinecone.vector" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 2 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/pinecone.vector", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "activemq.driver" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/activemq.driver", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.ad" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 5, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.ad", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ellucian.studentcharges" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ellucian.studentcharges", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "jira" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/jira", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sap.jco" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 1, + "patch": 4 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sap.jco", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "openai.finetunes" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 5 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/openai.finetunes", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.r4.aucore040" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.r4.aucore040", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.r4.smartconfiguration" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.r4.smartconfiguration", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "whatsapp.business" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/whatsapp.business", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "mi" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/mi", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.r4.patient" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 4 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.r4.patient", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "cloudmersive.currency" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/cloudmersive.currency", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "wso2.controlplane" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/wso2.controlplane", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.openai.embeddings" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 2 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.openai.embeddings", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.sqldb" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.sqldb", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "newrelic" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 7, + "patch": 2 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/newrelic", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "openai.moderations" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 5 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/openai.moderations", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.r4.metadata" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.r4.metadata", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "fraudlabspro.frauddetection" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/fraudlabspro.frauddetection", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "worldtimeapi" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/worldtimeapi", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "bitbucket" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/bitbucket", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.bigquery" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.bigquery", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.oauth2" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.oauth2", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "file360" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/file360", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "microsoft.dynamics365businesscentral" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/microsoft.dynamics365businesscentral", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.gcalendar" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 4, + "minor": 0, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.gcalendar", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.patient" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.patient", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.r4.encounter" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 3 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.r4.encounter", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "weaviate" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 2 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/weaviate", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.datalake" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.datalake", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "edifact.d03a.finance" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 9, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/edifact.d03a.finance", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.r4.athenaconnect" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 3 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.r4.athenaconnect", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "stabilityai" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/stabilityai", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "nytimes.books" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/nytimes.books", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "mathtools.numbers" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/mathtools.numbers", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "docusign.rooms" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/docusign.rooms", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.vision" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 6, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.vision", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.r4.epicconnect" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 3 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.r4.epicconnect", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.r4.cernerconnect" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 3 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.r4.cernerconnect", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.r4.practitioner" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 3 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.r4.practitioner", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.r4.observation" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 3 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.r4.observation", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "microsoft.outlook.calendar" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 4, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/microsoft.outlook.calendar", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "saps4hana.itcm.user" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/saps4hana.itcm.user", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "workday.customeraccounts" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/workday.customeraccounts", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "docusign.admin" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/docusign.admin", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.appsscript" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.appsscript", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "workday.connect" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/workday.connect", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "facebook.ads" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/facebook.ads", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.classroom" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.classroom", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "dataflowkit" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/dataflowkit", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "paylocity" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/paylocity", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "pandadoc" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/pandadoc", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "optirtc.public" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/optirtc.public", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "webscraping.ai" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/webscraping.ai", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "zoho.crm.rest" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/zoho.crm.rest", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "workday.common" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/workday.common", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "transformer" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/transformer", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.practitioner" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.practitioner", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.r4.organization" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 3 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.r4.organization", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "saps4hana.wls.screeninghits" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 4, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/saps4hana.wls.screeninghits", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "notion" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/notion", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "interzoid.currencyrate" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/interzoid.currencyrate", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "openair" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/openair", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "alfresco" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/alfresco", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ably" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ably", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "commercetools.customizebehavior" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 4, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/commercetools.customizebehavior", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "elmah" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/elmah", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "salesforce.einstein" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/salesforce.einstein", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.youtube.data" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.youtube.data", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.clouddatastore" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.clouddatastore", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ip2whois" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ip2whois", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "freshbooks" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/freshbooks", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "wordpress" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/wordpress", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "paypal.payments" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/paypal.payments", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "elasticsearch" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/elasticsearch", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "hubspot.marketing" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/hubspot.marketing", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "edocs" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/edocs", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "reckon.one" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/reckon.one", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "readme" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/readme", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "power.bi" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/power.bi", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sap.s4hana.api_sales_order_srv" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sap.s4hana.api_sales_order_srv", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "persist.redis" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/persist.redis", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "zendesk" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/zendesk", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.hl7v271" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 3, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.hl7v271", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "aws.simpledb" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 2, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/aws.simpledb", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.r4.uscore501.patient" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 3 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.r4.uscore501.patient", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.manufacturercenter" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.manufacturercenter", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "workday.absencemanagement" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/workday.absencemanagement", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "xero.bankfeeds" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/xero.bankfeeds", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "gitlab" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/gitlab", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sinch.sms" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sinch.sms", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.youtube.analytics" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.youtube.analytics", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "mitto.sms" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/mitto.sms", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "visiblethread" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/visiblethread", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.timeseries" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.timeseries", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "fraudlabspro.smsverification" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/fraudlabspro.smsverification", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "brex.team" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/brex.team", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.vault" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.vault", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "orbitcrm" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/orbitcrm", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "dracoon.public" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/dracoon.public", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "adp.paystatements" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/adp.paystatements", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ynab" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ynab", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "xero.assets" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/xero.assets", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "botify" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/botify", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "saps4hana.itcm.customer" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/saps4hana.itcm.customer", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "wordnik" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/wordnik", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "journey.io" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/journey.io", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sap.successfactors.litmos" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sap.successfactors.litmos", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "recurly" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/recurly", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "owler" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/owler", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "zoho.books" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/zoho.books", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "xero.files" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/xero.files", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "salesforce.pardot" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/salesforce.pardot", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "docusign.click" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/docusign.click", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "api2pdf" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/api2pdf", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "bulksms" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/bulksms", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "odweather" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/odweather", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.slides" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.slides", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "saps4hana.itcm.product" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/saps4hana.itcm.product", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "saps4hana.itcm.agreement" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/saps4hana.itcm.agreement", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "bintable" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/bintable", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "pdfbroker" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/pdfbroker", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "automata" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/automata", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "hubspot.crm.deal" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/hubspot.crm.deal", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "iris.helpdesk" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/iris.helpdesk", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "spotto" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/spotto", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.cloudtalentsolution" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.cloudtalentsolution", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.bigquery.datatransfer" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.bigquery.datatransfer", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "hubspot.crm.company" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/hubspot.crm.company", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "shorten.rest" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/shorten.rest", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.cloudfilestore" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.cloudfilestore", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "godaddy.abuse" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/godaddy.abuse", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "iris.lead" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/iris.lead", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "adobe.analytics" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/adobe.analytics", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "commercetools.auditlog" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 4, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/commercetools.auditlog", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sugarcrm" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 4, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sugarcrm", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "godaddy.orders" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/godaddy.orders", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "workday.coreaccounting" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/workday.coreaccounting", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "pagerduty" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/pagerduty", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "dropbox" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/dropbox", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "pocketsmith" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/pocketsmith", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "hubspot.crm.product" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/hubspot.crm.product", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "jira.servicemanagement" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/jira.servicemanagement", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "hubspot.analytics" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/hubspot.analytics", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ellucian.student" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ellucian.student", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sap.s4hana.api_salesorganization_srv" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sap.s4hana.api_salesorganization_srv", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sap.s4hana.api_sales_order_simulation_srv" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sap.s4hana.api_sales_order_simulation_srv", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sap.s4hana.ce_salesorder_0001" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sap.s4hana.ce_salesorder_0001", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sap.s4hana.api_sales_inquiry_srv" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sap.s4hana.api_sales_inquiry_srv", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sap.s4hana.api_sales_quotation_srv" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sap.s4hana.api_sales_quotation_srv", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sap.s4hana.api_sd_incoterms_srv" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sap.s4hana.api_sd_incoterms_srv", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sap.s4hana.salesarea_0001" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sap.s4hana.salesarea_0001", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sap.s4hana.api_sd_sa_soldtopartydetn" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sap.s4hana.api_sd_sa_soldtopartydetn", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sap.s4hana.api_salesdistrict_srv" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sap.s4hana.api_salesdistrict_srv", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "aws.dynamodbstreams" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/aws.dynamodbstreams", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "guidewire.insnow" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/guidewire.insnow", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "docusign.dsesign" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/docusign.dsesign", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "docusign.dsadmin" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/docusign.dsadmin", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "docusign.dsclick" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/docusign.dsclick", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.servicerequest" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.servicerequest", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.messageheader" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.messageheader", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.visionprescription" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.visionprescription", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.terminologycapabilities" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.terminologycapabilities", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.substance" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.substance", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.specimen" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.specimen", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.supplyrequest" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.supplyrequest", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.observation" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.observation", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.researchelementdefinition" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.researchelementdefinition", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.schedule" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.schedule", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.riskevidencesynthesis" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.riskevidencesynthesis", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.organizationaffiliation" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.organizationaffiliation", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.person" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.person", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.testreport" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.testreport", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.researchsubject" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.researchsubject", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.riskassessment" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.riskassessment", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.supplydelivery" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.supplydelivery", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.plandefinition" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.plandefinition", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.specimendefinition" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.specimendefinition", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.task" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.task", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.paymentnotice" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.paymentnotice", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.relatedperson" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.relatedperson", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.structuredefinition" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.structuredefinition", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.researchdefinition" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.researchdefinition", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.searchparameter" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.searchparameter", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.substanceprotein" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.substanceprotein", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.substancespecification" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.substancespecification", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.testscript" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.testscript", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.substancenucleicacid" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.substancenucleicacid", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.questionnaire" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.questionnaire", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.organization" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.organization", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.substancepolymer" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.substancepolymer", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.observationdefinition" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.observationdefinition", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.substancereferenceinformation" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.substancereferenceinformation", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.researchstudy" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.researchstudy", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.namingsystem" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.namingsystem", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.substancesourcematerial" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.substancesourcematerial", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.practitionerrole" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.practitionerrole", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.verificationresult" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.verificationresult", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.structuremap" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.structuremap", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.provenance" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.provenance", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.slot" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.slot", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.subscription" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.subscription", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.paymentreconciliation" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.paymentreconciliation", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.requestgroup" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.requestgroup", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.procedure" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.procedure", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.questionnaireresponse" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.questionnaireresponse", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.nutritionorder" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.nutritionorder", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.molecularsequence" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.molecularsequence", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.parameters" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.parameters", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.operationdefinition" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.operationdefinition", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.messagedefinition" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.messagedefinition", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.medicinalproduct" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.medicinalproduct", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.medicinalproductindication" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.medicinalproductindication", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.medicinalproductcontraindication" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.medicinalproductcontraindication", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.medicinalproductinteraction" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.medicinalproductinteraction", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.medicinalproductpackaged" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.medicinalproductpackaged", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.medicinalproductundesirableeffect" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.medicinalproductundesirableeffect", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.medicinalproductmanufactured" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.medicinalproductmanufactured", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.medicinalproductpharmaceutical" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.medicinalproductpharmaceutical", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.medicinalproductingredient" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.medicinalproductingredient", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.medicinalproductauthorization" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.medicinalproductauthorization", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.medicationadministration" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.medicationadministration", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.medicationstatement" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.medicationstatement", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.medicationknowledge" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.medicationknowledge", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.medicationrequest" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.medicationrequest", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.medicationdispense" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.medicationdispense", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.medication" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.medication", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.media" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.media", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.location" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.location", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.measure" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.measure", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.measurereport" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.measurereport", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.implementationguide" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.implementationguide", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.list" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.list", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.linkage" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.linkage", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.invoice" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.invoice", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.insuranceplan" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.insuranceplan", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.library" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.library", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.imagingstudy" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.imagingstudy", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.immunization" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.immunization", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.immunizationevaluation" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.immunizationevaluation", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.immunizationrecommendation" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.immunizationrecommendation", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.careplan" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.careplan", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.activitydefinition" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.activitydefinition", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.enrollmentrequest" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.enrollmentrequest", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.healthcareservice" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.healthcareservice", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.devicedefinition" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.devicedefinition", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.contract" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.contract", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.deviceusestatement" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.deviceusestatement", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.biologicallyderivedproduct" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.biologicallyderivedproduct", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.guidanceresponse" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.guidanceresponse", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.auditevent" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.auditevent", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.effectevidencesynthesis" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.effectevidencesynthesis", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.explanationofbenefit" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.explanationofbenefit", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.evidencevariable" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.evidencevariable", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.compartmentdefinition" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.compartmentdefinition", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.encounter" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.encounter", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.devicerequest" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.devicerequest", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.eventdefinition" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.eventdefinition", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.careteam" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.careteam", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.goal" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.goal", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.endpoint" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.endpoint", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.documentreference" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.documentreference", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.evidence" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.evidence", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.diagnosticreport" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.diagnosticreport", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.group" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.group", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.clinicalimpression" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.clinicalimpression", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.communicationrequest" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.communicationrequest", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.appointmentresponse" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.appointmentresponse", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.chargeitem" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.chargeitem", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.claim" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.claim", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.device" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.device", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.coverage" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.coverage", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.episodeofcare" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.episodeofcare", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.flag" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.flag", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.examplescenario" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.examplescenario", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.devicemetric" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.devicemetric", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.allergyintolerance" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.allergyintolerance", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.documentmanifest" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.documentmanifest", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.graphdefinition" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.graphdefinition", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.basic" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.basic", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.claimresponse" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.claimresponse", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.consent" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.consent", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.familymemberhistory" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.familymemberhistory", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.enrollmentresponse" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.enrollmentresponse", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.composition" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.composition", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.chargeitemdefinition" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.chargeitemdefinition", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.catalogentry" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.catalogentry", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.bodystructure" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.bodystructure", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.coverageeligibilityrequest" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.coverageeligibilityrequest", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.communication" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.communication", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.detectedissue" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.detectedissue", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.conceptmap" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.conceptmap", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.appointment" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.appointment", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.coverageeligibilityresponse" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.coverageeligibilityresponse", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.adverseevent" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.adverseevent", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.condition" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.condition", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.international401.account" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 0, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.international401.account", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "beezup.merchant" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 6, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/beezup.merchant", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "edifact.d03a.shipping" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 9, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/edifact.d03a.shipping", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "edifact.d03a.services" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 9, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/edifact.d03a.services", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "edifact.d03a.logistics" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 9, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/edifact.d03a.logistics", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "edifact.d03a.manufacturing" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 9, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/edifact.d03a.manufacturing", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.r4.uscore501.encounter" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 3 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.r4.uscore501.encounter", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.r4.servicerequest" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 3 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.r4.servicerequest", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.r4.diagnosticreport" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 3 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.r4.diagnosticreport", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "health.fhir.templates.r4.repositorysync" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 2 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/health.fhir.templates.r4.repositorysync", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "microsoft.onenote" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 4, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/microsoft.onenote", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "hubspot.crm.quote" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/hubspot.crm.quote", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "powertoolsdeveloper.datetime" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/powertoolsdeveloper.datetime", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.qnamaker" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.qnamaker", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "godaddy.aftermarket" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/godaddy.aftermarket", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "cloudmersive.barcode" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/cloudmersive.barcode", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "hubspot.crm.pipeline" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/hubspot.crm.pipeline", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "insightly.custom" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/insightly.custom", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "brex.onboarding" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/brex.onboarding", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "docusign.monitor" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/docusign.monitor", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "godaddy.subscriptions" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/godaddy.subscriptions", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "onepassword" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/onepassword", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "powertoolsdeveloper.text" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/powertoolsdeveloper.text", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "isbndb" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/isbndb", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "cloudmersive.security" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/cloudmersive.security", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "bitly" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/bitly", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.cloudbillingaccount" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.cloudbillingaccount", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "gototraining" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/gototraining", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "apideck.proxy" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/apideck.proxy", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "magento.address" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/magento.address", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ebay.inventory" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ebay.inventory", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "vonage.numberinsight" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/vonage.numberinsight", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "vonage.verify" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/vonage.verify", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sinch.verification" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sinch.verification", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "techport" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/techport", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "opendesign" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/opendesign", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.openai.deployment" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.openai.deployment", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.books" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.books", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "xero.appstore" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 4, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/xero.appstore", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "nowpayments" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/nowpayments", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "clever.data" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/clever.data", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "earthref.fiesta" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/earthref.fiesta", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "godaddy.certificates" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/godaddy.certificates", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "nytimes.timestags" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/nytimes.timestags", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "saps4hana.itcm.promotion" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/saps4hana.itcm.promotion", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "pushcut" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/pushcut", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "iris.residuals" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/iris.residuals", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "iptwist" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/iptwist", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "commercetools.cartsordershoppinglists" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 4, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/commercetools.cartsordershoppinglists", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "thinkific" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/thinkific", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "shipwire.carrier" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/shipwire.carrier", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "siemens.iotandstorage.iotfileservice" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/siemens.iotandstorage.iotfileservice", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "hubspot.events" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/hubspot.events", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "giphy" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/giphy", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "hubspot.crm.lineitem" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/hubspot.crm.lineitem", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ebay.negotiation" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ebay.negotiation", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "prodpad" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/prodpad", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "optitune" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/optitune", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "godaddy.domains" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/godaddy.domains", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "shipwire.containers" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/shipwire.containers", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "uber" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/uber", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "workday.accountspayable" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/workday.accountspayable", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "shippit" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/shippit", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "supportbee" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/supportbee", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "interzoid.emailinfo" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/interzoid.emailinfo", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "powertoolsdeveloper.weather" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/powertoolsdeveloper.weather", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "siemens.analytics.anomalydetection" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/siemens.analytics.anomalydetection", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "nytimes.mostpopular" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/nytimes.mostpopular", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sakari" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sakari", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "openfigi" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/openfigi", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "hubspot.crm.schema" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/hubspot.crm.schema", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "isendpro" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/isendpro", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "neutrino" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/neutrino", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ritekit" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ritekit", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "tableau" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 4, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/tableau", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "saps4hana.itcm.customerhierarchy" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/saps4hana.itcm.customerhierarchy", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "interzoid.globalnumberinfo" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/interzoid.globalnumberinfo", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "saps4hana.itcm.producthierarchy" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/saps4hana.itcm.producthierarchy", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "box" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/box", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "saps4hana.itcm.uom" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/saps4hana.itcm.uom", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.cloudscheduler" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.cloudscheduler", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "keap" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/keap", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ebay.recommendation" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ebay.recommendation", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.cloudtranslation" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.cloudtranslation", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "nytimes.newswire" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/nytimes.newswire", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "optimizely" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/optimizely", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ebay.account" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ebay.account", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "magento.async.customer" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/magento.async.customer", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "commercetools.configuration" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 4, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/commercetools.configuration", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "extpose" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/extpose", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "rumble.run" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/rumble.run", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.retail" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.retail", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "vonage.numbers" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/vonage.numbers", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "iris.subscriptions" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/iris.subscriptions", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "listen.notes" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/listen.notes", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.cloudpubsub" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.cloudpubsub", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "nytimes.archive" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/nytimes.archive", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "commercetools.customizedata" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 4, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/commercetools.customizedata", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ebay.browse" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ebay.browse", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "shortcut" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/shortcut", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "apideck.lead" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/apideck.lead", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.iothub" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.iothub", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "zuora.revenue" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/zuora.revenue", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "saps4hana.pp.idm" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 4, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/saps4hana.pp.idm", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "flatapi" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/flatapi", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "fungenerators.barcode" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/fungenerators.barcode", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "adp.workerpayrollinstructions" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/adp.workerpayrollinstructions", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "shipstation" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 4, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/shipstation", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "iris.esignature" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/iris.esignature", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "bisstats" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/bisstats", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "nytimes.semantic" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/nytimes.semantic", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "instagram" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/instagram", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "constantcontact" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/constantcontact", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "disqus" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/disqus", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "eloqua" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/eloqua", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "powertoolsdeveloper.data" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/powertoolsdeveloper.data", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.discovery" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.discovery", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "storecove" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/storecove", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "logoraisr" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/logoraisr", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.abusiveexperiencereport" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.abusiveexperiencereport", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "freshdesk" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/freshdesk", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.mybusiness" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.mybusiness", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "workday.businessprocess" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/workday.businessprocess", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "soundcloud" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/soundcloud", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "launchdarkly" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/launchdarkly", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "cloudmersive.validate" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/cloudmersive.validate", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "figshare" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/figshare", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "avatax" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/avatax", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "graphhopper.directions" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/graphhopper.directions", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "fungenerators.uuid" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/fungenerators.uuid", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "saps4hana.itcm.accountreceivable" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/saps4hana.itcm.accountreceivable", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "atspoke" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/atspoke", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "hubspot.crm.feedback" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/hubspot.crm.feedback", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "godaddy.shopper" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/godaddy.shopper", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "samcart" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/samcart", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sinch.conversation" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sinch.conversation", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "bmc.truesightpresentationserver" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/bmc.truesightpresentationserver", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "powertoolsdeveloper.finance" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/powertoolsdeveloper.finance", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "vimeo" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/vimeo", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "powertoolsdeveloper.collections" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/powertoolsdeveloper.collections", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "zuora.collection" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/zuora.collection", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.blogger" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.blogger", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "kinto" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/kinto", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.youtube.reporting" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.youtube.reporting", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "files.com" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/files.com", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "hubspot.crm.property" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/hubspot.crm.property", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ebay.finances" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ebay.finances", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "pinecone.index" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 2 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/pinecone.index", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "commercetools.productcatalog" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 4, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/commercetools.productcatalog", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ebay.compliance" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ebay.compliance", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "saps4hana.itcm.currency" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/saps4hana.itcm.currency", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "bing.autosuggest" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/bing.autosuggest", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "saps4hana.itcm.organizationaldata" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/saps4hana.itcm.organizationaldata", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "gotowebinar" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/gotowebinar", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "workday.payroll" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/workday.payroll", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ebay.listing" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ebay.listing", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "interzoid.zipinfo" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/interzoid.zipinfo", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ocpi" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ocpi", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "iris.disputeresponder" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/iris.disputeresponder", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "yodlee" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/yodlee", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "interzoid.globalpageload" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/interzoid.globalpageload", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "xero.finance" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/xero.finance", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "geonames" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/geonames", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "avaza" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/avaza", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "gotomeeting" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/gotomeeting", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.anomalydetector" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.anomalydetector", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "selz" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/selz", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "text2data" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/text2data", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.cloudnaturallanguage" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.cloudnaturallanguage", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "icons8" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/icons8", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "chaingateway" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/chaingateway", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "nytimes.articlesearch" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/nytimes.articlesearch", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "workday.expense" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/workday.expense", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "powertoolsdeveloper.files" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/powertoolsdeveloper.files", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "dev.to" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/dev.to", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.iotcentral" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.iotcentral", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "cloudmersive.virusscan" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/cloudmersive.virusscan", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "vonage.voice" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/vonage.voice", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "quickbase" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/quickbase", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "commercetools.customer" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 4, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/commercetools.customer", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "nytimes.topstories" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/nytimes.topstories", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.cloudbuild" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.cloudbuild", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ebay.analytics" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ebay.analytics", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "instagram.bussiness" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/instagram.bussiness", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "godaddy.agreements" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/godaddy.agreements", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "nytimes.moviereviews" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/nytimes.moviereviews", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "siemens.platformcore.identitymanagement" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/siemens.platformcore.identitymanagement", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "karbon" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/karbon", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "iris.merchants" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/iris.merchants", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "shipwire.warehouse" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/shipwire.warehouse", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "zendesk.voice" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/zendesk.voice", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "mailscript" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/mailscript", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sinch.voice" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sinch.voice", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "apple.appstore" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/apple.appstore", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "whohoststhis" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/whohoststhis", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "hubspot.crm.import" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/hubspot.crm.import", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "hubspot.crm.ticket" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/hubspot.crm.ticket", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "formstack" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/formstack", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "commercetools.pricingdiscount" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 4, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/commercetools.pricingdiscount", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "godaddy.countries" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/godaddy.countries", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "namsor" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/namsor", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "squareup" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/squareup", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "interzoid.statedata" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/interzoid.statedata", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ebay.logistics" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ebay.logistics", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "browshot" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/browshot", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ebay.metadata" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ebay.metadata", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "interzoid.globaltime" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/interzoid.globaltime", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "ipgeolocation" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/ipgeolocation", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "powertoolsdeveloper.math" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/powertoolsdeveloper.math", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.analysisservices" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.analysisservices", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "symantotextanalytics" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/symantotextanalytics", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "hubspot.files" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 2, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/hubspot.files", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "shipwire.receivings" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/shipwire.receivings", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "magento.cart" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/magento.cart", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "sap.fieldglass.approval" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/sap.fieldglass.approval", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "saps4hana.externaltaxcalculation.taxquote" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 3, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/saps4hana.externaltaxcalculation.taxquote", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "azure.openai.finetunes" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 0, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/azure.openai.finetunes", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "workday.compensation" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/workday.compensation", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "apideck.accounting" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/apideck.accounting", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "xero.projects" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/xero.projects", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "googleapis.tasks" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/googleapis.tasks", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "plaid" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 1, + "minor": 5, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/plaid", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "interzoid.currencyexchange" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/interzoid.currencyexchange", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "boxapi" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 1, + "patch": 1 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/boxapi", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + }, + { + "packageOrg": { + "packageOrgStr": "ballerinax" + }, + "packageName": { + "packageNameStr": "Apache" + }, + "packageVersion": { + "version": { + "version": { + "normal": { + "major": 0, + "minor": 1, + "patch": 0 + }, + "preRelease": {}, + "build": {} + } + } + }, + "moduleIdentifier": "ballerinax/Apache", + "isModuleFromCurrentPackage": false, + "listenerMetaData": [] + } +] \ No newline at end of file diff --git a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/runner/DiagnosticsTest.java b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/runner/DiagnosticsTest.java new file mode 100644 index 000000000000..ad2fc690d803 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/runner/DiagnosticsTest.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.ballerinalang.langserver.runner; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.ballerinalang.langserver.util.FileUtils; +import org.ballerinalang.langserver.util.TestUtil; +import org.eclipse.lsp4j.jsonrpc.Endpoint; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Map; + +/** + * Tests {@link org.ballerinalang.langserver.extensions.ballerina.runner.BallerinaRunnerService} diagnostics api. + * + * @since 2201.11.0 + */ +public class DiagnosticsTest { + + private Path resourceRoot; + private Endpoint serviceEndpoint; + + @BeforeClass + public void init() { + this.resourceRoot = FileUtils.RES_DIR.resolve("runner").resolve("diagnostics"); + this.serviceEndpoint = TestUtil.initializeLanguageSever(); + } + + @Test(description = "Test getting all diagnostics in a package", dataProvider = "data-provider") + public void testDiagnosticsInPackage(String projectDir, String expected) throws IOException { + Path sourceRoot = this.resourceRoot.resolve("source"); + JsonObject expectedObj = FileUtils.fileContentAsObject(this.resourceRoot.resolve("config") + .resolve(expected).toString()); + + Path projectPath = this.resourceRoot.resolve("source").resolve(projectDir); + String response = TestUtil.getRunnerDiagnosticsResponse(this.serviceEndpoint, projectPath.toString()); + JsonObject actualJson = getResponseJson(response); + + JsonObject actualErrorDiagnosticMap = alterActualErrorMapPaths(actualJson.get("result") + .getAsJsonObject().get("errorDiagnosticMap").getAsJsonObject()); + JsonObject expectedErrorDiagnosticMap = alterErrorMapPaths(expectedObj.getAsJsonObject("result") + .getAsJsonObject("errorDiagnosticMap"), sourceRoot); + + compareResponse(actualErrorDiagnosticMap, expectedErrorDiagnosticMap); + } + + private void compareResponse(JsonObject actualMap, JsonObject expectedMap) { + for (Map.Entry entry : expectedMap.entrySet()) { + String key = entry.getKey(); + JsonArray expectedDiagnostics = entry.getValue().getAsJsonArray(); + if (!actualMap.has(key)) { + Assert.fail("Expected errors not found in file: " + key); + } + JsonArray actualDiagnostics = actualMap.getAsJsonArray(key); + Assert.assertEquals(actualDiagnostics, expectedDiagnostics); + } + } + + protected JsonObject alterErrorMapPaths(JsonObject errMap, Path root) throws IOException { + JsonObject newErrMap = new JsonObject(); + for (Map.Entry entry : errMap.entrySet()) { + String key = entry.getKey(); + JsonArray diagnostics = entry.getValue().getAsJsonArray(); + String[] uriComponents = key.replace("\"", "").split("/"); + Path expectedPath = Paths.get(root.toUri()); + for (String uriComponent : uriComponents) { + expectedPath = expectedPath.resolve(uriComponent); + } + newErrMap.add(expectedPath.toFile().getCanonicalPath(), diagnostics); + } + return newErrMap; + } + + protected JsonObject alterActualErrorMapPaths(JsonObject errMap) throws IOException { + JsonObject newErrMap = new JsonObject(); + for (Map.Entry entry : errMap.entrySet()) { + String key = entry.getKey(); + JsonArray diagnostics = entry.getValue().getAsJsonArray(); + String uri = key.replace("\"", ""); + newErrMap.add(new File(URI.create(uri)).getCanonicalPath(), diagnostics); + } + return newErrMap; + } + + @DataProvider(name = "data-provider") + public Object[][] getDataProvider() { + return new Object[][]{ + {"project1", "project1.json"} + }; + } + + @AfterClass + public void cleanupLanguageServer() { + TestUtil.shutdownLanguageServer(this.serviceEndpoint); + } + + private JsonObject getResponseJson(String response) { + JsonObject responseJson = JsonParser.parseString(response).getAsJsonObject(); + responseJson.remove("id"); + return responseJson; + } +} diff --git a/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/runner/MainFunctionParamsTest.java b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/runner/MainFunctionParamsTest.java new file mode 100644 index 000000000000..2363d2e0d524 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/java/org/ballerinalang/langserver/runner/MainFunctionParamsTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.ballerinalang.langserver.runner; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.ballerinalang.langserver.util.FileUtils; +import org.ballerinalang.langserver.util.TestUtil; +import org.eclipse.lsp4j.jsonrpc.Endpoint; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.nio.file.Path; + +/** + * Tests {@link org.ballerinalang.langserver.extensions.ballerina.runner.BallerinaRunnerService} mainFunctionParams api. + * + * @since 2201.11.0 + */ +public class MainFunctionParamsTest { + + private Path resourceRoot; + private Endpoint serviceEndpoint; + + @BeforeClass + public void init() { + this.resourceRoot = FileUtils.RES_DIR.resolve("runner").resolve("mainFuncArgs"); + this.serviceEndpoint = TestUtil.initializeLanguageSever(); + } + + @Test(description = "Test main function params in a project", dataProvider = "data-provider") + public void testDiagnosticsInPackage(String projectDir, String expected) { + JsonObject expectedObj = FileUtils.fileContentAsObject(this.resourceRoot.resolve("config") + .resolve(expected).toString()); + + Path projectPath = this.resourceRoot.resolve("source").resolve(projectDir); + String response = TestUtil.getRunnerMainFuncParamsResponse(this.serviceEndpoint, projectPath.toString()); + JsonObject actualJson = getResponseJson(response); + Assert.assertEquals(actualJson.get("result").getAsJsonObject(), + expectedObj.getAsJsonObject("result").getAsJsonObject()); + } + + @DataProvider(name = "data-provider") + public Object[][] getDataProvider() { + return new Object[][]{ + {"project1", "project1.json"}, + {"project2", "project2.json"} + }; + } + + @AfterClass + public void cleanupLanguageServer() { + TestUtil.shutdownLanguageServer(this.serviceEndpoint); + } + + private JsonObject getResponseJson(String response) { + JsonObject responseJson = JsonParser.parseString(response).getAsJsonObject(); + responseJson.remove("id"); + return responseJson; + } +} diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/change-var-type/config/changeVarType5.json b/language-server/modules/langserver-core/src/test/resources/codeaction/change-var-type/config/changeVarType5.json index 03d1ad056bb0..39668a65d61d 100644 --- a/language-server/modules/langserver-core/src/test/resources/codeaction/change-var-type/config/changeVarType5.json +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/change-var-type/config/changeVarType5.json @@ -6,7 +6,7 @@ "source": "changeVarType2.bal", "expected": [ { - "title": "Change variable 'clientResponse' type to 'module1:Response|anydata'", + "title": "Change variable 'clientResponse' type to 'X|module1:ClientError'", "kind": "quickfix", "edits": [ { @@ -20,7 +20,7 @@ "character": 5 } }, - "newText": "module1:Response|anydata" + "newText": "X|module1:ClientError" } ] } diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/change-var-type/source/changeVarType2.bal b/language-server/modules/langserver-core/src/test/resources/codeaction/change-var-type/source/changeVarType2.bal index 8ded0cd20a49..c4c2bc874f88 100644 --- a/language-server/modules/langserver-core/src/test/resources/codeaction/change-var-type/source/changeVarType2.bal +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/change-var-type/source/changeVarType2.bal @@ -2,5 +2,7 @@ import ballerina/module1; public function main() returns error? { module1:Client clientEP = check new("http://example.com"); - x clientResponse = clientEP->get("/"); + X clientResponse = clientEP->get("/"); } + +type X anydata; diff --git a/language-server/modules/langserver-core/src/test/resources/completion/action_node_context/config/client_remote_action_config1.json b/language-server/modules/langserver-core/src/test/resources/completion/action_node_context/config/client_remote_action_config1.json index 5cbef70cce12..c73e11972bbf 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/action_node_context/config/client_remote_action_config1.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/action_node_context/config/client_remote_action_config1.json @@ -461,10 +461,10 @@ { "label": "message = ...", "kind": "Snippet", - "detail": "message = \"\"", + "detail": "message = ()", "sortText": "AR", "filterText": "message", - "insertText": "message = ${1:\"\"}", + "insertText": "message = ${1:()}", "insertTextFormat": "Snippet" }, { diff --git a/language-server/modules/langserver-core/src/test/resources/completion/expression_context/config/check_expression_ctx_config9.json b/language-server/modules/langserver-core/src/test/resources/completion/expression_context/config/check_expression_ctx_config9.json index 26fd93151e15..534dc2fb67c0 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/expression_context/config/check_expression_ctx_config9.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/expression_context/config/check_expression_ctx_config9.json @@ -104,11 +104,11 @@ { "label": "next()", "kind": "Function", - "detail": "record {|string value; string...;|}|stream:CompletionType", + "detail": "record {|string value;|}|stream:CompletionType", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns the next element in the stream wrapped in a record or () if the stream ends.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nscores.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `record {|string value; string...;|}|stream:CompletionType` \n- If the stream has elements, return the element wrapped in a record with single field called `value`, \notherwise returns () \n \n" + "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns the next element in the stream wrapped in a record or () if the stream ends.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nscores.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `record {|string value;|}|stream:CompletionType` \n- If the stream has elements, return the element wrapped in a record with single field called `value`, \notherwise returns () \n \n" } }, "sortText": "BD", @@ -119,11 +119,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|string value; string...;|}|stream:CompletionType;}", + "detail": "object {public isolated function next() returns record {|string value;|}|stream:CompletionType;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns an iterator over a stream.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = scores.iterator();\niterator.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|string value; string...;|}|stream:CompletionType;}` \n- a new iterator object that will iterate over the members of parameter `stm`. \n \n" + "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns an iterator over a stream.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = scores.iterator();\niterator.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|string value;|}|stream:CompletionType;}` \n- a new iterator object that will iterate over the members of parameter `stm`. \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_ctx_config28.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_ctx_config28.json index 2f6a4dd238f6..cb47c62a119d 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_ctx_config28.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_ctx_config28.json @@ -440,11 +440,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|NodeCredential value; NodeCredential...;|}?;}", + "detail": "object {public isolated function next() returns record {|NodeCredential value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|NodeCredential value; NodeCredential...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|NodeCredential value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_ctx_config34.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_ctx_config34.json index 5d1e41158f3a..84151704e5ed 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_ctx_config34.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_ctx_config34.json @@ -388,11 +388,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|int value; int...;|}?;}", + "detail": "object {public isolated function next() returns record {|int value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value; int...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_on_method_call_expression_config1.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_on_method_call_expression_config1.json index e76bcbf7cd4b..537787e6a6bf 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_on_method_call_expression_config1.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_on_method_call_expression_config1.json @@ -92,11 +92,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|string value; string...;|}?;}", + "detail": "object {public isolated function next() returns record {|string value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|string value; string...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|string value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_on_method_call_expression_config2.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_on_method_call_expression_config2.json index a38463e10a92..0487bf79a0ad 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_on_method_call_expression_config2.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_on_method_call_expression_config2.json @@ -92,11 +92,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|string value; string...;|}?;}", + "detail": "object {public isolated function next() returns record {|string value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|string value; string...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|string value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config10.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config10.json index de3b672ee103..0a1279f71935 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config10.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config10.json @@ -440,11 +440,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|int[] value; int[]...;|}?;}", + "detail": "object {public isolated function next() returns record {|int[] value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int[] value; int[]...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int[] value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config14.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config14.json index cbee75c2542a..53b204912b79 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config14.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config14.json @@ -440,11 +440,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|[string, int] value; [string, int]...;|}?;}", + "detail": "object {public isolated function next() returns record {|[string, int] value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|[string, int] value; [string, int]...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|[string, int] value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config17.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config17.json index f3d76f9d08ae..6e605eb203ee 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config17.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config17.json @@ -319,11 +319,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|MyType value; MyType...;|}?;}", + "detail": "object {public isolated function next() returns record {|MyType value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.map:0.0.0_ \n \nReturns an iterator over a map.\n\nThe iterator will iterate over the members of the map not the keys.\nThe function `entries` can be used to iterate over the keys and members together.\nThe function `keys` can be used to iterator over just the keys.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = {\"Carl\": 85, \"Bob\": 50, \"Max\": 60}.iterator();\niterator.next() ⇒ {\"value\":85}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|MyType value; MyType...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `m` \n \n" + "value": "**Package:** _ballerina/lang.map:0.0.0_ \n \nReturns an iterator over a map.\n\nThe iterator will iterate over the members of the map not the keys.\nThe function `entries` can be used to iterate over the keys and members together.\nThe function `keys` can be used to iterator over just the keys.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = {\"Carl\": 85, \"Bob\": 50, \"Max\": 60}.iterator();\niterator.next() ⇒ {\"value\":85}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|MyType value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `m` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config19.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config19.json index 72749ef9da30..eeea3bb1ed93 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config19.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config19.json @@ -130,11 +130,11 @@ { "label": "next()", "kind": "Function", - "detail": "record {|int value; int...;|}|stream:CompletionType", + "detail": "record {|int value;|}|stream:CompletionType", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns the next element in the stream wrapped in a record or () if the stream ends.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nscores.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `record {|int value; int...;|}|stream:CompletionType` \n- If the stream has elements, return the element wrapped in a record with single field called `value`, \notherwise returns () \n \n" + "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns the next element in the stream wrapped in a record or () if the stream ends.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nscores.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `record {|int value;|}|stream:CompletionType` \n- If the stream has elements, return the element wrapped in a record with single field called `value`, \notherwise returns () \n \n" } }, "sortText": "CD", @@ -145,11 +145,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|int value; int...;|}|stream:CompletionType;}", + "detail": "object {public isolated function next() returns record {|int value;|}|stream:CompletionType;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns an iterator over a stream.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = scores.iterator();\niterator.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value; int...;|}|stream:CompletionType;}` \n- a new iterator object that will iterate over the members of parameter `stm`. \n \n" + "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns an iterator over a stream.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = scores.iterator();\niterator.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value;|}|stream:CompletionType;}` \n- a new iterator object that will iterate over the members of parameter `stm`. \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config21.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config21.json index 8ae6b56a6b6d..578741b838de 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config21.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config21.json @@ -440,11 +440,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|int value; int...;|}?;}", + "detail": "object {public isolated function next() returns record {|int value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value; int...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config22.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config22.json index 42e5b4b6f1c9..d8a184ba6f03 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config22.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config22.json @@ -402,11 +402,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|Report value; Report...;|}?;}", + "detail": "object {public isolated function next() returns record {|Report value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|Report value; Report...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|Report value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config25.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config25.json index e2ee21237d3b..e9f47f7bccf4 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config25.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config25.json @@ -104,11 +104,11 @@ { "label": "next()", "kind": "Function", - "detail": "record {|int value; int...;|}|stream:CompletionType", + "detail": "record {|int value;|}|stream:CompletionType", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns the next element in the stream wrapped in a record or () if the stream ends.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nscores.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `record {|int value; int...;|}|stream:CompletionType` \n- If the stream has elements, return the element wrapped in a record with single field called `value`, \notherwise returns () \n \n" + "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns the next element in the stream wrapped in a record or () if the stream ends.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nscores.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `record {|int value;|}|stream:CompletionType` \n- If the stream has elements, return the element wrapped in a record with single field called `value`, \notherwise returns () \n \n" } }, "sortText": "CD", @@ -119,11 +119,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|int value; int...;|}|stream:CompletionType;}", + "detail": "object {public isolated function next() returns record {|int value;|}|stream:CompletionType;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns an iterator over a stream.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = scores.iterator();\niterator.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value; int...;|}|stream:CompletionType;}` \n- a new iterator object that will iterate over the members of parameter `stm`. \n \n" + "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns an iterator over a stream.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = scores.iterator();\niterator.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value;|}|stream:CompletionType;}` \n- a new iterator object that will iterate over the members of parameter `stm`. \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config9.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config9.json index ce58c3e79a5b..a9143384f6ef 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config9.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config9.json @@ -440,11 +440,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|int value; int...;|}?;}", + "detail": "object {public isolated function next() returns record {|int value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value; int...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/module_const_context/config/config7.json b/language-server/modules/langserver-core/src/test/resources/completion/module_const_context/config/config7.json index acabac212753..85ac8710eae0 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/module_const_context/config/config7.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/module_const_context/config/config7.json @@ -199,7 +199,7 @@ "value": "" } }, - "sortText": "F", + "sortText": "AB", "insertText": "varName", "insertTextFormat": "Snippet" }, diff --git a/language-server/modules/langserver-core/src/test/resources/packages/components/multiple-packages_expected.json b/language-server/modules/langserver-core/src/test/resources/packages/components/multiple-packages_expected.json index 6f3785771ba7..4a225bd496fb 100644 --- a/language-server/modules/langserver-core/src/test/resources/packages/components/multiple-packages_expected.json +++ b/language-server/modules/langserver-core/src/test/resources/packages/components/multiple-packages_expected.json @@ -1,21 +1,31 @@ { "result": [ { - "filePath": "file:///", "name": "project", + "filePath": "file:///", "modules": [ { - "functions": [ + "functions": [], + "services": [ { - "name": "main", - "filePath": "main.bal", + "name": "/hello ", + "filePath": "hello.bal", "startLine": 3, "startColumn": 0, - "endLine": 6, - "endColumn": 1 + "endLine": 11, + "endColumn": 1, + "resources": [ + { + "name": "get-satyHello", + "filePath": "hello.bal", + "startLine": 8, + "startColumn": 4, + "endLine": 10, + "endColumn": 5 + } + ] } ], - "services": [], "records": [], "objects": [], "classes": [], @@ -23,30 +33,23 @@ "constants": [], "enums": [], "listeners": [], - "moduleVariables": [] + "moduleVariables": [], + "configurableVariables": [], + "automations": [], + "name": "services" }, { - "functions": [], - "services": [ + "functions": [ { - "name": "/hello ", - "resources": [ - { - "name": "get", - "filePath": "hello.bal", - "startLine": 8, - "startColumn": 4, - "endLine": 10, - "endColumn": 5 - } - ], - "filePath": "hello.bal", + "name": "main", + "filePath": "main.bal", "startLine": 3, "startColumn": 0, - "endLine": 11, + "endLine": 6, "endColumn": 1 } ], + "services": [], "records": [], "objects": [], "classes": [], @@ -55,13 +58,23 @@ "enums": [], "listeners": [], "moduleVariables": [], - "name": "services" + "configurableVariables": [], + "automations": [ + { + "name": "main", + "filePath": "main.bal", + "startLine": 3, + "startColumn": 0, + "endLine": 6, + "endColumn": 1 + } + ] } ] }, { - "filePath": "file:///", "name": "project_functions", + "filePath": "file:///", "modules": [ { "functions": [ @@ -83,13 +96,15 @@ "enums": [], "listeners": [], "moduleVariables": [], + "configurableVariables": [], + "automations": [], "name": "storage" }, { "functions": [ { - "name": "runServices", - "filePath": "svc.bal", + "name": "main", + "filePath": "main.bal", "startLine": 3, "startColumn": 0, "endLine": 6, @@ -105,13 +120,23 @@ "enums": [], "listeners": [], "moduleVariables": [], - "name": "services" + "configurableVariables": [], + "automations": [ + { + "name": "main", + "filePath": "main.bal", + "startLine": 3, + "startColumn": 0, + "endLine": 6, + "endColumn": 1 + } + ] }, { "functions": [ { - "name": "main", - "filePath": "main.bal", + "name": "runServices", + "filePath": "svc.bal", "startLine": 3, "startColumn": 0, "endLine": 6, @@ -126,34 +151,37 @@ "constants": [], "enums": [], "listeners": [], - "moduleVariables": [] + "moduleVariables": [], + "configurableVariables": [], + "automations": [], + "name": "services" } ] }, { - "filePath": "file:///", "name": "project_services", + "filePath": "file:///", "modules": [ { "functions": [], "services": [ { "name": "/ ", + "filePath": "main.bal", + "startLine": 3, + "startColumn": 0, + "endLine": 11, + "endColumn": 1, "resources": [ { - "name": "get", + "name": "get-welcome", "filePath": "main.bal", "startLine": 8, "startColumn": 4, "endLine": 10, "endColumn": 5 } - ], - "filePath": "main.bal", - "startLine": 3, - "startColumn": 0, - "endLine": 11, - "endColumn": 1 + ] } ], "records": [], @@ -163,28 +191,30 @@ "constants": [], "enums": [], "listeners": [], - "moduleVariables": [] + "moduleVariables": [], + "configurableVariables": [], + "automations": [] }, { "functions": [], "services": [ { "name": "\"process\" ", + "filePath": "db.bal", + "startLine": 3, + "startColumn": 0, + "endLine": 8, + "endColumn": 1, "resources": [ { - "name": "post", + "name": "post-connect", "filePath": "db.bal", "startLine": 5, "startColumn": 4, "endLine": 7, "endColumn": 5 } - ], - "filePath": "db.bal", - "startLine": 3, - "startColumn": 0, - "endLine": 8, - "endColumn": 1 + ] } ], "records": [], @@ -195,6 +225,8 @@ "enums": [], "listeners": [], "moduleVariables": [], + "configurableVariables": [], + "automations": [], "name": "db_service" }, { @@ -202,21 +234,21 @@ "services": [ { "name": "/hello ", + "filePath": "hello.bal", + "startLine": 5, + "startColumn": 0, + "endLine": 13, + "endColumn": 1, "resources": [ { - "name": "get", + "name": "get-satyHello", "filePath": "hello.bal", "startLine": 10, "startColumn": 4, "endLine": 12, "endColumn": 5 } - ], - "filePath": "hello.bal", - "startLine": 5, - "startColumn": 0, - "endLine": 13, - "endColumn": 1 + ] } ], "records": [], @@ -236,13 +268,15 @@ } ], "moduleVariables": [], + "configurableVariables": [], + "automations": [], "name": "hello_service" } ] }, { - "filePath": "file:///", "name": ".", + "filePath": "file:///", "modules": [ { "functions": [ @@ -258,21 +292,21 @@ "services": [ { "name": "/hey ", + "filePath": "main.bal", + "startLine": 48, + "startColumn": 0, + "endLine": 53, + "endColumn": 1, "resources": [ { - "name": "get", + "name": "get-satyHello", "filePath": "main.bal", "startLine": 50, "startColumn": 4, "endLine": 52, "endColumn": 5 } - ], - "filePath": "main.bal", - "startLine": 48, - "startColumn": 0, - "endLine": 53, - "endColumn": 1 + ] } ], "records": [ @@ -298,6 +332,11 @@ "classes": [ { "name": "Person", + "filePath": "main.bal", + "startLine": 25, + "startColumn": 0, + "endLine": 31, + "endColumn": 1, "functions": [ { "name": "init", @@ -307,12 +346,7 @@ "endLine": 30, "endColumn": 5 } - ], - "filePath": "main.bal", - "startLine": 25, - "startColumn": 0, - "endLine": 31, - "endColumn": 1 + ] } ], "types": [ @@ -371,6 +405,26 @@ "endLine": 23, "endColumn": 41 } + ], + "configurableVariables": [ + { + "name": "enableRemote ", + "filePath": "main.bal", + "startLine": 23, + "startColumn": 0, + "endLine": 23, + "endColumn": 41 + } + ], + "automations": [ + { + "name": "main", + "filePath": "main.bal", + "startLine": 44, + "startColumn": 0, + "endLine": 46, + "endColumn": 1 + } ] } ] diff --git a/language-server/modules/langserver-core/src/test/resources/packages/components/project-other_expected.json b/language-server/modules/langserver-core/src/test/resources/packages/components/project-other_expected.json index 84c2aca172b7..9aef28e69104 100644 --- a/language-server/modules/langserver-core/src/test/resources/packages/components/project-other_expected.json +++ b/language-server/modules/langserver-core/src/test/resources/packages/components/project-other_expected.json @@ -1,30 +1,9 @@ { "result": [ { - "filePath": "file:///", "name": "project_other", + "filePath": "file:///", "modules": [ - { - "functions": [ - { - "name": "multiply", - "filePath": "main.bal", - "startLine": 0, - "startColumn": 0, - "endLine": 2, - "endColumn": 1 - } - ], - "services": [], - "records": [], - "objects": [], - "classes": [], - "types": [], - "constants": [], - "enums": [], - "listeners": [], - "moduleVariables": [] - }, { "functions": [], "services": [], @@ -51,6 +30,11 @@ "classes": [ { "name": "Tokenizer", + "filePath": "model.bal", + "startLine": 35, + "startColumn": 0, + "endLine": 72, + "endColumn": 1, "functions": [ { "name": "init", @@ -84,12 +68,7 @@ "endLine": 71, "endColumn": 5 } - ], - "filePath": "model.bal", - "startLine": 35, - "startColumn": 0, - "endLine": 72, - "endColumn": 1 + ] } ], "types": [ @@ -189,7 +168,32 @@ "endColumn": 2 } ], + "configurableVariables": [], + "automations": [], "name": "models" + }, + { + "functions": [ + { + "name": "multiply", + "filePath": "main.bal", + "startLine": 0, + "startColumn": 0, + "endLine": 2, + "endColumn": 1 + } + ], + "services": [], + "records": [], + "objects": [], + "classes": [], + "types": [], + "constants": [], + "enums": [], + "listeners": [], + "moduleVariables": [], + "configurableVariables": [], + "automations": [] } ] } diff --git a/language-server/modules/langserver-core/src/test/resources/packages/components/single-file-package_expected.json b/language-server/modules/langserver-core/src/test/resources/packages/components/single-file-package_expected.json index 3ccad17edee1..e3b25c9184f0 100644 --- a/language-server/modules/langserver-core/src/test/resources/packages/components/single-file-package_expected.json +++ b/language-server/modules/langserver-core/src/test/resources/packages/components/single-file-package_expected.json @@ -1,8 +1,8 @@ { "result": [ { - "filePath": "file:///", "name": ".", + "filePath": "file:///", "modules": [ { "functions": [ @@ -18,21 +18,21 @@ "services": [ { "name": "/hey ", + "filePath": "main.bal", + "startLine": 48, + "startColumn": 0, + "endLine": 53, + "endColumn": 1, "resources": [ { - "name": "get", + "name": "get-satyHello", "filePath": "main.bal", "startLine": 50, "startColumn": 4, "endLine": 52, "endColumn": 5 } - ], - "filePath": "main.bal", - "startLine": 48, - "startColumn": 0, - "endLine": 53, - "endColumn": 1 + ] } ], "records": [ @@ -58,6 +58,11 @@ "classes": [ { "name": "Person", + "filePath": "main.bal", + "startLine": 25, + "startColumn": 0, + "endLine": 31, + "endColumn": 1, "functions": [ { "name": "init", @@ -67,12 +72,7 @@ "endLine": 30, "endColumn": 5 } - ], - "filePath": "main.bal", - "startLine": 25, - "startColumn": 0, - "endLine": 31, - "endColumn": 1 + ] } ], "types": [ @@ -131,6 +131,26 @@ "endLine": 23, "endColumn": 41 } + ], + "configurableVariables": [ + { + "name": "enableRemote ", + "filePath": "main.bal", + "startLine": 23, + "startColumn": 0, + "endLine": 23, + "endColumn": 41 + } + ], + "automations": [ + { + "name": "main", + "filePath": "main.bal", + "startLine": 44, + "startColumn": 0, + "endLine": 46, + "endColumn": 1 + } ] } ] diff --git a/language-server/modules/langserver-core/src/test/resources/packages/components/single-package_expected.json b/language-server/modules/langserver-core/src/test/resources/packages/components/single-package_expected.json index ba7b5ba2e276..ba5333b77f24 100644 --- a/language-server/modules/langserver-core/src/test/resources/packages/components/single-package_expected.json +++ b/language-server/modules/langserver-core/src/test/resources/packages/components/single-package_expected.json @@ -1,21 +1,31 @@ { "result": [ { - "filePath": "file:///", "name": "project", + "filePath": "file:///", "modules": [ { - "functions": [ + "functions": [], + "services": [ { - "name": "main", - "filePath": "main.bal", + "name": "/hello ", + "filePath": "hello.bal", "startLine": 3, "startColumn": 0, - "endLine": 6, - "endColumn": 1 + "endLine": 11, + "endColumn": 1, + "resources": [ + { + "name": "get-satyHello", + "filePath": "hello.bal", + "startLine": 8, + "startColumn": 4, + "endLine": 10, + "endColumn": 5 + } + ] } ], - "services": [], "records": [], "objects": [], "classes": [], @@ -23,30 +33,23 @@ "constants": [], "enums": [], "listeners": [], - "moduleVariables": [] + "moduleVariables": [], + "configurableVariables": [], + "automations": [], + "name": "services" }, { - "functions": [], - "services": [ + "functions": [ { - "name": "/hello ", - "resources": [ - { - "name": "get", - "filePath": "hello.bal", - "startLine": 8, - "startColumn": 4, - "endLine": 10, - "endColumn": 5 - } - ], - "filePath": "hello.bal", + "name": "main", + "filePath": "main.bal", "startLine": 3, "startColumn": 0, - "endLine": 11, + "endLine": 6, "endColumn": 1 } ], + "services": [], "records": [], "objects": [], "classes": [], @@ -55,7 +58,17 @@ "enums": [], "listeners": [], "moduleVariables": [], - "name": "services" + "configurableVariables": [], + "automations": [ + { + "name": "main", + "filePath": "main.bal", + "startLine": 3, + "startColumn": 0, + "endLine": 6, + "endColumn": 1 + } + ] } ] } diff --git a/language-server/modules/langserver-core/src/test/resources/runner/diagnostics/config/project1.json b/language-server/modules/langserver-core/src/test/resources/runner/diagnostics/config/project1.json new file mode 100644 index 000000000000..26c6b146e595 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/runner/diagnostics/config/project1.json @@ -0,0 +1,45 @@ +{ + "sourceRoot": "project1", + "result": { + "errorDiagnosticMap": { + "project1/sample.bal": [ + { + "range": { + "start": { + "line": 0, + "character": 8 + }, + "end": { + "line": 0, + "character": 17 + } + }, + "severity": "Error", + "code": { + "left": "BCE2066" + }, + "message": "incompatible types: expected 'int', found 'string'" + } + ], + "project1/another.bal": [ + { + "range": { + "start": { + "line": 5, + "character": 9 + }, + "end": { + "line": 5, + "character": 10 + } + }, + "severity": "Error", + "code": { + "left": "BCE2066" + }, + "message": "incompatible types: expected 'string', found 'int'" + } + ] + } + } +} diff --git a/language-server/modules/langserver-core/src/test/resources/runner/diagnostics/source/project1/Ballerina.toml b/language-server/modules/langserver-core/src/test/resources/runner/diagnostics/source/project1/Ballerina.toml new file mode 100644 index 000000000000..8f1512a11c65 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/runner/diagnostics/source/project1/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "wso2" +name = "project" +version = "0.1.0" \ No newline at end of file diff --git a/language-server/modules/langserver-core/src/test/resources/runner/diagnostics/source/project1/another.bal b/language-server/modules/langserver-core/src/test/resources/runner/diagnostics/source/project1/another.bal new file mode 100644 index 000000000000..30380533be38 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/runner/diagnostics/source/project1/another.bal @@ -0,0 +1,7 @@ +function foo() returns string { + return "foo"; +} + +function bar() returns string { + return 1; +} diff --git a/language-server/modules/langserver-core/src/test/resources/runner/diagnostics/source/project1/sample.bal b/language-server/modules/langserver-core/src/test/resources/runner/diagnostics/source/project1/sample.bal new file mode 100644 index 000000000000..d41ea690964f --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/runner/diagnostics/source/project1/sample.bal @@ -0,0 +1,5 @@ +int a = "invalid"; + +public function main() { + int unused = 0; +} diff --git a/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/config/project1.json b/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/config/project1.json new file mode 100644 index 000000000000..efa69c7c0f77 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/config/project1.json @@ -0,0 +1,20 @@ +{ + "sourceRoot": "project1", + "result": { + "hasMain": true, + "params": [ + { + "type": "string", + "paramName": "host" + }, + { + "type": "int", + "paramName": "port" + } + ], + "restParams": { + "type": "string", + "paramName": "rest" + } + } +} diff --git a/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/config/project2.json b/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/config/project2.json new file mode 100644 index 000000000000..f519f13ec65f --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/config/project2.json @@ -0,0 +1,17 @@ +{ + "sourceRoot": "project1", + "result": { + "hasMain": true, + "params": [ + { + "type": "string", + "paramName": "host" + }, + { + "type": "int", + "paramName": "port", + "defaultValue": "9000" + } + ] + } +} diff --git a/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/source/project1/Ballerina.toml b/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/source/project1/Ballerina.toml new file mode 100644 index 000000000000..4106b646d782 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/source/project1/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "wso2" +name = "project1" +version = "0.1.0" diff --git a/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/source/project1/sample.bal b/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/source/project1/sample.bal new file mode 100644 index 000000000000..de1128b3a19a --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/source/project1/sample.bal @@ -0,0 +1,3 @@ +public function main(string host, int port, string... rest) { + +} diff --git a/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/source/project2/Ballerina.toml b/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/source/project2/Ballerina.toml new file mode 100644 index 000000000000..4106b646d782 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/source/project2/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "wso2" +name = "project1" +version = "0.1.0" diff --git a/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/source/project2/sample.bal b/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/source/project2/sample.bal new file mode 100644 index 000000000000..9b3384a8c910 --- /dev/null +++ b/language-server/modules/langserver-core/src/test/resources/runner/mainFuncArgs/source/project2/sample.bal @@ -0,0 +1,3 @@ +public function main(string host, int port = 9000) { + +} diff --git a/language-server/modules/langserver-core/src/test/resources/testng.xml b/language-server/modules/langserver-core/src/test/resources/testng.xml index 91e31f1637cd..b0f6d2ee1bf3 100644 --- a/language-server/modules/langserver-core/src/test/resources/testng.xml +++ b/language-server/modules/langserver-core/src/test/resources/testng.xml @@ -44,6 +44,7 @@ under the License. + diff --git a/misc/debug-adapter/modules/debug-adapter-runtime/src/main/java/org/ballerinalang/debugadapter/runtime/VariableUtils.java b/misc/debug-adapter/modules/debug-adapter-runtime/src/main/java/org/ballerinalang/debugadapter/runtime/VariableUtils.java index cd15f0d33bf5..870006970d2a 100644 --- a/misc/debug-adapter/modules/debug-adapter-runtime/src/main/java/org/ballerinalang/debugadapter/runtime/VariableUtils.java +++ b/misc/debug-adapter/modules/debug-adapter-runtime/src/main/java/org/ballerinalang/debugadapter/runtime/VariableUtils.java @@ -18,7 +18,7 @@ package org.ballerinalang.debugadapter.runtime; -import io.ballerina.runtime.internal.types.BMapType; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.internal.values.MapValueImpl; /** @@ -46,7 +46,7 @@ public static String getBMapType(Object mapObject) { return String.format(MAP_TYPE_TEMPLATE, UNKNOWN); } - if (!(mapValue.getType() instanceof BMapType type)) { + if (!(mapValue.getType() instanceof MapType type)) { return String.format(MAP_TYPE_TEMPLATE, UNKNOWN); } diff --git a/misc/diagram-util/src/main/java/org/ballerinalang/diagramutil/SyntaxTreeMapGenerator.java b/misc/diagram-util/src/main/java/org/ballerinalang/diagramutil/SyntaxTreeMapGenerator.java index a6fa0c61c6b9..b66d5fa845c1 100644 --- a/misc/diagram-util/src/main/java/org/ballerinalang/diagramutil/SyntaxTreeMapGenerator.java +++ b/misc/diagram-util/src/main/java/org/ballerinalang/diagramutil/SyntaxTreeMapGenerator.java @@ -38,6 +38,7 @@ import io.ballerina.compiler.syntax.tree.ChildNodeEntry; import io.ballerina.compiler.syntax.tree.ChildNodeList; import io.ballerina.compiler.syntax.tree.ClassDefinitionNode; +import io.ballerina.compiler.syntax.tree.ClientResourceAccessActionNode; import io.ballerina.compiler.syntax.tree.Minutiae; import io.ballerina.compiler.syntax.tree.MinutiaeList; import io.ballerina.compiler.syntax.tree.ModulePartNode; @@ -302,6 +303,16 @@ protected JsonElement transformSyntaxNode(Node node) { markVisibleEp(variableSymbol, symbolJson, remoteMethodCallActionNode.expression(), true); } } + } else if (node.kind() == SyntaxKind.CLIENT_RESOURCE_ACCESS_ACTION) { + ClientResourceAccessActionNode resourceCallActionNode = (ClientResourceAccessActionNode) node; + if (semanticModel != null) { + Optional expressionSymbol = this.semanticModel.symbol( + resourceCallActionNode.expression()); + if (expressionSymbol.isPresent() && + expressionSymbol.get() instanceof VariableSymbol variableSymbol) { + markVisibleEp(variableSymbol, symbolJson, resourceCallActionNode.expression(), true); + } + } } nodeJson.add("typeData", symbolJson); diff --git a/misc/diagram-util/src/main/java/org/ballerinalang/diagramutil/connector/models/connector/Type.java b/misc/diagram-util/src/main/java/org/ballerinalang/diagramutil/connector/models/connector/Type.java index ed74dd4437c7..40d63bce3912 100644 --- a/misc/diagram-util/src/main/java/org/ballerinalang/diagramutil/connector/models/connector/Type.java +++ b/misc/diagram-util/src/main/java/org/ballerinalang/diagramutil/connector/models/connector/Type.java @@ -33,6 +33,7 @@ import io.ballerina.compiler.api.symbols.Symbol; import io.ballerina.compiler.api.symbols.SymbolKind; import io.ballerina.compiler.api.symbols.TableTypeSymbol; +import io.ballerina.compiler.api.symbols.TypeDefinitionSymbol; import io.ballerina.compiler.api.symbols.TypeReferenceTypeSymbol; import io.ballerina.compiler.api.symbols.TypeSymbol; import io.ballerina.compiler.api.symbols.UnionTypeSymbol; @@ -359,6 +360,8 @@ public static Type fromSemanticSymbol(Symbol symbol) { typeName = typeName.substring(1, typeName.length() - 1); } type = new PrimitiveType(typeName); + } else if (symbol instanceof TypeDefinitionSymbol) { + type = fromSemanticSymbol(((TypeDefinitionSymbol) symbol).typeDescriptor()); } return type; } diff --git a/misc/ls-extensions/modules/bal-shell-service/src/test/java/io/ballerina/shell/service/test/getresult/QueryExpressionsTests.java b/misc/ls-extensions/modules/bal-shell-service/src/test/java/io/ballerina/shell/service/test/getresult/QueryExpressionsTests.java index 8a8530dfdc1a..2bc9e648a35d 100644 --- a/misc/ls-extensions/modules/bal-shell-service/src/test/java/io/ballerina/shell/service/test/getresult/QueryExpressionsTests.java +++ b/misc/ls-extensions/modules/bal-shell-service/src/test/java/io/ballerina/shell/service/test/getresult/QueryExpressionsTests.java @@ -38,7 +38,8 @@ public void testQueryExpressionsWithTables() throws ExecutionException, IOExcept runGetResultTest("query.tables.json"); } - @Test(description = "Test for querying with streams") + // We no longer has fixed names for internal narrowed types so we can't hardcode them + @Test(description = "Test for querying with streams", enabled = false) public void testQueryExpressionsWithStreams() throws ExecutionException, IOException, InterruptedException { runGetResultTest("query.streams.json"); } diff --git a/misc/ls-extensions/modules/bal-shell-service/src/test/resources/testcases/getResult/functional.programming.json b/misc/ls-extensions/modules/bal-shell-service/src/test/resources/testcases/getResult/functional.programming.json index b09f41f282c9..ba9053506774 100644 --- a/misc/ls-extensions/modules/bal-shell-service/src/test/resources/testcases/getResult/functional.programming.json +++ b/misc/ls-extensions/modules/bal-shell-service/src/test/resources/testcases/getResult/functional.programming.json @@ -39,9 +39,9 @@ "source": "f", "result": { "shellValue": { - "value": "function IntFilter", + "value": "function isolated function (int) returns (boolean)", "mimeType":"plain/text", - "type":"IntFilter" + "type":"isolated function (int) returns (boolean)" }, "errors":[], "diagnostics":[], diff --git a/misc/ls-extensions/modules/trigger-service/src/main/java/io/ballerina/trigger/BallerinaTriggerService.java b/misc/ls-extensions/modules/trigger-service/src/main/java/io/ballerina/trigger/BallerinaTriggerService.java index 81dce1c6b14d..bbd6b2a8f2a2 100644 --- a/misc/ls-extensions/modules/trigger-service/src/main/java/io/ballerina/trigger/BallerinaTriggerService.java +++ b/misc/ls-extensions/modules/trigger-service/src/main/java/io/ballerina/trigger/BallerinaTriggerService.java @@ -18,9 +18,11 @@ package io.ballerina.trigger; +import com.google.common.reflect.TypeToken; import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.stream.JsonReader; import io.ballerina.projects.Settings; import io.ballerina.trigger.entity.BallerinaTriggerListRequest; import io.ballerina.trigger.entity.BallerinaTriggerListResponse; @@ -34,6 +36,7 @@ import org.ballerinalang.langserver.commons.client.ExtendedLanguageClient; import org.ballerinalang.langserver.commons.service.spi.ExtendedLanguageServerService; import org.ballerinalang.langserver.commons.workspace.WorkspaceManager; +import org.ballerinalang.toml.exceptions.SettingsTomlException; import org.eclipse.lsp4j.MessageParams; import org.eclipse.lsp4j.MessageType; import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; @@ -42,6 +45,14 @@ import org.eclipse.lsp4j.services.LanguageServer; import org.wso2.ballerinalang.util.RepoUtils; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; @@ -59,6 +70,45 @@ @JsonSegment("ballerinaTrigger") public class BallerinaTriggerService implements ExtendedLanguageServerService { private LanguageClient languageClient; + private final Map inBuiltTriggers; + private static final Type MAP_TYPE = MapTypeToken.TYPE; + + + // Represents the in-built trigger basic information + public record InBuiltTrigger(String name, String orgName, String packageName, List keywords) { + public InBuiltTrigger(String name, String orgName, String packageName, List keywords) { + this.name = name; + this.orgName = orgName; + this.packageName = packageName; + // Make a defensive copy of the list to ensure immutability + this.keywords = keywords == null ? List.of() : new ArrayList<>(keywords); + } + + @Override + public List keywords() { + // Return an unmodifiable or defensive copy, as needed + return List.copyOf(keywords); + } + } + + public BallerinaTriggerService() { + InputStream propertiesStream = getClass().getClassLoader() + .getResourceAsStream("inbuilt-triggers/properties.json"); + Map triggers = Map.of(); + if (propertiesStream != null) { + try (JsonReader reader = new JsonReader(new InputStreamReader(propertiesStream, StandardCharsets.UTF_8))) { + triggers = new Gson().fromJson(reader, MAP_TYPE); + } catch (IOException e) { + // Ignore + } + } + this.inBuiltTriggers = triggers; + } + + // Static inner class to hold the type token + private static class MapTypeToken { + private static final Type TYPE = new TypeToken>() { }.getType(); + } @Override public void init(LanguageServer langServer, WorkspaceManager workspaceManager, @@ -71,20 +121,10 @@ public CompletableFuture triggers(BallerinaTrigger return CompletableFuture.supplyAsync(() -> { BallerinaTriggerListResponse triggersList = new BallerinaTriggerListResponse(); try { - Settings settings = RepoUtils.readSettings(); - CentralAPIClient client = new CentralAPIClient(RepoUtils.getRemoteRepoURL(), - initializeProxy(settings.getProxy()), settings.getProxy().username(), - settings.getProxy().password(), getAccessTokenOfCLI(settings), - settings.getCentral().getConnectTimeout(), - settings.getCentral().getReadTimeout(), settings.getCentral().getWriteTimeout(), - settings.getCentral().getCallTimeout(), settings.getCentral().getMaxRetries()); - JsonElement triggerSearchResult = client.getTriggers(request.getQueryMap(), - "any", RepoUtils.getBallerinaVersion()); - CentralTriggerListResult centralTriggerListResult = new Gson().fromJson( - triggerSearchResult.getAsString(), CentralTriggerListResult.class); + CentralTriggerListResult centralTriggerListResult = getCentralTriggerListResult(request); triggersList.setCentralTriggers(centralTriggerListResult.getTriggers()); return triggersList; - } catch (CentralClientException e) { + } catch (CentralClientException | SettingsTomlException e) { String msg = "Operation 'ballerinaTrigger/triggers' failed!"; this.languageClient.logMessage(new MessageParams(MessageType.Error, msg)); return triggersList; @@ -92,6 +132,16 @@ public CompletableFuture triggers(BallerinaTrigger }); } + @JsonRequest + public CompletableFuture triggersNew(BallerinaTriggerListRequest request) { + return CompletableFuture.supplyAsync(() -> { + JsonObject triggersList = new JsonObject(); + List inBuiltTriggers = getInBuiltTriggers(request); + triggersList.add("central", new Gson().toJsonTree(inBuiltTriggers)); + return triggersList; + }); + } + @JsonRequest public CompletableFuture trigger(BallerinaTriggerRequest request) { return CompletableFuture.supplyAsync(() -> { @@ -100,6 +150,44 @@ public CompletableFuture trigger(BallerinaTriggerRequest request) { }); } + @JsonRequest + public CompletableFuture triggerNew(BallerinaTriggerRequest request) { + return CompletableFuture.supplyAsync(() -> { + if (expectsTriggerByName(request)) { + return getTriggerByName(request).orElseGet(JsonObject::new); + } + if (request.getTriggerId() != null) { + Optional trigger = getInBuiltTriggerJsonById(request.getTriggerId()); + if (trigger.isPresent()) { + return trigger.get(); + } + } + return new JsonObject(); + }); + } + + private static boolean expectsTriggerByName(BallerinaTriggerRequest request) { + return request.getTriggerId() == null && request.getOrgName() != null && request.getPackageName() != null; + } + + private Optional getTriggerByName(BallerinaTriggerRequest request) { + return getInBuiltTriggerJson(request.getPackageName()); + } + + private static CentralTriggerListResult getCentralTriggerListResult(BallerinaTriggerListRequest triggerListRequest) + throws SettingsTomlException, CentralClientException { + Settings settings = RepoUtils.readSettings(); + CentralAPIClient client = new CentralAPIClient(RepoUtils.getRemoteRepoURL(), + initializeProxy(settings.getProxy()), settings.getProxy().username(), + settings.getProxy().password(), getAccessTokenOfCLI(settings), + settings.getCentral().getConnectTimeout(), + settings.getCentral().getReadTimeout(), settings.getCentral().getWriteTimeout(), + settings.getCentral().getCallTimeout(), settings.getCentral().getMaxRetries()); + JsonElement triggerSearchResult = client.getTriggers(triggerListRequest.getQueryMap(), + "any", RepoUtils.getBallerinaVersion()); + return new Gson().fromJson(triggerSearchResult.getAsString(), CentralTriggerListResult.class); + } + private Optional getTriggerFromCentral(BallerinaTriggerRequest request) { JsonObject trigger; try { @@ -131,4 +219,52 @@ public Class getRemoteInterface() { public String getName() { return Constants.CAPABILITY_NAME; } + + private List getInBuiltTriggers(BallerinaTriggerListRequest request) { + return inBuiltTriggers.values().stream() + .filter(inBuiltTrigger -> filterInBuiltTriggers(inBuiltTrigger, request)) + .map(inBuiltTrigger -> getInBuiltTriggerJson(inBuiltTrigger.name())) + .flatMap(Optional::stream) + .limit(request.getLimit() > 0 ? request.getLimit() : Long.MAX_VALUE) + .toList(); + } + + private boolean filterInBuiltTriggers(InBuiltTrigger inBuiltTrigger, BallerinaTriggerListRequest request) { + return (request.getOrganization() == null || request.getOrganization().equals(inBuiltTrigger.orgName())) && + (request.getPackageName() == null || request.getPackageName().equals(inBuiltTrigger.packageName())) && + (request.getKeyword() == null || inBuiltTrigger.keywords().stream() + .anyMatch(keyword -> keyword.equalsIgnoreCase(request.getKeyword()))) && + (request.getQuery() == null || inBuiltTrigger.keywords().stream() + .anyMatch(keyword -> keyword.contains(request.getQuery()))); + } + + private Optional getInBuiltTriggerJsonById(String triggerId) { + if (!inBuiltTriggers.containsKey(triggerId)) { + return Optional.empty(); + } + return getInBuiltTriggerJson(inBuiltTriggers.get(triggerId).name()); + } + + private Optional getInBuiltTriggerJson(String triggerName) { + if (inBuiltTriggers.values().stream() + .noneMatch(inBuiltTrigger -> inBuiltTrigger.name().equals(triggerName))) { + return Optional.empty(); + } + InputStream resourceStream = getClass().getClassLoader().getResourceAsStream( + String.format("inbuilt-triggers/%s.json", triggerName)); + if (resourceStream == null) { + String msg = String.format("Trigger info file not found for the trigger: %s", triggerName); + this.languageClient.logMessage(new MessageParams(MessageType.Error, msg)); + return Optional.empty(); + } + + try (JsonReader reader = new JsonReader(new InputStreamReader(resourceStream, StandardCharsets.UTF_8))) { + return Optional.of(new Gson().fromJson(reader, JsonObject.class)); + } catch (IOException e) { + String msg = String.format("Error occurred while reading the trigger info file for the trigger: %s", + triggerName); + this.languageClient.logMessage(new MessageParams(MessageType.Error, msg)); + return Optional.empty(); + } + } } diff --git a/misc/ls-extensions/modules/trigger-service/src/main/java/io/ballerina/trigger/entity/BallerinaTriggerListResponse.java b/misc/ls-extensions/modules/trigger-service/src/main/java/io/ballerina/trigger/entity/BallerinaTriggerListResponse.java index 4207542c2809..0d0fa6e6a084 100644 --- a/misc/ls-extensions/modules/trigger-service/src/main/java/io/ballerina/trigger/entity/BallerinaTriggerListResponse.java +++ b/misc/ls-extensions/modules/trigger-service/src/main/java/io/ballerina/trigger/entity/BallerinaTriggerListResponse.java @@ -50,6 +50,18 @@ public void setCentralTriggers(List central) { } } + public void addCentralTriggers(List central) { + if (central != null && !central.isEmpty()) { + this.central.addAll(central); + } + } + + public void addInBuiltTriggers(List inBuiltTriggers) { + if (inBuiltTriggers != null && !inBuiltTriggers.isEmpty()) { + this.central.addAll(inBuiltTriggers); + } + } + public List getLocalTriggers() { return local; } diff --git a/misc/ls-extensions/modules/trigger-service/src/main/java/module-info.java b/misc/ls-extensions/modules/trigger-service/src/main/java/module-info.java index 1eacddb74a37..62dc0be6b2d8 100644 --- a/misc/ls-extensions/modules/trigger-service/src/main/java/module-info.java +++ b/misc/ls-extensions/modules/trigger-service/src/main/java/module-info.java @@ -10,4 +10,5 @@ requires io.ballerina.language.server.commons; requires org.eclipse.lsp4j; requires org.eclipse.lsp4j.jsonrpc; + requires com.google.common; } diff --git a/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/asb.json b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/asb.json new file mode 100644 index 000000000000..a3b83d964495 --- /dev/null +++ b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/asb.json @@ -0,0 +1,402 @@ +{ + "id": 7, + "name": "ASB Service", + "type": "inbuilt", + "displayName": "ASB", + "documentation": "This ASB service can be attached to a ASB listener which listens to a given ASB topic/queue and triggers the service when a message is received. The service should have the `onMessage` remote function to handle the received message. Additionally, the service can have the `onError` remote method to handle errors that occur during the message processing.", + "listenerProtocol": "asb", + "displayAnnotation": { + "label": "ASB", + "iconPath": "docs/icon.png" + }, + "package": { + "id": 19946, + "organization": "ballerinax", + "name": "asb", + "version": "3.8.2", + "platform": "java17", + "languageSpecificationVersion": "2023R1", + "isDeprecated": false, + "deprecateMessage": "", + "URL": "/ballerinax/asb/3.8.2", + "balaVersion": "2.0.0", + "balaURL": "https://fileserver.central.ballerina.io/2.0/ballerinax/asb/3.8.2/ballerinax-asb-java17-3.8.2.bala?Expires=1730862517&Signature=N~sZ4dOOu9vC74hlcpKXySnm1U~flnHxRvW4O~XBQxpea0fbQCrRZvUHqMcxG218O2FM6yZzu9dm2-NaV8y6rQz~grHh88yRdnJcnf0RVloWakL8MtCkmz88AxgSihK3uJLloJBrV7hsHLsIm7K~QyARVyS9KXs5hGneCGz5FM7WrVsi9C3gk2gAYIk2PBnYk~hk2dshHgciZAwlJKf95i66hpN9RjY1ZQa49zsrxT-8y-HtghMdQZgYNaSwPZktVv9VCGrVKbLJUxC0R883PVXyBXq6MDut17TlNWORVqyGPkwZnDJMeUmjeIS-tOXNuXVZpUsEt87ZDJWCwEjUvg__&Key-Pair-Id=K27IQ7NPTKLKDU", + "digest": "sha-256=847f1c1ec1de7ce81891be8845772ae0e64e8cc7d5b4b0d302a4565b54cda4b0", + "summary": "The [Azure Service Bus](https://docs.microsoft.com/en-us/azure/service-bus-messaging/) is a fully managed enterprise message broker with message queues and publish-subscribe topics. It", + "readme": "## Overview\n\nThe [Azure Service Bus](https:\/\/docs.microsoft.com\/en-us\/azure\/service-bus-messaging\/) is a fully managed enterprise message broker with message queues and publish-subscribe topics. It\nprovides the capability to send and receive messages from Service Bus queues, topics, and subscriptions. The Azure\nService Bus handles messages that include data representing any kind of information, including structured data encoded\nwith common formats such as the following ones: JSON, XML, and Plain Text.\n\nThe [Ballerina](https:\/\/ballerina.io\/) connector for Azure Service Bus allows you to connect to\nan [Azure Service Bus](https:\/\/docs.microsoft.com\/en-us\/azure\/service-bus-messaging\/) via the Ballerina language.\n\nThis connector supports the following operations:\n- Manage (Get\/Create\/Update\/Delete\/list) a queue, topic, subscription or rule.\n- Send messages to a queue, topic, or subscription.\n- Receive messages from a queue, topic, or subscription.\n\nThe Ballerina Azure Service Bus module utilizes Microsoft's [Azure Service Bus JAVA SDK 7.13.1](https:\/\/learn.microsoft.com\/en-us\/java\/api\/overview\/azure\/service-bus?view=azure-java-stable#libraries-for-data-access). \n\n## Setup guide\n\nBefore using this connector in your Ballerina application, complete the following:\n\n### Create a namespace in the Azure portal\n\nTo begin using Service Bus messaging in Azure, you must first create a namespace with a name that is unique across Azure. A namespace provides a scoping container for Service Bus resources within your application.\n\nTo create a namespace:\n\n#### Step 1: Sign in to the [Azure portal](https:\/\/portal.azure.com\/)\nIf you don't have an Azure subscription, [sign up for a free Azure account](https:\/\/azure.microsoft.com\/free\/).\n\n#### Step 2: Go to the Create Resource Service Bus menu\n\nIn the left navigation pane of the portal, select **All services**, select **Integration** from the list of categories, hover the mouse over **Service Bus**, and then select **Create** on the Service Bus tile.\n\n![Create Resource Service Bus Menu](https:\/\/raw.githubusercontent.com\/ballerina-platform\/module-ballerinax-azure-service-bus\/main\/ballerina\/resources\/create-resource-service-bus-menu.png)\n\n#### Step 3: In the Basics tag of the Create namespace page, follow these steps:\n\n1. For **Subscription**, choose an Azure subscription in which to create the namespace.\n\n2. For **Resource group**, choose an existing resource group in which the namespace will live, or create a new one.\n\n3. Enter a **name for the namespace**. The namespace name should adhere to the following naming conventions:\n\n* The name must be unique across Azure. The system immediately checks to see if the name is available.\n* The name length is at least 6 and at most 50 characters.\n* The name can contain only letters, numbers, and hyphens ?-?.\n* The name must start with a letter and end with a letter or number.\n* The name doesn't end with ?-sb? or ?-mgmt?.\n\n4. For **Location**, choose the region in which your namespace should be hosted.\n\n5. For **Pricing tier**, select the pricing tier (Basic, Standard, or Premium) for the namespace. For this quickstart, select Standard.\n\n> **Notice:** If you want to use topics and subscriptions, choose either Standard or Premium. Topics\/subscriptions aren't supported in the Basic pricing tier. If you selected the Premium pricing tier, specify the number of messaging units. The premium tier provides resource isolation at the CPU and memory level so that each workload runs in isolation. This resource container is called a messaging unit. A premium namespace has at least one messaging unit. You can select 1, 2, 4, 8, or 16 messaging units for each Service Bus Premium namespace. For more information, see [Service Bus Premium Messaging](https:\/\/learn.microsoft.com\/en-us\/azure\/service-bus-messaging\/service-bus-premium-messaging).`\n\n6. Select **Review + create** at the bottom of the page.\n\n![Create Namespace](https:\/\/raw.githubusercontent.com\/ballerina-platform\/module-ballerinax-azure-service-bus\/main\/ballerina\/resources\/create-namespace.png)\n\n7. On the **Review + create** page, review settings, and select **Create**.\n\n\n### Obtain tokens for authentication\n\nTo send and receive messages from a Service Bus queue or topic, clients must use a token that is signed by a shared access key, which is part of a shared access policy. A shared access policy defines a set of permissions that can be assigned to one or more Service Bus entities (queues, topics, event hubs, or relays). A shared access policy can be assigned to more than one entity, and a single entity can have more than one shared access policy assigned to it.\n\nTo obtain a token following steps should be followed:\n\n1. In the left navigation pane of the portal, select *All services*, select *Integration* from the list of categories, hover the mouse over *Service Bus*, and then select your namespace.\n\n2. In the left navigation pane of the namespace page, select *Shared access policies*.\n\n3. Click on the *RootManageSharedAccessKey* policy.\n\n4. Copy the *Primary Connection String* value and save it in a secure location. This is the connection string that you use to authenticate with the Service Bus service.\n\n![Connection String](https:\/\/raw.githubusercontent.com\/ballerina-platform\/module-ballerinax-azure-service-bus\/main\/ballerina\/resources\/connection-string.png)\n\n\n## Quickstart\nTo use the ASB connector in your Ballerina application, modify the .bal file as follows:\n\n### Step 1: Import connector\n\nImport the `ballerinax\/asb` module into the Ballerina project.\n\n```ballerina\nimport ballerinax\/asb;\n```\n\n### Step 2: Create a new connector instance\n\n#### Initialize an Admin Client\n\nThis can be done by providing a connection string.\n\n````ballerina\n configurable string connectionString = ?;\n asb:AdminClient admin = check new (connectionString);\n````\n\n#### Initialize a Message Sender client\n\nThis can be done by providing a connection string with a queue or topic name.\n\n```ballerina\n configurable string connectionString = ?;\n\n ASBServiceSenderConfig senderConfig = {\n connectionString: connectionString,\n entityType: QUEUE,\n topicOrQueueName: \"myQueue\"\n };\n asb:MessageSender sender = check new (senderConfig);\n```\n\n#### Initialize a Message Receiver client\n\nThis can be done by providing a connection string with a queue name, topic name, or subscription path. \n\n> Here, the Receive mode is optional. (Default: PEEKLOCK)\n\n```ballerina\n configurable string connectionString = ?;\n\n ASBServiceReceiverConfig receiverConfig = {\n connectionString: connectionString,\n entityConfig: {\n queueName: \"myQueue\"\n },\n receiveMode: PEEK_LOCK\n };\n asb:MessageReceiver receiver = check new (receiverConfig);\n```\n\n#### Initialize a message listener\n\nThis can be done by providing a connection string with a queue name, topic name, or subscription path.\n\n> Here, the Receive mode is optional. (Default: PEEKLOCK)\n\n```ballerina\n configurable string connectionString = ?;\n\n listener asb:Listener asbListener = check new (\n connectionString = connectionString,\n entityConfig = {\n queueName: \"myQueue\"\n }\n );\n```\n\n### Step 3: Invoke connector operation\n\nNow you can use the remote operations available within the connector,\n\n**Create a queue in the Azure Service Bus**\n\n ```ballerina\npublic function main() returns error? {\n asb:AdminClient admin = check new (adminConfig);\n\n check admin->createQueue(\"myQueue\");\n\n check admin->close();\n}\n ```\n\n**Send a message to the Azure Service Bus**\n\n```ballerina\npublic function main() returns error? {\n asb:MessageSender queueSender = check new (senderConfig);\n\n string stringContent = \"This is My Message Body\"; \n byte[] byteContent = stringContent.toBytes();\n int timeToLive = 60; \/\/ In seconds\n\n asb:ApplicationProperties applicationProperties = {\n properties: {a: \"propertyValue1\", b: \"propertyValue2\"}\n };\n\n asb:Message message = {\n body: byteContent,\n contentType: asb:TEXT,\n timeToLive: timeToLive,\n applicationProperties: applicationProperties\n };\n\n check queueSender->send(message);\n\n check queueSender->close();\n}\n```\n\n**Receive a message from the Azure Service Bus**\n\n```ballerina\npublic function main() returns error? {\n asb:MessageReceiver queueReceiver = check new (receiverConfig);\n\n int serverWaitTime = 60; \/\/ In seconds\n\n asb:Message|asb:Error? messageReceived = queueReceiver->receive(serverWaitTime);\n\n if (messageReceived is asb:Message) {\n log:printInfo(\"Reading Received Message : \" + messageReceived.toString());\n } else if (messageReceived is ()) {\n log:printError(\"No message in the queue.\");\n } else {\n log:printError(\"Receiving message via Asb receiver connection failed.\");\n }\n\n check queueReceiver->close();\n}\n```\n\n**Receive messages from Azure service bus using `asb:Service`**\n\n```ballerina\nservice asb:Service on asbListener {\n\n isolated remote function onMessage(asb:Message message) returns error? {\n log:printInfo(\"Reading Received Message : \" + message.toString());\n }\n\n isolated remote function onError(asb:MessageRetrievalError 'error) returns error? {\n log:printError(\"Error occurred while receiving messages from ASB\", 'error);\n }\n}\n```\n\n### Step 4: Run the Ballerina application\n\n```bash\nbal run\n```\n\n## Examples\n\nThere are two sets of examples demonstrating the use of the Ballerina Azure Service Bus (ASB) Connector.\n\n- **[Management Related Examples](https:\/\/github.com\/ballerina-platform\/module-ballerinax-azure-service-bus\/tree\/main\/examples\/admin)**: These examples cover operations related to managing the Service Bus, such as managing queues, topics, subscriptions, and rules. \n\n- **[Message Sending and Receiving Related Examples](https:\/\/github.com\/ballerina-platform\/module-ballerinax-azure-service-bus\/tree\/main\/examples\/sender_reciever)**: This set includes examples for sending to and receiving messages from queues, topics, and subscriptions in the Service Bus.", + "template": false, + "licenses": [ + "Apache-2.0" + ], + "authors": [ + "Ballerina" + ], + "sourceCodeLocation": "https://github.com/ballerina-platform/module-ballerinax-azure-service-bus", + "keywords": [ + "IT Operations/Message Brokers", + "Cost/Paid", + "Vendor/Microsoft" + ], + "ballerinaVersion": "2201.8.0", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerinax_asb_3.8.2.png", + "ownerUUID": "b5a9e54d-8ade-47a1-8abc-6bc46e89069d", + "createdDate": 1727786875000, + "pullCount": 6, + "visibility": "public", + "modules": [ + { + "packageURL": "/ballerinax/asb/3.8.2", + "apiDocURL": "https://lib.ballerina.io/ballerinax/asb/3.8.2", + "name": "asb", + "summary": "The [Azure Service Bus](https://docs.microsoft.com/en-us/azure/service-bus-messaging/) is a fully managed enterprise message broker with message queues and publish-subscribe topics. It", + "readme": "## Overview\n\nThe [Azure Service Bus](https:\/\/docs.microsoft.com\/en-us\/azure\/service-bus-messaging\/) is a fully managed enterprise message broker with message queues and publish-subscribe topics. It\nprovides the capability to send and receive messages from Service Bus queues, topics, and subscriptions. The Azure\nService Bus handles messages that include data representing any kind of information, including structured data encoded\nwith common formats such as the following ones: JSON, XML, and Plain Text.\n\nThe [Ballerina](https:\/\/ballerina.io\/) connector for Azure Service Bus allows you to connect to\nan [Azure Service Bus](https:\/\/docs.microsoft.com\/en-us\/azure\/service-bus-messaging\/) via the Ballerina language.\n\nThis connector supports the following operations:\n- Manage (Get\/Create\/Update\/Delete\/list) a queue, topic, subscription or rule.\n- Send messages to a queue, topic, or subscription.\n- Receive messages from a queue, topic, or subscription.\n\nThe Ballerina Azure Service Bus module utilizes Microsoft's [Azure Service Bus JAVA SDK 7.13.1](https:\/\/learn.microsoft.com\/en-us\/java\/api\/overview\/azure\/service-bus?view=azure-java-stable#libraries-for-data-access). \n\n## Setup guide\n\nBefore using this connector in your Ballerina application, complete the following:\n\n### Create a namespace in the Azure portal\n\nTo begin using Service Bus messaging in Azure, you must first create a namespace with a name that is unique across Azure. A namespace provides a scoping container for Service Bus resources within your application.\n\nTo create a namespace:\n\n#### Step 1: Sign in to the [Azure portal](https:\/\/portal.azure.com\/)\nIf you don't have an Azure subscription, [sign up for a free Azure account](https:\/\/azure.microsoft.com\/free\/).\n\n#### Step 2: Go to the Create Resource Service Bus menu\n\nIn the left navigation pane of the portal, select **All services**, select **Integration** from the list of categories, hover the mouse over **Service Bus**, and then select **Create** on the Service Bus tile.\n\n![Create Resource Service Bus Menu](https:\/\/raw.githubusercontent.com\/ballerina-platform\/module-ballerinax-azure-service-bus\/main\/ballerina\/resources\/create-resource-service-bus-menu.png)\n\n#### Step 3: In the Basics tag of the Create namespace page, follow these steps:\n\n1. For **Subscription**, choose an Azure subscription in which to create the namespace.\n\n2. For **Resource group**, choose an existing resource group in which the namespace will live, or create a new one.\n\n3. Enter a **name for the namespace**. The namespace name should adhere to the following naming conventions:\n\n* The name must be unique across Azure. The system immediately checks to see if the name is available.\n* The name length is at least 6 and at most 50 characters.\n* The name can contain only letters, numbers, and hyphens ?-?.\n* The name must start with a letter and end with a letter or number.\n* The name doesn't end with ?-sb? or ?-mgmt?.\n\n4. For **Location**, choose the region in which your namespace should be hosted.\n\n5. For **Pricing tier**, select the pricing tier (Basic, Standard, or Premium) for the namespace. For this quickstart, select Standard.\n\n> **Notice:** If you want to use topics and subscriptions, choose either Standard or Premium. Topics\/subscriptions aren't supported in the Basic pricing tier. If you selected the Premium pricing tier, specify the number of messaging units. The premium tier provides resource isolation at the CPU and memory level so that each workload runs in isolation. This resource container is called a messaging unit. A premium namespace has at least one messaging unit. You can select 1, 2, 4, 8, or 16 messaging units for each Service Bus Premium namespace. For more information, see [Service Bus Premium Messaging](https:\/\/learn.microsoft.com\/en-us\/azure\/service-bus-messaging\/service-bus-premium-messaging).`\n\n6. Select **Review + create** at the bottom of the page.\n\n![Create Namespace](https:\/\/raw.githubusercontent.com\/ballerina-platform\/module-ballerinax-azure-service-bus\/main\/ballerina\/resources\/create-namespace.png)\n\n7. On the **Review + create** page, review settings, and select **Create**.\n\n\n### Obtain tokens for authentication\n\nTo send and receive messages from a Service Bus queue or topic, clients must use a token that is signed by a shared access key, which is part of a shared access policy. A shared access policy defines a set of permissions that can be assigned to one or more Service Bus entities (queues, topics, event hubs, or relays). A shared access policy can be assigned to more than one entity, and a single entity can have more than one shared access policy assigned to it.\n\nTo obtain a token following steps should be followed:\n\n1. In the left navigation pane of the portal, select *All services*, select *Integration* from the list of categories, hover the mouse over *Service Bus*, and then select your namespace.\n\n2. In the left navigation pane of the namespace page, select *Shared access policies*.\n\n3. Click on the *RootManageSharedAccessKey* policy.\n\n4. Copy the *Primary Connection String* value and save it in a secure location. This is the connection string that you use to authenticate with the Service Bus service.\n\n![Connection String](https:\/\/raw.githubusercontent.com\/ballerina-platform\/module-ballerinax-azure-service-bus\/main\/ballerina\/resources\/connection-string.png)\n\n\n## Quickstart\nTo use the ASB connector in your Ballerina application, modify the .bal file as follows:\n\n### Step 1: Import connector\n\nImport the `ballerinax\/asb` module into the Ballerina project.\n\n```ballerina\nimport ballerinax\/asb;\n```\n\n### Step 2: Create a new connector instance\n\n#### Initialize an Admin Client\n\nThis can be done by providing a connection string.\n\n````ballerina\n configurable string connectionString = ?;\n asb:AdminClient admin = check new (connectionString);\n````\n\n#### Initialize a Message Sender client\n\nThis can be done by providing a connection string with a queue or topic name.\n\n```ballerina\n configurable string connectionString = ?;\n\n ASBServiceSenderConfig senderConfig = {\n connectionString: connectionString,\n entityType: QUEUE,\n topicOrQueueName: \"myQueue\"\n };\n asb:MessageSender sender = check new (senderConfig);\n```\n\n#### Initialize a Message Receiver client\n\nThis can be done by providing a connection string with a queue name, topic name, or subscription path. \n\n> Here, the Receive mode is optional. (Default: PEEKLOCK)\n\n```ballerina\n configurable string connectionString = ?;\n\n ASBServiceReceiverConfig receiverConfig = {\n connectionString: connectionString,\n entityConfig: {\n queueName: \"myQueue\"\n },\n receiveMode: PEEK_LOCK\n };\n asb:MessageReceiver receiver = check new (receiverConfig);\n```\n\n#### Initialize a message listener\n\nThis can be done by providing a connection string with a queue name, topic name, or subscription path.\n\n> Here, the Receive mode is optional. (Default: PEEKLOCK)\n\n```ballerina\n configurable string connectionString = ?;\n\n listener asb:Listener asbListener = check new (\n connectionString = connectionString,\n entityConfig = {\n queueName: \"myQueue\"\n }\n );\n```\n\n### Step 3: Invoke connector operation\n\nNow you can use the remote operations available within the connector,\n\n**Create a queue in the Azure Service Bus**\n\n ```ballerina\npublic function main() returns error? {\n asb:AdminClient admin = check new (adminConfig);\n\n check admin->createQueue(\"myQueue\");\n\n check admin->close();\n}\n ```\n\n**Send a message to the Azure Service Bus**\n\n```ballerina\npublic function main() returns error? {\n asb:MessageSender queueSender = check new (senderConfig);\n\n string stringContent = \"This is My Message Body\"; \n byte[] byteContent = stringContent.toBytes();\n int timeToLive = 60; \/\/ In seconds\n\n asb:ApplicationProperties applicationProperties = {\n properties: {a: \"propertyValue1\", b: \"propertyValue2\"}\n };\n\n asb:Message message = {\n body: byteContent,\n contentType: asb:TEXT,\n timeToLive: timeToLive,\n applicationProperties: applicationProperties\n };\n\n check queueSender->send(message);\n\n check queueSender->close();\n}\n```\n\n**Receive a message from the Azure Service Bus**\n\n```ballerina\npublic function main() returns error? {\n asb:MessageReceiver queueReceiver = check new (receiverConfig);\n\n int serverWaitTime = 60; \/\/ In seconds\n\n asb:Message|asb:Error? messageReceived = queueReceiver->receive(serverWaitTime);\n\n if (messageReceived is asb:Message) {\n log:printInfo(\"Reading Received Message : \" + messageReceived.toString());\n } else if (messageReceived is ()) {\n log:printError(\"No message in the queue.\");\n } else {\n log:printError(\"Receiving message via Asb receiver connection failed.\");\n }\n\n check queueReceiver->close();\n}\n```\n\n**Receive messages from Azure service bus using `asb:Service`**\n\n```ballerina\nservice asb:Service on asbListener {\n\n isolated remote function onMessage(asb:Message message) returns error? {\n log:printInfo(\"Reading Received Message : \" + message.toString());\n }\n\n isolated remote function onError(asb:MessageRetrievalError 'error) returns error? {\n log:printError(\"Error occurred while receiving messages from ASB\", 'error);\n }\n}\n```\n\n### Step 4: Run the Ballerina application\n\n```bash\nbal run\n```\n\n## Examples\n\nThere are two sets of examples demonstrating the use of the Ballerina Azure Service Bus (ASB) Connector.\n\n- **[Management Related Examples](https:\/\/github.com\/ballerina-platform\/module-ballerinax-azure-service-bus\/tree\/main\/examples\/admin)**: These examples cover operations related to managing the Service Bus, such as managing queues, topics, subscriptions, and rules. \n\n- **[Message Sending and Receiving Related Examples](https:\/\/github.com\/ballerina-platform\/module-ballerinax-azure-service-bus\/tree\/main\/examples\/sender_reciever)**: This set includes examples for sending to and receiving messages from queues, topics, and subscriptions in the Service Bus." + } + ], + "balToolId": "", + "graalvmCompatible": "Yes" + }, + "serviceTypes": [ + { + "name": "ASB", + "description": "ASB Service", + "enabled": true, + "functions": [ + { + "name": "onMessage", + "documentation": "The `onMessage` remote method will be triggered when a message is received for the ASB topic/queue.", + "optional": false, + "qualifiers": [ + "remote" + ], + "enabled": true, + "parameters": [ + { + "name": "message", + "typeName": "asb:Message", + "optional": false, + "type": [ + "asb:Message" + ], + "typeInfo": { + "name": "Message", + "orgName": "ballerinax", + "moduleName": "asb", + "version": "3.8.2" + }, + "documentation": "The message received for ASB topic/queue.", + "enabled": true, + "value": "asb:Message" + }, + { + "name": "caller", + "typeName": "asb:Caller", + "type": [ + "asb:Caller" + ], + "typeInfo": { + "name": "Caller", + "orgName": "ballerinax", + "moduleName": "asb", + "version": "3.8.2" + }, + "optional": true, + "documentation": "The ASB caller object which can be used to mark messages as complete, abandon, deadLetter, or defer.", + "enabled": false, + "value": "asb:Caller" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "name": "onError", + "documentation": "The `onError` remote method will be triggered when an error occurs during the message processing.", + "optional": true, + "enabled": false, + "qualifiers": [ + "remote" + ], + "parameters": [ + { + "name": "err", + "typeName": "asb:MessageRetrievalError", + "type": [ + "asb:MessageRetrievalError" + ], + "optional": false, + "typeInfo": { + "name": "MessageRetrievalError", + "orgName": "ballerinax", + "moduleName": "asb", + "version": "3.8.2" + }, + "documentation": "The error occurred during the message processing.", + "enabled": true, + "value": "asb:MessageRetrievalError" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + } + ] + } + ], + "listener": { + "metadata": { + "label": "ASB Listener", + "description": "The ASB listener to which the ASB service should be attached." + }, + "valueType": "OBJECT", + "valueTypeConstraint": "asb:Listener", + "value": "", + "enabled": true, + "optional": false, + "editable": true, + "properties": { + "config": { + "metadata": { + "label": "ASB Listener Configuration", + "description": "The configuration of the ASB listener." + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "asb:ListenerConfiguration", + "placeholder": "", + "editable": true, + "enabled": true, + "optional": false, + "advanced": true, + "fields": { + "connectionString": { + "metadata": { + "label": "Connection String", + "description": "Service bus connection string with Shared Access Signatures." + }, + "valueType": "STRING", + "valueTypeConstraint": "string", + "placeholder": "", + "editable": true, + "enabled": true, + "optional": false, + "advanced": false + }, + "entityConfig": { + "metadata": { + "label": "Entity Configuration", + "description": "This field holds the configuration details of either a topic or a queue. The type of the entity is determined by the entityType field. The actual configuration details are stored in either a TopicSubsConfig or a QueueConfig record" + }, + "valueType": "UNION", + "valueTypeConstraint": "asb:TopicSubsConfig|asb:QueueConfig", + "placeholder": "", + "editable": true, + "enabled": true, + "optional": false, + "advanced": true, + "unionTypes": [ + { + "metadata": { + "label": "Topic Configuration", + "description": "The configuration details of a topic." + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "asb:TopicSubsConfig", + "placeholder": "", + "editable": true, + "enabled": true, + "optional": false, + "advanced": true, + "fields": { + "topicName": { + "metadata": { + "label": "Topic Name", + "description": "The name of the topic." + }, + "valueType": "STRING", + "valueTypeConstraint": "string", + "placeholder": "", + "editable": true, + "enabled": true, + "optional": false, + "advanced": false + }, + "subscriptionName": { + "metadata": { + "label": "Subscription Name", + "description": "A string field that holds the name of the topic." + }, + "valueType": "STRING", + "valueTypeConstraint": "string", + "placeholder": "", + "editable": true, + "enabled": true, + "optional": false, + "advanced": false + } + } + }, + { + "metadata": { + "label": "Queue Configuration", + "description": "The configuration details of a queue." + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "asb:QueueConfig", + "placeholder": "", + "editable": true, + "enabled": true, + "optional": false, + "advanced": true, + "fields": { + "queueName": { + "metadata": { + "label": "Queue Name", + "description": "A string field that holds the name of the queue." + }, + "valueType": "STRING", + "valueTypeConstraint": "string", + "placeholder": "", + "editable": true, + "enabled": true, + "optional": false, + "advanced": false + } + } + } + ] + }, + "receiveMode": { + "metadata": { + "label": "Receive Mode", + "description": "This field holds the receive modes(RECEIVE_AND_DELETE/PEEK_LOCK) for the connection. The receive mode determines how messages are retrieved from the entity" + }, + "valueType": "ENUM", + "valueTypeConstraint": "asb:ReceiveMode", + "placeholder": "asb:PEEK_LOCK", + "editable": true, + "enabled": true, + "optional": true, + "advanced": true, + "enum": [ + "asb:PEEK_LOCK", + "asb:RECEIVE_AND_DELETE" + ] + }, + "maxAutoLockRenewDuration": { + "metadata": { + "label": "Max Auto Lock Renew Duration", + "description": "Max lock renewal duration under PEEK_LOCK mode in seconds. Setting to 0 disables auto-renewal. For RECEIVE_AND_DELETE mode, auto-renewal is disabled." + }, + "valueType": "INT", + "valueTypeConstraint": "", + "placeholder": 300, + "editable": true, + "enabled": true, + "optional": true, + "advanced": false + }, + "amqpRetryOptions": { + "metadata": { + "label": "AMQP Retry Options", + "description": "The AMQP retry options." + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "asb:AmqpRetryOptions", + "placeholder": "", + "editable": true, + "enabled": true, + "optional": true, + "advanced": true, + "fields": { + "maxRetries": { + "metadata": { + "label": "Max Retries", + "description": "Maximum number of retry attempts." + }, + "valueType": "INT", + "valueTypeConstraint": "int", + "placeholder": 3, + "editable": true, + "enabled": true, + "optional": true, + "advanced": false + }, + "delay": { + "metadata": { + "label": "Delay", + "description": "Delay between retry attempts in seconds." + }, + "valueType": "DECIMAL", + "valueTypeConstraint": "decimal", + "placeholder": 10, + "editable": true, + "enabled": true, + "optional": true, + "advanced": false + }, + "maxDelay": { + "metadata": { + "label": "Max Delay", + "description": "Maximum permissible delay between retry attempts in seconds." + }, + "valueType": "DECIMAL", + "valueTypeConstraint": "decimal", + "placeholder": 60, + "editable": true, + "enabled": true, + "optional": true, + "advanced": false + }, + "tryTimeout": { + "metadata": { + "label": "Try Timeout", + "description": "Maximum duration to wait for completion of a single attempt in seconds." + }, + "valueType": "DECIMAL", + "valueTypeConstraint": "decimal", + "placeholder": 60, + "editable": true, + "enabled": true, + "optional": true, + "advanced": false + }, + "retryMode": { + "metadata": { + "label": "Retry Mode", + "description": "The retry mode." + }, + "valueType": "ENUM", + "valueTypeConstraint": "asb:AmqpRetryMode", + "placeholder": "asb:FIXED", + "editable": true, + "enabled": true, + "optional": true, + "advanced": false, + "enum": [ + "asb:EXPONENTIAL", + "asb:FIXED" + ] + } + } + } + } + } + } + } +} diff --git a/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/ftp.json b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/ftp.json new file mode 100644 index 000000000000..e270ee683a2a --- /dev/null +++ b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/ftp.json @@ -0,0 +1,358 @@ +{ + "id": 6, + "name": "FTP Service", + "type": "inbuilt", + "displayName": "FTP", + "documentation": "The FTP service can be attached to a FTP listener which listens to file changes and trigger the service when a file change event occurs. The FTP service should implement the `onFileChange` function which will be triggered when a file change event occurs.", + "moduleName": "ftp", + "listenerProtocol": "ftp", + "displayAnnotation": { + "label": "FTP", + "iconPath": "docs/icon.png" + }, + "package": { + "id": 15465, + "organization": "ballerina", + "name": "ftp", + "version": "2.11.0", + "platform": "java17", + "languageSpecificationVersion": "2024R1", + "isDeprecated": false, + "deprecateMessage": "", + "URL": "/ballerina/ftp/2.11.0", + "balaVersion": "2.0.0", + "balaURL": "https://fileserver.central.ballerina.io/2.0/ballerina/ftp/2.11.0/ballerina-ftp-java17-2.11.0.bala?Expires=1729737633&Signature=Vt6WENHWLGFqM-qeVxaq1WFXKm42DI5EmrbMPXRjF1eK-8cRUgYOLP4K-sJMmw2l0RN8lXk-TMdygUPTp-PBnI57ATHA1C7lrO3BI68HSlcL5c6F-~S6bvEC~Vo66lLKlOwCj9ieXiWVP9zAr-ocDiLMiWXbhST5TS4fQ-Qzqda7-tdvCcfYZcf34CpBg5JH3Y5-VtqjPF54xKxPaYNx22XdwAzQlNEcWL~mGRTR~cnb2iL9FLUi0UWplOfIeuCbYPEUb4N78LqOgAacq5Et03SqJvx7zYGb~hueWa7Y9rY-KRliFLLJ~lgwAD7BOa7kweSKaJpQ8obqZZ3GWPckkw__&Key-Pair-Id=K27IQ7NPTKLKDU", + "digest": "sha-256=ae9400754d769c92b58a0bb94ac468761701a68d5f5589e15de55551984efd22", + "summary": "This package provides an FTP/SFTP client, and an FTP/SFTP server listener implementation to facilitate an FTP/SFTP connection connected to a remote location.", + "readme": "## Package overview\n\nThis package provides an FTP\/SFTP client, and an FTP\/SFTP server listener implementation to facilitate an FTP\/SFTP connection connected to a remote location.\n\n### FTP client\n\nThe `ftp:Client` connects to an FTP server and performs various operations on the files. Currently, it supports the\ngeneric FTP operations; `get`, `delete`, `put`, `append`, `mkdir`, `rmdir`, `isDirectory`, `rename`, `size`, and\n`list`.\n\nAn FTP client is defined using the `protocol` and `host` parameters and optionally, the `port` and\n`auth`. Authentication configuration can be configured using the `auth` parameter for Basic Auth and\nprivate key.\n\nAn authentication-related configuration can be given to the FTP client with the `auth` configuration.\n\n##### Create a client\n\nThe following code creates an FTP client and performs the I\/O operations, which connect to the FTP server with Basic Auth.\n```ballerina\n\/\/ Define the FTP client configuration.\nftp:ClientConfiguration ftpConfig = {\n protocol: ftp:FTP,\n host: \"\",\n port: ,\n auth: {\n credentials: {\n username: \"\",\n password: \"\"\n }\n }\n};\n\n\/\/ Create the FTP client.\nftp:Client|ftp:Error ftpClient = new(ftpConfig);\n```\n\n##### Create a directory\n\nThe following code creates a directory in the remote FTP server.\n\n```ballerina\nftp:Error? mkdirResponse = ftpClient->mkdir(\"\");\n```\n\n##### Upload a file to a remote server\n\nThe following code uploads a file to a remote FTP server.\n\n```ballerina\nstream fileByteStream\n = check io:fileReadBlocksAsStream(putFilePath, );\nftp:Error? putResponse = ftpClient->put(\"\", fileByteStream);\n```\n\n##### Compress and upload a file to a remote server\n\nThe following code compresses and uploads a file to a remote FTP server.\n\n```ballerina\n\/\/ Set the optional boolean flag as 'true' to compress before uploading\nstream fileByteStream\n = check io:fileReadBlocksAsStream(putFilePath, );\nftp:Error? compressedPutResponse = ftpClient->put(\"\",\n fileByteStream, compressionType=ZIP);\n```\n\n##### Get the size of a remote file\n\nThe following code gets the size of a file in a remote FTP server.\n\n```ballerina\nint|ftp:Error sizeResponse = ftpClient->size(\"\");\n```\n\n##### Read the content of a remote file\n\nThe following code reads the content of a file in a remote FTP server.\n\n```ballerina\nstream|Error str = clientEP -> get(\"\");\nif (str is stream) {\n record {|byte[] value;|}|io:Error? arr1 = str.next();\n if (arr1 is record {|byte[] value;|}) {\n string fileContent = check strings:fromBytes(arr1.value);\n \/\/ `fileContent` is the `string` value of first byte array\n record {|byte[] value;|}|io:Error? arr2 = str.next();\n \/\/ Similarly following content chunks can be iteratively read with `next` method.\n \/\/ Final chunk will contain the terminal value which is `()`.\n }\n io:Error? closeResult = str.close();\n}\n```\n\n##### Rename\/move a remote file\n\nThe following code renames or moves a file to another location in the same remote FTP server.\n\n```ballerina\nftp:Error? renameResponse = ftpClient->rename(\"\",\n \"\");\n```\n\n##### Delete a remote file\n\nThe following code deletes a remote file in a remote FTP server.\n\n```ballerina\nftp:Error? deleteResponse = ftpClient->delete(\"\");\n```\n\n##### Remove a directory from a remote server\n\nThe following code removes a directory in a remote FTP server.\n\n```ballerina\nftp:Error? rmdirResponse = ftpClient->rmdir(\"\");\n```\n\n### FTP listener\n\nThe `ftp:Listener` is used to listen to a remote FTP location and trigger a `WatchEvent` type of event when new\nfiles are added to or deleted from the directory. The `fileResource` function is invoked when a new file is added\nand\/or deleted.\n\nAn FTP listener is defined using the mandatory `protocol`, `host`, and `path` parameters. The authentication\nconfiguration can be done using the `auth` parameter and the polling interval can be configured using the `pollingInterval` parameter.\nThe default polling interval is 60 seconds.\n\nThe `fileNamePattern` parameter can be used to define the type of files the FTP listener will listen to.\nFor instance, if the listener gets invoked for text files, the value `(.*).txt` can be given for the config.\n\nAn authentication-related configuration can be given to the FTP listener with the `auth` configuration.\n\n##### Create a listener\n\nThe FTP Listener can be used to listen to a remote directory. It will keep listening to the specified directory and\nnotify on file addition and deletion periodically.\n\n```ballerina\nlistener ftp:Listener remoteServer = check new({\n protocol: ftp:FTP,\n host: \"\",\n auth: {\n credentials: {\n username: \"\",\n password: \"\"\n }\n },\n port: ,\n path: \"\",\n pollingInterval: ,\n fileNamePattern: \"\"\n});\n\nservice on remoteServer {\n remote function onFileChange(ftp:WatchEvent fileEvent) {\n\n foreach ftp:FileInfo addedFile in fileEvent.addedFiles {\n log:print(\"Added file path: \" + addedFile.path);\n }\n foreach string deletedFile in fileEvent.deletedFiles {\n log:print(\"Deleted file path: \" + deletedFile);\n }\n }\n}\n```\n\n### Secure access with SFTP\n\nSFTP is a secure protocol alternative to the FTP, which runs on top of the SSH protocol.\nThere are several ways to authenticate an SFTP server. One is using the username and the password.\nAnother way is using the client's private key. The Ballerina SFTP client and the listener support only those authentication standards.\nAn authentication-related configuration can be given to the SFTP client\/listener with the `auth` configuration.\nPassword-based authentication is defined with the `credentials` configuration while the private key based authentication is defined with the `privateKey` configuration.\n\n#### SFTP client configuration\n\n```ballerina\nftp:ClientConfiguration sftpConfig = {\n protocol: ftp:SFTP,\n host: \"\",\n port: ,\n auth: {\n credentials: {username: \"\", password: \"\"},\n privateKey: {\n path: \"\",\n password: \"\"\n }\n }\n};\n```\n\n#### SFTP listener configuration\n\n```ballerina\nlistener ftp:Listener remoteServer = check new({\n protocol: ftp:SFTP,\n host: \"\",\n port: ,\n path: \"\",\n pollingInterval: ,\n fileNamePattern: \"\",\n auth: {\n credentials: {username: \"\", password: \"\"},\n privateKey: {\n path: \"\",\n password: \"\"\n }\n }\n});\n```\n\n## Report issues\n\nTo report bugs, request new features, start new discussions, view project boards, etc., go to the [Ballerina standard library parent repository](https:\/\/github.com\/ballerina-platform\/ballerina-standard-library).\n\n## Useful links\n\n- Chat live with us via our [Discord server](https:\/\/discord.gg\/ballerinalang).\n- Post all technical questions on Stack Overflow with the [#ballerina](https:\/\/stackoverflow.com\/questions\/tagged\/ballerina) tag.", + "template": false, + "licenses": [ + "Apache-2.0" + ], + "authors": [ + "Ballerina" + ], + "sourceCodeLocation": "https://github.com/ballerina-platform/module-ballerina-ftp", + "keywords": [ + "FTP", + "SFTP", + "remote file", + "file transfer", + "client", + "service" + ], + "ballerinaVersion": "2201.10.0", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerina_ftp_2.11.0.png", + "ownerUUID": "ecc9b221-6e0c-4c60-93fe-40e1b877a313", + "createdDate": 1724139776000, + "pullCount": 44, + "visibility": "public", + "modules": [ + { + "packageURL": "/ballerina/ftp/2.11.0", + "apiDocURL": "https://lib.ballerina.io/ballerina/ftp/2.11.0", + "name": "ftp", + "summary": "This module provides an FTP/SFTP client and an FTP/SFTP server listener implementation to facilitate an FTP/SFTP connection connected to a remote location.", + "readme": "## Overview\n\nThis module provides an FTP\/SFTP client and an FTP\/SFTP server listener implementation to facilitate an FTP\/SFTP connection connected to a remote location.\n\n### FTP client\n\nThe `ftp:Client` connects to an FTP server and performs various operations on the files. Currently, it supports the\ngeneric FTP operations; `get`, `delete`, `put`, `append`, `mkdir`, `rmdir`, `isDirectory`, `rename`, `size`, and\n `list`.\n\nAn FTP client is defined using the `protocol` and `host` parameters and optionally, the `port` and\n`auth`. Authentication configuration can be configured using the `auth` parameter for Basic Auth and\nprivate key.\n\nAn authentication-related configuration can be given to the FTP client with the `auth` configuration.\n\n##### Create a client\n\nThe following code creates an FTP client and performs the I\/O operations, which connect to the FTP server with Basic Auth.\n```ballerina\n\/\/ Define the FTP client configuration.\nftp:ClientConfiguration ftpConfig = {\n protocol: ftp:FTP,\n host: \"\",\n port: ,\n auth: {\n credentials: {\n username: \"\",\n password: \"\"\n }\n }\n};\n\n\/\/ Create the FTP client.\nftp:Client|ftp:Error ftpClient = new(ftpConfig);\n```\n\n##### Create a directory\n\nThe following code creates a directory in the remote FTP server.\n\n```ballerina\nftp:Error? mkdirResponse = ftpClient->mkdir(\"\");\n```\n\n##### Upload a file to a remote server\n\nThe following code uploads a file to a remote FTP server.\n\n```ballerina\nstream fileByteStream\n = check io:fileReadBlocksAsStream(putFilePath, );\nftp:Error? putResponse = ftpClient->put(\"\", fileByteStream);\n```\n\n##### Compress and upload a file to a remote server\n\nThe following code compresses and uploads a file to a remote FTP server.\n\n```ballerina\n\/\/ Set the optional boolean flag as 'true' to compress before uploading\nstream fileByteStream\n = check io:fileReadBlocksAsStream(putFilePath, );\nftp:Error? compressedPutResponse = ftpClient->put(\"\",\n fileByteStream, compressionType=ZIP);\n```\n\n##### Get the size of a remote file\n\nThe following code gets the size of a file in a remote FTP server.\n\n```ballerina\nint|ftp:Error sizeResponse = ftpClient->size(\"\");\n```\n\n##### Read the content of a remote file\n\nThe following code reads the content of a file in a remote FTP server.\n\n```ballerina\nstream|Error str = clientEP -> get(\"\");\nif (str is stream) {\n record {|byte[] value;|}|io:Error? arr1 = str.next();\n if (arr1 is record {|byte[] value;|}) {\n string fileContent = check strings:fromBytes(arr1.value);\n \/\/ `fileContent` is the `string` value of first byte array\n record {|byte[] value;|}|io:Error? arr2 = str.next();\n \/\/ Similarly following content chunks can be iteratively read with `next` method.\n \/\/ Final chunk will contain the terminal value which is `()`.\n }\n io:Error? closeResult = str.close();\n}\n```\n\n##### Rename\/move a remote file\n\nThe following code renames or moves a file to another location in the same remote FTP server.\n\n```ballerina\nftp:Error? renameResponse = ftpClient->rename(\"\",\n \"\");\n```\n\n##### Delete a remote file\n\nThe following code deletes a remote file in a remote FTP server.\n\n```ballerina\nftp:Error? deleteResponse = ftpClient->delete(\"\");\n```\n\n##### Remove a directory from a remote server\n\nThe following code removes a directory in a remote FTP server.\n\n```ballerina\nftp:Error? rmdirResponse = ftpClient->rmdir(\"\");\n```\n\n### FTP listener\n\nThe `ftp:Listener` is used to listen to a remote FTP location and trigger a `WatchEvent` type of event when new\nfiles are added to or deleted from the directory. The `fileResource` function is invoked when a new file is added\nand\/or deleted.\n\nAn FTP listener is defined using the mandatory `protocol`, `host`, and `path` parameters. The authentication\nconfiguration can be done using the `auth` parameter and the polling interval can be configured using the `pollingInterval` parameter.\nThe default polling interval is 60 seconds.\n\nThe `fileNamePattern` parameter can be used to define the type of files the FTP listener will listen to.\nFor instance, if the listener gets invoked for text files, the value `(.*).txt` can be given for the config.\n\nAn authentication-related configuration can be given to the FTP listener with the `auth` configuration.\n\n##### Create a listener\n\nThe FTP Listener can be used to listen to a remote directory. It will keep listening to the specified directory and\nnotify on file addition and deletion periodically.\n\n```ballerina\nlistener ftp:Listener remoteServer = check new({\n protocol: ftp:FTP,\n host: \"\",\n auth: {\n credentials: {\n username: \"\",\n password: \"\"\n }\n },\n port: ,\n path: \"\",\n pollingInterval: ,\n fileNamePattern: \"\"\n});\n\nservice on remoteServer {\n remote function onFileChange(ftp:WatchEvent fileEvent) {\n\n foreach ftp:FileInfo addedFile in fileEvent.addedFiles {\n log:print(\"Added file path: \" + addedFile.path);\n }\n foreach string deletedFile in fileEvent.deletedFiles {\n log:print(\"Deleted file path: \" + deletedFile);\n }\n }\n}\n```\n\n### Secure access with SFTP\n\nSFTP is a secure protocol alternative to the FTP, which runs on top of the SSH protocol.\nThere are several ways to authenticate an SFTP server. One is using the username and the password.\nAnother way is using the client's private key. The Ballerina SFTP client and the listener support only those authentication standards.\nAn authentication-related configuration can be given to the SFTP client\/listener with the `auth` configuration.\nPassword-based authentication is defined with the `credentials` configuration while the private key based authentication is defined with the `privateKey` configuration.\n\n#### SFTP client configuration\n\n```ballerina\nftp:ClientConfiguration sftpConfig = {\n protocol: ftp:SFTP,\n host: \"\",\n port: ,\n auth: {\n credentials: {username: \"\", password: \"\"},\n privateKey: {\n path: \"\",\n password: \"\"\n }\n }\n};\n```\n\n#### SFTP listener configuration\n\n```ballerina\nlistener ftp:Listener remoteServer = check new({\n protocol: ftp:SFTP,\n host: \"\",\n port: ,\n path: \"\",\n pollingInterval: ,\n fileNamePattern: \"\",\n auth: {\n credentials: {username: \"\", password: \"\"},\n privateKey: {\n path: \"\",\n password: \"\"\n }\n }\n});\n```" + } + ], + "balToolId": "", + "graalvmCompatible": "Yes" + }, + "serviceTypes": [ + { + "name": "FTP", + "description": "FTP Service", + "enabled": true, + "functions": [ + { + "name": "onFileChange", + "documentation": "The `onFileChange` remote method will be triggered when a file change event occurs.", + "optional": false, + "qualifiers": [ + "remote" + ], + "enabled": true, + "parameters": [ + { + "name": "event", + "typeName": "ftp:WatchEvent & readonly|ftp:WatchEvent", + "optional": false, + "type": [ + "ftp:WatchEvent & readonly", + "ftp:WatchEvent" + ], + "typeInfo": { + "name": "WatchEvent", + "orgName": "ballerina", + "moduleName": "ftp", + "version": "2.11.0" + }, + "defaultTypeName": "ftp:WatchEvent & readonly", + "documentation": "File watch event.", + "enabled": true, + "value": "ftp:WatchEvent & readonly" + }, + { + "name": "caller", + "typeName": "ftp:Caller", + "type": [ + "ftp:Caller" + ], + "typeInfo": { + "name": "Caller", + "orgName": "ballerina", + "moduleName": "ftp", + "version": "2.11.0" + }, + "optional": true, + "documentation": "The FTP caller object to execte file operations.", + "enabled": false, + "value": "ftp:Caller" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + } + ] + } + ], + "listener": { + "metadata": { + "label": "FTP Listener", + "description": "The FTP listener listens to file changes and triggers the service when a file change event occurs." + }, + "valueType": "ftp:Listener", + "valueTypeConstraint": "ftp:Listener", + "value": "", + "enabled": true, + "optional": false, + "editable": true, + "properties": { + "listenerConfig": { + "metadata": { + "label": "Listener Configuration", + "description": "The FTP listener configuration." + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "ftp:ListenerConfiguration", + "value": "", + "editable": true, + "optional": true, + "advanced": true, + "fields": { + "protocol": { + "metadata": { + "label": "protocol", + "description": "Supported FTP protocols" + }, + "valueType": "string", + "valueTypeConstraint": "Protocol", + "value": "", + "placeholder": "ftp:FTP", + "optional": true, + "editable": true, + "advanced": false, + "enum": [ + "ftp:FTP", + "ftp:SFTP" + ] + }, + "host": { + "metadata": { + "label": "host", + "description": "Target service url" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "127.0.0.1", + "optional": true, + "editable": true, + "advanced": false + }, + "port": { + "metadata": { + "label": "port", + "description": "Port number of the remote service" + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": 21, + "placeholder": "21", + "optional": true, + "editable": true, + "advanced": false + }, + "auth": { + "metadata": { + "label": "auth", + "description": "Authentication options" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "AuthConfiguration", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "credentials": { + "metadata": { + "label": "credentials", + "description": "Username and password to be used" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "Credentials", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "username": { + "metadata": { + "label": "username", + "description": "Username of the user" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "password": { + "metadata": { + "label": "password", + "description": "Password of the user" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + } + } + }, + "privateKey": { + "metadata": { + "label": "privateKey", + "description": "Private key to be used" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "ftp:PrivateKey", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "path": { + "metadata": { + "label": "path", + "description": "Path to the private key file" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "password": { + "metadata": { + "label": "password", + "description": "Private key password" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + } + } + }, + "preferredMethods": { + "metadata": { + "label": "preferredMethods", + "description": "Preferred authentication methods" + }, + "valueType": "array", + "valueTypeConstraint": "ftp:PreferredMethod[]", + "value": [ + "ftp:PUBLICKEY", + "ftp:PASSWORD" + ], + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false, + "enum": [ + "ftp:KEYBOARD_INTERACTIVE", + "ftp:GSSAPI_WITH_MIC", + "ftp:PASSWORD", + "ftp:PUBLICKEY" + ] + } + } + }, + "path": { + "metadata": { + "label": "path", + "description": "Remote FTP directory location" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "/", + "optional": true, + "editable": true, + "advanced": false + }, + "fileNamePattern": { + "metadata": { + "label": "fileNamePattern", + "description": "File name pattern that event need to trigger" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "pollingInterval": { + "metadata": { + "label": "pollingInterval", + "description": "Periodic time interval to check new update" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": 60, + "placeholder": "60", + "optional": true, + "editable": true, + "advanced": false + } + } + } + } + } +} diff --git a/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/github.json b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/github.json new file mode 100644 index 000000000000..98cf7041759e --- /dev/null +++ b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/github.json @@ -0,0 +1,1821 @@ +{ + "id": 9, + "name": "Trigger", + "displayName": "GitHub", + "documentation": "", + "moduleName": "trigger.github", + "serviceTypes": [ + { + "name": "IssuesService", + "enabled": false, + "description": "Triggers when a new event related to a GitHub issue is received. \nAvailable actions: onOpened, onClosed, onReopened, onAssigned, onUnassigned, onLabeled, and onUnlabeled\n", + "functions": [ + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onOpened", + "enabled": true, + "parameters": [ + { + "name": "payload", + "enabled": true, + "optional": false, + "type": "github:IssuesEvent", + "documentation": "The information about the triggered event.", + "value": "github:IssuesEvent", + "valueTypeConstraint": "github:IssuesEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onClosed", + "enabled": true, + "parameters": [ + { + "name": "payload", + "enabled": true, + "optional": false, + "type": "github:IssuesEvent", + "documentation": "The information about the triggered event.", + "value": "github:IssuesEvent", + "valueTypeConstraint": "github:IssuesEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onReopened", + "enabled": true, + "parameters": [ + { + "name": "payload", + "enabled": true, + "optional": false, + "type": "github:IssuesEvent", + "documentation": "The information about the triggered event.", + "value": "github:IssuesEvent", + "valueTypeConstraint": "github:IssuesEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onAssigned", + "enabled": true, + "parameters": [ + { + "name": "payload", + "enabled": true, + "optional": false, + "type": "github:IssuesEvent", + "documentation": "The information about the triggered event.", + "value": "github:IssuesEvent", + "valueTypeConstraint": "github:IssuesEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onLabeled", + "enabled": true, + "parameters": [ + { + "name": "payload", + "enabled": true, + "optional": false, + "type": "github:IssuesEvent", + "documentation": "The information about the triggered event.", + "value": "github:IssuesEvent", + "valueTypeConstraint": "github:IssuesEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onAssigned", + "enabled": true, + "parameters": [ + { + "name": "payload", + "enabled": true, + "optional": false, + "type": "github:IssuesEvent", + "documentation": "The information about the triggered event.", + "value": "github:IssuesEvent", + "valueTypeConstraint": "github:IssuesEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onUnlabeled", + "enabled": true, + "parameters": [ + { + "name": "payload", + "documentation": "The information about the triggered event.", + "enabled": true, + "optional": false, + "type": "github:IssuesEvent", + "value": "github:IssuesEvent", + "valueTypeConstraint": "github:IssuesEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + } + ] + }, + { + "name": "IssueCommentService", + "description": "Triggers when a new event is fired upon receiving a comment on a pull request or a GitHub issue.\nAvailable actions: onCreated, onEdited, and onDeleted\n", + "functions": [ + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onCreated", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "typeInfo": { + "name": "IssueCommentEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "optional": false, + "type": "github:IssuesEvent", + "value": "github:IssuesEvent", + "valueTypeConstraint": "github:IssuesEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onEdited", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "IssueCommentEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:IssueCommentEvent", + "valueTypeConstraint": "github:IssueCommentEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onDeleted", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "IssueCommentEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:IssueCommentEvent", + "valueTypeConstraint": "github:IssueCommentEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + } + ] + }, + { + "name": "PullRequestService", + "description": "Triggers when a new event related to a GitHub pull request is received.\nAvailable actions: onOpened, onClosed, onReopened, onAssigned, onUnassigned, \nonReviewRequested, onReviewRequestRemoved, onLabeled, onUnlabeled, and onEdited\n", + "functions": [ + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onOpened", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PullRequestEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PullRequestEvent", + "valueTypeConstraint": "github:PullRequestEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onClosed", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PullRequestEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PullRequestEvent", + "valueTypeConstraint": "github:PullRequestEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onReopened", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PullRequestEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PullRequestEvent", + "valueTypeConstraint": "github:PullRequestEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onAssigned", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PullRequestEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PullRequestEvent", + "valueTypeConstraint": "github:PullRequestEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onUnassigned", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PullRequestEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PullRequestEvent", + "valueTypeConstraint": "github:PullRequestEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onReviewRequested", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PullRequestEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PullRequestEvent", + "valueTypeConstraint": "github:PullRequestEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onReviewRequestRemoved", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PullRequestEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PullRequestEvent", + "valueTypeConstraint": "github:PullRequestEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onLabeled", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PullRequestEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PullRequestEvent", + "valueTypeConstraint": "github:PullRequestEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onUnlabeled", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PullRequestEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PullRequestEvent", + "valueTypeConstraint": "github:PullRequestEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onEdited", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PullRequestEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PullRequestEvent", + "valueTypeConstraint": "github:PullRequestEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + } + ] + }, + { + "name": "PullRequestReviewService", + "description": "Triggers when Choreo recieved a new event from GitHub related to a pull request review.\nAvailable actions: onSubmitted, onEdited, and onDismissed\n", + "functions": [ + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onSubmitted", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PullRequestReviewEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PullRequestReviewEvent", + "valueTypeConstraint": "github:PullRequestReviewEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onEdited", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PullRequestReviewEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PullRequestReviewEvent", + "valueTypeConstraint": "github:PullRequestReviewEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onDismissed", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PullRequestReviewEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PullRequestReviewEvent", + "valueTypeConstraint": "github:PullRequestReviewEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + } + ] + }, + { + "name": "PullRequestReviewCommentService", + "description": "Triggers when a new event is received from GitHub when a comment is added to a pull request review's unified diff view.\nAvailable actions: onCreated, onEdited, and onDeleted\n", + "functions": [ + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onCreated", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PullRequestReviewCommentEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PullRequestReviewCommentEvent", + "valueTypeConstraint": "github:PullRequestReviewCommentEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onEdited", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PullRequestReviewCommentEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PullRequestReviewCommentEvent", + "valueTypeConstraint": "github:PullRequestReviewCommentEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onDeleted", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PullRequestReviewCommentEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PullRequestReviewCommentEvent", + "valueTypeConstraint": "github:PullRequestReviewCommentEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + } + ] + }, + { + "name": "ReleaseService", + "description": "Triggers when a new event related to a GitHub release is received.\nAvailable actions: onPublished, onUnpublished, onCreated, onEdited, onDeleted, onPreReleased, and onReleased\n", + "functions": [ + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onPublished", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "ReleaseEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:ReleaseEvent", + "valueTypeConstraint": "github:ReleaseEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onUnpublished", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "ReleaseEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:ReleaseEvent", + "valueTypeConstraint": "github:ReleaseEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onCreated", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "ReleaseEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:ReleaseEvent", + "valueTypeConstraint": "github:ReleaseEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onEdited", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "ReleaseEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:ReleaseEvent", + "valueTypeConstraint": "github:ReleaseEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onDeleted", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "ReleaseEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:ReleaseEvent", + "valueTypeConstraint": "github:ReleaseEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onPreReleased", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "ReleaseEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:ReleaseEvent", + "valueTypeConstraint": "github:ReleaseEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onReleased", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "ReleaseEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:ReleaseEvent", + "valueTypeConstraint": "github:ReleaseEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + } + ] + }, + { + "name": "LabelService", + "description": "Triggers when a new event related to labels is received.\nAvailable actions: onCreated, onEdited, and onDeleted\n", + "functions": [ + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onCreated", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "LabelEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:LabelEvent", + "valueTypeConstraint": "github:LabelEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onEdited", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "LabelEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:LabelEvent", + "valueTypeConstraint": "github:LabelEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onDeleted", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "LabelEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:LabelEvent", + "valueTypeConstraint": "github:LabelEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + } + ] + }, + { + "name": "MilestoneService", + "description": "Triggers when a new event related to GitHub milestones is received.\nAvailable actions: onCreated, onEdited, onDeleted, onClosed, and onOpened\n", + "functions": [ + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onCreated", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "MilestoneEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:MilestoneEvent", + "valueTypeConstraint": "github:MilestoneEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onEdited", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "MilestoneEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:MilestoneEvent", + "valueTypeConstraint": "github:MilestoneEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onDeleted", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "MilestoneEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:MilestoneEvent", + "valueTypeConstraint": "github:MilestoneEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onClosed", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "MilestoneEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:MilestoneEvent", + "valueTypeConstraint": "github:MilestoneEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onOpened", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "MilestoneEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "displayAnnotation": {}, + "hasRestType": false, + "defaultable": false, + "enabled": true, + "value": "github:MilestoneEvent", + "valueTypeConstraint": "github:MilestoneEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + } + ] + }, + { + "name": "PushService", + "description": "Triggers when a push event is received.\nAvailable action: onPush\n", + "functions": [ + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onPush", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "PushEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:PushEvent", + "valueTypeConstraint": "github:PushEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + } + ] + }, + { + "name": "ProjectCardService", + "description": "Triggers when a new event related to project cards is received.\nAvailable actions: onCreated, onEdited, onMoved, onConverted, and onDeleted\n", + "functions": [ + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onCreated", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "ProjectCardEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:ProjectCardEvent", + "valueTypeConstraint": "github:ProjectCardEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onEdited", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "ProjectCardEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:ProjectCardEvent", + "valueTypeConstraint": "github:ProjectCardEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onMoved", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "ProjectCardEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:ProjectCardEvent", + "valueTypeConstraint": "github:ProjectCardEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onConverted", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "ProjectCardEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:ProjectCardEvent", + "valueTypeConstraint": "github:ProjectCardEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "qualifiers": [ + "remote" + ], + "documentation": "", + "name": "onDeleted", + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "record", + "optional": false, + "typeInfo": { + "name": "ProjectCardEvent", + "orgName": "ballerinax", + "moduleName": "trigger.github", + "version": "0.9.2" + }, + "enabled": true, + "value": "github:ProjectCardEvent", + "valueTypeConstraint": "github:ProjectCardEvent" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + } + ] + } + ], + "listener": { + "metadata": { + "label": "GitHub Listener", + "description": "The GitHub webhook listener." + }, + "valueType": "OBJECT", + "valueTypeConstraint": "github:Listener", + "value": "", + "enabled": true, + "editable": true, + "properties": { + "listenerConfig": { + "metadata": { + "label": "GitHub Listener Configuration", + "description": "The configuration of GitHub listener." + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "github:ListenerConfig", + "placeholder": "", + "editable": true, + "enabled": true, + "optional": false, + "advanced": true, + "fields": { + "webhookSecret": { + "metadata": { + "label": "Webhook Secret", + "description": "Secret specified when adding the github webhook" + }, + "valueType": "STRING", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "github:DEFAULT_SECRET", + "editable": true, + "enabled": true, + "optional": false, + "advanced": false + }, + "listenOn": { + "metadata": { + "label": "Listen On", + "description": "The port to listen on or the HTTP listener" + }, + "valueType": "UNION", + "valueTypeConstraint": "int|http:Listener", + "value": "", + "placeholder": "8090", + "editable": true, + "enabled": true, + "optional": false, + "advanced": false, + "unionTypes": [ + { + "metadata": { + "label": "Port", + "description": "The port to listen on" + }, + "valueType": "INT", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "8090", + "editable": true, + "enabled": true, + "optional": false, + "advanced": false + }, + { + "metadata": { + "label": "The HTTP Listener", + "description": "The HTTP listener to listen on" + }, + "valueType": "OBJECT", + "valueTypeConstraint": "http:Listener", + "value": "", + "placeholder": "", + "editable": true, + "enabled": true, + "optional": false, + "advanced": false + } + ] + } + } + } + } + }, + "listenerProtocol": "github", + "displayAnnotation": { + "label": "GitHub", + "iconPath": "docs/icon.png" + }, + "package": { + "id": 13850, + "organization": "ballerinax", + "name": "trigger.github", + "version": "0.9.2", + "platform": "any", + "languageSpecificationVersion": "2023R1", + "isDeprecated": false, + "deprecateMessage": "", + "URL": "/ballerinax/trigger.github/0.9.2", + "balaVersion": "2.0.0", + "balaURL": "https://fileserver.central.ballerina.io/2.0/ballerinax/trigger.github/0.9.2/ballerinax-trigger.github-any-0.9.2.bala?Expires=1730896710&Signature=H1DqnC32I2GT6gQVq8-j0HnR7A71HO0BMoHk6K8OGfWNQKarnDMLnnOMQauXsK3jpivAf9oAA7HIFxnqLahBMKHGWparjm7~Msqd3Qi9tEKRwFcavKJ4hpZT7mWbKekMlTvO0add1WbilyEs3w0OMQ1i0SAehPapLuu~ivWMBsoXgcl7bP2Q~cWXIz~vlu6SDq7U5nwk7EVNp08ReM~jNDfhfnMyFoipNaXjzjRtMETwFIl8key4dNvJQ79NGwdJ5j5XKlsu8E41HFINaK8hhAK2aSZQhVFaOc4tjuUqDLZKxfiqiQWAr0iLB~f5gIqzgR~3xvL-AsBLySQCQYnavA__&Key-Pair-Id=K27IQ7NPTKLKDU", + "digest": "sha-256=bfb65ff1ede0382897890facecc98681a1095062c9ffeb52864db1bf9955055e", + "summary": "Listen to [Github Events API](https://docs.github.com/en/developers/webhooks-and-events/webhooks) from Ballerina", + "readme": "Listen to [Github Events API](https://docs.github.com/en/developers/webhooks-and-events/webhooks) from Ballerina\n\n## Package overview\nThe `ballerinax/trigger.github` is a [Ballerina](https://ballerina.io/) trigger for Github events api.\nThis package provides the capability to access Github Webhook API.\n\n### Compatibility\n| | Version |\n|-------------------------------|--------------------------------|\n| Ballerina Language | Ballerina Swan Lake 2201.4.1 |\n\n## Report issues\nTo report bugs, request new features, start new discussions, view project boards, etc., go to the [Ballerina Extended Library repository](https://github.com/ballerina-platform/ballerina-extended-library)\n\n## Useful links\n- Discuss code changes of the Ballerina project in [ballerina-dev@googlegroups.com](mailto:ballerina-dev@googlegroups.com).\n- Chat live with us via our [Discord server](https://discord.gg/ballerinalang).\n- Post all technical questions on Stack Overflow with the [#ballerina](https://stackoverflow.com/questions/tagged/ballerina) tag", + "template": false, + "licenses": [ + "Apache-2.0" + ], + "authors": [ + "Ballerina" + ], + "sourceCodeLocation": "https://github.com/ballerina-platform/asyncapi-triggers", + "keywords": [ + "Communication/Team Chat", + "Cost/Freemium", + "Trigger" + ], + "ballerinaVersion": "2201.7.1", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerinax_trigger.github_0.9.2.png", + "ownerUUID": "b5a9e54d-8ade-47a1-8abc-6bc46e89069d", + "createdDate": 1706864765000, + "pullCount": 75, + "visibility": "public", + "modules": [ + { + "packageURL": "/ballerinax/trigger.github/0.9.2", + "apiDocURL": "https://lib.ballerina.io/ballerinax/trigger.github/0.9.2", + "name": "trigger.github", + "summary": "The GitHub Trigger module allows you to listen to following events occur in GitHub. ", + "readme": "## Overview\nThe GitHub Trigger module allows you to listen to following events occur in GitHub. \n- `Ping`, `Fork`, `Push`, `Create`, `Watch`\n- `Release`, `Issue`, `Label`, `Milestone`\n- `Pull Request`, `Pull Request Review`, `Pull Request Review Comment`\n\nThis module supports [GitHub API](https://docs.github.com/en/graphql) v4 version and only allows to perform functions behalf of the currently logged in user.\n\n\n## Prerequisites\nBefore using this trigger in your Ballerina application, complete the following:\n\n* Create github account\n\n## Quickstart\nTo use the GitHub Trigger in your Ballerina application, update the .bal file as follows:\n\n### Step 1: Import the GitHub Webhook Ballerina library\nFirst, import the ballerinax/github.webhook and ballerina/websub modules into the Ballerina project as follows.\n\n```ballerina\n import ballerinax/trigger.github;\n```\n\n### Step 2: Initialize the GitHub Webhook Listener\nInitialize the Trigger by providing the listener config & port number/httpListener object.\n\n```ballerina\n configurable github:ListenerConfig userInput = {\n secret: \"xxxxxx\"\n };\n listener github:Listener webhookListener = new (userInput, 8090);\n```\n\nListener config is not mandatory If you haven't setup secret in webhook page we can omit it and initialize as follows.\n\n```ballerina\n listener github:Listener webhookListener = new (listenOn = 8090);\n```\n\nIf you don't provide a port it will use the default port which is 8090.\n\n```ballerina\n listener github:Listener webhookListener = new ();\n```\n\n### Step 3: Use the correct service type to implement the service\nUse the correct service type for the corresponding channel when implementing the service.\nEx :- If you need to listen to Issue events you may use IssuesService service type as follows.\n\n```ballerina\nservice github:IssuesService on githubListener {\n \n remote function onAssigned(github:IssuesEvent payload) returns error? {\n return;\n }\n\n remote function onClosed(github:IssuesEvent payload) returns error? {\n return;\n } \n\n remote function onLabeled(github:IssuesEvent payload) returns error? {\n return;\n }\n\n remote function onOpened(github:IssuesEvent payload) returns error? {\n return;\n }\n\n remote function onReopened(github:IssuesEvent payload) returns error? {\n return;\n }\n\n remote function onUnassigned(github:IssuesEvent payload) returns error? {\n return;\n }\n\n remote function onUnlabeled(github:IssuesEvent payload) returns error? {\n return;\n }\n}\n```\n\n### Step 4: Provide remote functions corresponding to the events which you are interested on\nThe remote functions can be provided as follows.\n\n```ballerina\n remote function onPush(github:PushEvent payload) returns error? {\n log:printInfo(\"Received push-event-message \", eventPayload = payload);\n }\n```\n### Step 5: Run the service \nUse `bal run` command to compile and run the Ballerina program. \n\n### Step 5: Configure Github webhook with the URL of the service\n- Create a webhook in github following [github documentation](https://docs.github.com/en/developers/webhooks-and-events/webhooks/creating-webhooks)\n- Provide the public URL of the started service as the Payload URL (Add a trailing / to the URL if its not present). \n- Provide application/json for the content type. \n- Support for secret field will be available in the next github trigger releases. \n- Select the list of events you need to subscribe to and click on Add webhook.\n\nThis will add a subscription to github event api and the ballerina service functions will be triggerred once an event is fired." + } + ], + "balToolId": "", + "graalvmCompatible": "Unknown" + } +} diff --git a/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/jms.json b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/jms.json new file mode 100644 index 000000000000..14624fc9edcd --- /dev/null +++ b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/jms.json @@ -0,0 +1,421 @@ +{ + "id": 5, + "name": "JMS Service", + "type": "inbuilt", + "displayName": "JMS", + "documentation": "The JMS service can be attached to a JMS listener which listens to messages from a JMS broker. The service should implement the `onMessage` remote method to process the received messages. Additionally, the service can implement the `onError` remote method to handle errors that occur during message processing.", + "moduleName": "java.jms", + "listenerProtocol": "jms", + "displayAnnotation": { + "label": "jms", + "iconPath": "docs/icon.png" + }, + "package": { + "id": 14134, + "organization": "ballerinax", + "name": "java.jms", + "version": "1.0.1", + "platform": "java17", + "languageSpecificationVersion": "2023R1", + "isDeprecated": false, + "deprecateMessage": "", + "URL": "/ballerinax/java.jms/1.0.1", + "balaVersion": "2.0.0", + "balaURL": "https://fileserver.central.ballerina.io/2.0/ballerinax/java.jms/1.0.1/ballerinax-java.jms-java17-1.0.1.bala?Expires=1729736702&Signature=iGZK64YQyN7Ebpk8hVOtguqSkJitQbQPp7nJyT1DA50dHOWsbbYzO~39iXFbKCyyzsrf3CUWOj1fAK~TYwO-oZ0nGOH9t5ZOnlxRkh39r~76TpojOpGWkhydKJRfy5yEVdC5DV6WkbO2j13ruXELJnxDjsjAAN7QeAXR0xPwUw1785FqhrHNC0odeDEF7rtWmTZ~KoulrCXmsTO4o8p9Z9ig7IsXai7Ev~UHqIfamaoeiPVrfoAo9uM9lCZejn0uoi7Dw6-OkyrT65OmODqsDXudXiEAUD5WsL5r4edXBxoPbYSIzq0UhEMd2393MMuf3~K8YSGONWZlPSO4k--FmQ__&Key-Pair-Id=K27IQ7NPTKLKDU", + "digest": "sha-256=532c8a3f906efa50d5c91cc163ed794665b5d19ce9a77e9a75fc2f33b45405bf", + "summary": "The `ballerinax/java.jms` package provides an API to connect to an external JMS provider like ActiveMQ from Ballerina.", + "readme": "## Package overview\n\nThe `ballerinax\/java.jms` package provides an API to connect to an external JMS provider like ActiveMQ from Ballerina.\n\nThis package is created with minimal deviation from the JMS API to make it easy for the developers who are used to working with the JMS API. This package is written to support both JMS 2.0 and JMS 1.0 API. \n \nCurrently, the following JMS API Classes are supported through this package.\n \n - Connection\n - Session\n - Destination (Queue, Topic, TemporaryQueue, TemporaryTopic)\n - Message (TextMessage, MapMessage, BytesMessage)\n - MessageConsumer\n - MessageProducer\n \n The following sections provide details on how to use the JMS connector.\n \n - [Samples](#samples)\n\n## Samples\n\n### JMS message Producer\n\nThe following Ballerina program sends messages to a queue named *MyQueue*.\n\n```ballerina\nimport ballerinax\/activemq.driver as _;\nimport ballerinax\/java.jms;\n\npublic function main() returns error? {\n jms:Connection connection = check new (\n initialContextFactory = \"org.apache.activemq.jndi.ActiveMQInitialContextFactory\",\n providerUrl = \"tcp:\/\/localhost:61616\"\n );\n jms:Session session = check connection->createSession();\n jms:MessageProducer producer = check session.createProducer({\n 'type: jms:QUEUE,\n name: \"MyQueue\"\n });\n jms:TextMessage msg = {\n content: \"Hello Ballerina!\"\n };\n check producer->send(msg);\n}\n```\n\n## JMS message consumer\nThe following Ballerina program receives messages from a queue named *MyQueue*.\n```ballerina\nimport ballerinax\/activemq.driver as _;\nimport ballerinax\/java.jms;\nimport ballerina\/log;\n\npublic function main() returns error? {\n jms:Connection connection = check new (\n initialContextFactory = \"org.apache.activemq.jndi.ActiveMQInitialContextFactory\",\n providerUrl = \"tcp:\/\/localhost:61616\"\n );\n jms:Session session = check connection->createSession();\n jms:MessageConsumer consumer = check session.createConsumer(\n destination = {\n 'type: jms:QUEUE,\n name: \"MyQueue\"\n });\n while true {\n jms:Message? response = check consumer->receive(3000);\n if response is jms:TextMessage {\n log:printInfo(\"Message received: \", content = response.toString());\n }\n }\n}\n```\n\n### Asynchronous message consumer\n\nOne of the key deviations from the JMS API was the asynchronous message consumption using message listeners. In \nBallerina transport listener concept is covered with **service** type, hence we have used the Ballerina service to \nimplement the message listener. Following is a message listener example listening on a queue named *MyQueue*.\n\n```ballerina\nimport ballerinax\/activemq.driver as _;\nimport ballerina\/log;\nimport ballerinax\/java.jms;\n\nservice \"consumer-service\" on new jms:Listener(\n connectionConfig = {\n initialContextFactory: \"org.apache.activemq.jndi.ActiveMQInitialContextFactory\",\n providerUrl: \"tcp:\/\/localhost:61616\"\n },\n consumerOptions = {\n destination: {\n 'type: jms:QUEUE,\n name: \"MyQueue\"\n }\n }\n) {\n remote function onMessage(jms:Message message) returns error? {\n if message is jms:TextMessage {\n log:printInfo(\"Text message received\", content = message.content);\n }\n }\n}\n```", + "template": false, + "licenses": [ + "Apache-2.0" + ], + "authors": [ + "Ballerina" + ], + "sourceCodeLocation": "https://github.com/ballerina-platform/module-ballerina-java.jms", + "keywords": [ + "jms" + ], + "ballerinaVersion": "2201.8.0", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerinax_java.jms_1.0.1.png", + "ownerUUID": "02ea02ee-e509-41d5-bb61-60467748ddc5", + "createdDate": 1710919297000, + "pullCount": 36, + "visibility": "public", + "modules": [ + { + "packageURL": "/ballerinax/java.jms/1.0.1", + "apiDocURL": "https://lib.ballerina.io/ballerinax/java.jms/1.0.1", + "name": "java.jms", + "summary": "The `ballerinax/java.jms` module provides an API to connect to an external JMS provider like ActiveMQ from Ballerina.", + "readme": "## Overview\n\nThe `ballerinax\/java.jms` module provides an API to connect to an external JMS provider like ActiveMQ from Ballerina.\n\nThis module is created with minimal deviation from the JMS API to make it easy for the developers who are used to working \n with the JMS API. This module is written to support both JMS 2.0 and JMS 1.0 API. \n \nCurrently, the following JMS API Classes are supported through this module.\n \n - Connection\n - Session\n - Destination (Queue, Topic, TemporaryQueue, TemporaryTopic)\n - Message (TextMessage, MapMessage, BytesMessage)\n - MessageConsumer\n - MessageProducer\n \n The following sections provide details on how to use the JMS connector.\n \n - [Samples](#samples)\n\n## Samples\n\n### JMS message Producer\n\nThe following Ballerina program sends messages to a queue named *MyQueue*.\n\n```ballerina\nimport ballerinax\/activemq.driver as _;\nimport ballerinax\/java.jms;\n\npublic function main() returns error? {\n jms:Connection connection = check new (\n initialContextFactory = \"org.apache.activemq.jndi.ActiveMQInitialContextFactory\",\n providerUrl = \"tcp:\/\/localhost:61616\"\n );\n jms:Session session = check connection->createSession();\n jms:MessageProducer producer = check session.createProducer({\n 'type: jms:QUEUE,\n name: \"MyQueue\"\n });\n jms:TextMessage msg = {\n content: \"Hello Ballerina!\"\n };\n check producer->send(msg);\n}\n```\n\n## JMS message consumer\nThe following Ballerina program receives messages from a queue named *MyQueue*.\n```ballerina\nimport ballerinax\/activemq.driver as _;\nimport ballerinax\/java.jms;\nimport ballerina\/log;\n\npublic function main() returns error? {\n jms:Connection connection = check new (\n initialContextFactory = \"org.apache.activemq.jndi.ActiveMQInitialContextFactory\",\n providerUrl = \"tcp:\/\/localhost:61616\"\n );\n jms:Session session = check connection->createSession();\n jms:MessageConsumer consumer = check session.createConsumer(\n destination = {\n 'type: jms:QUEUE,\n name: \"MyQueue\"\n });\n while true {\n jms:Message? response = check consumer->receive(3000);\n if response is jms:TextMessage {\n log:printInfo(\"Message received: \", content = response.toString());\n }\n }\n}\n```\n\n### Asynchronous message consumer\n\nOne of the key deviations from the JMS API was the asynchronous message consumption using message listeners. In \nBallerina transport listener concept is covered with **service** type, hence we have used the Ballerina service to \nimplement the message listener. Following is a message listener example listening on a queue named *MyQueue*.\n\n```ballerina\nimport ballerinax\/activemq.driver as _;\nimport ballerina\/log;\nimport ballerinax\/java.jms;\n\nservice \"consumer-service\" on new jms:Listener(\n connectionConfig = {\n initialContextFactory: \"org.apache.activemq.jndi.ActiveMQInitialContextFactory\",\n providerUrl: \"tcp:\/\/localhost:61616\"\n },\n consumerOptions = {\n destination: {\n 'type: jms:QUEUE,\n name: \"MyQueue\"\n }\n }\n) {\n remote function onMessage(jms:Message message) returns error? {\n if message is jms:TextMessage {\n log:printInfo(\"Text message received\", content = message.content);\n }\n }\n}\n```" + } + ], + "balToolId": "", + "graalvmCompatible": "Yes" + }, + "serviceTypes": [ + { + "name": "JMS", + "description": "JMS Service", + "enabled": true, + "functions": [ + { + "name": "onMessage", + "documentation": "The `onMessage` remote method will be triggered when a message is received from the JMS broker.", + "optional": false, + "qualifiers": [ + "remote" + ], + "enabled": true, + "parameters": [ + { + "name": "message", + "typeName": "jms:Message", + "optional": false, + "type": [ + "jms:Message" + ], + "typeInfo": { + "name": "Message", + "orgName": "ballerinax", + "moduleName": "java.jms", + "version": "1.0.1" + }, + "documentation": "The message received from the JMS broker", + "enabled": true, + "value": "jms:Message" + }, + { + "name": "caller", + "typeName": "jms:Caller", + "type": [ + "jms:Caller" + ], + "typeInfo": { + "name": "Caller", + "orgName": "ballerinax", + "moduleName": "java.jms", + "version": "1.0.1" + }, + "optional": true, + "documentation": "The JMS caller object to acknowledge the message", + "enabled": false, + "value": "jms:Caller" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "name": "onError", + "documentation": "The `onError` remote method will be triggered when an error occurs during message processing.", + "optional": true, + "qualifiers": [ + "remote" + ], + "enabled": false, + "parameters": [ + { + "name": "err", + "typeName": "jms:Error", + "type": [ + "jms:Error" + ], + "optional": false, + "typeInfo": { + "name": "Error", + "orgName": "ballerinax", + "moduleName": "java.jms", + "version": "1.0.1" + }, + "documentation": "The error occurred during message processing", + "enabled": true, + "value": "jms:Error" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + } + ] + } + ], + "listener": { + "metadata": { + "label": "JMS Listener", + "description": "The JMS listener listens to messages from a JMS broker." + }, + "valueType": "jms:Listener", + "valueTypeConstraint": "jms:Listener", + "value": "", + "enabled": true, + "optional": false, + "editable": true, + "properties": { + "listenerConfig": { + "metadata": { + "label": "listenerConfig", + "description": "The JMS listener configuration" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "jms:MessageListenerConfigurations", + "value": "", + "editable": true, + "optional": true, + "advanced": true, + "fields": { + "connectionConfig": { + "metadata": { + "label": "connectionConfig", + "description": "Configurations related to the broker connection" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "jms:ConnectionConfiguration", + "value": "", + "placeholder": "", + "optional": false, + "editable": true, + "advanced": true, + "fields": { + "initialContextFactory": { + "metadata": { + "label": "initialContextFactory", + "description": "JMS provider specific initial context factory" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": false, + "editable": true, + "advanced": false + }, + "providerUrl": { + "metadata": { + "label": "providerUrl", + "description": "JMS provider specific provider URL used to configure a connection" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": false, + "editable": true, + "advanced": false + }, + "connectionFactoryName": { + "metadata": { + "label": "connectionFactoryName", + "description": "JMS connection factory to be used in creating JMS connections" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "ConnectionFactory", + "optional": true, + "editable": true, + "advanced": false + }, + "username": { + "metadata": { + "label": "username", + "description": "Username for the JMS connection" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "password": { + "metadata": { + "label": "password", + "description": "Password for the JMS connection" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "properties": { + "metadata": { + "label": "properties", + "description": "Additional properties used in initializing the initial context" + }, + "valueType": "map", + "valueTypeConstraint": "map", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + } + } + }, + "acknowledgementMode": { + "metadata": { + "label": "acknowledgementMode", + "description": "Configuration indicating how messages received by the session will be acknowledged" + }, + "valueType": "enum", + "valueTypeConstraint": "jms:AcknowledgementMode", + "value": "", + "placeholder": "jms:AUTO_ACKNOWLEDGE", + "optional": true, + "editable": true, + "advanced": false, + "enum": [ + "jms:SESSION_TRANSACTED", + "jms:AUTO_ACKNOWLEDGE", + "jms:CLIENT_ACKNOWLEDGE", + "jms:DUPS_OK_ACKNOWLEDGE" + ] + }, + "consumerOptions": { + "metadata": { + "label": "consumerOptions", + "description": "Underlying JMS message consumer configurations" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "jms:ConsumerOptions", + "value": "", + "placeholder": "", + "optional": false, + "editable": true, + "advanced": true, + "fields": { + "type": { + "metadata": { + "label": "type", + "description": "Message consumer type" + }, + "valueType": "enum", + "valueTypeConstraint": "ConsumerType", + "value": "jms:DEFAULT", + "placeholder": "jms:DEFAULT", + "optional": true, + "editable": true, + "advanced": false, + "enum": [ + "jms:DURABLE", + "jms:SHARED", + "jms:SHARED_DURABLE", + "jms:DEFAULT" + ] + }, + "destination": { + "metadata": { + "label": "destination", + "description": "Name of the JMS destination" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "jms:Destination", + "value": "", + "placeholder": "", + "optional": false, + "editable": true, + "advanced": true, + "fields": { + "type": { + "metadata": { + "label": "type", + "description": "JMS destination types" + }, + "valueType": "enum", + "valueTypeConstraint": "jms:DestinationType", + "value": "", + "placeholder": "", + "optional": false, + "editable": true, + "advanced": false, + "enum": [ + "jms:QUEUE", + "jms:TEMPORARY_QUEUE", + "jms:TOPIC", + "jms:TEMPORARY_TOPIC" + ] + }, + "name": { + "metadata": { + "label": "name", + "description": "Name of the destination" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + } + } + }, + "messageSelector": { + "metadata": { + "label": "messageSelector", + "description": "Only messages with properties matching the message selector expression are added to the durable subscription. An empty string indicates that there is no message selector for the durable subscription." + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "noLocal": { + "metadata": { + "label": "noLocal", + "description": "If true then any messages published to the topic using this session's connection, or any other connection with the same client identifier, will not be added to the durable subscription." + }, + "valueType": "boolean", + "valueTypeConstraint": "boolean", + "value": false, + "placeholder": "false", + "optional": true, + "editable": true, + "advanced": false + }, + "subscriberName": { + "metadata": { + "label": "subscriberName", + "description": "The name used to identify the subscription" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + } + } + } + } + } + } + } +} diff --git a/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/kafka.json b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/kafka.json new file mode 100644 index 000000000000..c3dbcd952d99 --- /dev/null +++ b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/kafka.json @@ -0,0 +1,921 @@ +{ + "id": 1, + "name": "Kafka Service", + "type": "inbuilt", + "displayName": "Kafka", + "documentation": "The Kafka service can be attached to a Kafka listener which listens to Kafka topic(s) and triggers the service when a message is received for Kafka topic(s). The service should implement the `onConsumerRecord` remote method to process the received message(s). Additionally, the service can implement the `onError` remote method to handle errors that occur during the message processing.", + "moduleName": "kafka", + "listenerProtocol": "kafka", + "displayAnnotation": { + "label": "Kafka", + "iconPath": "docs/icon.png" + }, + "package": { + "id": 15511, + "organization": "ballerinax", + "name": "kafka", + "version": "4.2.0", + "platform": "java17", + "languageSpecificationVersion": "2024R1", + "isDeprecated": false, + "deprecateMessage": "", + "URL": "/ballerinax/kafka/4.2.0", + "balaVersion": "2.0.0", + "balaURL": "https://fileserver.central.ballerina.io/2.0/ballerinax/kafka/4.2.0/ballerinax-kafka-java17-4.2.0.bala?Expires=1729564876&Signature=yyLs~P-0T666fFFKjpIqPJWc5LA9uuzqNaIz5yFGAh0FLgFJjAhA~kxi9~z5O1dIzsdN-si2srv7z0KmYrok5KRzczVdfMJmfab5Q7D0ZInrMguEAR9GZB6TYbrv6OMaTDOH6jXGaz0Dr0HLWZZj63IuDEpf4JLfy8cvfclcf2jKmYGFmcfii-tCQ-PfgBC6QBqQQmBX0xaHjutgNA0lLG9OVLaAT51qwOQsmmceYKD6wnK-DtWsPLshyHAKbORy2hZkTvahq-yz4kQf4PYDId51egzijhCO48hOUo1n8IAMnBfrMqB8dBy6s8KZNnbmljN5PXcJRzrj1hiIA8EQJA__&Key-Pair-Id=K27IQ7NPTKLKDU", + "digest": "sha-256=07b3e7b48e21aadb0c7bcfb3c221b2648c75f95b31b82de41f2b36119b70a53d", + "summary": "This package provides an implementation to interact with Kafka Brokers via Kafka Consumer and Kafka Producer clients.", + "readme": "## Overview\nThis package provides an implementation to interact with Kafka Brokers via Kafka Consumer and Kafka Producer clients.\n\nApache Kafka is an open-source distributed event streaming platform used for high-performance data pipelines, streaming analytics, data integration, and mission-critical applications.\n\nThis package supports Kafka 1.x.x, 2.x.x and 3.x.x versions.\n\n### Consumer and producer\n#### Kafka producer\nA Kafka producer is a Kafka client that publishes records to the Kafka cluster. The producer is thread-safe and sharing a single producer instance across threads will generally be faster than having multiple instances. When working with a Kafka producer, the first thing to do is to initialize the producer.\nFor the producer to execute successfully, an active Kafka broker should be available.\n\nThe code snippet given below initializes a producer with the basic configuration.\n```ballerina\nimport ballerinax\/kafka;\n\nkafka:ProducerConfiguration producerConfiguration = {\n clientId: \"basic-producer\",\n acks: \"all\",\n retryCount: 3\n};\n\nkafka:Producer kafkaProducer = check new (kafka:DEFAULT_URL, producerConfiguration);\n```\n#### Kafka consumer\nA Kafka consumer is a subscriber responsible for reading records from one or more topics and one or more partitions of a topic. When working with a Kafka consumer, the first thing to do is initialize the consumer.\nFor the consumer to execute successfully, an active Kafka broker should be available.\n\nThe code snippet given below initializes a consumer with the basic configuration.\n```ballerina\nkafka:ConsumerConfiguration consumerConfiguration = {\n groupId: \"group-id\", \/\/ Unique string that identifies the consumer\n offsetReset: \"earliest\", \/\/ Offset reset strategy if no initial offset\n topics: [\"kafka-topic\"]\n};\n\nkafka:Consumer kafkaConsumer = check new (kafka:DEFAULT_URL, consumerConfiguration);\n```\n### Listener\nThe Kafka consumer can be used as a listener to a set of topics without the need to manually `poll` the messages.\n\nYou can use the `Caller` to manually commit the offsets of the messages that are read by the service. The following code snippet shows how to initialize and define the listener and how to commit the offsets manually.\n```ballerina\nkafka:ConsumerConfiguration consumerConfiguration = {\n groupId: \"group-id\",\n topics: [\"kafka-topic-1\"],\n pollingInterval: 1,\n autoCommit: false\n};\n\nlistener kafka:Listener kafkaListener = new (kafka:DEFAULT_URL, consumerConfiguration);\n\nservice on kafkaListener {\n remote function onConsumerRecord(kafka:Caller caller, kafka:BytesConsumerRecord[] records) {\n \/\/ processes the records\n ...\n \/\/ commits the offsets manually\n kafka:Error? commitResult = caller->commit();\n\n if commitResult is kafka:Error {\n log:printError(\"Error occurred while committing the offsets for the consumer \", 'error = commitResult);\n }\n }\n}\n```\n### Data serialization\nSerialization is the process of converting data into a stream of bytes that is used for transmission. Kafka\nstores and transmits these bytes of arrays in its queue. Deserialization does the opposite of serialization\nin which bytes of arrays are converted into the desired data type.\n\nCurrently, this package only supports the `byte array` data type for both the keys and values. The following code snippets\nshow how to produce and read a message from Kafka.\n```ballerina\nstring message = \"Hello World, Ballerina\";\nstring key = \"my-key\";\n\/\/ converts the message and key to a byte array\ncheck kafkaProducer->send({ topic: \"test-kafka-topic\", key: key.toBytes(), value: message.toBytes() });\n```\n```ballerina\nkafka:BytesConsumerRecord[] records = check kafkaConsumer->poll(1);\n\nforeach var kafkaRecord in records {\n byte[] messageContent = kafkaRecord.value;\n \/\/ tries to generate the string value from the byte array\n string result = check string:fromBytes(messageContent);\n io:println(\"The result is : \", result);\n}\n```\n### Concurrency\nIn Kafka, records are grouped into smaller units called partitions. These can be processed independently without\ncompromising the correctness of the results and lays the foundation for parallel processing. This can be achieved by\nusing multiple consumers within the same group each reading and processing data from a subset of topic partitions and\nrunning in a single thread.\n\nTopic partitions are assigned to consumers automatically or you can manually assign topic partitions.\n\nThe following code snippet joins a consumer to the `consumer-group` and assigns it to a topic partition manually.\n```ballerina\nkafka:ConsumerConfiguration consumerConfiguration = {\n \/\/ `groupId` determines the consumer group\n groupId: \"consumer-group\",\n pollingInterval: 1,\n autoCommit: false\n};\n\nkafka:Consumer kafkaConsumer = check new (kafka:DEFAULT_URL, consumerConfiguration);\n\/\/ creates a topic partition\nkafka:TopicPartition topicPartition = {\n topic: \"kafka-topic-1\",\n partition: 1\n};\n\/\/ passes the topic partitions to the assign function as an array\ncheck kafkaConsumer->assign([topicPartition]);\n```\n\n### Report issues\n\nTo report bugs, request new features, start new discussions, view project boards, etc., go to the [Ballerina standard library parent repository](https:\/\/github.com\/ballerina-platform\/ballerina-standard-library).\n\n### Useful links\n\n- Chat live with us via our [Discord server](https:\/\/discord.gg\/ballerinalang).\n- Post all technical questions on Stack Overflow with the [#ballerina](https:\/\/stackoverflow.com\/questions\/tagged\/ballerina) tag.", + "template": false, + "licenses": [ + "Apache-2.0" + ], + "authors": [ + "Ballerina" + ], + "sourceCodeLocation": "https://github.com/ballerina-platform/module-ballerinax-kafka", + "keywords": [ + "kafka", + "event streaming", + "network", + "messaging" + ], + "ballerinaVersion": "2201.10.0", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerinax_kafka_4.2.0.png", + "ownerUUID": "b5a9e54d-8ade-47a1-8abc-6bc46e89069d", + "createdDate": 1724147879000, + "pullCount": 1270, + "visibility": "public", + "modules": [ + { + "packageURL": "/ballerinax/kafka/4.2.0", + "apiDocURL": "https://lib.ballerina.io/ballerinax/kafka/4.2.0", + "name": "kafka", + "summary": "This module provides an implementation to interact with Kafka Brokers via Kafka Consumer and Kafka Producer clients.", + "readme": "## Overview\n\nThis module provides an implementation to interact with Kafka Brokers via Kafka Consumer and Kafka Producer clients.\n\nApache Kafka is an open-source distributed event streaming platform used for high-performance data pipelines, streaming analytics, data integration, and mission-critical applications.\n\nThis module supports Kafka 1.x.x, 2.x.x and 3.x.x versions.\n\n### Consumer and producer\n#### Kafka producer\nA Kafka producer is a Kafka client that publishes records to the Kafka cluster. The producer is thread-safe and sharing a single producer instance across threads will generally be faster than having multiple instances. When working with a Kafka producer, the first thing to do is to initialize the producer.\nFor the producer to execute successfully, an active Kafka broker should be available.\n\nThe code snippet given below initializes a producer with the basic configuration.\n```ballerina\nimport ballerinax\/kafka;\n\nkafka:ProducerConfiguration producerConfiguration = {\n clientId: \"basic-producer\",\n acks: \"all\",\n retryCount: 3\n};\n\nkafka:Producer kafkaProducer = check new (kafka:DEFAULT_URL, producerConfiguration);\n```\n#### Kafka consumer\nA Kafka consumer is a subscriber responsible for reading records from one or more topics and one or more partitions of a topic. When working with a Kafka consumer, the first thing to do is initialize the consumer.\nFor the consumer to execute successfully, an active Kafka broker should be available.\n\nThe code snippet given below initializes a consumer with the basic configuration.\n```ballerina\nkafka:ConsumerConfiguration consumerConfiguration = {\n groupId: \"group-id\", \/\/ Unique string that identifies the consumer\n offsetReset: \"earliest\", \/\/ Offset reset strategy if no initial offset\n topics: [\"kafka-topic\"]\n};\n\nkafka:Consumer kafkaConsumer = check new (kafka:DEFAULT_URL, consumerConfiguration);\n```\n### Listener\nThe Kafka consumer can be used as a listener to a set of topics without the need to manually `poll` the messages.\n\nYou can use the `Caller` to manually commit the offsets of the messages that are read by the service. The following code snippet shows how to initialize and define the listener and how to commit the offsets manually.\n```ballerina\nkafka:ConsumerConfiguration consumerConfiguration = {\n groupId: \"group-id\",\n topics: [\"kafka-topic-1\"],\n pollingInterval: 1,\n autoCommit: false\n};\n\nlistener kafka:Listener kafkaListener = new (kafka:DEFAULT_URL, consumerConfiguration);\n\nservice on kafkaListener {\n remote function onConsumerRecord(kafka:Caller caller, kafka:BytesConsumerRecord[] records) {\n \/\/ processes the records\n ...\n \/\/ commits the offsets manually\n kafka:Error? commitResult = caller->commit();\n\n if commitResult is kafka:Error {\n log:printError(\"Error occurred while committing the offsets for the consumer \", 'error = commitResult);\n }\n }\n}\n```\n### Data serialization\nSerialization is the process of converting data into a stream of bytes that is used for transmission. Kafka\nstores and transmits these bytes of arrays in its queue. Deserialization does the opposite of serialization\nin which bytes of arrays are converted into the desired data type.\n\nCurrently, this module only supports the `byte array` data type for both the keys and values. The following code snippets\nshow how to produce and read a message from Kafka.\n```ballerina\nstring message = \"Hello World, Ballerina\";\nstring key = \"my-key\";\n\/\/ converts the message and key to a byte array\ncheck kafkaProducer->send({ topic: \"test-kafka-topic\", key: key.toBytes(), value: message.toBytes() });\n```\n```ballerina\nkafka:BytesConsumerRecord[] records = check kafkaConsumer->poll(1);\n\nforeach var kafkaRecord in records {\n byte[] messageContent = kafkaRecord.value;\n \/\/ tries to generate the string value from the byte array\n string result = check string:fromBytes(messageContent);\n io:println(\"The result is : \", result);\n}\n```\n### Concurrency\nIn Kafka, records are grouped into smaller units called partitions. These can be processed independently without\ncompromising the correctness of the results and lays the foundation for parallel processing. This can be achieved by\nusing multiple consumers within the same group each reading and processing data from a subset of topic partitions and \nrunning in a single thread.\n\nTopic partitions are assigned to consumers automatically or you can manually assign topic partitions.\n\nThe following code snippet joins a consumer to the `consumer-group` and assigns it to a topic partition manually.\n```ballerina\nkafka:ConsumerConfiguration consumerConfiguration = {\n \/\/ `groupId` determines the consumer group\n groupId: \"consumer-group\",\n pollingInterval: 1,\n autoCommit: false\n};\n\nkafka:Consumer kafkaConsumer = check new (kafka:DEFAULT_URL, consumerConfiguration);\n\/\/ creates a topic partition\nkafka:TopicPartition topicPartition = {\n topic: \"kafka-topic-1\",\n partition: 1\n};\n\/\/ passes the topic partitions to the assign function as an array\ncheck kafkaConsumer->assign([topicPartition]);\n```" + } + ], + "balToolId": "", + "graalvmCompatible": "Yes" + }, + "serviceTypes": [ + { + "name": "Kafka", + "description": "Kafka Service", + "functions": [ + { + "name": "onConsumerRecord", + "enabled": true, + "documentation": "The `onConsumerRecord` remote method will be triggered when a message is received from Kafka topic(s).", + "optional": false, + "editable": true, + "qualifiers": [ + "remote" + ], + "parameters": [ + { + "name": "records", + "typeName": "kafka:ConsumerAnydataRecord[]|anydata[]", + "optional": false, + "arrayType": true, + "defaultTypeName": "kafka:ConsumerAnydataRecord[]", + "type": [ + "kafka:ConsumerAnydataRecord[]", + "anydata[]" + ], + "documentation": "The records received for Kafka topic(s).", + "enabled": true, + "value": "kafka:ConsumerAnydataRecord[]" + }, + { + "name": "caller", + "typeName": "kafka:Caller", + "type": [ + "kafka:Caller" + ], + "typeInfo": { + "name": "Caller", + "orgName": "ballerinax", + "moduleName": "kafka", + "version": "4.2.0" + }, + "enabled": false, + "optional": true, + "documentation": "The Kafka caller object to commit the offsets." + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": "true", + "value": "error?" + } + }, + { + "name": "onError", + "enabled": false, + "documentation": "The `onError` remote method will be triggered when an error occurs during the message processing.", + "optional": true, + "qualifiers": [ + "remote" + ], + "parameters": [ + { + "name": "err", + "typeName": "kafka:Error", + "type": [ + "kafka:Error" + ], + "optional": false, + "typeInfo": { + "name": "Error", + "orgName": "ballerinax", + "moduleName": "kafka", + "version": "4.2.0" + }, + "documentation": "The error occurred during the message processing.", + "enabled": "true", + "value": "kafka:Error" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": "true", + "value": "error?" + } + } + ] + } + ], + "listener": { + "metadata": { + "label": "Kafka Listener", + "description": "The Kafka listener listens to Kafka topic(s) and triggers the service when a message is received for Kafka topic(s)." + }, + "valueType": "kafka:Listener", + "valueTypeConstraint": "kafka:Listener", + "value": "", + "optional": false, + "editable": true, + "properties": { + "bootstrapServers": { + "metadata": { + "label": "bootstrapServers", + "description": "The Kafka bootstrap server URL. For a clustered use case, provide a comma-separated list of URLs." + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "string|string[]", + "value": "", + "placeholder": "kafka:DEFAULT_URL", + "optional": false, + "editable": true, + "advanced": false + }, + "config": { + "metadata": { + "label": "config", + "description": "The Kafka listener configurations." + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "kafka:ConsumerConfiguration", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "groupId": { + "metadata": { + "label": "groupId", + "description": "Unique string that identifies the consumer" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": false, + "editable": true, + "advanced": false + }, + "topics": { + "metadata": { + "label": "topics", + "description": "Topics to be subscribed by the consumer" + }, + "valueType": "string[]", + "valueTypeConstraint": "string[]", + "value": "", + "placeholder": "", + "optional": false, + "editable": true, + "advanced": false + }, + "offsetReset": { + "metadata": { + "label": "offsetReset", + "description": "Offset reset strategy if no initial offset" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false, + "enum": [ + "kafka:OFFSET_RESET_EARLIEST", + "kafka:OFFSET_RESET_LATEST", + "kafka:OFFSET_RESET_NONE" + ] + }, + "partitionAssignmentStrategy": { + "metadata": { + "label": "partitionAssignmentStrategy", + "description": "Strategy class for handling the partition assignment among consumers" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "metricsRecordingLevel": { + "metadata": { + "label": "metricsRecordingLevel", + "description": "Metrics recording level" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "metricsReporterClasses": { + "metadata": { + "label": "metricsReporterClasses", + "description": "Metrics reporter classes" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "clientId": { + "metadata": { + "label": "clientId", + "description": "Identifier to be used for server side logging" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "interceptorClasses": { + "metadata": { + "label": "interceptorClasses", + "description": "Interceptor classes to be used before sending the records" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "isolationLevel": { + "metadata": { + "label": "isolationLevel", + "description": "Transactional message reading method" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false, + "enum": [ + "kafka:ISOLATION_COMMITTED", + "kafka:ISOLATION_UNCOMMITTED" + ] + }, + "schemaRegistryUrl": { + "metadata": { + "label": "schemaRegistryUrl", + "description": "Avro schema registry URL. Use this field to specify the schema registry URL, if the Avro serializer is used" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "additionalProperties": { + "metadata": { + "label": "additionalProperties", + "description": "Additional properties for the property fields not provided by the Ballerina `kafka` module. Use this with caution since this can override any of the fields. It is not recommended to use this field except in an extreme situation" + }, + "valueType": "map", + "valueTypeConstraint": "map", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "sessionTimeout": { + "metadata": { + "label": "sessionTimeout", + "description": "Timeout (in seconds) used to detect consumer failures when the heartbeat threshold is reached" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "heartBeatInterval": { + "metadata": { + "label": "heartBeatInterval", + "description": "Expected time (in seconds) between the heartbeats" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "metadataMaxAge": { + "metadata": { + "label": "metadataMaxAge", + "description": "Maximum time (in seconds) to force a refresh of metadata" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "autoCommitInterval": { + "metadata": { + "label": "autoCommitInterval", + "description": "Auto committing interval (in seconds) for commit offset when auto-committing is enabled" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "maxPartitionFetchBytes": { + "metadata": { + "label": "maxPartitionFetchBytes", + "description": "The maximum amount of data the server returns per partition" + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "sendBuffer": { + "metadata": { + "label": "sendBuffer", + "description": "Size of the TCP send buffer (SO_SNDBUF)" + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "receiveBuffer": { + "metadata": { + "label": "receiveBuffer", + "description": "Size of the TCP receive buffer (SO_RCVBUF)" + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "fetchMinBytes": { + "metadata": { + "label": "fetchMinBytes", + "description": "Minimum amount of data the server should return for a fetch request" + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "fetchMaxBytes": { + "metadata": { + "label": "fetchMaxBytes", + "description": "Maximum amount of data the server should return for a fetch request" + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "fetchMaxWaitTime": { + "metadata": { + "label": "fetchMaxWaitTime", + "description": "Maximum amount of time (in seconds) the server will block before answering the fetch request" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "reconnectBackoffTimeMax": { + "metadata": { + "label": "reconnectBackoffTimeMax", + "description": "Maximum amount of time in seconds to wait when reconnecting" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "retryBackoff": { + "metadata": { + "label": "retryBackoff", + "description": "Time (in seconds) to wait before attempting to retry a failed request" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "metricsSampleWindow": { + "metadata": { + "label": "metricsSampleWindow", + "description": "Window of time (in seconds) a metrics sample is computed over" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "metricsNumSamples": { + "metadata": { + "label": "metricsNumSamples", + "description": "Number of samples maintained to compute metrics" + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "requestTimeout": { + "metadata": { + "label": "requestTimeout", + "description": "Wait time (in seconds) for response of a request" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "connectionMaxIdleTime": { + "metadata": { + "label": "connectionMaxIdleTime", + "description": "Close idle connections after the number of seconds" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "maxPollRecords": { + "metadata": { + "label": "maxPollRecords", + "description": "Maximum number of records returned in a single call to poll" + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "maxPollInterval": { + "metadata": { + "label": "maxPollInterval", + "description": "Maximum delay between invocations of poll" + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "reconnectBackoffTime": { + "metadata": { + "label": "reconnectBackoffTime", + "description": "Time (in seconds) to wait before attempting to reconnect" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "pollingTimeout": { + "metadata": { + "label": "pollingTimeout", + "description": "Timeout interval for polling in seconds" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "pollingInterval": { + "metadata": { + "label": "pollingInterval", + "description": "Polling interval for the consumer in seconds" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "concurrentConsumers": { + "metadata": { + "label": "concurrentConsumers", + "description": "Number of concurrent consumers" + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "defaultApiTimeout": { + "metadata": { + "label": "defaultApiTimeout", + "description": "Default API timeout value (in seconds) for APIs with duration" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "autoCommit": { + "metadata": { + "label": "autoCommit", + "description": "Enables auto committing offsets" + }, + "valueType": "boolean", + "valueTypeConstraint": "boolean", + "value": "", + "placeholder": "true", + "optional": true, + "editable": true, + "advanced": false + }, + "checkCRCS": { + "metadata": { + "label": "checkCRCS", + "description": "Checks the CRC32 of the records consumed. This ensures that no on-the-wire or on-disk corruption occurred to the messages. This may add some overhead and might need to be set to `false` if extreme performance is required" + }, + "valueType": "boolean", + "valueTypeConstraint": "boolean", + "value": "", + "placeholder": "true", + "optional": true, + "editable": true, + "advanced": false + }, + "excludeInternalTopics": { + "metadata": { + "label": "excludeInternalTopics", + "description": "Whether records from internal topics should be exposed to the consumer" + }, + "valueType": "boolean", + "valueTypeConstraint": "boolean", + "value": "", + "placeholder": "true", + "optional": true, + "editable": true, + "advanced": false + }, + "decoupleProcessing": { + "metadata": { + "label": "decoupleProcessing", + "description": "Decouples processing" + }, + "valueType": "boolean", + "valueTypeConstraint": "boolean", + "value": "", + "placeholder": "true", + "optional": true, + "editable": true, + "advanced": false + }, + "validation": { + "metadata": { + "label": "validation", + "description": "Configuration related to constraint validation check" + }, + "valueType": "boolean", + "valueTypeConstraint": "boolean", + "value": "", + "placeholder": "true", + "optional": true, + "editable": true, + "advanced": false + }, + "autoSeekOnValidationFailure": { + "metadata": { + "label": "autoSeekOnValidationFailure", + "description": "Automatically seeks past the errornous records in the event of an data-binding or validating constraints failure" + }, + "valueType": "boolean", + "valueTypeConstraint": "boolean", + "value": "", + "placeholder": "true", + "optional": true, + "editable": true, + "advanced": false + }, + "secureSocket": { + "metadata": { + "label": "secureSocket", + "description": "Configurations related to SSL/TLS encryption" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "kafka:SecureSocket", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "cert": { + "metadata": { + "label": "cert", + "description": "Configurations associated with crypto:TrustStore or single certificate file that the client trusts" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "key": { + "metadata": { + "label": "key", + "description": "Configurations associated with crypto:KeyStore or combination of certificate and private key of the client" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "protocol": { + "metadata": { + "label": "protocol", + "description": "SSL/TLS protocol related options" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "record", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "name": { + "metadata": { + "label": "name", + "description": "The name of the protocol" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false, + "enum": [ + "kafka:SSL", + "kafka:TLS", + "kafka:DTLS" + ] + }, + "versions": { + "metadata": { + "label": "versions", + "description": "The versions of the protocol" + }, + "valueType": "string[]", + "valueTypeConstraint": "string[]", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + } + } + }, + "ciphers": { + "metadata": { + "label": "ciphers", + "description": "List of ciphers to be used. By default, all the available cipher suites are supported" + }, + "valueType": "string[]", + "valueTypeConstraint": "string[]", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "provider": { + "metadata": { + "label": "provider", + "description": "Name of the security provider used for SSL connections. The default value is the default security provider of the JVM" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + } + } + }, + "auth": { + "metadata": { + "label": "auth", + "description": "Authentication-related configurations for the `kafka:Consumer`" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "kafka:AuthenticationConfiguration", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "mechanism": { + "metadata": { + "label": "mechanism", + "description": "Type of the authentication mechanism. Currently `SASL_PLAIN`, `SASL_SCRAM_256` & `SASL_SCRAM_512` is supported" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "kafka:AUTH_SASL_PLAIN", + "optional": true, + "editable": true, + "advanced": false, + "enum": [ + "kafka:AUTH_SASL_PLAIN", + "kafka:AUTH_SASL_SCRAM_SHA_256", + "kafka:AUTH_SASL_SCRAM_SHA_512" + ] + }, + "username": { + "metadata": { + "label": "username", + "description": "The username to authenticate the Kafka producer/consumer" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "password": { + "metadata": { + "label": "password", + "description": "The password to authenticate the Kafka producer/consumer" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + } + } + }, + "securityProtocol": { + "metadata": { + "label": "securityProtocol", + "description": "Type of the security protocol to use in the broker connection" + }, + "valueType": "string", + "valueTypeConstraint": "kafka:SecurityProtocol", + "value": "", + "placeholder": "kafka:PROTOCOL_PLAINTEXT", + "optional": true, + "editable": true, + "advanced": false, + "enum": [ + "kafka:PROTOCOL_PLAINTEXT", + "kafka:PROTOCOL_SASL_PLAINTEXT", + "kafka:PROTOCOL_SASL_SSL", + "kafka:PROTOCOL_SSL" + ] + } + } + } + } + } +} diff --git a/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/mqtt.json b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/mqtt.json new file mode 100644 index 000000000000..35a3451520c0 --- /dev/null +++ b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/mqtt.json @@ -0,0 +1,446 @@ +{ + "id": 4, + "name": "MQTT Service", + "type": "inbuilt", + "displayName": "MQTT", + "documentation": "This MQTT service can be attached to a MQTT listener which subscribes to MQTT topic(s) and triggers the service when a message is received for MQTT topic(s). The service should have the `onMessage` remote method to process the received message(s). Additionally, the service can have the `onError` remote method to handle errors that occur during the message processing.", + "moduleName": "mqtt", + "listenerProtocol": "mqtt", + "displayAnnotation": { + "label": "MQTT", + "iconPath": "docs/icon.png" + }, + "package": { + "id": 15474, + "organization": "ballerina", + "name": "mqtt", + "version": "1.2.0", + "platform": "java17", + "languageSpecificationVersion": "2024R1", + "isDeprecated": false, + "deprecateMessage": "", + "URL": "/ballerina/mqtt/1.2.0", + "balaVersion": "2.0.0", + "balaURL": "https://fileserver.central.ballerina.io/2.0/ballerina/mqtt/1.2.0/ballerina-mqtt-java17-1.2.0.bala?Expires=1729736003&Signature=u0gRsw-prRH8Cp4RpeXli0h46~80JeTWeloZI2i7BeM9VANXNDRaHVvK496K-mYjnitBCzM-jUX2UuhYNPTPWLywk0qFJAWYK1-ZpZ89Hoe6Nfm34CQQjmm55esvZOB1MjtJKxTeE-4FenhtcLPMO839IDQbrT3RywG-2zdo8UfAC1Xhm2WUNHSSsA53Pt6SbAi1RV7~wwM6hVjfgLrDIdBJpKa5uFPlIP-9kGSWm761LLRrfWd1ITf-CFmuET-FWAngAdOd1aOo1SD06m53nv4uo1ilQ-bPHRUUUNuNHZe5RUrElZLyQrm61Si048TE88POz5QIk9vt7-B7gE6eEA__&Key-Pair-Id=K27IQ7NPTKLKDU", + "digest": "sha-256=fd072ae39af5a1928aab6b10ce920efd483024d2fa032f129e8d409fbfc50a0f", + "summary": "This package provides an implementation to interact with MQTT servers via MQTT client and listener.", + "readme": "## Overview\nThis package provides an implementation to interact with MQTT servers via MQTT client and listener.\n\nMQTT is a lightweight, publish-subscribe, machine to machine network protocol for message queue\/message queuing service.\n\n### Publisher and subscriber\n#### MQTT publisher\nA MQTT publisher is a MQTT client that publishes messages to the MQTT server. When working with a MQTT client, the first thing to do is to initialize the client.\nFor the publisher to work successfully, an active MQTT server should be available.\n\nThe code snippet given below initializes a publisher client with the basic configuration.\n```ballerina\nimport ballerina\/mqtt;\nimport ballerina\/uuid;\n \nmqtt:ClientConfiguration clientConfiguration = {\n connectionConfig: {\n username: \"ballerina\",\n password: \"ballerinamqtt\"\n }\n};\n\nmqtt:Client mqttClient = check new (mqtt:DEFAULT_URL, uuid:createType1AsString(), clientConfiguration); \/\/ A unique id needs to be provided as the client id\n```\nUsing the `publish` api of this client, messages can be sent to the MQTT server.\n```ballerina\ncheck mqttClient->publish(\"mqtt\/test\", {payload: \"This is Ballerina MQTT client!!!\".toBytes()});\n```\n#### MQTT subscriber\nA MQTT subscriber is a client responsible for reading messages from one or more topics in the server. When working with a MQTT subscriber, the first thing to do is initialize the subscriber.\nFor the subscriber to work successfully, an active MQTT server should be available.\n\nThe code snippet given below initializes a subscriber with the basic configuration.\n```ballerina\nmqtt:ListenerConfiguration listenerConfiguration = {\n connectionConfig: {\n username: \"ballerina\",\n password: \"ballerinamqtt\"\n },\n manualAcks: false \/\/ When set to false, the MQTT acknowledgements are not sent automatically by the subscriber\n};\n\nmqtt:Listener mqttSubscriber = check new (mqtt:DEFAULT_URL, uuid:createType1AsString(), \"mqtt\/test\", listenerConfiguration);\n```\nThis subscriber can be used in the `mqtt:Service` to listen to messages in `mqtt\/test` topic.\n```ballerina\nservice on mqttSubscriber {\n remote function onMessage(mqtt:Message message, mqtt:Caller caller) returns error? {\n log:printInfo(check string:fromBytes(message.payload));\n check caller->complete();\n }\n\n remote function onError(mqtt:Error err) returns error? {\n log:printError(\"Error occured \", err);\n }\n}\n```\nThe `mqtt:Caller` can be used to indicate that the application has completed processing the message by using `complete()` api.", + "template": false, + "licenses": [], + "authors": [ + "ballerina" + ], + "sourceCodeLocation": "https://github.com/ballerina-platform/module-ballerina-mqtt", + "keywords": [ + "mqtt", + "client", + "messaging", + "network", + "pubsub", + "iot" + ], + "ballerinaVersion": "2201.10.0", + "icon": "", + "ownerUUID": "b5a9e54d-8ade-47a1-8abc-6bc46e89069d", + "createdDate": 1724140280000, + "pullCount": 44, + "visibility": "public", + "modules": [ + { + "packageURL": "/ballerina/mqtt/1.2.0", + "apiDocURL": "https://lib.ballerina.io/ballerina/mqtt/1.2.0", + "name": "mqtt", + "summary": "This module provides an implementation to interact with MQTT servers via MQTT client and listener.", + "readme": "## Overview\nThis module provides an implementation to interact with MQTT servers via MQTT client and listener.\n\nMQTT is a lightweight, publish-subscribe, machine to machine network protocol for message queue\/message queuing service.\n\n### Publisher and subscriber\n#### MQTT publisher\nA MQTT publisher is a MQTT client that publishes messages to the MQTT server. When working with a MQTT client, the first thing to do is to initialize the client.\nFor the publisher to work successfully, an active MQTT server should be available.\n\nThe code snippet given below initializes a publisher client with the basic configuration.\n```ballerina\nimport ballerina\/mqtt;\nimport ballerina\/uuid;\n \nmqtt:ClientConfiguration clientConfiguration = {\n connectionConfig: {\n username: \"ballerina\",\n password: \"ballerinamqtt\"\n }\n};\n\nmqtt:Client mqttClient = check new (mqtt:DEFAULT_URL, uuid:createType1AsString(), clientConfiguration); \/\/ A unique id needs to be provided as the client id\n```\nUsing the `publish` api of this client, messages can be sent to the MQTT server.\n```ballerina\ncheck mqttClient->publish(\"mqtt\/test\", {payload: \"This is Ballerina MQTT client!!!\".toBytes()});\n```\n#### MQTT subscriber\nA MQTT subscriber is a client responsible for reading messages from one or more topics in the server. When working with a MQTT subscriber, the first thing to do is initialize the subscriber.\nFor the subscriber to work successfully, an active MQTT server should be available.\n\nThe code snippet given below initializes a subscriber with the basic configuration.\n```ballerina\nmqtt:ListenerConfiguration listenerConfiguration = {\n connectionConfig: {\n username: \"ballerina\",\n password: \"ballerinamqtt\"\n },\n manualAcks: false \/\/ When set to false, the MQTT acknowledgements are not sent automatically by the subscriber\n};\n\nmqtt:Listener mqttSubscriber = check new (mqtt:DEFAULT_URL, uuid:createType1AsString(), \"mqtt\/test\", listenerConfiguration);\n```\nThis subscriber can be used in the `mqtt:Service` to listen to messages in `mqtt\/test` topic.\n```ballerina\nservice on mqttSubscriber {\n remote function onMessage(mqtt:Message message, mqtt:Caller caller) returns error? {\n log:printInfo(check string:fromBytes(message.payload));\n check caller->complete();\n }\n\n remote function onError(mqtt:Error err) returns error? {\n log:printError(\"Error occured \", err);\n }\n}\n```\nThe `mqtt:Caller` can be used to indicate that the application has completed processing the message by using `complete()` api." + } + ], + "balToolId": "", + "graalvmCompatible": "Yes" + }, + "serviceTypes": [ + { + "name": "MQTT", + "description": "MQTT Service", + "enabled": true, + "functions": [ + { + "name": "onMessage", + "documentation": "The `onMessage` remote method will be triggered when a message is received for MQTT topic(s).", + "optional": false, + "qualifiers": [ + "remote" + ], + "enabled": true, + "parameters": [ + { + "name": "message", + "typeName": "mqtt:Message", + "optional": false, + "type": [ + "mqtt:Message" + ], + "typeInfo": { + "name": "Message", + "orgName": "ballerina", + "moduleName": "mqtt", + "version": "1.2.0" + }, + "documentation": "The message received for MQTT topic(s).", + "enabled": true, + "value": "mqtt:Message" + }, + { + "name": "caller", + "typeName": "mqtt:Caller", + "type": [ + "mqtt:Caller" + ], + "typeInfo": { + "name": "Caller", + "orgName": "ballerina", + "moduleName": "mqtt", + "version": "1.2.0" + }, + "optional": true, + "documentation": "The MQTT caller object to indicate the completion of the message processing or to send a response.", + "enabled": false, + "value": "mqtt:Caller" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "name": "onError", + "documentation": "The `onError` remote method will be triggered when an error occurs during the message processing.", + "optional": true, + "enabled": false, + "qualifiers": [ + "remote" + ], + "parameters": [ + { + "name": "err", + "typeName": "mqtt:Error", + "type": [ + "mqtt:Error" + ], + "optional": false, + "typeInfo": { + "name": "Error", + "orgName": "ballerina", + "moduleName": "mqtt", + "version": "1.2.0" + }, + "documentation": "The error occurred during the message processing.", + "enabled": true, + "value": "mqtt:Error" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + } + ] + } + ], + "listener": { + "metadata": { + "label": "MQTT Listener", + "description": "The MQTT listener to which the MQTT service should be attached." + }, + "valueType": "mqtt:Listener", + "valueTypeConstraint": "mqtt:Listener", + "value": "", + "enabled": true, + "optional": false, + "editable": true, + "properties": { + "serviceUri": { + "metadata": { + "label": "url", + "description": "The URI of the remote MQTT server." + }, + "valueType": "string", + "valueTypeConstraint": "string", + "placeholder": "", + "editable": true, + "optional": false, + "advanced": false + }, + "clientId": { + "metadata": { + "label": "client ID", + "description": "The unique client ID to identify the listener." + }, + "valueType": "string", + "valueTypeConstraint": "string", + "placeholder": "", + "editable": true, + "optional": false, + "advanced": false + }, + "subscriptions": { + "metadata": { + "label": "subscriptions", + "description": "The MQTT topics to be subscribed to." + }, + "valueType": "string", + "valueTypeConstraint": "string|string[]", + "placeholder": "", + "editable": true, + "optional": false, + "advanced": false + }, + "config": { + "metadata": { + "label": "config", + "description": "The listener configurations." + }, + "valueType": "mqtt:ListenerConfiguration", + "valueTypeConstraint": "mqtt:ListenerConfiguration", + "placeholder": "", + "editable": true, + "optional": true, + "advanced": true, + "fields": { + "connectionConfig": { + "metadata": { + "label": "connectionConfig", + "description": "The related connection configuration" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "ConnectionConfiguration", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "username": { + "metadata": { + "label": "username", + "description": "The username to use for the connection" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "password": { + "metadata": { + "label": "password", + "description": "The password to use for the connection" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "secureSocket": { + "metadata": { + "label": "secureSocket", + "description": "The configurations related to secure communication with the MQTT server" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "SecureSocket", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "cert": { + "metadata": { + "label": "cert", + "description": "Certificate file that the client trusts or a `crypto:TrustStore`" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "key": { + "metadata": { + "label": "key", + "description": "Combination of certificate and private key of the client or a `crypto:KeyStore`" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "protocol": { + "metadata": { + "label": "protocol", + "description": "Related protocol" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "record", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "name": { + "metadata": { + "label": "name", + "description": "The name of the protocol" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false, + "enum": [ + "mqtt:SSL", + "mqtt:TLS" + ] + }, + "version": { + "metadata": { + "label": "version", + "description": "The version of the protocol" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + } + } + } + } + }, + "maxReconnectDelay": { + "metadata": { + "label": "maxReconnectDelay", + "description": "The maximum delay between reconnects in milliseconds" + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "keepAliveInterval": { + "metadata": { + "label": "keepAliveInterval", + "description": "The maximum time interval between messages sent or received in seconds" + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "connectionTimeout": { + "metadata": { + "label": "connectionTimeout", + "description": "Maximum time interval in seconds the client will wait for the network connection to the MQTT server to be established" + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "cleanStart": { + "metadata": { + "label": "cleanStart", + "description": "Whether the client and server should remember state for the client across reconnects" + }, + "valueType": "boolean", + "valueTypeConstraint": "boolean", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "serverUris": { + "metadata": { + "label": "serverUris", + "description": "List of serverURIs the client may connect to" + }, + "valueType": "string[]", + "valueTypeConstraint": "string[]", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "automaticReconnect": { + "metadata": { + "label": "automaticReconnect", + "description": "Whether the client will automatically attempt to reconnect to the server if the connection is lost" + }, + "valueType": "boolean", + "valueTypeConstraint": "boolean", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + } + } + }, + "manualAcks": { + "metadata": { + "label": "manualAcks", + "description": "Indicates whether or not the client should automatically ack messages" + }, + "valueType": "boolean", + "valueTypeConstraint": "boolean", + "value": "", + "placeholder": "false", + "optional": true, + "editable": true, + "advanced": false + } + } + } + } + } +} diff --git a/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/nats.json b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/nats.json new file mode 100644 index 000000000000..3b74e99f87e8 --- /dev/null +++ b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/nats.json @@ -0,0 +1,559 @@ +{ + "id": 3, + "name": "NATS Service", + "type": "inbuilt", + "displayName": "NATS", + "documentation": "The NATS service can be attached to a NATS listener to listen to messages from the NATS server. The service should define the subject name as the base path and should implement wither the `onMessage` or `onRequest` remote method to handle incoming messages. Additionally, the service can implement the `onError` remote method to handle errors.", + "moduleName": "nats", + "listenerProtocol": "nats", + "displayAnnotation": { + "label": "NATS", + "iconPath": "docs/icon.png" + }, + "package": { + "id": 15727, + "organization": "ballerinax", + "name": "nats", + "version": "3.1.0", + "platform": "java17", + "languageSpecificationVersion": "2024R1", + "isDeprecated": false, + "deprecateMessage": "", + "URL": "/ballerinax/nats/3.1.0", + "balaVersion": "2.0.0", + "balaURL": "https://fileserver.central.ballerina.io/2.0/ballerinax/nats/3.1.0/ballerinax-nats-java17-3.1.0.bala?Expires=1729734714&Signature=zi0ymoW3~H-QcrUyFYSIKuHH3S2SDQC6hvvFrOo9Ev1HzSNIbtpmDsMUpRT6tm5lHVooNTqvInoP-rFG4tFuQ0~vzjcMxk7pjEeCR6PFfH~pN0qEoXe1q7~Y6agp9omjpp61CJ8i~9O1mFl-D3z~5qx4VhzSzi741Kr~fjBI9qWWVM1mrK8IyIWUbN7G3TsTF7QwTDLBemdl3XI21NHpabv71CfNp26LfhST-DQEC0yZmIdIsYq8JARe~JPCfUnaFSHVCcNZjmN5QygXQnI8FngSkb8N8uoKfgSkRJ2GUVfUjYnq4bVOvRWRNIzdQSaveu5O7jQkPuO-L7zckgtoQQ__&Key-Pair-Id=K27IQ7NPTKLKDU", + "digest": "sha-256=835e5b66b017233318eef15766f44d075b9181ab5a23d69d03d55afb4e9b1e9b", + "summary": "This package provides the capability to send and receive messages by connecting to the NATS server.", + "readme": "## Package Overview\n\nThis package provides the capability to send and receive messages by connecting to the NATS server.\n\nNATS messaging enables the communication of data that is segmented into messages among computer applications and services. Data is encoded and framed as a message and sent by a publisher. The message is received, decoded, and processed by one or more subscribers. NATS makes it easy for programs to communicate across different environments, languages, cloud providers, and on-premise systems. Clients connect to the NATS system usually via a single URL and then subscribe or publish messages to a subject.\n\n### Basic usage\n\n#### Set up the connection\n\nFirst, you need to set up the connection with the NATS Basic server. The following ways can be used to connect to a\nNATS Basic server.\n\n1. Connect to a server using the default URL:\n```ballerina\nnats:Client natsClient = check new(nats:DEFAULT_URL);\n```\n\n2. Connect to a server using the URL:\n```ballerina\nnats:Client natsClient = check new(\"nats:\/\/serverone:4222\");\n```\n\n3. Connect to one or more servers with custom configurations:\n```ballerina\nnats:ConnectionConfiguration config = {\n connectionName: \"my-nats\",\n noEcho: true\n};\nnats:Client natsClient = check new([\"nats:\/\/serverone:4222\", \"nats:\/\/servertwo:4222\"], config);\n```\n\n#### Publish messages\n\n##### Publish messages to the NATS basic server\n\nOnce connected, publishing is accomplished via one of the three methods below.\n\n1. Publish with the subject and the message content:\n```ballerina\nstring message = \"hello world\";\nnats:Error? result = \n natsClient->publishMessage({ content: message.toBytes(), subject: \"demo.nats.basic\"});\n```\n\n2. Publish as a request that expects a reply:\n```ballerina\nstring message = \"hello world\";\nnats:AnydataMessage|nats:Error reqReply = \n natsClient->requestMessage({ content: message.toBytes(), subject: \"demo.nats.basic\"}, 5);\n```\n\n3. Publish messages with a `replyTo` subject:\n```ballerina\nstring message = \"hello world\";\nnats:Error? result = natsClient->publish({ content: message.toBytes(), subject: \"demo.nats.basic\",\n replyTo: \"demo.reply\" });\n```\n\n#### Listen to incoming messages\n\n##### Listen to messages from a NATS server\n\n1. Listen to incoming messages with the `onMessage` remote method:\n```ballerina\n\/\/ Binds the consumer to listen to the messages published to the 'demo.example.*' subject\n@nats:ServiceConfig {\n subject: \"demo.example.*\"\n}\nservice nats:Service on new nats:Listener(nats:DEFAULT_URL) {\n\n remote function onMessage(nats:AnydataMessage message) {\n }\n}\n```\n\n2. Listen to incoming messages and reply directly with the `onRequest` remote method:\n```ballerina\n\/\/ Binds the consumer to listen to the messages published to the 'demo.example.*' subject\n@nats:ServiceConfig {\n subject: \"demo.example.*\"\n}\nservice nats:Service on new nats:Listener(nats:DEFAULT_URL) {\n\n \/\/ The returned message will be published to the replyTo subject of the consumed message\n remote function onRequest(nats:AnydataMessage message) returns string? {\n return \"Reply Message\";\n }\n}\n```\n\n### Advanced usage\n\n#### Set up TLS\n\nThe Ballerina NATS package allows the use of TLS in communication. This setting expects a secure socket to be\nset in the connection configuration as shown below.\n\n##### Configure TLS in the `nats:Listener`\n```ballerina\nnats:SecureSocket secured = {\n cert: {\n path: \"\/truststore.p12\",\n password: \"password\"\n },\n key: {\n path: \"\/keystore.p12\",\n password: \"password\"\n }\n};\nnats:Listener natsListener = check new(\"nats:\/\/serverone:4222\", secureSocket = secured);\n```\n\n##### Configure TLS in the `nats:Client`\n```ballerina\nnats:SecureSocket secured = {\n cert: {\n path: \"\/truststore.p12\",\n password: \"password\"\n },\n key: {\n path: \"\/keystore.p12\",\n password: \"password\"\n }\n};\nnats:Client natsClient = check new(\"nats:\/\/serverone:4222\", secureSocket = secured);\n```\n\n### Report issues\n\nTo report bugs, request new features, start new discussions, view project boards, etc., go to the [Ballerina standard library parent repository](https:\/\/github.com\/ballerina-platform\/ballerina-standard-library).\n\n### Useful links\n\n- Chat live with us via our [Discord server](https:\/\/discord.gg\/ballerinalang).\n- Post all technical questions on Stack Overflow with the [#ballerina](https:\/\/stackoverflow.com\/questions\/tagged\/ballerina) tag.", + "template": false, + "licenses": [ + "Apache-2.0" + ], + "authors": [ + "Ballerina" + ], + "sourceCodeLocation": "https://github.com/ballerina-platform/module-ballerinax-nats", + "keywords": [ + "service", + "client", + "messaging", + "network", + "pubsub" + ], + "ballerinaVersion": "2201.10.0", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerinax_nats_3.1.0.png", + "ownerUUID": "b5a9e54d-8ade-47a1-8abc-6bc46e89069d", + "createdDate": 1724302519000, + "pullCount": 32, + "visibility": "public", + "modules": [ + { + "packageURL": "/ballerinax/nats/3.1.0", + "apiDocURL": "https://lib.ballerina.io/ballerinax/nats/3.1.0", + "name": "nats", + "summary": "This module provides the capability to send and receive messages by connecting to the NATS server.", + "readme": "## Overview\n\nThis module provides the capability to send and receive messages by connecting to the NATS server.\n\nNATS messaging enables the communication of data that is segmented into messages among computer applications and services. Data is encoded and framed as a message and sent by a publisher. The message is received, decoded, and processed by one or more subscribers. NATS makes it easy for programs to communicate across different environments, languages, cloud providers, and on-premise systems. Clients connect to the NATS system usually via a single URL and then subscribe or publish messages to a subject.\n\n### Basic usage\n\n#### Set up the connection\n\nFirst, you need to set up the connection with the NATS Basic server. The following ways can be used to connect to a\nNATS Basic server.\n\n1. Connect to a server using the default URL:\n```ballerina\nnats:Client natsClient = check new(nats:DEFAULT_URL);\n```\n\n2. Connect to a server using the URL:\n```ballerina\nnats:Client natsClient = check new(\"nats:\/\/serverone:4222\");\n```\n\n3. Connect to one or more servers with custom configurations:\n```ballerina\nnats:ConnectionConfiguration config = {\n connectionName: \"my-nats\",\n noEcho: true\n};\nnats:Client natsClient = check new([\"nats:\/\/serverone:4222\", \"nats:\/\/servertwo:4222\"], config);\n```\n\n#### Publish messages\n\n##### Publish messages to the NATS basic server\n\nOnce connected, publishing is accomplished via one of the three methods below.\n\n1. Publish with the subject and the message content:\n```ballerina\nstring message = \"hello world\";\nnats:Error? result = \n natsClient->publishMessage({ content: message.toBytes(), subject: \"demo.nats.basic\"});\n```\n\n2. Publish as a request that expects a reply:\n```ballerina\nstring message = \"hello world\";\nnats:AnydataMessage|nats:Error reqReply = \n natsClient->requestMessage({ content: message.toBytes(), subject: \"demo.nats.basic\"}, 5);\n```\n\n3. Publish messages with a `replyTo` subject:\n```ballerina\nstring message = \"hello world\";\nnats:Error? result = natsClient->publish({ content: message.toBytes(), subject: \"demo.nats.basic\",\n replyTo: \"demo.reply\" });\n```\n\n#### Listen to incoming messages\n\n##### Listen to messages from a NATS server\n\n1. Listen to incoming messages with the `onMessage` remote method:\n```ballerina\n\/\/ Binds the consumer to listen to the messages published to the 'demo.example.*' subject\n@nats:ServiceConfig {\n subject: \"demo.example.*\"\n}\nservice nats:Service on new nats:Listener(nats:DEFAULT_URL) {\n\n remote function onMessage(nats:AnydataMessage message) {\n }\n}\n```\n\n2. Listen to incoming messages and reply directly with the `onRequest` remote method:\n```ballerina\n\/\/ Binds the consumer to listen to the messages published to the 'demo.example.*' subject\n@nats:ServiceConfig {\n subject: \"demo.example.*\"\n}\nservice nats:Service on new nats:Listener(nats:DEFAULT_URL) {\n\n \/\/ The returned message will be published to the replyTo subject of the consumed message\n remote function onRequest(nats:AnydataMessage message) returns string? {\n return \"Reply Message\";\n }\n}\n```\n\n### Advanced usage\n\n#### Set up TLS\n\nThe Ballerina NATS module allows the use of TLS in communication. This setting expects a secure socket to be\nset in the connection configuration as shown below.\n\n##### Configure TLS in the `nats:Listener`\n```ballerina\nnats:SecureSocket secured = {\n cert: {\n path: \"\/truststore.p12\",\n password: \"password\"\n },\n key: {\n path: \"\/keystore.p12\",\n password: \"password\"\n }\n};\nnats:Listener natsListener = check new(\"nats:\/\/serverone:4222\", secureSocket = secured);\n```\n\n##### Configure TLS in the `nats:Client`\n```ballerina\nnats:SecureSocket secured = {\n cert: {\n path: \"\/truststore.p12\",\n password: \"password\"\n },\n key: {\n path: \"\/keystore.p12\",\n password: \"password\"\n }\n};\nnats:Client natsClient = check new(\"nats:\/\/serverone:4222\", secureSocket = secured);\n```" + } + ], + "balToolId": "", + "graalvmCompatible": "Yes" + }, + "serviceTypes": [ + { + "name": "NATS", + "description": "NATS Service", + "enabled": true, + "basePath": { + "optional": false, + "typeName": "string", + "type": [ + "string" + ], + "defaultable": false, + "documentation": "The NATS subject name.", + "enabled": true, + "value": "", + "placeholder": "nats.subject" + }, + "functions": [ + { + "name": "onMessage", + "documentation": "The `onMessage` remote method will be triggered when a message is received for the specified subject", + "optional": false, + "qualifiers": [ + "remote" + ], + "enabled": true, + "parameters": [ + { + "name": "message", + "typeName": "nats:AnydataMessage", + "type": [ + "nats:AnydataMessage" + ], + "optional": false, + "typeInfo": { + "name": "AnydataMessage", + "orgName": "ballerinax", + "moduleName": "nats", + "version": "3.1.0" + }, + "documentation": "The message received for the NATS subject.", + "enabled": true, + "value": "nats:AnydataMessage" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?" + }, + "group": { + "id": 1, + "name": "group-1", + "type": "exclusive", + "documentation": "NATS message trigger method.", + "default": "onMessage" + } + }, + { + "name": "onRequest", + "documentation": "The `onRequest` remote method will be triggered when a request is received for the specified subject and a response is expected.", + "optional": false, + "qualifiers": [ + "remote" + ], + "enabled": false, + "parameters": [ + { + "name": "message", + "typeName": "nats:AnydataMessage", + "type": [ + "nats:AnydataMessage" + ], + "optional": false, + "enabled": true, + "value": "nats:AnydataMessage", + "typeInfo": { + "name": "AnydataMessage", + "orgName": "ballerinax", + "moduleName": "nats", + "version": "3.1.0" + }, + "documentation": "The message received for the NATS subject." + } + ], + "returnType": { + "typeName": "anydata|error", + "type": [ + "anydata|error" + ], + "optional": false, + "documentation": "The response message.", + "editable": false, + "enabled": true, + "value": "anydata|error" + }, + "group": { + "id": 1, + "name": "group-1", + "type": "exclusive", + "documentation": "NATS message trigger method.", + "default": "onMessage" + } + }, + { + "name": "onError", + "optional": true, + "enabled": false, + "documentation": "The `onError` remote method will be triggered when an error occurs during the message processing.", + "qualifiers": [ + "remote" + ], + "parameters": [ + { + "name": "message", + "typeName": "nats:AnydataMessage", + "type": [ + "nats:AnydataMessage" + ], + "optional": false, + "typeInfo": { + "name": "AnydataMessage", + "orgName": "ballerinax", + "moduleName": "nats", + "version": "3.1.0" + }, + "documentation": "The message received for the NATS subject.", + "enabled": true, + "value": "nats:AnydataMessage" + }, + { + "name": "err", + "typeName": "nats:Error", + "type": [ + "nats:Error" + ], + "typeInfo": { + "name": "Error", + "orgName": "ballerinax", + "moduleName": "nats", + "version": "3.1.0" + }, + "optional": false, + "documentation": "The error occurred during the message processing.", + "enabled": true, + "value": "nats:Error" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + } + ] + } + ], + "listener": { + "metadata": { + "label": "NATS Listener", + "description": "The NATS listener listens to messages from the NATS server" + }, + "valueType": "nats:Listener", + "valueTypeConstraint": "nats:Listener", + "value": "", + "enabled": true, + "optional": false, + "editable": true, + "properties": { + "url": { + "metadata": { + "label": "url", + "description": "The NATS broker URL. For a clustered use case, provide the URLs as a string array" + }, + "valueType": "string", + "valueTypeConstraint": "string|string[]", + "value": "", + "placeholder": "nats:DEFAULT_URL", + "editable": true, + "optional": false, + "advanced": false + }, + "config": { + "metadata": { + "label": "config", + "description": "The NATS connection configurations" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "nats:ConnectionConfiguration", + "value": "", + "editable": true, + "optional": true, + "advanced": true, + "fields": { + "connectionName": { + "metadata": { + "label": "connectionName", + "description": "The name of the connection" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "ballerina-nats", + "optional": true, + "editable": true, + "advanced": false + }, + "retryConfig": { + "metadata": { + "label": "retryConfig", + "description": "The configurations related to connection reconnect attempts" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "nats:RetryConfig", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "maxReconnect": { + "metadata": { + "label": "maxReconnect", + "description": "Maximum number of reconnect attempts. The reconnect state is triggered when an already established connection is lost. During the initial connection attempt, the client will cycle over its server list one time regardless of the `maxReconnects` value that is set. Use 0 to turn off auto reconnecting. Use -1 to turn on infinite reconnects." + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "60", + "optional": true, + "editable": true, + "advanced": false + }, + "reconnectWait": { + "metadata": { + "label": "reconnectWait", + "description": "The time(in seconds) to wait between the reconnect attempts to reconnect to the same server" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "2", + "optional": true, + "editable": true, + "advanced": false + }, + "connectionTimeout": { + "metadata": { + "label": "connectionTimeout", + "description": "The timeout (in seconds) for the connection attempts" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "2", + "optional": true, + "editable": true, + "advanced": false + } + } + }, + "ping": { + "metadata": { + "label": "ping", + "description": "The configurations related to pinging the server" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "nats:Ping", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "pingInterval": { + "metadata": { + "label": "pingInterval", + "description": "The interval (in seconds) between the attempts of pinging the server" + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "120", + "optional": true, + "editable": true, + "advanced": false + }, + "maxPingsOut": { + "metadata": { + "label": "maxPingsOut", + "description": "The maximum number of pings the client can have in flight" + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "2", + "optional": true, + "editable": true, + "advanced": false + } + } + }, + "auth": { + "metadata": { + "label": "auth", + "description": "The configurations related to authentication" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "nats:Tokens", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "token": { + "metadata": { + "label": "token", + "description": "The token for token-based authentication" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": false, + "editable": true, + "advanced": false + } + } + }, + "inboxPrefix": { + "metadata": { + "label": "inboxPrefix", + "description": "The connection's inbox prefix, which all inboxes will start with" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "_INBOX.", + "optional": true, + "editable": true, + "advanced": false + }, + "noEcho": { + "metadata": { + "label": "noEcho", + "description": "Turns off echoing. This prevents the server from echoing messages back to the connection if it has subscriptions on the subject being published to" + }, + "valueType": "boolean", + "valueTypeConstraint": "boolean", + "value": "", + "placeholder": "false", + "optional": true, + "editable": true, + "advanced": false + }, + "secureSocket": { + "metadata": { + "label": "secureSocket", + "description": "The configurations related to SSL/TLS" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "SecureSocket", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "cert": { + "metadata": { + "label": "cert", + "description": "Configurations associated with `crypto:TrustStore` or single certificate file that the client trusts" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": false, + "editable": true, + "advanced": false + }, + "key": { + "metadata": { + "label": "key", + "description": "Configurations associated with `crypto:KeyStore` or combination of certificate and private key of the client" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "CertKey", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "certFile": { + "metadata": { + "label": "certFile", + "description": "A file containing the certificate" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": false, + "editable": true, + "advanced": false + }, + "keyFile": { + "metadata": { + "label": "keyFile", + "description": "A file containing the private key in PKCS8 format" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": false, + "editable": true, + "advanced": false + }, + "keyPassword": { + "metadata": { + "label": "keyPassword", + "description": "Password of the private key if it is encrypted" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + } + } + }, + "protocol": { + "metadata": { + "label": "protocol", + "description": "SSL/TLS protocol related options" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "record", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "name": { + "metadata": { + "label": "name", + "description": "The name of the protocol" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": false, + "editable": true, + "advanced": false, + "enum": [ + "nats:SSL", + "nats:TLS", + "nats:DTLS" + ] + } + } + } + } + }, + "validation": { + "metadata": { + "label": "validation", + "description": "Configuration related to constraint validation check" + }, + "valueType": "boolean", + "valueTypeConstraint": "boolean", + "value": "", + "placeholder": "true", + "optional": true, + "editable": true, + "advanced": false + } + } + } + } + } +} diff --git a/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/properties.json b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/properties.json new file mode 100644 index 000000000000..2918c842283b --- /dev/null +++ b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/properties.json @@ -0,0 +1,104 @@ +{ + "1": { + "name": "kafka", + "orgName": "ballerinax", + "packageName": "kafka", + "keywords": [ + "kafka", + "network", + "messaging", + "event" + ] + }, + "2": { + "name": "rabbitmq", + "orgName": "ballerinax", + "packageName": "rabbitmq", + "keywords": [ + "messaging", + "network", + "event" + ] + }, + "3": { + "name": "nats", + "orgName": "ballerinax", + "packageName": "nats", + "keywords": [ + "messaging", + "network", + "event" + ] + }, + "4": { + "name": "mqtt", + "orgName": "ballerina", + "packageName": "mqtt", + "keywords": [ + "mqtt", + "messaging", + "network", + "iot", + "event" + ] + }, + "5": { + "name": "jms", + "orgName": "ballerinax", + "packageName": "java.jms", + "keywords": [ + "jms", + "java", + "messaging", + "network", + "event" + ] + }, + "6": { + "name": "ftp", + "orgName": "ballerina", + "packageName": "ftp", + "keywords": [ + "FTP", + "SFTP", + "remote file", + "file", + "event" + ] + }, + "7": { + "name": "asb", + "orgName": "ballerinax", + "packageName": "asb", + "keywords": [ + "asb", + "azure", + "service", + "messaging", + "network", + "event" + ] + }, + "8": { + "name": "salesforce", + "orgName": "ballerinax", + "packageName": "salesforce", + "keywords": [ + "salesforce", + "service", + "messaging", + "network", + "event" + ] + }, + "9": { + "name": "github", + "orgName": "ballerinax", + "packageName": "trigger.github", + "keywords": [ + "github", + "trigger", + "event" + ] + } +} \ No newline at end of file diff --git a/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/rabbitmq.json b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/rabbitmq.json new file mode 100644 index 000000000000..2e344c771ef8 --- /dev/null +++ b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/rabbitmq.json @@ -0,0 +1,644 @@ +{ + "id": 2, + "name": "RabbitMQ Service", + "type": "inbuilt", + "displayName": "RabbitMQ", + "documentation": "The RabbitMQ service can be attached to a RabbitMQ listener which listens to messages from a RabbitMQ server. The service should define the queue name as the base path and should implement either the `onMessage` or `onRequest` remote method to handle the incoming messages. Additionally, the service can implement the `onError` remote method to handle errors.", + "moduleName": "rabbitmq", + "listenerProtocol": "rabbitmq", + "displayAnnotation": { + "label": "RabbitMQ", + "iconPath": "docs/icon.png" + }, + "package": { + "id": 15584, + "organization": "ballerinax", + "name": "rabbitmq", + "version": "3.1.0", + "platform": "java17", + "languageSpecificationVersion": "2024R1", + "isDeprecated": false, + "deprecateMessage": "", + "URL": "/ballerinax/rabbitmq/3.1.0", + "balaVersion": "2.0.0", + "balaURL": "https://fileserver.central.ballerina.io/2.0/ballerinax/rabbitmq/3.1.0/ballerinax-rabbitmq-java17-3.1.0.bala?Expires=1729566129&Signature=G2~Nt3a2HtLalmYyl~v~PnM6F7owpZC6u8KOOyKDERjMmHoWsAbY3w3Nhj4aKUINjkHrKX-Gy3KTNtbvrw6MwaDmYN~wz7zCP09kkKb-LQMRgxrmRU9gUhSSoToQELQBGmYs5UzYsUnNPtEU1tdpc8fDFmKHmUSyFPt8nsAZ5f7981KA22bdFNxYV-FT-squ06I0cq9D8Q-E4Tt4g9k~d~bD5TLc536Rxz-BJaJ5p7IG9Bi8mzU-FHr12jPW4nAJ5LEqxq71dV8L0FGMmeFGNHiHNvp8l~AgWw4UMBu8BiV6MuUoA-0T0LVU91EFmgeKnusZt7BFSg5P-M0MQDTqLA__&Key-Pair-Id=K27IQ7NPTKLKDU", + "digest": "sha-256=3b44e994547d8a28034078e0695c1ceaceab4524647ccdd4430fd9800e7daaa3", + "summary": "This package provides the capability to send and receive messages by connecting to the RabbitMQ server.", + "readme": "## Package Overview\n\nThis package provides the capability to send and receive messages by connecting to the RabbitMQ server.\n\nRabbitMQ gives your applications a common platform to send and receive messages and a safe place for your messages to live until received. RabbitMQ is one of the most popular open-source message brokers. It is lightweight and easy to deploy on-premise and in the cloud.\n\n### Basic usage\n\n#### Set up the connection\n\nFirst, you need to set up the connection with the RabbitMQ server. The following ways can be used to connect to a\nRabbitMQ server.\n\n1. Connect to a RabbitMQ node with the default host and port:\n```ballerina\n rabbitmq:Client rabbitmqClient = check new(rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT);\n```\n\n2. Connect to a RabbitMQ node with a custom host and port:\n```ballerina\n rabbitmq:Client rabbitmqClient = check new(\"localhost\", 5672);\n```\n\n3. Connect to a RabbitMQ node with host, port, and additional configurations:\n```ballerina\n rabbitmq:ConnectionConfiguration config = {\n username: \"ballerina\",\n password: \"password\"\n };\n rabbitmq:Client rabbitmqClient = check new(\"localhost\", 5672, configs);\n```\n\nThe `rabbitmq:Client` can now be used to send and receive messages as described in the subsequent sections.\n\n#### Exchanges and queues\n\nClient applications work with exchanges and queues, which are the high-level building blocks of the AMQP protocol. These must be declared before they can be used. The following code declares an exchange and a server-named queue and then binds them together.\n\n```ballerina\n check rabbitmqClient->exchangeDeclare(\"MyExchange\", rabbitmq:DIRECT_EXCHANGE);\n check rabbitmqClient->queueDeclare(\"MyQueue\");\n check rabbitmqClient->queueBind(\"MyQueue\", \"MyExchange\", \"routing-key\");\n```\n\nThis sample code will declare,\n- a durable auto-delete exchange of the type `rabbitmq:DIRECT_EXCHANGE`\n- a non-durable, exclusive auto-delete queue with an auto-generated name\n\nNext, the `queueBind` function is called to bind the queue to the exchange with the given routing key.\n\n```ballerina\n check rabbitmqClient->exchangeDeclare(\"MyExchange\", rabbitmq:DIRECT_EXCHANGE);\n check rabbitmqClient->queueDeclare(\"MyQueue\", { durable: true,\n exclusive: false,\n autoDelete: false });\n check rabbitmqClient->queueBind(\"MyQueue\", \"MyExchange\", \"routing-key\");\n```\n\nThis sample code will declare,\n- a durable auto-delete exchange of the type `rabbitmq:DIRECT_EXCHANGE`\n- a durable, non-exclusive, non-auto-delete queue with a well-known name\n\n#### Delete entities and purge queues\n\n- Delete a queue:\n```ballerina\n check rabbitmqClient->queueDelete(\"MyQueue\");\n```\n- Delete a queue only if it is empty:\n```ballerina\n check rabbitmqClient->queueDelete(\"MyQueue\", false, true);\n```\n- Delete a queue only if it is unused (does not have any consumers):\n```ballerina\n check rabbitmqClient->queueDelete(\"MyQueue\", true, false);\n```\n- Delete an exchange:\n```ballerina\n check rabbitmqClient->exchangeDelete(\"MyExchange\");\n```\n- Purge a queue (delete all of its messages):\n```ballerina\n check rabbitmqClient->queuePurge(\"MyQueue\");\n```\n\n#### Publish messages\n\nTo publish a message to an exchange, use the `publishMessage()` function as follows:\n\n```ballerina\n string message = \"Hello from Ballerina\";\n check rabbitmqClient->publishMessage({ content: message.toBytes(), routingKey: queueName });\n``` \nSetting other properties of the message such as routing headers can be done by using the `BasicProperties` record with the appropriate values.\n\n```ballerina\n rabbitmq:BasicProperties props = {\n replyTo: \"reply-queue\" \n };\n string message = \"Hello from Ballerina\";\n check rabbitmqClient->publishMessage({ content: message.toBytes(), routingKey: queueName, properties: props });\n```\n\n#### Consume messages using consumer services\n\nThe most efficient way to receive messages is to set up a subscription using a Ballerina RabbitMQ `rabbitmq:Listener` and any number of consumer services. The messages will then be delivered automatically as they arrive rather than having to be explicitly requested. Multiple consumer services can be bound to one Ballerina RabbitMQ `rabbitmq:Listener`. The queue to which the service is listening is configured in the `rabbitmq:ServiceConfig` annotation of the service or else as the name of the service.\n\n1. Listen to incoming messages with the `onMessage` remote method:\n\n```ballerina\n listener rabbitmq:Listener channelListener= new(rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT);\n \n @rabbitmq:ServiceConfig {\n queueName: \"MyQueue\"\n }\n service rabbitmq:Service on channelListener {\n remote function onMessage(rabbitmq:BytesMessage message) {\n }\n }\n```\n\n2. Listen to incoming messages and reply directly with the `onRequest` remote method:\n\n```ballerina\n listener rabbitmq:Listener channelListener= new(rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT);\n \n @rabbitmq:ServiceConfig {\n queueName: \"MyQueue\"\n }\n service rabbitmq:Service on channelListener {\n remote function onRequest(rabbitmq:BytesMessage message) returns string {\n return \"Hello Back!\";\n }\n }\n```\n\nThe `rabbitmq:BytesMessage` record received can be used to retrieve its contents.\n\n### Advanced usage\n\n#### Client acknowledgements\n\nThe message consuming is supported by mainly two types of acknowledgement modes, which are auto acknowledgements and client acknowledgements.\nClient acknowledgements can further be divided into two different types as positive and negative acknowledgements.\nThe default acknowledgement mode is auto-ack (messages are acknowledged immediately after consuming). The following examples show the usage of positive and negative acknowledgements.\n> WARNING: To ensure the reliability of receiving messages, use the client-ack mode.\n\n1. Positive client acknowledgement:\n```ballerina\n listener rabbitmq:Listener channelListener= new(rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT);\n \n @rabbitmq:ServiceConfig {\n queueName: \"MyQueue\",\n autoAck: false\n }\n service rabbitmq:Service on channelListener {\n remote function onMessage(rabbitmq:BytesMessage message, rabbitmq:Caller caller) {\n rabbitmq:Error? result = caller->basicAck();\n }\n }\n```\n\n2. Negative client acknowledgement:\n```ballerina\n listener rabbitmq:Listener channelListener= new(rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT);\n \n @rabbitmq:ServiceConfig {\n queueName: \"MyQueue\",\n autoAck: false\n }\n service rabbitmq:Service on channelListener {\n remote function onMessage(rabbitmq:BytesMessage message) {\n rabbitmq:Error? result = caller->basicNack(true, requeue = false);\n }\n }\n```\n\nThe negatively-acknowledged (rejected) messages can be re-queued by setting the `requeue` to `true`.\n\n### Report issues\n\nTo report bugs, request new features, start new discussions, view project boards, etc., go to the [Ballerina standard library parent repository](https:\/\/github.com\/ballerina-platform\/ballerina-standard-library).\n\n### Useful links\n\n- Chat live with us via our [Discord server](https:\/\/discord.gg\/ballerinalang).\n- Post all technical questions on Stack Overflow with the [#ballerina](https:\/\/stackoverflow.com\/questions\/tagged\/ballerina) tag.", + "template": false, + "licenses": [ + "Apache-2.0" + ], + "authors": [ + "Ballerina" + ], + "sourceCodeLocation": "https://github.com/ballerina-platform/module-ballerinax-rabbitmq", + "keywords": [ + "service", + "client", + "messaging", + "network", + "pubsub" + ], + "ballerinaVersion": "2201.10.0", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerinax_rabbitmq_3.1.0.png", + "ownerUUID": "b5a9e54d-8ade-47a1-8abc-6bc46e89069d", + "createdDate": 1724153093000, + "pullCount": 24, + "visibility": "public", + "modules": [ + { + "packageURL": "/ballerinax/rabbitmq/3.1.0", + "apiDocURL": "https://lib.ballerina.io/ballerinax/rabbitmq/3.1.0", + "name": "rabbitmq", + "summary": "This module provides the capability to send and receive messages by connecting to the RabbitMQ server.", + "readme": "## Overview\n\nThis module provides the capability to send and receive messages by connecting to the RabbitMQ server.\n\nRabbitMQ gives your applications a common platform to send and receive messages and a safe place for your messages to live until received. RabbitMQ is one of the most popular open-source message brokers. It is lightweight and easy to deploy on-premise and in the cloud.\n\n### Basic usage\n\n#### Set up the connection\n\nFirst, you need to set up the connection with the RabbitMQ server. The following ways can be used to connect to a\nRabbitMQ server.\n\n1. Connect to a RabbitMQ node with the default host and port:\n```ballerina\n rabbitmq:Client rabbitmqClient = check new(rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT);\n```\n\n2. Connect to a RabbitMQ node with a custom host and port:\n```ballerina\n rabbitmq:Client rabbitmqClient = check new(\"localhost\", 5672);\n```\n\n3. Connect to a RabbitMQ node with host, port, and additional configurations:\n```ballerina\n rabbitmq:ConnectionConfiguration config = {\n username: \"ballerina\",\n password: \"password\"\n };\n rabbitmq:Client rabbitmqClient = check new(\"localhost\", 5672, configs);\n```\n\nThe `rabbitmq:Client` can now be used to send and receive messages as described in the subsequent sections.\n\n#### Exchanges and queues\n\nClient applications work with exchanges and queues, which are the high-level building blocks of the AMQP protocol. These must be declared before they can be used. The following code declares an exchange and a server-named queue and then binds them together.\n\n```ballerina\n check rabbitmqClient->exchangeDeclare(\"MyExchange\", rabbitmq:DIRECT_EXCHANGE);\n check rabbitmqClient->queueDeclare(\"MyQueue\");\n check rabbitmqClient->queueBind(\"MyQueue\", \"MyExchange\", \"routing-key\");\n```\n\nThis sample code will declare,\n- a durable auto-delete exchange of the type `rabbitmq:DIRECT_EXCHANGE`\n- a non-durable, exclusive auto-delete queue with an auto-generated name\n\nNext, the `queueBind` function is called to bind the queue to the exchange with the given routing key.\n\n```ballerina\n check rabbitmqClient->exchangeDeclare(\"MyExchange\", rabbitmq:DIRECT_EXCHANGE);\n check rabbitmqClient->queueDeclare(\"MyQueue\", { durable: true,\n exclusive: false,\n autoDelete: false });\n check rabbitmqClient->queueBind(\"MyQueue\", \"MyExchange\", \"routing-key\");\n```\n\nThis sample code will declare,\n- a durable auto-delete exchange of the type `rabbitmq:DIRECT_EXCHANGE`\n- a durable, non-exclusive, non-auto-delete queue with a well-known name\n\n#### Delete entities and purge queues\n\n- Delete a queue:\n```ballerina\n check rabbitmqClient->queueDelete(\"MyQueue\");\n```\n- Delete a queue only if it is empty:\n```ballerina\n check rabbitmqClient->queueDelete(\"MyQueue\", false, true);\n```\n- Delete a queue only if it is unused (does not have any consumers):\n```ballerina\n check rabbitmqClient->queueDelete(\"MyQueue\", true, false);\n```\n- Delete an exchange:\n```ballerina\n check rabbitmqClient->exchangeDelete(\"MyExchange\");\n```\n- Purge a queue (delete all of its messages):\n```ballerina\n check rabbitmqClient->queuePurge(\"MyQueue\");\n```\n\n#### Publish messages\n\nTo publish a message to an exchange, use the `publishMessage()` function as follows:\n\n```ballerina\n string message = \"Hello from Ballerina\";\n check rabbitmqClient->publishMessage({ content: message.toBytes(), routingKey: queueName });\n``` \nSetting other properties of the message such as routing headers can be done by using the `BasicProperties` record with the appropriate values.\n\n```ballerina\n rabbitmq:BasicProperties props = {\n replyTo: \"reply-queue\" \n };\n string message = \"Hello from Ballerina\";\n check rabbitmqClient->publishMessage({ content: message.toBytes(), routingKey: queueName, properties: props });\n```\n\n#### Consume messages using consumer services\n\nThe most efficient way to receive messages is to set up a subscription using a Ballerina RabbitMQ `rabbitmq:Listener` and any number of consumer services. The messages will then be delivered automatically as they arrive rather than having to be explicitly requested. Multiple consumer services can be bound to one Ballerina RabbitMQ `rabbitmq:Listener`. The queue to which the service is listening is configured in the `rabbitmq:ServiceConfig` annotation of the service or else as the name of the service.\n\n1. Listen to incoming messages with the `onMessage` remote method:\n\n```ballerina\n listener rabbitmq:Listener channelListener= new(rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT);\n \n @rabbitmq:ServiceConfig {\n queueName: \"MyQueue\"\n }\n service rabbitmq:Service on channelListener {\n remote function onMessage(rabbitmq:AnydataMessage message) {\n }\n }\n```\n\n2. Listen to incoming messages and reply directly with the `onRequest` remote method:\n\n```ballerina\n listener rabbitmq:Listener channelListener= new(rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT);\n \n @rabbitmq:ServiceConfig {\n queueName: \"MyQueue\"\n }\n service rabbitmq:Service on channelListener {\n remote function onRequest(rabbitmq:AnydataMessage message) returns string {\n return \"Hello Back!\";\n }\n }\n```\n\nThe `rabbitmq:AnydataMessage` record received can be used to retrieve its contents.\n\n### Advanced usage\n\n#### Client acknowledgements\n\nThe message consuming is supported by mainly two types of acknowledgement modes, which are auto acknowledgements and client acknowledgements.\nClient acknowledgements can further be divided into two different types as positive and negative acknowledgements.\nThe default acknowledgement mode is auto-ack (messages are acknowledged immediately after consuming). The following examples show the usage of positive and negative acknowledgements.\n> WARNING: To ensure the reliability of receiving messages, use the client-ack mode.\n\n1. Positive client acknowledgement:\n```ballerina\n listener rabbitmq:Listener channelListener= new(rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT);\n \n @rabbitmq:ServiceConfig {\n queueName: \"MyQueue\",\n autoAck: false\n }\n service rabbitmq:Service on channelListener {\n remote function onMessage(rabbitmq:BytesMessage message, rabbitmq:Caller caller) {\n rabbitmq:Error? result = caller->basicAck();\n }\n }\n```\n\n2. Negative client acknowledgement:\n```ballerina\n listener rabbitmq:Listener channelListener= new(rabbitmq:DEFAULT_HOST, rabbitmq:DEFAULT_PORT);\n \n @rabbitmq:ServiceConfig {\n queueName: \"MyQueue\",\n autoAck: false\n }\n service rabbitmq:Service on channelListener {\n remote function onMessage(rabbitmq:BytesMessage message) {\n rabbitmq:Error? result = caller->basicNack(true, requeue = false);\n }\n }\n```\n\nThe negatively-acknowledged (rejected) messages can be re-queued by setting the `requeue` to `true`." + } + ], + "balToolId": "", + "graalvmCompatible": "Yes" + }, + "serviceTypes": [ + { + "name": "RabbitMQ", + "description": "RabbitMQ Service", + "enabled": true, + "basePath": { + "optional": false, + "typeName": "string", + "type": [ + "string" + ], + "defaultable": false, + "documentation": "The RabbitMQ queue name.", + "enabled": true, + "value": "", + "placeholder": "queue-name" + }, + "functions": [ + { + "name": "onMessage", + "documentation": "The `onMessage` remote method will be triggered when a message is received in the specified queue.", + "optional": false, + "qualifiers": [ + "remote" + ], + "enabled": true, + "parameters": [ + { + "name": "message", + "typeName": "rabbitmq:AnydataMessage", + "type": [ + "rabbitmq:AnydataMessage" + ], + "optional": false, + "typeInfo": { + "name": "AnydataMessage", + "orgName": "ballerinax", + "moduleName": "rabbitmq", + "version": "3.1.0" + }, + "documentation": "The message received from the RabbitMQ queue.", + "enabled": true, + "value": "rabbitmq:AnydataMessage" + }, + { + "name": "caller", + "typeName": "rabbitmq:Caller", + "type": [ + "rabbitmq:Caller" + ], + "typeInfo": { + "name": "Caller", + "orgName": "ballerinax", + "moduleName": "rabbitmq", + "version": "3.1.0" + }, + "optional": true, + "documentation": "The RabbitMQ caller object to acknowledge the message.", + "enabled": false + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + }, + "group": { + "id": 1, + "name": "group-1", + "type": "exclusive", + "documentation": "RabbitMQ message trigger method.", + "default": "onMessage" + } + }, + { + "name": "onRequest", + "documentation": "The `onRequest` remote method will be triggered when a message is received in the specified queue and a response is expected.", + "optional": false, + "qualifiers": [ + "remote" + ], + "enabled": false, + "parameters": [ + { + "name": "message", + "typeName": "rabbitmq:AnydataMessage", + "type": [ + "rabbitmq:AnydataMessage" + ], + "optional": false, + "typeInfo": { + "name": "AnydataMessage", + "orgName": "ballerinax", + "moduleName": "rabbitmq", + "version": "3.1.0" + }, + "documentation": "The message received from the RabbitMQ queue.", + "enabled": true, + "value": "rabbitmq:AnydataMessage" + }, + { + "name": "caller", + "typeName": "rabbitmq:Caller", + "type": [ + "rabbitmq:Caller" + ], + "typeInfo": { + "name": "Caller", + "orgName": "ballerinax", + "moduleName": "rabbitmq", + "version": "3.1.0" + }, + "optional": true, + "documentation": "The RabbitMQ caller object to acknowledge the message.", + "enabled": "false" + } + ], + "returnType": { + "typeName": "anydata|error", + "type": [ + "anydata|error" + ], + "optional": false, + "documentation": "The response message.", + "editable": false, + "enabled": true, + "value": "anydata|error" + }, + "group": { + "id": 1, + "name": "group-1", + "type": "exclusive", + "documentation": "RabbitMQ message trigger method.", + "default": "onMessage" + } + }, + { + "name": "onError", + "optional": true, + "documentation": "The `onError` remote method will be triggered when an error occurs during the message processing.", + "qualifiers": [ + "remote" + ], + "enabled": false, + "parameters": [ + { + "name": "message", + "typeName": "rabbitmq:AnydataMessage", + "type": [ + "rabbitmq:AnydataMessage" + ], + "optional": false, + "typeInfo": { + "name": "AnydataMessage", + "orgName": "ballerinax", + "moduleName": "rabbitmq", + "version": "3.1.0" + }, + "documentation": "The message received from the RabbitMQ queue.", + "enabled": "true", + "value": "rabbitmq:AnydataMessage" + }, + { + "name": "err", + "typeName": "rabbitmq:Error", + "type": [ + "rabbitmq:Error" + ], + "typeInfo": { + "name": "Error", + "orgName": "ballerinax", + "moduleName": "rabbitmq", + "version": "3.1.0" + }, + "optional": false, + "documentation": "The error occurred during the message processing.", + "enabled": "true", + "value": "rabbitmq:Error" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": "true", + "value": "error?" + } + } + ] + } + ], + "listener": { + "metadata": { + "label": "RabbitMQ Listener", + "description": "The RabbitMQ listener listens to messages from a RabbitMQ server." + }, + "valueType": "rabbitmq:Listener", + "valueTypeConstraint": "rabbitmq:Listener", + "value": "", + "enabled": true, + "optional": false, + "editable": true, + "properties": { + "host": { + "metadata": { + "label": "host", + "description": "The RabbitMQ server host name used for establishing the connection." + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "rabbitmq:DEFAULT_HOST", + "editable": true, + "optional": false, + "advanced": false + }, + "port": { + "metadata": { + "label": "port", + "description": "The RabbitMQ server port used for establishing the connection." + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "rabbitmq:DEFAULT_PORT", + "editable": true, + "optional": false, + "advanced": false + }, + "qosSettings": { + "metadata": { + "label": "qosSettings", + "description": "The consumer prefetch settings." + }, + "valueType": "rabbitmq:QosSettings", + "valueTypeConstraint": "rabbitmq:QosSettings", + "value": "", + "placeholder": "", + "editable": true, + "optional": true, + "advanced": true, + "fields": { + "prefetchCount": { + "metadata": { + "label": "prefetchCount", + "description": "The maximum number of messages that the server will deliver. Give the value as 0 if unlimited" + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "", + "editable": true, + "optional": false, + "advanced": false + }, + "prefetchSize": { + "metadata": { + "label": "prefetchSize", + "description": "The maximum amount of content (measured in octets) that the server will deliver and 0 if unlimited" + }, + "valueType": "int", + "valueTypeConstraint": "int", + "value": "", + "placeholder": "", + "editable": true, + "optional": true, + "advanced": false + }, + "global": { + "metadata": { + "label": "global", + "description": "Indicates whether the settings should be shared among all the consumers." + }, + "valueType": "boolean", + "valueTypeConstraint": "boolean", + "value": "", + "placeholder": "false", + "editable": true, + "optional": true, + "advanced": false + } + } + }, + "connectionData": { + "metadata": { + "label": "connectionData", + "description": "The connection data used for establishing the connection." + }, + "valueType": "rabbitmq:ConnectionData", + "valueTypeConstraint": "rabbitmq:ConnectionData", + "value": "", + "placeholder": "", + "editable": true, + "optional": true, + "advanced": true, + "fields": { + "username": { + "metadata": { + "label": "username", + "description": "The username used for establishing the connection." + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "editable": true, + "optional": true, + "advanced": false + }, + "password": { + "metadata": { + "label": "password", + "description": "The password used for establishing the connection." + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "editable": true, + "optional": true, + "advanced": false + }, + "virtualHost": { + "metadata": { + "label": "virtualHost", + "description": "The virtual host to use when connecting to the broker." + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "editable": true, + "optional": true, + "advanced": false + }, + "connectionTimeout": { + "metadata": { + "label": "connectionTimeout", + "description": "Connection TCP establishment timeout in seconds and zero for infinite." + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "editable": true, + "optional": true, + "advanced": false + }, + "handshakeTimeout": { + "metadata": { + "label": "handshakeTimeout", + "description": "The AMQP 0-9-1 protocol handshake timeout in seconds." + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "editable": true, + "optional": true, + "advanced": false + }, + "shutdownTimeout": { + "metadata": { + "label": "shutdownTimeout", + "description": "Shutdown timeout in seconds, zero for infinite, and the default value is 10. If the consumers exceed this timeout, then any remaining queued deliveries (and other Consumer callbacks) will be lost." + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "editable": true, + "optional": true, + "advanced": false + }, + "heartbeat": { + "metadata": { + "label": "heartbeat", + "description": "The initially-requested heartbeat timeout in seconds and zero for none." + }, + "valueType": "decimal", + "valueTypeConstraint": "decimal", + "value": "", + "placeholder": "", + "editable": true, + "optional": true, + "advanced": false + }, + "validation": { + "metadata": { + "label": "validation", + "description": "Configuration related to constraint validation check." + }, + "valueType": "boolean", + "valueTypeConstraint": "boolean", + "value": "", + "placeholder": "true", + "editable": true, + "optional": true, + "advanced": false + }, + "secureSocket": { + "metadata": { + "label": "secureSocket", + "description": "Configurations for facilitating secure connections." + }, + "valueType": "rabbitmq:SecureSocket", + "valueTypeConstraint": "rabbitmq:SecureSocket", + "value": "", + "placeholder": "", + "editable": true, + "optional": true, + "advanced": true, + "fields": { + "cert": { + "metadata": { + "label": "cert", + "description": "Configurations associated with `crypto:TrustStore` or single certificate file that the client trusts" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "key": { + "metadata": { + "label": "key", + "description": "Configurations associated with `crypto:KeyStore` or combination of certificate and private key of the client" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "rabbitmq:CertKey", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "certFile": { + "metadata": { + "label": "certFile", + "description": "A file containing the certificate" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "keyFile": { + "metadata": { + "label": "keyFile", + "description": "A file containing the private key in PKCS8 format" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + }, + "keyPassword": { + "metadata": { + "label": "keyPassword", + "description": "Password of the private key if it is encrypted" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false + } + }, + "protocol": { + "metadata": { + "label": "protocol", + "description": "SSL/TLS protocol related options" + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "record", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": true, + "fields": { + "name": { + "metadata": { + "label": "name", + "description": "The name of the protocol" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "optional": true, + "editable": true, + "advanced": false, + "enum": [ + "rabbitmq:SSL", + "rabbitmq:TLS", + "rabbitmq:DTLS" + ] + } + } + }, + "verifyHostName": { + "metadata": { + "label": "verifyHostName", + "description": "Enable/disable host name verification" + }, + "valueType": "boolean", + "valueTypeConstraint": "boolean", + "value": "", + "placeholder": "true", + "optional": true, + "editable": true, + "advanced": false + } + } + }, + "auth": { + "metadata": { + "label": "auth", + "description": "Configurations related to authentication." + }, + "valueType": "rabbitmq:Credentials", + "valueTypeConstraint": "rabbitmq:Credentials", + "value": "", + "placeholder": "", + "editable": true, + "optional": true, + "advanced": true, + "fields": { + "username": { + "metadata": { + "label": "username", + "description": "Username to use for authentication" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "editable": true, + "optional": true, + "advanced": false + }, + "password": { + "metadata": { + "label": "password", + "description": "Password/secret/token to use for authentication" + }, + "valueType": "string", + "valueTypeConstraint": "string", + "value": "", + "placeholder": "", + "editable": true, + "optional": true, + "advanced": false + } + } + } + } + } + } + } + } +} diff --git a/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/salesforce.json b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/salesforce.json new file mode 100644 index 000000000000..28f8e6c61cf0 --- /dev/null +++ b/misc/ls-extensions/modules/trigger-service/src/main/resources/inbuilt-triggers/salesforce.json @@ -0,0 +1,393 @@ +{ + "id": 8, + "name": "Salesforce Service", + "type": "inbuilt", + "displayName": "Salesforce", + "documentation": "This Salesforce service can be attached to a Salesforce listener which listens to Salesforce events and triggers the service when an event occurs.", + "listenerProtocol": "salesforce", + "displayAnnotation": { + "label": "Salesforce", + "iconPath": "docs/icon.png" + }, + "package": { + "id": 15892, + "organization": "ballerinax", + "name": "salesforce", + "version": "8.1.0", + "platform": "java17", + "languageSpecificationVersion": "2024R1", + "isDeprecated": false, + "deprecateMessage": "", + "URL": "/ballerinax/salesforce/8.1.0", + "balaVersion": "2.0.0", + "balaURL": "https://fileserver.central.ballerina.io/2.0/ballerinax/salesforce/8.1.0/ballerinax-salesforce-java17-8.1.0.bala?Expires=1730886498&Signature=SNtxnwUSeYzR6odZVf-z~jtLoIEC27UQXvC9FX65P58jJdte0vMkPHp7GMRCrJCvvZXbid4bXtFhNiEtd~j5mBq-8qSaNeY2KnqH~rrIBG4gEf2IYzcndXWx3aJJi4G6rUQBzvph8XA-4Q1HTi6r8XaK-kY3ngT0msa9LOExdCfrYqMFerTJlYyRvRltGziVTH9yG8gI6Ukfh2OCmmVE9q-5lIf48yKqQuTQiWUpWhfBcyecPSxQ0XvTGtcxbGpsIpBj2346jTrvIVkc7G3q-nOubYJiUd4CwqcHZrfbJNWsNYnWU3UEhCjWrfvnUtbznETnP1ziWoE2ezkPIEmXqQ__&Key-Pair-Id=K27IQ7NPTKLKDU", + "digest": "sha-256=48743d8381d2edee978813d70d0dc603709916d6c718e4519f26feb7789f0065", + "summary": "Salesforce Sales Cloud is one of the leading Customer Relationship Management(CRM) software, provided by Salesforce.Inc. Salesforce enable users to efficiently manage sales and customer relationships through its APIs, robust and secure databases, and analytics services. Sales cloud provides serveral API packages to make operations on sObjects and metadata, execute queries and searches, and listen to change events through API calls using REST, SOAP, and CometD protocols. ", + "readme": "## Overview\n\nSalesforce Sales Cloud is one of the leading Customer Relationship Management(CRM) software, provided by Salesforce.Inc. Salesforce enable users to efficiently manage sales and customer relationships through its APIs, robust and secure databases, and analytics services. Sales cloud provides serveral API packages to make operations on sObjects and metadata, execute queries and searches, and listen to change events through API calls using REST, SOAP, and CometD protocols. \n\nBallerina Salesforce connector supports [Salesforce v59.0 REST API](https:\/\/developer.salesforce.com\/docs\/atlas.en-us.224.0.api_rest.meta\/api_rest\/intro_what_is_rest_api.htm), [Salesforce v59.0 SOAP API](https:\/\/developer.salesforce.com\/docs\/atlas.en-us.api.meta\/api\/sforce_api_quickstart_intro.htm), [Salesforce v59.0 APEX REST API](https:\/\/developer.salesforce.com\/docs\/atlas.en-us.apexcode.meta\/apexcode\/apex_rest_intro.htm), [Salesforce v59.0 BULK API](https:\/\/developer.salesforce.com\/docs\/atlas.en-us.api_asynch.meta\/api_asynch\/api_asynch_introduction_bulk_api.htm), and [Salesforce v59.0 BULK V2 API](https:\/\/developer.salesforce.com\/docs\/atlas.en-us.api_asynch.meta\/api_asynch\/bulk_api_2_0.htm).\n\n## Setup guide\n\n1. Create a Salesforce account with the REST capability.\n\n2. Go to Setup --> Apps --> App Manager \n\n \"Setup\n\n3. Create a New Connected App.\n\n \"Create\n\n - Here we will be using https:\/\/test.salesforce.com as we are using sandbox environment. Users can use https:\/\/login.salesforce.com for normal usage.\n\n \"Create\n\n4. After the creation user can get consumer key and secret through clicking on the `Manage Consumer Details` button.\n\n \"Consumer\n\n5. Next step would be to get the token.\n - Log in to salesforce in your preferred browser and enter the following url.\n ```https:\/\/.salesforce.com\/services\/oauth2\/authorize?response_type=code&client_id=&redirect_uri=```\n - Allow access if an alert pops up and the browser will be redirected to a Url like follows.\n \n `https:\/\/login.salesforce.com\/?code=`\n \n - The code can be obtained after decoding the encoded code\n\n6. Get Access and Refresh tokens\n - Following request can be sent to obtain the tokens.\n \n ```curl -X POST https:\/\/.salesforce.com\/services\/oauth2\/token?code=&grant_type=authorization_code&client_id=&client_secret=&redirect_uri=https:\/\/test.salesforce.com\/``` \n - Tokens can be obtained from the response.\n\n## Quickstart\n\nTo use the Salesforce connector in your Ballerina application, modify the .bal file as follows:\n\n#### Step 1: Import connector\n\nImport the `ballerinax\/salesforce` package into the Ballerina project.\n\n```ballerina\nimport ballerinax\/salesforce;\n```\n\n#### Step 2: Create a new connector instance\n\nCreate a `salesforce:ConnectionConfig` with the obtained OAuth2 tokens and initialize the connector with it.\n```ballerina\nsalesforce:ConnectionConfig config = {\n baseUrl: baseUrl,\n auth: {\n clientId: clientId,\n clientSecret: clientSecret,\n refreshToken: refreshToken,\n refreshUrl: refreshUrl\n }\n};\n\nsalesforce:Client salesforce = check new (config);\n```\n\n#### Step 3: Invoke connector operation\n\n1. Now you can utilize the available operations. Note that they are in the form of remote operations. \n\nFollowing is an example on how to create a record using the connector.\n\n ```ballerina\n salesforce:CreationResponse response = check \n baseClient->create(\"Account\", {\n \"Name\": \"IT World\",\n \"BillingCity\": \"New York\"\n });\n\n ```\n\n2. To integrate the Salesforce listener into your Ballerina application, update the .bal file as follows:\n\nCreate an instance of `salesforce:Listener` using your Salesforce username, password, security token, and subscribe channel name.\n\n```ballerina\nimport ballerinax\/salesforce;\n\nsalesforce:ListenerConfig listenerConfig = {\n auth: {\n username: \"username\",\n password: \"password\" + \"security token\"\n }\n};\nlistener salesforce:Listener eventListener = new (listenerConfig);\n```\n\nImplement the listener?s remote functions and specify the channel name to be subscribed to as the service name.\n\n```ballerina\nimport ballerina\/io;\nimport ballerinax\/salesforce;\n\nsalesforce:ListenerConfig listenerConfig = {\n auth: {\n username: \"username\",\n password: \"password\" + \"security token\"\n }\n};\nlistener salesforce:Listener eventListener = new (listenerConfig);\n\nservice \"\/data\/ChangeEvents\" on eventListener {\n remote function onCreate(salesforce:EventData payload) {\n io:println(\"Created \" + payload.toString());\n }\n\n remote isolated function onUpdate(salesforce:EventData payload) {\n io:println(\"Updated \" + payload.toString());\n }\n\n remote function onDelete(salesforce:EventData payload) {\n io:println(\"Deleted \" + payload.toString());\n }\n\n remote function onRestore(salesforce:EventData payload) {\n io:println(\"Restored \" + payload.toString());\n }\n}\n```\n\n3. Integrate custom SObject types\n\nTo seamlessly integrate custom SObject types into your Ballerina project, you have the option to either generate a package using the Ballerina Open API tool or utilize the `ballerinax\/salesforce.types` module. Follow the steps given [here](https:\/\/github.com\/ballerina-platform\/module-ballerinax-salesforce\/blob\/master\/ballerina\/modules\/types\/Module.md) based on your preferred approach.\n\n```ballerina\nimport ballerinax\/salesforce.types;\n\npublic function main() returns error? {\n types:AccountSObject accountRecord = {\n Name: \"IT World\",\n BillingCity: \"New York\"\n };\n\n salesforce:CreationResponse res = check salesforce->create(\"Account\", accountRecord);\n}\n```\n\n4. Use following command to compile and run the Ballerina program.\n\n```\nbal run\n````\n\n## Examples\n\nThe `salesforce` integration samples illustrate its usage in various integration scenarios. Explore these examples below, covering the use of salesforce APIs in integrations.\n\n## Examples\n\nThe `salesforce` connector provides practical examples illustrating usage in various scenarios. Explore these examples below, covering use cases like creating sObjects, retrieving records, and executing bulk operations.\n\n1. [Salesforce REST API use cases](https:\/\/github.com\/ballerina-platform\/module-ballerinax-salesforce\/tree\/master\/examples\/rest_api_usecases) - How to employ REST API of Salesforce to carryout various tasks.\n\n2. [Salesforce Bulk API use cases](https:\/\/github.com\/ballerina-platform\/module-ballerinax-salesforce\/tree\/master\/examples\/bulk_api_usecases) - How to employ Bulk API of Salesforce to execute Bulk jobs.\n\n3. [Salesforce Bulk v2 API use cases](https:\/\/github.com\/ballerina-platform\/module-ballerinax-salesforce\/tree\/master\/examples\/bulkv2_api_usecases) - How to employ Bulk v2 API to execute an ingest job.\n\n4. [Salesforce APEX REST API use cases](https:\/\/github.com\/ballerina-platform\/module-ballerinax-salesforce\/tree\/master\/examples\/apex_rest_api_usecases) - How to employ APEX REST API to create a case in Salesforce.", + "template": false, + "licenses": [ + "Apache-2.0" + ], + "authors": [ + "Ballerina" + ], + "sourceCodeLocation": "https://github.com/ballerina-platform/module-ballerinax-salesforce", + "keywords": [ + "Sales & CRM/Customer Relationship Management", + "Cost/Freemium" + ], + "ballerinaVersion": "2201.10.0", + "icon": "https://bcentral-packageicons.azureedge.net/images/ballerinax_salesforce_8.1.0.png", + "ownerUUID": "b5a9e54d-8ade-47a1-8abc-6bc46e89069d", + "createdDate": 1724472595000, + "pullCount": 166, + "visibility": "public", + "modules": [ + { + "packageURL": "/ballerinax/salesforce/8.1.0", + "apiDocURL": "https://lib.ballerina.io/ballerinax/salesforce/8.1.0", + "name": "salesforce", + "summary": "Salesforce Sales Cloud is one of the leading Customer Relationship Management(CRM) software, provided by Salesforce.Inc. Salesforce enable users to efficiently manage sales and customer relationships through its APIs, robust and secure databases, and analytics services. Sales cloud provides serveral API packages to make operations on sObjects and metadata, execute queries and searches, and listen to change events through API calls using REST, SOAP, and CometD protocols. ", + "readme": "## Overview\n\nSalesforce Sales Cloud is one of the leading Customer Relationship Management(CRM) software, provided by Salesforce.Inc. Salesforce enable users to efficiently manage sales and customer relationships through its APIs, robust and secure databases, and analytics services. Sales cloud provides serveral API packages to make operations on sObjects and metadata, execute queries and searches, and listen to change events through API calls using REST, SOAP, and CometD protocols. \n\nBallerina Salesforce connector supports [Salesforce v59.0 REST API](https:\/\/developer.salesforce.com\/docs\/atlas.en-us.224.0.api_rest.meta\/api_rest\/intro_what_is_rest_api.htm), [Salesforce v59.0 SOAP API](https:\/\/developer.salesforce.com\/docs\/atlas.en-us.api.meta\/api\/sforce_api_quickstart_intro.htm), [Salesforce v59.0 APEX REST API](https:\/\/developer.salesforce.com\/docs\/atlas.en-us.apexcode.meta\/apexcode\/apex_rest_intro.htm), [Salesforce v59.0 BULK API](https:\/\/developer.salesforce.com\/docs\/atlas.en-us.api_asynch.meta\/api_asynch\/api_asynch_introduction_bulk_api.htm), and [Salesforce v59.0 BULK V2 API](https:\/\/developer.salesforce.com\/docs\/atlas.en-us.api_asynch.meta\/api_asynch\/bulk_api_2_0.htm).\n\n## Setup guide\n\n1. Create a Salesforce account with the REST capability.\n\n2. Go to Setup --> Apps --> App Manager \n\n \"Setup\n\n3. Create a New Connected App.\n\n \"Create\n\n - Here we will be using https:\/\/test.salesforce.com as we are using sandbox environment. Users can use https:\/\/login.salesforce.com for normal usage.\n\n \"Create\n\n4. After the creation user can get consumer key and secret through clicking on the `Manage Consumer Details` button.\n\n \"Consumer\n\n5. Next step would be to get the token.\n - Log in to salesforce in your preferred browser and enter the following url.\n ```\n https:\/\/.salesforce.com\/services\/oauth2\/authorize?response_type=code&client_id=&redirect_uri=\n ```\n - Allow access if an alert pops up and the browser will be redirected to a Url like follows.\n \n ```\n https:\/\/login.salesforce.com\/?code=\n ```\n \n - The code can be obtained after decoding the encoded code\n\n6. Get Access and Refresh tokens\n - Following request can be sent to obtain the tokens.\n \n ```\n curl -X POST https:\/\/.salesforce.com\/services\/oauth2\/token?code=&grant_type=authorization_code&client_id=&client_secret=&redirect_uri=https:\/\/test.salesforce.com\/\n ``` \n - Tokens can be obtained from the response.\n\n## Quickstart\n\nTo use the Salesforce connector in your Ballerina application, modify the .bal file as follows:\n\n#### Step 1: Import connector\n\nImport the `ballerinax\/salesforce` package into the Ballerina project.\n\n```ballerina\nimport ballerinax\/salesforce;\n```\n\n#### Step 2: Create a new connector instance\n\nCreate a `salesforce:ConnectionConfig` with the obtained OAuth2 tokens and initialize the connector with it.\n```ballerina\nsalesforce:ConnectionConfig config = {\n baseUrl: baseUrl,\n auth: {\n clientId: clientId,\n clientSecret: clientSecret,\n refreshToken: refreshToken,\n refreshUrl: refreshUrl\n }\n};\n\nsalesforce:Client salesforce = check new (config);\n```\n\n#### Step 3: Invoke connector operation\n\n1. Now you can utilize the available operations. Note that they are in the form of remote operations. \n\nFollowing is an example on how to create a record using the connector.\n\n ```ballerina\n salesforce:CreationResponse response = check \n salesforce->create(\"Account\", {\n \"Name\": \"IT World\",\n \"BillingCity\": \"New York\"\n });\n\n ```\n\n2. To integrate the Salesforce listener into your Ballerina application, update the .bal file as follows:\n\nCreate an instance of `salesforce:Listener` using your Salesforce username, password, security token, and subscribe channel name.\n\n```ballerina\nimport ballerinax\/salesforce;\n\nsalesforce:ListenerConfig listenerConfig = {\n auth: {\n username: \"username\",\n password: \"password\" + \"security token\"\n }\n};\nlistener salesforce:Listener eventListener = new (listenerConfig);\n```\n\nImplement the listener?s remote functions and specify the channel name to be subscribed to as the service name.\n\n```ballerina\nimport ballerina\/io;\nimport ballerinax\/salesforce;\n\nsalesforce:ListenerConfig listenerConfig = {\n auth: {\n username: \"username\",\n password: \"password\" + \"security token\"\n }\n};\nlistener salesforce:Listener eventListener = new (listenerConfig);\n\nservice \"\/data\/ChangeEvents\" on eventListener {\n remote function onCreate(salesforce:EventData payload) {\n io:println(\"Created \" + payload.toString());\n }\n\n remote isolated function onUpdate(salesforce:EventData payload) {\n io:println(\"Updated \" + payload.toString());\n }\n\n remote function onDelete(salesforce:EventData payload) {\n io:println(\"Deleted \" + payload.toString());\n }\n\n remote function onRestore(salesforce:EventData payload) {\n io:println(\"Restored \" + payload.toString());\n }\n}\n```\n\n3. Integrate custom SObject types\n\nTo seamlessly integrate custom SObject types into your Ballerina project, you have the option to either generate a package using the Ballerina Open API tool or utilize the `ballerinax\/salesforce.types` module. Follow the steps given [here](https:\/\/github.com\/ballerina-platform\/module-ballerinax-salesforce\/blob\/master\/ballerina\/modules\/types\/Module.md) based on your preferred approach.\n\n```ballerina\nimport ballerinax\/salesforce.types;\n\npublic function main() returns error? {\n types:AccountSObject accountRecord = {\n Name: \"IT World\",\n BillingCity: \"New York\"\n };\n\n salesforce:CreationResponse res = check salesforce->create(\"Account\", accountRecord);\n}\n```\n\n4. Use following command to compile and run the Ballerina program.\n\n```\nbal run\n````\n\n## Examples\n\nThe `salesforce` connector provides practical examples illustrating usage in various scenarios. Explore these examples below, covering use cases like creating sObjects, retrieving records, and executing bulk operations.\n\n1. [Salesforce REST API use cases](https:\/\/github.com\/ballerina-platform\/module-ballerinax-salesforce\/tree\/master\/examples\/rest_api_usecases) - How to employ REST API of Salesforce to carryout various tasks.\n\n2. [Salesforce Bulk API use cases](https:\/\/github.com\/ballerina-platform\/module-ballerinax-salesforce\/tree\/master\/examples\/bulk_api_usecases) - How to employ Bulk API of Salesforce to execute Bulk jobs.\n\n3. [Salesforce Bulk v2 API use cases](https:\/\/github.com\/ballerina-platform\/module-ballerinax-salesforce\/tree\/master\/examples\/bulkv2_api_usecases) - How to employ Bulk v2 API to execute an ingest job.\n\n4. [Salesforce APEX REST API use cases](https:\/\/github.com\/ballerina-platform\/module-ballerinax-salesforce\/tree\/master\/examples\/apex_rest_api_usecases) - How to employ APEX REST API to create a case in Salesforce." + }, + { + "packageURL": "/ballerinax/salesforce/8.1.0", + "apiDocURL": "https://lib.ballerina.io/ballerinax/salesforce.apex/8.1.0", + "name": "salesforce.apex", + "summary": "Salesforce Apex REST API enables you to expose your Apex classes and methods as RESTful web services. This module provides operations for executing custom Apex REST endpoints, allowing you to perform various HTTP operations on these endpoints and handle responses accordingly.", + "readme": "## Overview\n\nSalesforce Apex REST API enables you to expose your Apex classes and methods as RESTful web services. This module provides operations for executing custom Apex REST endpoints, allowing you to perform various HTTP operations on these endpoints and handle responses accordingly.\n\nBallerina Salesforce Apex REST API client supports the [Salesforce v59.0 APEX REST API](https:\/\/developer.salesforce.com\/docs\/atlas.en-us.apexcode.meta\/apexcode\/apex_rest_intro.htm).\n\n## Setup guide\n\n1. Create a Salesforce account with the REST capability.\n\n2. Go to Setup --> Apps --> App Manager \n\n \"Setup\n\n3. Create a New Connected App.\n\n \"Create\n\n - Here we will be using https:\/\/test.salesforce.com as we are using sandbox environment. Users can use https:\/\/login.salesforce.com for normal usage.\n\n \"Create\n\n4. After the creation user can get consumer key and secret through clicking on the `Manage Consumer Details` button.\n\n \"Consumer\n\n5. The next step is to get the token.\n\n - Log in to Salesforce in your preferred browser and enter the following URL:\n `https:\/\/.salesforce.com\/services\/oauth2\/authorize?response_type=code&client_id=&redirect_uri=`\n - Allow access if an alert pops up, and the browser will be redirected to a URL like the following:\n `https:\/\/login.salesforce.com\/?code=`\n \n - The code can be obtained after decoding the encoded code\n\n6. Get Access and Refresh tokens\n\n - The following request can be sent to obtain the tokens.\n ```curl -X POST https:\/\/.salesforce.com\/services\/oauth2\/token?code=&grant_type=authorization_code&client_id=&client_secret=&redirect_uri=https:\/\/test.salesforce.com\/``` \n - Tokens can be obtained from the response.\n\n## Quickstart\n\nTo use the Salesforce Apex client in your Ballerina application, update the .bal file as follows:\n\n### Step 1: Import connector\n\nImport the `ballerinax\/salesforce.apex` module into the Ballerina project.\n\n```ballerina\nimport ballerinax\/salesforce.apex;\n```\n\n### Step 2: Create a new connector instance\n\nCreate a `ConnectionConfig` with the OAuth2 tokens obtained, and initialize the connector with it.\n```ballerina\napex:ConnectionConfig sfConfig = {\n baseUrl: baseUrl,\n auth: {\n clientId: clientId,\n clientSecret: clientSecret,\n refreshToken: refreshToken,\n refreshUrl: refreshUrl\n }\n};\n\napex:Client apexClient = check new (sfConfig);\n```\n\n### Step 3: Invoke connector operation\n\n1. Now you can use the operations available within the connector. Note that they are in the form of remote operations.\nFollowing is an example of how to execute a custom Apex REST endpoint using the connector.\n\n```ballerina\npublic function main() returns error? {\n string caseId = check apexClient->apexRestExecute(\"Cases\", \"POST\",\n {\n \"subject\": \"Item Fault!\",\n \"status\": \"New\",\n \"priority\": \"High\"\n });\n return;\n}\n```\n\n2. Use `bal run` command to compile and run the Ballerina program. \n\n## Examples\n\n1. [Salesforce APEX REST API use cases](https:\/\/github.com\/ballerina-platform\/module-ballerinax-salesforce\/tree\/master\/examples\/apex_rest_api_usecases) - How to employ APEX REST API to create a case in Salesforce." + }, + { + "packageURL": "/ballerinax/salesforce/8.1.0", + "apiDocURL": "https://lib.ballerina.io/ballerinax/salesforce.bulk/8.1.0", + "name": "salesforce.bulk", + "summary": "Salesforce Bulk API is a specialized asynchronous RESTful API for loading and querying bulk of data at once. This module provides bulk data operations for CSV, JSON, and XML data types.", + "readme": "## Overview\nSalesforce Bulk API is a specialized asynchronous RESTful API for loading and querying bulk of data at once. This module provides bulk data operations for CSV, JSON, and XML data types.\n\nThis module supports [Salesforce Bulk API v1](https:\/\/developer.salesforce.com\/docs\/atlas.en-us.224.0.api_asynch.meta\/api_asynch\/asynch_api_reference.htm).\n \n## Setup guide\n\n1. Create a Salesforce account with the REST capability.\n\n2. Go to Setup --> Apps --> App Manager \n\n \"Setup\n\n3. Create a New Connected App.\n\n \"Create\n\n - Here we will be using https:\/\/test.salesforce.com as we are using sandbox environment. Users can use https:\/\/login.salesforce.com for normal usage.\n\n \"Create\n\n4. After the creation user can get consumer key and secret through clicking on the `Manage Consumer Details` button.\n\n \"Consumer\n\n5. Next step would be to get the token.\n - Log in to salesforce in your preferred browser and enter the following url.\n `https:\/\/.salesforce.com\/services\/oauth2\/authorize?response_type=code&client_id=&redirect_uri=`\n - Allow access if an alert pops up and the browser will be redirected to a Url like follows.\n `https:\/\/login.salesforce.com\/?code=`\n \n - The code can be obtained after decoding the encoded code\n\n6. Get Access and Refresh tokens\n - Following request can be sent to obtain the tokens\n ```curl -X POST https:\/\/.salesforce.com\/services\/oauth2\/token?code=&grant_type=authorization_code&client_id=&client_secret=&redirect_uri=https:\/\/test.salesforce.com\/``` \n - Tokens can be obtained from the response.\n\n## Quickstart\nTo use the Salesforce connector in your Ballerina application, update the .bal file as follows:\n### Step 1: Import connector\nImport the `ballerinax\/salesforce.bulk` module into the Ballerina project.\n\n```ballerina\nimport ballerinax\/salesforce.bulk;\n```\n\n### Step 2: Create a new connector instance\nCreate a `ConnectionConfig` with the OAuth2 tokens obtained, and initialize the connector with it.\n```ballerina\nconfigurable string clientId = ?;\nconfigurable string clientSecret = ?;\nconfigurable string refreshToken = ?;\nconfigurable string refreshUrl = ?;\nconfigurable string baseUrl = ?;\n\nbulk:ConnectionConfig sfConfig = {\n baseUrl: baseUrl,\n auth: {\n clientId: clientId,\n clientSecret: clientSecret,\n refreshToken: refreshToken,\n refreshUrl: refreshUrl\n }\n};\n\nbulk:Client bulkClient = check new (sfConfig);\n```\n\n### Step 3: Invoke connector operation\n\n1. Now you can use the operations available within the connector. Note that they are in the form of remote operations. \nFollowing is an example on how to insert bulk contacts using the connector.\n\n```ballerina\njson contacts = [\n {\n description: \"Created_from_Ballerina_Sf_Bulk_API\",\n FirstName: \"Morne\",\n LastName: \"Morkel\",\n Title: \"Professor Grade 03\",\n Phone: \"0442226670\",\n Email: \"morne89@gmail.com\"\n }\n];\n\npublic function main() returns error? {\n bulk:BulkJob insertJob = check bulkClient->createJob(\"insert\", \"Contact\", \"JSON\");\n\n bulk:BatchInfo batch = check bulkClient->addBatch(insertJob, contacts);\n}\n```\n\n2. Use `bal run` command to compile and run the Ballerina program. \n\n**[You can find a list of samples here](https:\/\/github.com\/ballerina-platform\/module-ballerinax-salesforce\/tree\/master\/examples\/bulk_api_usecases)**" + }, + { + "packageURL": "/ballerinax/salesforce/8.1.0", + "apiDocURL": "https://lib.ballerina.io/ballerinax/salesforce.bulkv2/8.1.0", + "name": "salesforce.bulkv2", + "summary": "Salesforce Bulk API 2.0 enables you to handle large data sets asynchronously, optimizing performance for high-volume data operations. This module provides operations for executing bulk jobs and batches, allowing you to perform various data operations efficiently.", + "readme": "## Overview\n\nSalesforce Bulk API 2.0 enables you to handle large data sets asynchronously, optimizing performance for high-volume data operations. This module provides operations for executing bulk jobs and batches, allowing you to perform various data operations efficiently.\n\n## Setup guide\n\n1. Create a Salesforce Account with the Bulk API 2.0 Capability.\n\n2. Go to Setup --> Apps --> App Manager\n\n \"Setup\n\n3. Create a New Connected App.\n\n \"Create\n\n - Here we will be using https:\/\/test.salesforce.com as we are using sandbox environment. Users can use https:\/\/login.salesforce.com for normal usage.\n\n \"Create\n\n4. After the creation user can get consumer key and secret through clicking on the `Manage Consumer Details` button.\n\n \"Consumer\n\n5. The next step is to get the token.\n\n - Log in to Salesforce in your preferred browser and enter the following URL:\n `https:\/\/.salesforce.com\/services\/oauth2\/authorize?response_type=code&client_id=&redirect_uri=`\n - Allow access if an alert pops up, and the browser will be redirected to a URL like the following:\n `https:\/\/login.salesforce.com\/?code=`\n\n - The code can be obtained after decoding the encoded code\n\n6. Get Access and Refresh tokens\n\n - The following request can be sent to obtain the tokens.\n ```curl -X POST https:\/\/.salesforce.com\/services\/oauth2\/token?code=&grant_type=authorization_code&client_id=&client_secret=&redirect_uri=https:\/\/test.salesforce.com\/```\n - Tokens can be obtained from the response.\n\n## Quickstart\n\nTo use the Salesforce Bulk API client in your Ballerina application, update the .bal file as follows:\n\n#### Step 1: Import connector\n\nImport the `ballerinax\/salesforce.bulkv2` package into the Ballerina project.\n\n```ballerina\nimport ballerinax\/salesforce.bulkv2;\n```\n\n#### Step 2: Create a new connector instance\n\nCreate a `salesforce:ConnectionConfig` with the obtained OAuth2 tokens and initialize the connector with it.\n```ballerina\nbulkv2:ConnectionConfig config = {\n baseUrl: baseUrl,\n auth: {\n clientId: clientId,\n clientSecret: clientSecret,\n refreshToken: refreshToken,\n refreshUrl: refreshUrl\n }\n};\n\nbulkv2:Client bulkv2Client = check new (config);\n```\n\n#### Step 3: Invoke connector operation\n\n1. Now you can use the operations available within the connector. Note that they are in the form of remote operations.\n\nFollowing is an example of how to create a bulk job using the connector.\n\n```ballerina\nbulkv2:BulkCreatePayload payload = {\n 'object: \"Contact\",\n contentType: \"CSV\",\n operation: \"insert\",\n lineEnding: \"LF\"\n};\nbulkv2:BulkJob insertJob = check baseClient->createIngestJob(payload);\n```\n\n2. Use following command to compile and run the Ballerina program.\n\n```\nbal run\n````\n\n## Examples\n\n1. [Salesforce Bulk v2 API use cases](https:\/\/github.com\/ballerina-platform\/module-ballerinax-salesforce\/tree\/master\/examples\/bulkv2_api_usecases) - How to employ Bulk v2 API to execute an ingest job." + }, + { + "packageURL": "/ballerinax/salesforce/8.1.0", + "apiDocURL": "https://lib.ballerina.io/ballerinax/salesforce.soap/8.1.0", + "name": "salesforce.soap", + "summary": "Salesforce SOAP API provides CRUD operations for SObjects and allows you to maintain passwords, perform searches, and much more.", + "readme": "## Overview\nSalesforce SOAP API provides CRUD operations for SObjects and allows you to maintain passwords, perform searches, and much more.\n\nThis module supports [Salesforce v48.0 SOAP API Enterprise WDSL](https:\/\/developer.salesforce.com\/docs\/atlas.en-us.224.0.api.meta\/api\/sforce_api_quickstart_intro.htm).\n \n## Setup guide\n\n1. Create a Salesforce account with the REST capability.\n\n2. Go to Setup --> Apps --> App Manager \n\n \"Setup\n\n3. Create a New Connected App.\n\n \"Create\n\n - Here we will be using https:\/\/test.salesforce.com as we are using sandbox environment. Users can use https:\/\/login.salesforce.com for normal usage.\n\n \"Create\n\n4. After the creation user can get consumer key and secret through clicking on the `Manage Consumer Details` button.\n\n \"Consumer\n\n5. Next step would be to get the token.\n - Log in to salesforce in your preferred browser and enter the following url.\n `https:\/\/.salesforce.com\/services\/oauth2\/authorize?response_type=code&client_id=&redirect_uri=`\n - Allow access if an alert pops up and the browser will be redirected to a Url like follows.\n `https:\/\/login.salesforce.com\/?code=`\n \n - The code can be obtained after decoding the encoded code\n\n6. Get Access and Refresh tokens\n - Following request can be sent to obtain the tokens\n ```curl -X POST https:\/\/.salesforce.com\/services\/oauth2\/token?code=&grant_type=authorization_code&client_id=&client_secret=&redirect_uri=https:\/\/test.salesforce.com\/``` \n - Tokens can be obtained from the response.\n\n## Quickstart\nTo use the Salesforce connector in your Ballerina application, update the .bal file as follows:\n\n### Step 1: Import connector\nImport the `ballerinax\/salesforce.soap` module into the Ballerina project.\n\n```ballerina\nimport ballerinax\/salesforce.soap;\n```\n\n### Step 2: Create a new connector instance\nCreate a `soap:ConnectionConfig` with the OAuth2 tokens obtained, and initialize the connector with it.\n\n```ballerina\n\nsoap:ConnectionConfig config = {\n baseUrl: baseUrl,\n auth: {\n clientId: clientId,\n clientSecret: clientSecret,\n refreshToken: refreshToken,\n refreshUrl: refreshUrl\n }\n};\n\nsoap:Client salesforce = check new (config);\n```\n\n### Step 3: Invoke connector operation\n1. Now you can use the operations available within the connector. Note that they are in the form of remote operations. \nFollowing is an example on how to convert lead using the connector.\n ```ballerina\n public function main() returns error? {\n soap:ConvertedLead response = check soapClient->convertLead({leadId = \"xxx\", convertedStatus: \"Closed - Converted\"});\n }\n ```\n2. Use `bal run` command to compile and run the Ballerina program. \n\n**[You can find a sample here](https:\/\/github.com\/ballerina-platform\/module-ballerinax-salesforce\/tree\/master\/examples\/soap_api_usecases)**" + }, + { + "packageURL": "/ballerinax/salesforce/8.1.0", + "apiDocURL": "https://lib.ballerina.io/ballerinax/salesforce.types/8.1.0", + "name": "salesforce.types", + "summary": "", + "readme": "## Overview\n\nSalesforce is a leading customer relationship management (CRM) platform that helps businesses manage and streamline their sales, service, and marketing operations. The [Ballerina Salesforce Connector](https:\/\/central.ballerina.io\/ballerinax\/salesforce\/latest) is a project designed to enhance integration capabilities with Salesforce by providing a seamless connection for Ballerina. Notably, this Ballerina project incorporates record type definitions for the base types of Salesforce objects, offering a comprehensive and adaptable solution for developers working on Salesforce integration projects.\n\n## Setup Guide\n\nTo customize this project for your Salesforce account and include your custom SObjects, follow the steps below:\n\n### Step 1: Login to Your Salesforce Developer Account\n\nBegin by logging into your [Salesforce Developer Account](https:\/\/developer.salesforce.com\/).\n\n### Step 2: Generate Open API Specification for Your SObjects\n\n#### Step 2.1: Initiate OpenAPI Document Generation\n\nUse the following command to send a POST request to start the OpenAPI document generation process.\n\n```bash\ncurl -X POST -H \"Content-Type: application\/json\" -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\nhttps:\/\/MyDomainName.my.salesforce.com\/services\/data\/vXX.X\/async\/specifications\/oas3 \\\n-d '{\"resources\": [\"*\"]}'\n```\nReplace YOUR_ACCESS_TOKEN and MyDomainName with your actual access token and Salesforce domain. If successful, you'll receive a response with a URI. Extract the locator ID from the URI.\n\n#### Step 2.2: Retrieve the OpenAPI Document\n\nSend a GET request to fetch the generated OpenAPI document using the following command.\n\n```bash\ncurl -X GET -H \"Content-Type: application\/json\" -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\nhttps:\/\/MyDomainName.my.salesforce.com\/services\/data\/vXX.X\/async\/specifications\/oas3\/LOCATOR_ID -o oas.json\n```\nReplace YOUR_ACCESS_TOKEN, MyDomainName, and LOCATOR_ID with your actual values.\n\n### Step 3: Configure Cluster Settings\n\nTo prevent Out-of-Memory (OOM) issues, execute the following command:\n\n```bash\nexport JAVA_OPTS=\"$JAVA_OPTS -DmaxYamlCodePoints=99999999\"\n```\n\nGenerate the Ballerina project for the OpenAPI spec using the Ballerina Open API tool with the following commands.\n\n1. Create a new Ballerina project, naming the project as desired (e.g., custom_types, salesforce_types, etc.).\n\n```bash\nbal new custom_types\n```\n\n2. Customize the package details by editing the `Ballerina.toml` file. For instance, you can modify the [package] section as follows:\n\n```toml\n[package]\norg = \"example\"\nname = \"salesforce.types\"\nversion = \"0.1.0\"\n```\n\nFeel free to replace \"salesforce.types\" with one of the suitable desired names like \"custom.types\" or \"integration.types,\" or come up with your own unique package name.\n\n4. Move the OpenAPI spec into the newly created project directory and execute the following command:\n\n```bash\nbal openapi -i oas.json --mode client --client-methods resource\n```\n\nThis will generate the Ballerina project structure, record types that correspond to the SObject definitions, and client methods based on the provided OpenAPI specification.\n\n### Step 4: Edit the Generated Client and Push it to Local Repository\n\n#### Step 4.1 Delete the utils.bal and clients.bal files.\n\n#### Step 4.2 Use the following commands to build, pack, and push the package:\n\n````bash\nbal pack\n\nbal push --repository=local\n````\n\nBy following these steps, you can set up and customize the Ballerina Salesforce Connector for your Salesforce account with ease.\n\n## Quickstart\n\nTo use the `salesforce.types` module in your Ballerina application, modify the `.bal` file as follows:\n\n### Step 1: Import the package\n\nImport `ballerinax\/salesforce.types` module.\n\n```ballerina\nimport ballerinax\/salesforce;\nimport ballerinax\/salesforce.types;\n```\n\n### Step 2: Instantiate a new client\n\nObtain the tokens using the following the [`ballerinax\/salesforce` connector set up guide](https:\/\/central.ballerina.io\/ballerinax\/salesforce\/latest). Create a salesforce:ConnectionConfig with the obtained OAuth2 tokens and initialize the connector with it.\n\n```ballerina\nsalesforce:ConnectionConfig config = {\n baseUrl: baseUrl,\n auth: {\n clientId: clientId,\n clientSecret: clientSecret,\n refreshToken: refreshToken,\n refreshUrl: refreshUrl\n }\n};\n\nsalesforce:Client salesforce = new(config);\n```\n\n### Step 3: Invoke the connector operation\n\nNow you can utilize the available operations. Note that they are in the form of remote operations. Following is an example on how to create a record using the connector.\n\n```ballerina\nsalesforce:Client salesforce = check new (config);\nstypes:AccountSObject response = {\n Name: \"IT World\",\n BillingCity: \"New York\"\n};\n\nsalesforce:CreationResponse response = check salesforce->create(\"Account\", response);\n```\n\nUse following command to compile and run the Ballerina program.\n\n```bash\nbal run\n```" + } + ], + "balToolId": "", + "graalvmCompatible": "Yes" + }, + "serviceTypes": [ + { + "name": "Salesforce", + "description": "Salesforce Service", + "enabled": true, + "basePath": { + "optional": false, + "typeName": "string", + "type": [ + "string" + ], + "defaultable": false, + "documentation": "The Salesforce channel name.", + "enabled": true, + "value": "", + "placeholder": "/events" + }, + "functions": [ + { + "name": "onCreate", + "documentation": "The `onCreate` method is triggered when a new record create event is received from Salesforce.", + "optional": false, + "qualifiers": [ + "remote" + ], + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "salesforce:EventData", + "optional": false, + "type": [ + "salesforce:EventData" + ], + "typeInfo": { + "name": "EventData", + "orgName": "ballerinax", + "moduleName": "salesforce", + "version": "8.1.0" + }, + "documentation": "The information about the triggered event.", + "enabled": true, + "value": "saleforce:EventData" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "name": "onUpdate", + "documentation": "The `onUpdate` method is triggered when a new record update event is received from Salesforce.", + "optional": false, + "qualifiers": [ + "remote" + ], + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "salesforce:EventData", + "optional": false, + "type": [ + "salesforce:EventData" + ], + "typeInfo": { + "name": "EventData", + "orgName": "ballerinax", + "moduleName": "salesforce", + "version": "8.1.0" + }, + "documentation": "The information about the triggered event.", + "enabled": true, + "value": "saleforce:EventData" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "name": "onDelete", + "documentation": "The `onDelete` method is triggered when a new record delete event is received from Salesforce.", + "optional": false, + "qualifiers": [ + "remote" + ], + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "salesforce:EventData", + "optional": false, + "type": [ + "salesforce:EventData" + ], + "typeInfo": { + "name": "EventData", + "orgName": "ballerinax", + "moduleName": "salesforce", + "version": "8.1.0" + }, + "documentation": "The information about the triggered event.", + "enabled": true, + "value": "saleforce:EventData" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + }, + { + "name": "onRestore", + "documentation": "The `onRestore` method is triggered when a new record restore event is received from Salesforce.", + "optional": false, + "qualifiers": [ + "remote" + ], + "enabled": true, + "parameters": [ + { + "name": "payload", + "typeName": "salesforce:EventData", + "optional": false, + "type": [ + "salesforce:EventData" + ], + "typeInfo": { + "name": "EventData", + "orgName": "ballerinax", + "moduleName": "salesforce", + "version": "8.1.0" + }, + "documentation": "The information about the triggered event.", + "enabled": true, + "value": "saleforce:EventData" + } + ], + "returnType": { + "typeName": "error?", + "type": [ + "error?" + ], + "optional": true, + "documentation": "Error object.", + "defaultTypeName": "error?", + "enabled": true, + "value": "error?" + } + } + ] + } + ], + "listener": { + "metadata": { + "label": "Salesforce Listener", + "description": "The Salesforce listener to which the Salesforce service should be attached." + }, + "valueType": "OBJECT", + "valueTypeConstraint": "salesforce:Listener", + "value": "", + "enabled": true, + "optional": false, + "editable": true, + "properties": { + "listenerConfig": { + "metadata": { + "label": "Salesforce Listener Configuration", + "description": "The configuration of Salesforce ASB listener." + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "salesforce:ListenerConfig", + "placeholder": "", + "editable": true, + "enabled": true, + "optional": false, + "advanced": true, + "fields": { + "auth": { + "metadata": { + "label": "Authentication Configuration", + "description": "Configurations related to username/password authentication." + }, + "valueType": "EXPRESSION", + "valueTypeConstraint": "salesforce:CredentialsConfig", + "value": "", + "placeholder": "", + "editable": true, + "enabled": true, + "optional": false, + "advanced": true, + "fields": { + "username": { + "metadata": { + "label": "Username", + "description": "Salesforce login username." + }, + "valueType": "STRING", + "editable": true, + "enabled": true, + "optional": false, + "advanced": false + }, + "password": { + "metadata": { + "label": "Password", + "description": "Salesforce login password appended with the security token ()." + }, + "valueType": "STRING", + "editable": true, + "enabled": true, + "optional": false, + "advanced": false + } + } + }, + "replayFrom": { + "metadata": { + "label": "Replay From", + "description": "The replay ID to change the point in time when events are read." + }, + "valueType": "UNION", + "value": "", + "placeholder": "salesforce:REPLAY_FROM_TIP", + "editable": true, + "enabled": true, + "optional": true, + "advanced": false, + "unionTypes": [ + { + "metadata": { + "label": "Replay ID", + "description": "The integer value of the replay ID." + }, + "valueType": "INT", + "editable": true, + "enabled": true, + "optional": false, + "value": "", + "advanced": false, + "placeholder": "" + }, + { + "metadata": { + "label": "Replay Options", + "description": "The options to specify the replay ID." + }, + "valueType": "ENUM", + "editable": true, + "enabled": true, + "optional": false, + "value": "", + "advanced": false, + "placeholder": "", + "enum": [ + "salesforce:REPLAY_FROM_TIP", + "salesforce:REPLAY_FROM_EARLIEST" + ] + } + ] + }, + "isSandBox": { + "metadata": { + "label": "Is Sandbox", + "description": "The type of salesforce environment, if sandbox environment or not." + }, + "valueType": "BOOLEAN", + "value": "false", + "editable": true, + "enabled": true, + "optional": true, + "advanced": false + } + } + } + } + } +} diff --git a/misc/ls-extensions/modules/trigger-service/src/test/java/io/ballerina/trigger/TriggerServiceTest.java b/misc/ls-extensions/modules/trigger-service/src/test/java/io/ballerina/trigger/TriggerServiceTest.java index f861a6333221..f9dd387077c7 100644 --- a/misc/ls-extensions/modules/trigger-service/src/test/java/io/ballerina/trigger/TriggerServiceTest.java +++ b/misc/ls-extensions/modules/trigger-service/src/test/java/io/ballerina/trigger/TriggerServiceTest.java @@ -37,6 +37,8 @@ public class TriggerServiceTest { private static final String BALLERINA_TRIGGERS = "ballerinaTrigger/triggers"; private static final String BALLERINA_TRIGGER = "ballerinaTrigger/trigger"; + private static final String BALLERINA_TRIGGERS_NEW = "ballerinaTrigger/triggersNew"; + private static final String BALLERINA_TRIGGER_NEW = "ballerinaTrigger/triggerNew"; @Test(description = "Test triggers endpoint of trigger service") public void testTriggersService() throws ExecutionException, InterruptedException { @@ -49,6 +51,22 @@ public void testTriggersService() throws ExecutionException, InterruptedExceptio Assert.assertTrue(!response.getCentralTriggers().isEmpty()); } + @Test(description = "Test new triggers endpoint of trigger service") + public void testTriggersNewService() throws ExecutionException, InterruptedException { + Endpoint serviceEndpoint = TestUtil.initializeLanguageSever(); + + BallerinaTriggerListRequest request = new BallerinaTriggerListRequest(); + CompletableFuture result = serviceEndpoint.request(BALLERINA_TRIGGERS_NEW, request); + JsonObject response = (JsonObject) result.get(); + + Assert.assertTrue(response.has("central")); + Assert.assertFalse(response.getAsJsonArray("central").isEmpty()); + Assert.assertTrue(response.getAsJsonArray("central").asList().stream() + .anyMatch(trigger -> trigger.getAsJsonObject().get("moduleName").getAsString().contains("kafka"))); + Assert.assertTrue(response.getAsJsonArray("central").asList().stream() + .anyMatch(trigger -> trigger.getAsJsonObject().get("moduleName").getAsString().contains("ftp"))); + } + @Test(description = "Test trigger endpoint of trigger service") public void testTriggerService() throws ExecutionException, InterruptedException { Endpoint serviceEndpoint = TestUtil.initializeLanguageSever(); @@ -59,4 +77,95 @@ public void testTriggerService() throws ExecutionException, InterruptedException Assert.assertEquals(response.get("id").getAsString(), "2"); } + + @Test(description = "Test new trigger endpoint of trigger service") + public void testTriggerNewService() throws ExecutionException, InterruptedException { + Endpoint serviceEndpoint = TestUtil.initializeLanguageSever(); + + BallerinaTriggerRequest request = new BallerinaTriggerRequest("2"); + CompletableFuture result = serviceEndpoint.request(BALLERINA_TRIGGER_NEW, request); + JsonObject response = (JsonObject) result.get(); + + Assert.assertEquals(response.get("id").getAsString(), "2"); + Assert.assertEquals(response.get("moduleName").getAsString(), "rabbitmq"); + } + + @Test(description = "Test new trigger endpoint of trigger service without id") + public void testTriggerNewServiceWithoutId() throws ExecutionException, InterruptedException { + Endpoint serviceEndpoint = TestUtil.initializeLanguageSever(); + + BallerinaTriggerRequest request = new BallerinaTriggerRequest("ballerinax", "kafka", "kafka", + "*", "Kafka Event Listener"); + CompletableFuture result = serviceEndpoint.request(BALLERINA_TRIGGER_NEW, request); + JsonObject response = (JsonObject) result.get(); + + Assert.assertEquals(response.get("id").getAsString(), "1"); + Assert.assertEquals(response.get("moduleName").getAsString(), "kafka"); + + request = new BallerinaTriggerRequest("ballerina", "mqtt", "mqtt", + "*", "MQTT Event Listener"); + result = serviceEndpoint.request(BALLERINA_TRIGGER_NEW, request); + response = (JsonObject) result.get(); + + Assert.assertEquals(response.get("id").getAsString(), "4"); + Assert.assertEquals(response.get("moduleName").getAsString(), "mqtt"); + } + + @Test(description = "Test new triggers endpoint of trigger service with query") + public void testTriggersNewServiceWithQuery() throws ExecutionException, InterruptedException { + Endpoint serviceEndpoint = TestUtil.initializeLanguageSever(); + + BallerinaTriggerListRequest request = new BallerinaTriggerListRequest(); + request.setQuery("kafka"); + CompletableFuture result = serviceEndpoint.request(BALLERINA_TRIGGERS_NEW, request); + JsonObject response = (JsonObject) result.get(); + + Assert.assertTrue(response.has("central")); + Assert.assertFalse(response.getAsJsonArray("central").isEmpty()); + Assert.assertTrue(response.getAsJsonArray("central").asList().stream() + .anyMatch(trigger -> trigger.getAsJsonObject().get("moduleName").getAsString().contains("kafka"))); + Assert.assertTrue(response.getAsJsonArray("central").asList().stream() + .noneMatch(trigger -> trigger.getAsJsonObject().get("moduleName").getAsString().contains("ftp"))); + } + + @Test(description = "Test new triggers endpoint of trigger service with organization") + public void testTriggersNewServiceWithOrg() throws ExecutionException, InterruptedException { + Endpoint serviceEndpoint = TestUtil.initializeLanguageSever(); + + BallerinaTriggerListRequest request = new BallerinaTriggerListRequest(); + request.setOrganization("ballerina"); + CompletableFuture result = serviceEndpoint.request(BALLERINA_TRIGGERS_NEW, request); + JsonObject response = (JsonObject) result.get(); + + Assert.assertTrue(response.has("central")); + Assert.assertFalse(response.getAsJsonArray("central").isEmpty()); + Assert.assertTrue(response.getAsJsonArray("central").asList().stream() + .anyMatch(trigger -> trigger.getAsJsonObject().get("moduleName").getAsString().contains("mqtt"))); + Assert.assertTrue(response.getAsJsonArray("central").asList().stream() + .anyMatch(trigger -> trigger.getAsJsonObject().get("moduleName").getAsString().contains("ftp"))); + Assert.assertTrue(response.getAsJsonArray("central").asList().stream() + .noneMatch(trigger -> trigger.getAsJsonObject().get("moduleName").getAsString().contains("kafka"))); + } + + @Test(description = "Test new triggers endpoint of trigger service with limit") + public void testTriggersNewServiceWithLimit() throws ExecutionException, InterruptedException { + Endpoint serviceEndpoint = TestUtil.initializeLanguageSever(); + + BallerinaTriggerListRequest request = new BallerinaTriggerListRequest(); + request.setLimit(10); + CompletableFuture result = serviceEndpoint.request(BALLERINA_TRIGGERS_NEW, request); + JsonObject response = (JsonObject) result.get(); + + Assert.assertTrue(response.has("central")); + Assert.assertFalse(response.getAsJsonArray("central").isEmpty()); + Assert.assertTrue(response.getAsJsonArray("central").size() == 9); + + request.setLimit(2); + result = serviceEndpoint.request(BALLERINA_TRIGGERS_NEW, request); + response = (JsonObject) result.get(); + + Assert.assertTrue(response.has("central")); + Assert.assertFalse(response.getAsJsonArray("central").isEmpty()); + Assert.assertTrue(response.getAsJsonArray("central").size() == 2); + } } diff --git a/semtypes/build.gradle b/semtypes/build.gradle index b9b3488eafd8..22f85a25bf86 100644 --- a/semtypes/build.gradle +++ b/semtypes/build.gradle @@ -7,6 +7,9 @@ dependencies { } test { + // Add additional system property to distinguish tests requiring all basic types + systemProperty "ballerina.semtype.all.types.test", "true" + useTestNG() { suites 'src/test/resources/testng.xml' } diff --git a/semtypes/spotbugs-exclude.xml b/semtypes/spotbugs-exclude.xml index 97c14919f38e..599e9236ea20 100644 --- a/semtypes/spotbugs-exclude.xml +++ b/semtypes/spotbugs-exclude.xml @@ -21,32 +21,83 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + - - - + + + + + + + + diff --git a/semtypes/src/main/java/io/ballerina/semtype/ComplexSemType.java b/semtypes/src/main/java/io/ballerina/semtype/ComplexSemType.java deleted file mode 100644 index 09b191eb7c35..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/ComplexSemType.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -import java.util.ArrayList; - -/** - * ComplexSemType node. - * - * @since 2.0.0 - */ -public class ComplexSemType implements SemType { - // For a uniform type with code c, - // all & (1 << c) is non-zero iff this type contains all of the uniform type - // some & (1 << c) is non-zero iff this type contains some but not all of the uniform type - public final UniformTypeBitSet all; - public final UniformTypeBitSet some; - // There is one member of subtypes for each bit set in some. - // Ordered in increasing order of UniformTypeCode - public final SubtypeData[] subtypeDataList; - - public ComplexSemType(UniformTypeBitSet all, UniformTypeBitSet some, SubtypeData[] subtypeDataList) { - this.all = all; - this.some = some; - this.subtypeDataList = subtypeDataList; - } - - public static ComplexSemType createComplexSemType(int allBitset, UniformSubtype... subtypeList) { - int some = 0; - ArrayList dataList = new ArrayList<>(); - for (UniformSubtype uniformSubtype : subtypeList) { - dataList.add(uniformSubtype.subtypeData); - long c = uniformSubtype.uniformTypeCode; - some |= 1L << c; - } - return new ComplexSemType( - new UniformTypeBitSet(allBitset), new UniformTypeBitSet(some), dataList.toArray(new SubtypeData[]{})); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/Env.java b/semtypes/src/main/java/io/ballerina/semtype/Env.java deleted file mode 100644 index ee00c5044853..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/Env.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -import java.util.ArrayList; -import java.util.HashMap; - -/** - * Env node. - * - * @since 2.0.0 - */ -public class Env { - private final HashMap atomTable; - private final ArrayList recListAtoms; - private final ArrayList recMappingAtoms; - private final ArrayList recFunctionAtoms; - - public Env() { - this.atomTable = new HashMap<>(); - // Set up index 0 for use by bddFixReadOnly - this.recListAtoms = new ArrayList<>(); - this.recListAtoms.add(ListAtomicType.LIST_SUBTYPE_RO); - - this.recMappingAtoms = new ArrayList<>(); - // todo: add MAPPING_SUBTYPE_RO - this.recFunctionAtoms = new ArrayList<>(); - } - - -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/FunctionAtomicType.java b/semtypes/src/main/java/io/ballerina/semtype/FunctionAtomicType.java deleted file mode 100644 index 8ca845b74f22..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/FunctionAtomicType.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -/** - * FunctionAtomicType node. - * - * @since 2.0.0 - */ -public class FunctionAtomicType { - SemType paramType; - SemType retType; -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/ListAtomicType.java b/semtypes/src/main/java/io/ballerina/semtype/ListAtomicType.java deleted file mode 100644 index 6dedbb509453..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/ListAtomicType.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -import java.util.ArrayList; - -/** - * ListAtomicType node. - * - * @since 2.0.0 - */ -public class ListAtomicType implements AtomicType { - final ArrayList members; - final SemType rest; - - public static final ListAtomicType LIST_SUBTYPE_RO = new ListAtomicType(new ArrayList<>(), PredefinedType.READONLY); - - public ListAtomicType(ArrayList members, SemType rest) { - this.members = members; - this.rest = rest; - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/MappingAtomicType.java b/semtypes/src/main/java/io/ballerina/semtype/MappingAtomicType.java deleted file mode 100644 index a455f45c165d..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/MappingAtomicType.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -/** - * MappingAtomicType node. - * - * @since 2.0.0 - */ -public class MappingAtomicType implements AtomicType { -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/PredefinedType.java b/semtypes/src/main/java/io/ballerina/semtype/PredefinedType.java deleted file mode 100644 index fcad89624357..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/PredefinedType.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -import io.ballerina.semtype.subtypedata.IntSubtype; - -/** - * Contain predefined types used for constructing other types. - * - * @since 2.0.0 - */ -public final class PredefinedType { - - public static final UniformTypeBitSet NEVER = uniformTypeUnion(0); - public static final UniformTypeBitSet NIL = uniformType(UniformTypeCode.UT_NIL); - public static final UniformTypeBitSet BOOLEAN = uniformType(UniformTypeCode.UT_BOOLEAN); - public static final UniformTypeBitSet INT = uniformType(UniformTypeCode.UT_INT); - public static final UniformTypeBitSet FLOAT = uniformType(UniformTypeCode.UT_FLOAT); - public static final UniformTypeBitSet DECIMAL = uniformType(UniformTypeCode.UT_DECIMAL); - public static final UniformTypeBitSet STRING = uniformType(UniformTypeCode.UT_STRING); - public static final UniformTypeBitSet ERROR = uniformType(UniformTypeCode.UT_ERROR); - public static final UniformTypeBitSet LIST_RW = uniformType(UniformTypeCode.UT_LIST_RW); - public static final UniformTypeBitSet LIST = - uniformTypeUnion((1 << UniformTypeCode.UT_LIST_RO) | (1 << UniformTypeCode.UT_LIST_RW)); - public static final UniformTypeBitSet MAPPING_RW = uniformType(UniformTypeCode.UT_MAPPING_RW); - public static final UniformTypeBitSet MAPPING = - uniformTypeUnion((1 << UniformTypeCode.UT_MAPPING_RO) | (1 << UniformTypeCode.UT_MAPPING_RW)); - - // matches all functions - public static final UniformTypeBitSet FUNCTION = uniformType(UniformTypeCode.UT_FUNCTION); - public static final UniformTypeBitSet TYPEDESC = uniformType(UniformTypeCode.UT_TYPEDESC); - public static final UniformTypeBitSet HANDLE = uniformType(UniformTypeCode.UT_HANDLE); - - public static final UniformTypeBitSet XML = - uniformTypeUnion((1 << UniformTypeCode.UT_XML_RO) | (1 << UniformTypeCode.UT_XML_RW)); - public static final UniformTypeBitSet STREAM = uniformType(UniformTypeCode.UT_STREAM); - public static final UniformTypeBitSet FUTURE = uniformType(UniformTypeCode.UT_FUTURE); - - // this is SubtypeData|error - public static final UniformTypeBitSet TOP = uniformTypeUnion(UniformTypeCode.UT_MASK); - public static final UniformTypeBitSet ANY = - uniformTypeUnion(UniformTypeCode.UT_MASK & ~(1 << UniformTypeCode.UT_ERROR)); - public static final UniformTypeBitSet READONLY = uniformTypeUnion(UniformTypeCode.UT_READONLY); - public static final UniformTypeBitSet SIMPLE_OR_STRING = - uniformTypeUnion((1 << UniformTypeCode.UT_NIL) - | (1 << UniformTypeCode.UT_BOOLEAN) - | (1 << UniformTypeCode.UT_INT) - | (1 << UniformTypeCode.UT_FLOAT) - | (1 << UniformTypeCode.UT_DECIMAL) - | (1 << UniformTypeCode.UT_STRING)); - public static final SemType BYTE = IntSubtype.intWidthUnsigned(8); - - private PredefinedType() { - } - - private static UniformTypeBitSet uniformTypeUnion(int bitset) { - return new UniformTypeBitSet(bitset); - } - - private static UniformTypeBitSet uniformType(int code) { - return new UniformTypeBitSet(1 << code); - } - - public static SemType uniformSubtype(int code, ProperSubtypeData data) { - return ComplexSemType.createComplexSemType(0, new UniformSubtype(code, data)); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/SemTypeMock.java b/semtypes/src/main/java/io/ballerina/semtype/SemTypeMock.java deleted file mode 100644 index f908b14ee024..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/SemTypeMock.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -/** - * SemType Interface. - */ -public interface SemTypeMock { - -} - -/** - * Complex SemType implementation. - */ -class ComplexSemTypeMock implements SemTypeMock { - UniformTypeBitSet all; - UniformTypeBitSet some; - -} - -/** - * UniformTypeBitSet SemType implementation. - */ -class UniformTypeBitSetMock implements SemTypeMock { - int value; - - public UniformTypeBitSetMock(int value) { - this.value = value; - } - - public int getValue() { - return value; - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/TypeAtom.java b/semtypes/src/main/java/io/ballerina/semtype/TypeAtom.java deleted file mode 100644 index 1a629bdf616d..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/TypeAtom.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -/** - * Represent a TypeAtom. - * - * @since 2.0.0 - */ -public class TypeAtom implements Atom { - long index; - AtomicType atomicType; - - public TypeAtom(long index, AtomicType atomicType) { - this.index = index; - this.atomicType = atomicType; - } - - public static TypeAtom createTypeAtom(long index, AtomicType atomicType) { - return new TypeAtom(index, atomicType); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/TypeCheckContext.java b/semtypes/src/main/java/io/ballerina/semtype/TypeCheckContext.java deleted file mode 100644 index ec5acf707f68..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/TypeCheckContext.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -/** - * TypeCheckContext node. - * - * @since 2.0.0 - */ -public class TypeCheckContext { - private final Env env; - // todo: Normal hash tables should do here - // BddMemoTable listMemo = table []; - // BddMemoTable mappingMemo = table []; - // BddMemoTable functionMemo = table []; - - public TypeCheckContext(Env env) { - this.env = env; - } - - -// function listAtomType(Atom atom) returns ListAtomicType { -// if atom is RecAtom { -// return self.env.getRecListAtomType(atom); -// } -// else { -// return atom.atomicType; -// } -// } -// -// function mappingAtomType(Atom atom) returns MappingAtomicType { -// if atom is RecAtom { -// return self.env.getRecMappingAtomType(atom); -// } -// else { -// return atom.atomicType; -// } -// } -// -// function functionAtomType(Atom atom) returns FunctionAtomicType { -// return self.env.getRecFunctionAtomType(atom); -// } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/UniformTypeCode.java b/semtypes/src/main/java/io/ballerina/semtype/UniformTypeCode.java deleted file mode 100644 index 2b3adf472510..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/UniformTypeCode.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -/** - * Represent bit field that indicate which uniform type a semType belongs to. - * Regular types are divided longo mutable part and immutable part and these parts are called an uniform type. - * 5th bit indicate mutability; 0 immutable, 1 mutable. - * - * @since 2.0.0 - */ -public final class UniformTypeCode { - - // Inherently immutable - public static final int UT_NIL = 0x00; - public static final int UT_BOOLEAN = 0x01; - - // Selectively immutable; immutable half - public static final int UT_LIST_RO = 0x02; - public static final int UT_MAPPING_RO = 0x03; - public static final int UT_TABLE_RO = 0x04; - public static final int UT_XML_RO = 0x05; - public static final int UT_OBJECT_RO = 0x06; - - // Rest of inherently immutable - public static final int UT_INT = 0x07; - public static final int UT_FLOAT = 0x08; - public static final int UT_DECIMAL = 0x09; - public static final int UT_STRING = 0x0A; - public static final int UT_ERROR = 0x0B; - public static final int UT_FUNCTION = 0x0C; - public static final int UT_TYPEDESC = 0x0D; - public static final int UT_HANDLE = 0x0E; - - // Inherently mutable - public static final int UT_FUTURE = 0x10; - public static final int UT_STREAM = 0x11; - - // Selectively immutable; mutable half - public static final int UT_LIST_RW = 0x12; - public static final int UT_MAPPING_RW = 0x13; - public static final int UT_TABLE_RW = 0x14; - public static final int UT_XML_RW = 0x15; - public static final int UT_OBJECT_RW = 0x16; - - // Helper bit fields (does not represent uniform type tag) - static final int UT_COUNT = UT_OBJECT_RW + 1; - static final int UT_MASK = (1 << UT_COUNT) - 1; - - static final int UT_COUNT_RO = 0x10; - static final int UT_READONLY = (1 << UT_COUNT_RO) - 1; - - static final int UT_RW_MASK = UT_MASK ^ ~UT_READONLY; - - private UniformTypeCode() { - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/UniformTypeOps.java b/semtypes/src/main/java/io/ballerina/semtype/UniformTypeOps.java deleted file mode 100644 index 2fb53b153b62..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/UniformTypeOps.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -/** - * Interface representing type operations on uniform types. - * - * @since 2.0.0 - */ -public interface UniformTypeOps extends IsEmptyOp, CommonUniformTypeOps { -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/definition/FunctionDefinition.java b/semtypes/src/main/java/io/ballerina/semtype/definition/FunctionDefinition.java deleted file mode 100644 index 34028895f964..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/definition/FunctionDefinition.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype.definition; - -import io.ballerina.semtype.Definition; -import io.ballerina.semtype.Env; -import io.ballerina.semtype.SemType; - -/** - * Represent function type desc. - * - * @since 2.0.0 - */ -public class FunctionDefinition implements Definition { - @Override - public SemType getSemType(Env env) { - throw new AssertionError(); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/definition/ListDefinition.java b/semtypes/src/main/java/io/ballerina/semtype/definition/ListDefinition.java deleted file mode 100644 index 3a277c03c12b..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/definition/ListDefinition.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype.definition; - -import io.ballerina.semtype.Definition; -import io.ballerina.semtype.Env; -import io.ballerina.semtype.SemType; - -/** - * Represent list/tuple type desc. - * - * @since 2.0.0 - */ -public class ListDefinition implements Definition { - @Override - public SemType getSemType(Env env) { - throw new AssertionError(); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/definition/MappingDefinition.java b/semtypes/src/main/java/io/ballerina/semtype/definition/MappingDefinition.java deleted file mode 100644 index d9651e97a3b8..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/definition/MappingDefinition.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype.definition; - -import io.ballerina.semtype.Definition; -import io.ballerina.semtype.Env; -import io.ballerina.semtype.SemType; - -/** - * Represent mapping type desc. - * - * @since 2.0.0 - */ -public class MappingDefinition implements Definition { - @Override - public SemType getSemType(Env env) { - throw new AssertionError(); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/typeops/FunctionOps.java b/semtypes/src/main/java/io/ballerina/semtype/typeops/FunctionOps.java deleted file mode 100644 index 507e271c1ed9..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/typeops/FunctionOps.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype.typeops; - -import io.ballerina.semtype.SubtypeData; -import io.ballerina.semtype.TypeCheckContext; -import io.ballerina.semtype.UniformTypeOps; - -/** - * Function specific methods operate on SubtypeData. - * - * @since 2.0.0 - */ -public class FunctionOps extends CommonOps implements UniformTypeOps { - @Override - public boolean isEmpty(TypeCheckContext tc, SubtypeData t) { - throw new AssertionError(); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/typeops/ListTypeRWOps.java b/semtypes/src/main/java/io/ballerina/semtype/typeops/ListTypeRWOps.java deleted file mode 100644 index d6734164f8d4..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/typeops/ListTypeRWOps.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype.typeops; - -import io.ballerina.semtype.SubtypeData; -import io.ballerina.semtype.TypeCheckContext; -import io.ballerina.semtype.UniformTypeOps; - -/** - * List read/write specific methods operate on SubtypeData. - * - * @since 2.0.0 - */ -public class ListTypeRWOps extends CommonOps implements UniformTypeOps { - @Override - public boolean isEmpty(TypeCheckContext tc, SubtypeData t) { - throw new AssertionError(); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/typeops/ListTypeRoOps.java b/semtypes/src/main/java/io/ballerina/semtype/typeops/ListTypeRoOps.java deleted file mode 100644 index 9ee27e5105e9..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/typeops/ListTypeRoOps.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype.typeops; - -import io.ballerina.semtype.SubtypeData; -import io.ballerina.semtype.TypeCheckContext; -import io.ballerina.semtype.UniformTypeOps; - -/** - * List readonly specific methods operate on SubtypeData. - * - * @since 2.0.0 - */ -public class ListTypeRoOps extends CommonOps implements UniformTypeOps { - @Override - public boolean isEmpty(TypeCheckContext tc, SubtypeData t) { - throw new AssertionError(); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/typeops/MappingCommonOps.java b/semtypes/src/main/java/io/ballerina/semtype/typeops/MappingCommonOps.java deleted file mode 100644 index 055ccb15133f..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/typeops/MappingCommonOps.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype.typeops; - -import io.ballerina.semtype.UniformTypeOps; - -// todo: use this to place common things between Ro and RW, if there are non; delete this. -/** - * Common mapping related methods operate on SubtypeData. - * - * @since 2.0.0 - */ -public abstract class MappingCommonOps extends CommonOps implements UniformTypeOps { - -} diff --git a/semtypes/src/main/java/io/ballerina/types/Atom.java b/semtypes/src/main/java/io/ballerina/types/Atom.java new file mode 100644 index 000000000000..b1e053887d11 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/Atom.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Represent the BDD atom. + * + * @since 2201.8.0 + */ +public interface Atom { + + /** + * Get the unique index of the atom. + */ + int index(); + + /** + * Get the kind of the atom. + */ + Kind kind(); + + /** + * This method returns a unique identifier for an Atom. + * The identifier is a combination of the atom's index and kind. + * + * @return AtomIdentifier - a record containing the index and kind of the atom. + */ + default AtomIdentifier getIdentifier() { + return new AtomIdentifier(index(), kind()); + } + + record AtomIdentifier(int index, Kind kind) { + } + + enum Kind { + LIST_ATOM, + FUNCTION_ATOM, + MAPPING_ATOM, + CELL_ATOM, + XML_ATOM, + DISTINCT_ATOM + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/AtomicType.java b/semtypes/src/main/java/io/ballerina/types/AtomicType.java similarity index 68% rename from semtypes/src/main/java/io/ballerina/semtype/AtomicType.java rename to semtypes/src/main/java/io/ballerina/types/AtomicType.java index 3eb7f7377e11..7de1e82a3867 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/AtomicType.java +++ b/semtypes/src/main/java/io/ballerina/types/AtomicType.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,16 +11,18 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** * Represent AtomicType. * - * @since 2.0.0 + * @since 2201.8.0 */ public interface AtomicType { + + Atom.Kind atomKind(); } diff --git a/semtypes/src/main/java/io/ballerina/types/BasicSubtype.java b/semtypes/src/main/java/io/ballerina/types/BasicSubtype.java new file mode 100644 index 000000000000..3fd190893077 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/BasicSubtype.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * BasicSubtype node. + * + * @since 2201.8.0 + */ +public class BasicSubtype { + public final BasicTypeCode basicTypeCode; + public final ProperSubtypeData subtypeData; + + private BasicSubtype(BasicTypeCode basicTypeCode, ProperSubtypeData properSubtypeData) { + this.basicTypeCode = basicTypeCode; + this.subtypeData = properSubtypeData; + } + + public static BasicSubtype from(BasicTypeCode typeCode, ProperSubtypeData data) { + return new BasicSubtype(typeCode, data); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/BasicTypeBitSet.java b/semtypes/src/main/java/io/ballerina/types/BasicTypeBitSet.java new file mode 100644 index 000000000000..3d14765053c0 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/BasicTypeBitSet.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * BasicTypeBitSet node. + * + * @since 2201.8.0 + */ +public final class BasicTypeBitSet implements SemType { + + public final int bitset; + + private BasicTypeBitSet(int bitset) { + this.bitset = bitset; + } + + public static BasicTypeBitSet from(int bitset) { + if (bitset == 0) { + return BitSetCache.ZERO; + } + if (Integer.bitCount(bitset) == 1) { + return BitSetCache.CACHE[Integer.numberOfTrailingZeros(bitset)]; + } + return new BasicTypeBitSet(bitset); + } + + public static BasicTypeBitSet union(BasicTypeBitSet t1, BasicTypeBitSet t2) { + return BasicTypeBitSet.from(t1.bitset | t2.bitset); + } + + @Override + public String toString() { + return PredefinedType.toString(this.bitset); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o instanceof BasicTypeBitSet b) { + return b.bitset == this.bitset; + } + return false; + } + + @Override + public int hashCode() { + return bitset; + } + + @Override + public int all() { + return bitset; + } + + private static final class BitSetCache { + + private static final int SIZE = 0x14; + private static final BasicTypeBitSet[] CACHE = new BasicTypeBitSet[SIZE]; + private static final BasicTypeBitSet ZERO = new BasicTypeBitSet(0); + + static { + for (int i = 0; i < SIZE; i++) { + CACHE[i] = new BasicTypeBitSet(1 << i); + } + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/BasicTypeCode.java b/semtypes/src/main/java/io/ballerina/types/BasicTypeCode.java new file mode 100644 index 000000000000..d614491fd6c8 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/BasicTypeCode.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +/** + * Represent bit field that indicate which basic type a semType belongs to. + * + * @since 2201.10.0 + */ +public class BasicTypeCode { + // Inherently immutable + public static final BasicTypeCode BT_NIL = from(0x00); + public static final BasicTypeCode BT_BOOLEAN = from(0x01); + public static final BasicTypeCode BT_INT = from(0x02); + public static final BasicTypeCode BT_FLOAT = from(0x03); + public static final BasicTypeCode BT_DECIMAL = from(0x04); + public static final BasicTypeCode BT_STRING = from(0x05); + public static final BasicTypeCode BT_ERROR = from(0x06); + public static final BasicTypeCode BT_TYPEDESC = from(0x07); + public static final BasicTypeCode BT_HANDLE = from(0x08); + public static final BasicTypeCode BT_FUNCTION = from(0x09); + public static final BasicTypeCode BT_REGEXP = from(0x0A); + + // Inherently mutable + public static final BasicTypeCode BT_FUTURE = from(0x0B); + public static final BasicTypeCode BT_STREAM = from(0x0C); + + // Selectively immutable + public static final BasicTypeCode BT_LIST = from(0x0D); + public static final BasicTypeCode BT_MAPPING = from(0x0E); + public static final BasicTypeCode BT_TABLE = from(0x0F); + public static final BasicTypeCode BT_XML = from(0x10); + public static final BasicTypeCode BT_OBJECT = from(0x11); + + // Non-val + public static final BasicTypeCode BT_CELL = from(0x12); + public static final BasicTypeCode BT_UNDEF = from(0x13); + + // Helper bit fields (does not represent basic type tag) + static final int VT_COUNT = BT_OBJECT.code + 1; + static final int VT_MASK = (1 << VT_COUNT) - 1; + + static final int VT_COUNT_INHERENTLY_IMMUTABLE = BT_FUTURE.code; + public static final int VT_INHERENTLY_IMMUTABLE = (1 << VT_COUNT_INHERENTLY_IMMUTABLE) - 1; + + public final int code; + + // There is an integer for each basic type. + private BasicTypeCode(int code) { + this.code = code; + } + + public static BasicTypeCode from(int code) { + // todo: Add validation + return new BasicTypeCode(code); + } + + // Only used for .toString() method to aid debugging. + private static Map fieldNames = new HashMap<>(); + static { + for (Field field : BasicTypeCode.class.getDeclaredFields()) { + if (field.getType() == BasicTypeCode.class) { + try { + BasicTypeCode o = (BasicTypeCode) field.get(null); + fieldNames.put(o.code, field.getName()); + } catch (IllegalAccessException e) { + throw new IllegalStateException(); + } + } + } + } + + @Override + public String toString() { + return fieldNames.get(this.code); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/BasicTypeOps.java b/semtypes/src/main/java/io/ballerina/types/BasicTypeOps.java new file mode 100644 index 000000000000..71dcaf7b01fe --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/BasicTypeOps.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Interface representing type operations on basic types. + * + * @since 2201.8.0 + */ +public interface BasicTypeOps extends IsEmptyOp, CommonBasicTypeOps { +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/Bdd.java b/semtypes/src/main/java/io/ballerina/types/Bdd.java similarity index 69% rename from semtypes/src/main/java/io/ballerina/semtype/Bdd.java rename to semtypes/src/main/java/io/ballerina/types/Bdd.java index 351df65a953b..a51aa98545b7 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/Bdd.java +++ b/semtypes/src/main/java/io/ballerina/types/Bdd.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,16 +11,16 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** * Represent BDD node. * - * @since 2.0.0 + * @since 2201.8.0 */ public interface Bdd extends ProperSubtypeData { } diff --git a/semtypes/src/main/java/io/ballerina/types/BddMemo.java b/semtypes/src/main/java/io/ballerina/types/BddMemo.java new file mode 100644 index 000000000000..c9b82a59ac3b --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/BddMemo.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Represent BddMomo type used for memoization. + * + * @since 2201.8.0 + */ +public class BddMemo { + + protected MemoStatus isEmpty; + + public BddMemo() { + this.isEmpty = MemoStatus.NULL; + } + + public void setIsEmpty(boolean isEmpty) { + this.isEmpty = isEmpty ? MemoStatus.TRUE : MemoStatus.FALSE; + } + + public boolean isEmpty() { + return this.isEmpty == MemoStatus.TRUE; + } + /** + * Represent if BddMemo is null or not. + * + * @since 3.0.0 + */ + public enum MemoStatus { + LOOP, TRUE, FALSE, CYCLIC, PROVISIONAL, NULL; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/BddPath.java b/semtypes/src/main/java/io/ballerina/types/BddPath.java new file mode 100644 index 000000000000..4eceb36065f0 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/BddPath.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.typeops.BddCommonOps; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents path from root to leaf (ending with true). + * bdd gets the Bdd for this path + * + * @since 2201.8.0 + */ +public class BddPath { + Bdd bdd; + List pos; + List neg; + + private BddPath(BddPath bddPath) { + this.bdd = bddPath.bdd; + this.pos = new ArrayList<>(bddPath.pos); // pos: path.pos.clone() + this.neg = new ArrayList<>(bddPath.neg); // pos: path.pos.clone() + } + + public BddPath() { + this.bdd = BddAllOrNothing.bddAll(); + this.pos = new ArrayList<>(); + this.neg = new ArrayList<>(); + } + + public static void bddPaths(Bdd b, List paths, BddPath accum) { + if (b instanceof BddAllOrNothing allOrNothing) { + if (allOrNothing.isAll()) { + paths.add(accum); + } + } else { + BddPath left = bddPathClone(accum); + BddPath right = bddPathClone(accum); + BddNode bn = (BddNode) b; + left.pos.add(bn.atom()); + left.bdd = BddCommonOps.bddIntersect(left.bdd, BddCommonOps.bddAtom(bn.atom())); + bddPaths(bn.left(), paths, left); + bddPaths(bn.middle(), paths, accum); + right.neg.add(bn.atom()); + right.bdd = BddCommonOps.bddDiff(right.bdd, BddCommonOps.bddAtom(bn.atom())); + bddPaths(bn.right(), paths, right); + } + } + + private static BddPath bddPathClone(BddPath path) { + return new BddPath(path); + } + + public static BddPath from() { + return new BddPath(); + } + +} diff --git a/semtypes/src/main/java/io/ballerina/types/CellAtomicType.java b/semtypes/src/main/java/io/ballerina/types/CellAtomicType.java new file mode 100644 index 000000000000..a17744fa2625 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/CellAtomicType.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * CellAtomicType node. + * + * @param ty Type "wrapped" by this cell + * @param mut Mutability of the cell + * @since 2201.10.0 + */ +public record CellAtomicType(SemType ty, CellMutability mut) implements AtomicType { + + public CellAtomicType { + assert ty != null; + } + + public static CellAtomicType from(SemType ty, CellMutability mut) { + assert ty != null; + // TODO: return final fields where applicable + return new CellAtomicType(ty, mut); + } + + @Override + public Atom.Kind atomKind() { + return Atom.Kind.CELL_ATOM; + } + + public enum CellMutability { + CELL_MUT_NONE, + CELL_MUT_LIMITED, + CELL_MUT_UNLIMITED + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/CellSemType.java b/semtypes/src/main/java/io/ballerina/types/CellSemType.java new file mode 100644 index 000000000000..d799bfcd193f --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/CellSemType.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * This is to represent a SemType belonging to cell basic type. + * + * @since 2201.10.0 + */ +public class CellSemType implements ComplexSemType { + + // Holding on to the single value instead of the array with a single value is more memory efficient. However, if + // this start to cause problems in the future, we can change this to an array. + private final ProperSubtypeData subtypeData; + + private CellSemType(ProperSubtypeData[] subtypeDataList) { + assert subtypeDataList.length == 1; + this.subtypeData = subtypeDataList[0]; + } + + public static CellSemType from(ProperSubtypeData[] subtypeDataList) { + return new CellSemType(subtypeDataList); + } + + @Override + public String toString() { + return "CellSemType{" + subtypeDataList()[0] + '}'; + } + + @Override + public int all() { + return 0; + } + + @Override + public int some() { + return PredefinedType.CELL.bitset; + } + + @Override + public ProperSubtypeData[] subtypeDataList() { + return new ProperSubtypeData[]{subtypeData}; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/CombinedRange.java b/semtypes/src/main/java/io/ballerina/types/CombinedRange.java new file mode 100644 index 000000000000..f097004b2220 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/CombinedRange.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.Range; + +/** + * Represents a combined range. + * + * @param range range + * @param i1 i1 + * @param i2 i2 + * @since 2201.11.0 + */ +public record CombinedRange(Range range, Long i1, Long i2) { + + public static CombinedRange from(Range range, Long i1, Long i2) { + return new CombinedRange(range, i1, i2); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/Common.java b/semtypes/src/main/java/io/ballerina/types/Common.java new file mode 100644 index 000000000000..0a253308c48e --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/Common.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiFunction; + +import static io.ballerina.types.Conjunction.and; +import static io.ballerina.types.typeops.BddCommonOps.bddComplement; +import static io.ballerina.types.typeops.BddCommonOps.bddDiff; +import static io.ballerina.types.typeops.BddCommonOps.bddIntersect; +import static io.ballerina.types.typeops.BddCommonOps.bddUnion; + +/** + * Code common to implementation of multiple basic types. + * + * @since 2201.8.0 + */ +public class Common { + + // [from nballerina] A Bdd represents a disjunction of conjunctions of atoms, where each atom is either positive or + // negative (negated). Each path from the root to a leaf that is true represents one of the conjunctions + // We walk the tree, accumulating the positive and negative conjunctions for a path as we go. + // When we get to a leaf that is true, we apply the predicate to the accumulated conjunctions. + + public static boolean bddEvery(Context cx, + Bdd b, + Conjunction pos, + Conjunction neg, + BddPredicate predicate) { + if (b instanceof BddAllOrNothing allOrNothing) { + return !allOrNothing.isAll() || predicate.apply(cx, pos, neg); + } else { + BddNode bn = (BddNode) b; + return bddEvery(cx, bn.left(), and(bn.atom(), pos), neg, predicate) + && bddEvery(cx, bn.middle(), pos, neg, predicate) + && bddEvery(cx, bn.right(), pos, and(bn.atom(), neg), predicate); + } + } + + public static boolean bddEveryPositive(Context cx, Bdd b, Conjunction pos, Conjunction neg, + BddPredicate predicate) { + if (b instanceof BddAllOrNothing allOrNothing) { + return !allOrNothing.isAll() || predicate.apply(cx, pos, neg); + } else { + BddNode bn = (BddNode) b; + return bddEveryPositive(cx, bn.left(), andIfPositive(bn.atom(), pos), neg, predicate) + && bddEveryPositive(cx, bn.middle(), pos, neg, predicate) + && bddEveryPositive(cx, bn.right(), pos, andIfPositive(bn.atom(), neg), predicate); + } + } + + public static Conjunction andIfPositive(Atom atom, Conjunction next) { + if (atom instanceof RecAtom recAtom && recAtom.index < 0) { + return next; + } + return and(atom, next); + } + + public static boolean bddPosMaybeEmpty(Bdd b) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll(); + } else { + BddNode bddNode = (BddNode) b; + return bddPosMaybeEmpty(bddNode.middle()) || bddPosMaybeEmpty(bddNode.right()); + } + } + + public static SubtypeData bddSubtypeUnion(SubtypeData t1, SubtypeData t2) { + return bddUnion((Bdd) t1, (Bdd) t2); + } + + public static SubtypeData bddSubtypeIntersect(SubtypeData t1, SubtypeData t2) { + return bddIntersect((Bdd) t1, (Bdd) t2); + } + + public static SubtypeData bddSubtypeDiff(SubtypeData t1, SubtypeData t2) { + return bddDiff((Bdd) t1, (Bdd) t2); + } + + public static SubtypeData bddSubtypeComplement(SubtypeData t) { + return bddComplement((Bdd) t); + } + + public static SemType[] shallowCopyTypes(SemType[] v) { + return Arrays.copyOf(v, v.length); + } + + public static CellSemType[] shallowCopyCellTypes(CellSemType[] v) { + return shallowCopyCellTypes(v, v.length); + } + + public static CellSemType[] shallowCopyCellTypes(CellSemType[] v, int newLength) { + return Arrays.copyOf(v, newLength); + } + + public static List shallowCopyTypes(List v) { + return new ArrayList<>(v); + } + + public static String[] shallowCopyStrings(String[] v, int newLength) { + return Arrays.copyOf(v, newLength); + } + + public static boolean notIsEmpty(Context cx, SubtypeData d) { + return false; + } + + // Returns whether s1.codePoints < s2.codePoints + public static boolean codePointCompare(String s1, String s2) { + if (s1.equals(s2)) { + return false; + } + int len1 = s1.length(); + int len2 = s2.length(); + if (len1 < len2 && s2.substring(0, len1).equals(s1)) { + return true; + } + int cpCount1 = s1.codePointCount(0, len1); + int cpCount2 = s2.codePointCount(0, len2); + for (int cp = 0; cp < cpCount1 && cp < cpCount2;) { + int codepoint1 = s1.codePointAt(cp); + int codepoint2 = s2.codePointAt(cp); + if (codepoint1 == codepoint2) { + cp++; + continue; + } + return codepoint1 < codepoint2; + } + return false; + } + + + public static boolean isNothingSubtype(SubtypeData data) { + return data instanceof AllOrNothingSubtype allOrNothingSubtype && allOrNothingSubtype.isNothingSubtype(); + } + + /** + * Function interface used for method references. + * + * @since 3.0.0 + */ + public interface BddPredicate { + boolean apply(Context cx, Conjunction posList, Conjunction negList); + } + + public interface BddIsEmptyPredicate extends BiFunction { + + } + + public static boolean memoSubtypeIsEmpty(Context cx, Map memoTable, + BddIsEmptyPredicate isEmptyPredicate, Bdd b) { + BddMemo mm = memoTable.get(b); + BddMemo m; + if (mm != null) { + BddMemo.MemoStatus res = mm.isEmpty; + switch (res) { + case CYCLIC: + // Since we define types inductively we consider these to be empty + return true; + case TRUE, FALSE: + // We know whether b is empty or not for certain + return res == BddMemo.MemoStatus.TRUE; + case NULL: + // this is same as not having memo so fall through + m = mm; + break; + case LOOP, PROVISIONAL: + // We've got a loop. + mm.isEmpty = BddMemo.MemoStatus.LOOP; + return true; + default: + throw new AssertionError("Unexpected memo status: " + res); + } + } else { + m = new BddMemo(); + memoTable.put(b, m); + } + m.isEmpty = BddMemo.MemoStatus.PROVISIONAL; + int initStackDepth = cx.memoStack.size(); + cx.memoStack.add(m); + boolean isEmpty = isEmptyPredicate.apply(cx, b); + boolean isLoop = m.isEmpty == BddMemo.MemoStatus.LOOP; + if (!isEmpty || initStackDepth == 0) { + for (int i = initStackDepth + 1; i < cx.memoStack.size(); i++) { + BddMemo.MemoStatus memoStatus = cx.memoStack.get(i).isEmpty; + if (Objects.requireNonNull(memoStatus) == BddMemo.MemoStatus.PROVISIONAL || + memoStatus == BddMemo.MemoStatus.LOOP || memoStatus == BddMemo.MemoStatus.CYCLIC) { + cx.memoStack.get(i).isEmpty = isEmpty ? BddMemo.MemoStatus.TRUE : BddMemo.MemoStatus.NULL; + } + } + while (cx.memoStack.size() > initStackDepth) { + cx.memoStack.subList(initStackDepth, cx.memoStack.size()).clear(); + } + // The only way that we have found that this can be empty is by going through a loop. + // This means that the shapes in the type would all be infinite. + // But we define types inductively, which means we only consider finite shapes. + if (isLoop && isEmpty) { + m.isEmpty = BddMemo.MemoStatus.CYCLIC; + } else { + m.isEmpty = isEmpty ? BddMemo.MemoStatus.TRUE : BddMemo.MemoStatus.FALSE; + } + } + return isEmpty; + } + + public static boolean isAllSubtype(SubtypeData d) { + if (d instanceof AllOrNothingSubtype allOrNothingSubtype) { + return allOrNothingSubtype.isAllSubtype(); + } + return false; + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/CommonUniformTypeOps.java b/semtypes/src/main/java/io/ballerina/types/CommonBasicTypeOps.java similarity index 72% rename from semtypes/src/main/java/io/ballerina/semtype/CommonUniformTypeOps.java rename to semtypes/src/main/java/io/ballerina/types/CommonBasicTypeOps.java index bc7cd3067b89..2739e376f5eb 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/CommonUniformTypeOps.java +++ b/semtypes/src/main/java/io/ballerina/types/CommonBasicTypeOps.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,18 +11,18 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** * Operations common to most of the subtypes. * - * @since 2.0.0 + * @since 2201.8.0 */ -public interface CommonUniformTypeOps { +public interface CommonBasicTypeOps { SubtypeData union(SubtypeData t1, SubtypeData t2); SubtypeData intersect(SubtypeData t1, SubtypeData t2); SubtypeData diff(SubtypeData t1, SubtypeData t2); diff --git a/semtypes/src/main/java/io/ballerina/types/ComplexSemType.java b/semtypes/src/main/java/io/ballerina/types/ComplexSemType.java new file mode 100644 index 000000000000..9a72477dad31 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/ComplexSemType.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static io.ballerina.types.BasicTypeCode.BT_CELL; + +/** + * ComplexSemType node. + * + * @since 2201.8.0 + */ +public interface ComplexSemType extends SemType { + + static ComplexSemType createComplexSemType(int allBitset, BasicSubtype... subtypeList) { + return createComplexSemType(allBitset, Arrays.asList(subtypeList)); + } + + static ComplexSemType createComplexSemType(int allBitset, int someBitset, ProperSubtypeData[] subtypeData) { + if (allBitset == 0 && someBitset == (1 << BT_CELL.code)) { + return CellSemType.from(subtypeData); + } + return new ComplexSemTypeImpl(allBitset, someBitset, subtypeData); + } + + static ComplexSemType createComplexSemType(int allBitset, List subtypeList) { + int some = 0; + ArrayList dataList = new ArrayList<>(); + for (BasicSubtype basicSubtype : subtypeList) { + dataList.add(basicSubtype.subtypeData); + int c = basicSubtype.basicTypeCode.code; + some |= 1 << c; + } + return createComplexSemType(allBitset, some, dataList.toArray(ProperSubtypeData[]::new)); + } + + int all(); + + int some(); + + ProperSubtypeData[] subtypeDataList(); +} diff --git a/semtypes/src/main/java/io/ballerina/types/ComplexSemTypeImpl.java b/semtypes/src/main/java/io/ballerina/types/ComplexSemTypeImpl.java new file mode 100644 index 000000000000..d280d98a7081 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/ComplexSemTypeImpl.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.util.Arrays; +import java.util.Objects; + +/** + * @param all all & (1 << c) is non-zero iff this type contains all the basic type with code c + * @param some some & (1 << c) is non-zero iff this type contains some but not all the basic type with code c + * @param subtypeDataList There is one member of subtypes for each bit set in some. Ordered in increasing order of + * BasicTypeCode + */ +record ComplexSemTypeImpl(int all, int some, ProperSubtypeData[] subtypeDataList) implements ComplexSemType { + + @Override + public String toString() { + return "ComplexSemType{all=" + all + PredefinedType.toString(all) + ", some=" + some() + + PredefinedType.toString(some) + ", subtypeDataList=" + Arrays.toString(subtypeDataList()) + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ComplexSemType other)) { + return false; + } + return Objects.equals(all(), other.all()) && + Objects.equals(some(), other.some()) && + Arrays.equals(subtypeDataList(), other.subtypeDataList()); + } + + @Override + public int hashCode() { + return Objects.hash(all(), some(), Arrays.hashCode(subtypeDataList())); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/Conjunction.java b/semtypes/src/main/java/io/ballerina/types/Conjunction.java new file mode 100644 index 000000000000..84b524c1e7ef --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/Conjunction.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Represents the Conjunction record type. + * + * @since 2201.8.0 + */ +public class Conjunction { + public Atom atom; + public Conjunction next; + + private Conjunction(Atom atom, Conjunction next) { + this.atom = atom; + this.next = next; + } + + public static Conjunction and(Atom atom, Conjunction next) { + return new Conjunction(atom, next); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/Context.java b/semtypes/src/main/java/io/ballerina/types/Context.java new file mode 100644 index 000000000000..d0da307ccf5c --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/Context.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * TypeCheckContext node. + * + * @since 2201.8.0 + */ +public final class Context { + + public final Env env; + public final Map functionMemo = new HashMap<>(); + public final Map listMemo = new HashMap<>(); + public final Map mappingMemo = new HashMap<>(); + public final Map comparableMemo = new HashMap<>(); + + // Contains all BddMemo entries with isEmpty == PROVISIONAL + final List memoStack = new ArrayList<>(); + + private static volatile Context instance; + + SemType anydataMemo; + SemType jsonMemo; + SemType cloneableMemo; + SemType isolatedObjectMemo; + SemType serviceObjectMemo; + + private Context(Env env) { + this.env = env; + } + + public static Context from(Env env) { + if (instance == null) { + synchronized (Context.class) { + if (instance == null) { + instance = new Context(env); + } + } + } + if (instance.env == env) { + return instance; + } else { + instance = new Context(env); + } + return instance; + } + + public ListAtomicType listAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return this.env.getRecListAtomType(recAtom); + } else { + return (ListAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public MappingAtomicType mappingAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return this.env.getRecMappingAtomType(recAtom); + } else { + return (MappingAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public FunctionAtomicType functionAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return this.env.getRecFunctionAtomType(recAtom); + } else { + return (FunctionAtomicType) ((TypeAtom) atom).atomicType(); + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/Core.java b/semtypes/src/main/java/io/ballerina/types/Core.java new file mode 100644 index 000000000000..82ab6e91c296 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/Core.java @@ -0,0 +1,1001 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.definition.ListDefinition; +import io.ballerina.types.definition.MappingDefinition; +import io.ballerina.types.definition.ObjectDefinition; +import io.ballerina.types.definition.ObjectQualifiers; +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.subtypedata.BddNodeImpl; +import io.ballerina.types.subtypedata.BddNodeSimple; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.Range; +import io.ballerina.types.subtypedata.StringSubtype; +import io.ballerina.types.subtypedata.TableSubtype; +import io.ballerina.types.typeops.SubtypePair; +import io.ballerina.types.typeops.SubtypePairs; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static io.ballerina.types.BasicTypeCode.BT_BOOLEAN; +import static io.ballerina.types.BasicTypeCode.BT_CELL; +import static io.ballerina.types.BasicTypeCode.BT_DECIMAL; +import static io.ballerina.types.BasicTypeCode.BT_FLOAT; +import static io.ballerina.types.BasicTypeCode.BT_INT; +import static io.ballerina.types.BasicTypeCode.BT_LIST; +import static io.ballerina.types.BasicTypeCode.BT_MAPPING; +import static io.ballerina.types.BasicTypeCode.BT_NIL; +import static io.ballerina.types.BasicTypeCode.BT_STRING; +import static io.ballerina.types.BasicTypeCode.VT_MASK; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.Common.isNothingSubtype; +import static io.ballerina.types.PredefinedType.CELL_ATOMIC_VAL; +import static io.ballerina.types.PredefinedType.INNER; +import static io.ballerina.types.PredefinedType.LIST; +import static io.ballerina.types.PredefinedType.LIST_ATOMIC_INNER; +import static io.ballerina.types.PredefinedType.MAPPING; +import static io.ballerina.types.PredefinedType.MAPPING_ATOMIC_INNER; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.REGEXP; +import static io.ballerina.types.PredefinedType.SIMPLE_OR_STRING; +import static io.ballerina.types.PredefinedType.UNDEF; +import static io.ballerina.types.PredefinedType.VAL; +import static io.ballerina.types.PredefinedType.VAL_READONLY; +import static io.ballerina.types.PredefinedType.XML; +import static io.ballerina.types.subtypedata.CellSubtype.cellContaining; +import static io.ballerina.types.typeops.CellOps.intersectCellAtomicType; +import static io.ballerina.types.typeops.ListOps.bddListMemberTypeInnerVal; +import static io.ballerina.types.typeops.MappingOps.bddMappingMemberTypeInner; +import static java.lang.Long.MAX_VALUE; +import static java.lang.Long.MIN_VALUE; + +/** + * Contain functions defined in `core.bal` file. + * + * @since 2201.8.0 + */ +public final class Core { + + public static CellAtomicType cellAtomType(Atom atom) { + return (CellAtomicType) ((TypeAtom) atom).atomicType(); + } + + public static SemType diff(SemType t1, SemType t2) { + int all1, all2, some1, some2; + if (t1 instanceof BasicTypeBitSet b1) { + if (t2 instanceof BasicTypeBitSet b2) { + return BasicTypeBitSet.from(b1.bitset & ~b2.bitset); + } else { + if (b1.bitset == 0) { + return t1; + } + ComplexSemType c2 = (ComplexSemType) t2; + all2 = c2.all(); + some2 = c2.some(); + } + all1 = b1.bitset; + some1 = 0; + } else { + ComplexSemType c1 = (ComplexSemType) t1; + all1 = c1.all(); + some1 = c1.some(); + if (t2 instanceof BasicTypeBitSet b2) { + if (b2.bitset == BasicTypeCode.VT_MASK) { + return BasicTypeBitSet.from(0); + } + all2 = b2.bitset; + some2 = 0; + } else { + ComplexSemType c2 = (ComplexSemType) t2; + all2 = c2.all(); + some2 = c2.some(); + } + } + BasicTypeBitSet all = BasicTypeBitSet.from(all1 & ~(all2 | some2)); + + int someBitset = (all1 | some1) & ~all2; + someBitset = someBitset & ~all.bitset; + BasicTypeBitSet some = BasicTypeBitSet.from(someBitset); + + if (some.bitset == 0) { + return PredefinedType.basicTypeUnion(all.bitset); + } + List subtypes = new ArrayList<>(); + + for (SubtypePair pair : new SubtypePairs(t1, t2, some)) { + BasicTypeCode code = pair.basicTypeCode; + SubtypeData data1 = pair.subtypeData1; + SubtypeData data2 = pair.subtypeData2; + SubtypeData data; + if (data1 == null) { + data = OpsTable.OPS[code.code].complement(data2); + } else if (data2 == null) { + data = data1; + } else { + data = OpsTable.OPS[code.code].diff(data1, data2); + } + if (!(data instanceof AllOrNothingSubtype allOrNothingSubtype)) { + subtypes.add(BasicSubtype.from(code, (ProperSubtypeData) data)); + } else if (allOrNothingSubtype.isAllSubtype()) { + int c = code.code; + all = BasicTypeBitSet.from(all.bitset | (1 << c)); + } + // No need to consider `data == false` case. The `some` variable above is not used to create the SemType + } + if (subtypes.isEmpty()) { + return all; + } + return ComplexSemType.createComplexSemType(all.bitset, subtypes); + } + + public static List unpackComplexSemType(ComplexSemType t) { + int some = t.some(); + List subtypeList = new ArrayList<>(); + for (ProperSubtypeData data : t.subtypeDataList()) { + BasicTypeCode code = BasicTypeCode.from(Integer.numberOfTrailingZeros(some)); + subtypeList.add(BasicSubtype.from(code, data)); + int c = code.code; + some ^= (1 << c); + } + return subtypeList; + } + + public static SubtypeData getComplexSubtypeData(ComplexSemType t, BasicTypeCode code) { + int c = code.code; + c = 1 << c; + if ((t.all() & c) != 0) { + return AllOrNothingSubtype.createAll(); + } + if ((t.some() & c) == 0) { + return AllOrNothingSubtype.createNothing(); + } + int loBits = t.some() & (c - 1); + return t.subtypeDataList()[loBits == 0 ? 0 : Integer.bitCount(loBits)]; + } + + public static SemType union(SemType t1, SemType t2) { + assert t1 != null && t2 != null; + int all1, all2, some1, some2; + + if (t1 instanceof BasicTypeBitSet b1) { + if (t2 instanceof BasicTypeBitSet b2) { + return BasicTypeBitSet.from(b1.bitset | b2.bitset); + } else { + ComplexSemType complexT2 = (ComplexSemType) t2; + all2 = complexT2.all(); + some2 = complexT2.some(); + } + all1 = b1.bitset; + some1 = 0; + } else { + ComplexSemType complexT1 = (ComplexSemType) t1; + all1 = complexT1.all(); + some1 = complexT1.some(); + if (t2 instanceof BasicTypeBitSet b2) { + all2 = b2.bitset; + some2 = 0; + } else { + ComplexSemType complexT2 = (ComplexSemType) t2; + all2 = complexT2.all(); + some2 = complexT2.some(); + } + } + + BasicTypeBitSet all = BasicTypeBitSet.from(all1 | all2); + BasicTypeBitSet some = BasicTypeBitSet.from((some1 | some2) & ~all.bitset); + if (some.bitset == 0) { + return PredefinedType.basicTypeUnion(all.bitset); + } + + List subtypes = new ArrayList<>(); + + for (SubtypePair pair : new SubtypePairs(t1, t2, some)) { + BasicTypeCode code = pair.basicTypeCode; + SubtypeData data1 = pair.subtypeData1; + SubtypeData data2 = pair.subtypeData2; + + SubtypeData data; + if (data1 == null) { + data = data2; // // [from original impl] if they are both null, something's gone wrong + } else if (data2 == null) { + data = data1; + } else { + data = OpsTable.OPS[code.code].union(data1, data2); + } + + if (data instanceof AllOrNothingSubtype allOrNothingSubtype && allOrNothingSubtype.isAllSubtype()) { + int c = code.code; + all = BasicTypeBitSet.from(all.bitset | 1 << c); + } else { + // data cannot be false since data1 and data2 are not both false + subtypes.add(BasicSubtype.from(code, (ProperSubtypeData) data)); + } + } + + if (subtypes.isEmpty()) { + return all; + } + return ComplexSemType.createComplexSemType(all.bitset, subtypes); + } + + public static SemType intersect(SemType t1, SemType t2) { + int all1, all2, some1, some2; + + if (t1 instanceof BasicTypeBitSet b1) { + if (t2 instanceof BasicTypeBitSet b2) { + return BasicTypeBitSet.from(b1.bitset & b2.bitset); + } else { + if (b1.bitset == 0) { + return t1; + } + if (b1.bitset == VT_MASK) { + return t2; + } + ComplexSemType complexT2 = (ComplexSemType) t2; + all2 = complexT2.all(); + some2 = complexT2.some(); + } + all1 = b1.bitset; + some1 = 0; + } else { + ComplexSemType complexT1 = (ComplexSemType) t1; + all1 = complexT1.all(); + some1 = complexT1.some(); + if (t2 instanceof BasicTypeBitSet b2) { + if (b2.bitset == 0) { + return t2; + } + if (b2.bitset == VT_MASK) { + return t1; + } + all2 = b2.bitset; + some2 = 0; + } else { + ComplexSemType complexT2 = (ComplexSemType) t2; + all2 = complexT2.all(); + some2 = complexT2.some(); + } + } + + BasicTypeBitSet all = BasicTypeBitSet.from(all1 & all2); + BasicTypeBitSet some = BasicTypeBitSet.from((some1 | all1) & (some2 | all2)); + some = BasicTypeBitSet.from(some.bitset & ~all.bitset); + if (some.bitset == 0) { + return PredefinedType.basicTypeUnion(all.bitset); + } + + List subtypes = new ArrayList<>(); + + for (SubtypePair pair : new SubtypePairs(t1, t2, some)) { + BasicTypeCode code = pair.basicTypeCode; + SubtypeData data1 = pair.subtypeData1; + SubtypeData data2 = pair.subtypeData2; + + SubtypeData data; + if (data1 == null) { + data = data2; + } else if (data2 == null) { + data = data1; + } else { + data = OpsTable.OPS[code.code].intersect(data1, data2); + } + if (!(data instanceof AllOrNothingSubtype allOrNothingSubtype) || allOrNothingSubtype.isAllSubtype()) { + subtypes.add(BasicSubtype.from(code, (ProperSubtypeData) data)); + } + } + if (subtypes.isEmpty()) { + return all; + } + return ComplexSemType.createComplexSemType(all.bitset, subtypes); + } + + public static CellSemType intersectMemberSemTypes(Env env, CellSemType t1, CellSemType t2) { + CellAtomicType c1 = cellAtomicType(t1); + CellAtomicType c2 = cellAtomicType(t2); + assert c1 != null && c2 != null; + CellAtomicType atomicType = intersectCellAtomicType(c1, c2); + return cellContaining(env, atomicType.ty(), UNDEF.equals(atomicType.ty()) ? CELL_MUT_NONE : atomicType.mut()); + } + + public static SemType complement(SemType t) { + return diff(VAL, t); + } + + public static boolean isNever(SemType t) { + return (t instanceof BasicTypeBitSet b) && b.bitset == 0; + } + + public static boolean isEmpty(Context cx, SemType t) { + assert t != null && cx != null; + if (t instanceof BasicTypeBitSet b) { + return b.bitset == 0; + } else { + ComplexSemType ct = (ComplexSemType) t; + if (ct.all() != 0) { + // includes all of, one or more basic types + return false; + } + for (var st : unpackComplexSemType(ct)) { + if (!OpsTable.OPS[st.basicTypeCode.code].isEmpty(cx, st.subtypeData)) { + return false; + } + } + return true; + } + } + + public static boolean isSubtype(Context cx, SemType t1, SemType t2) { + return isEmpty(cx, diff(t1, t2)); + } + + public static boolean isSubtypeSimple(SemType t1, BasicTypeBitSet t2) { + int bits; + if (t1 instanceof BasicTypeBitSet b1) { + bits = b1.bitset; + } else { + ComplexSemType complexT1 = (ComplexSemType) t1; + bits = complexT1.all() | complexT1.some(); + } + return (bits & ~t2.bitset) == 0; + } + + public static boolean isSameType(Context context, SemType t1, SemType t2) { + return isSubtype(context, t1, t2) && isSubtype(context, t2, t1); + } + + public static BasicTypeBitSet widenToBasicTypes(SemType t) { + if (t instanceof BasicTypeBitSet b) { + return b; + } else { + ComplexSemType complexSemType = (ComplexSemType) t; + return BasicTypeBitSet.from(complexSemType.all() | complexSemType.some()); + } + } + + // If t is a non-empty subtype of a built-in unsigned int subtype (Unsigned8/16/32), + // then return the smallest such subtype. Otherwise, return t. + public static SemType wideUnsigned(SemType t) { + if (t instanceof BasicTypeBitSet) { + return t; + } else { + if (!isSubtypeSimple(t, PredefinedType.INT)) { + return t; + } + SubtypeData data = IntSubtype.intSubtypeWidenUnsigned(subtypeData(t, BT_INT)); + if (data instanceof AllOrNothingSubtype) { + return PredefinedType.INT; + } else { + return PredefinedType.basicSubtype(BT_INT, (ProperSubtypeData) data); + } + } + } + + public static SubtypeData booleanSubtype(SemType t) { + return subtypeData(t, BT_BOOLEAN); + } + + // Describes the subtype of int included in the type: true/false mean all or none of string + public static SubtypeData intSubtype(SemType t) { + return subtypeData(t, BT_INT); + } + + public static SubtypeData floatSubtype(SemType t) { + return subtypeData(t, BT_FLOAT); + } + + public static SubtypeData decimalSubtype(SemType t) { + return subtypeData(t, BT_DECIMAL); + } + + // Describes the subtype of string included in the type: true/false mean all or none of string + public static SubtypeData stringSubtype(SemType t) { + return subtypeData(t, BT_STRING); + } + + // This computes the spec operation called "member type of K in T", + // for the case when T is a subtype of list, and K is either `int` or a singleton int. + // This is what Castagna calls projection. + // We will extend this to allow `key` to be a SemType, which will turn into an IntSubtype. + // If `t` is not a list, NEVER is returned + public static SemType listMemberTypeInnerVal(Context cx, SemType t, SemType k) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & LIST.bitset) != 0 ? VAL : NEVER; + } else { + SubtypeData keyData = intSubtype(k); + if (isNothingSubtype(keyData)) { + return NEVER; + } + return bddListMemberTypeInnerVal(cx, (Bdd) getComplexSubtypeData((ComplexSemType) t, BT_LIST), keyData, + VAL); + } + } + + static final ListMemberTypes LIST_MEMBER_TYPES_ALL = ListMemberTypes.from( + List.of(Range.from(0, MAX_VALUE)), + List.of(VAL) + ); + + static final ListMemberTypes LIST_MEMBER_TYPES_NONE = ListMemberTypes.from(List.of(), List.of()); + + public static ListMemberTypes listAllMemberTypesInner(Context cx, SemType t) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & LIST.bitset) != 0 ? LIST_MEMBER_TYPES_ALL : LIST_MEMBER_TYPES_NONE; + } + + ComplexSemType ct = (ComplexSemType) t; + List ranges = new ArrayList<>(); + List types = new ArrayList<>(); + + + Range[] allRanges = bddListAllRanges(cx, (Bdd) getComplexSubtypeData(ct, BT_LIST), new Range[]{}); + for (Range r : allRanges) { + SemType m = listMemberTypeInnerVal(cx, t, IntSubtype.intConst(r.min)); + if (!NEVER.equals(m)) { + ranges.add(r); + types.add(m); + } + } + return ListMemberTypes.from(ranges, types); + } + + static Range[] bddListAllRanges(Context cx, Bdd b, Range[] accum) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? accum : new Range[0]; + } else { + BddNode bddNode = (BddNode) b; + ListMemberTypes listMemberTypes = listAtomicTypeAllMemberTypesInnerVal(cx.listAtomType(bddNode.atom())); + return distinctRanges(bddListAllRanges(cx, bddNode.left(), + distinctRanges(listMemberTypes.ranges().toArray(Range[]::new), accum)), + distinctRanges(bddListAllRanges(cx, bddNode.middle(), accum), + bddListAllRanges(cx, bddNode.right(), accum))); + } + } + + static Range[] distinctRanges(Range[] range1, Range[] range2) { + CombinedRange[] combined = combineRanges(range1, range2); + Range[] range = new Range[combined.length]; + for (int i = 0; i < combined.length; i++) { + range[i] = combined[i].range(); + } + return range; + } + + // If [r, i1, i2] is included in the result, then + // at least one of i1 and i2 are not () + // if i1 is not (), then r is completely included in ranges1[i1] + // if i2 is not (), then r is completely included in ranges2[i2] + // The ranges in the result are ordered and non-overlapping. + public static CombinedRange[] combineRanges(Range[] ranges1, Range[] ranges2) { + List combined = new ArrayList<>(); + int i1 = 0; + int i2 = 0; + int len1 = ranges1.length; + int len2 = ranges2.length; + long cur = MIN_VALUE; + // This iterates over the boundaries between ranges + while (true) { + while (i1 < len1 && cur > ranges1[i1].max) { + i1 += 1; + } + while (i2 < len2 && cur > ranges2[i2].max) { + i2 += 1; + } + + Long next = null; + if (i1 < len1) { + next = nextBoundary(cur, ranges1[i1], next); + } + if (i2 < len2) { + next = nextBoundary(cur, ranges2[i2], next); + } + long max = next == null ? MAX_VALUE : next - 1; + Long in1 = null; + if (i1 < len1) { + Range r = ranges1[i1]; + if (cur >= r.min && max <= r.max) { + in1 = (long) i1; + } + } + Long in2 = null; + if (i2 < len2) { + Range r = ranges2[i2]; + if (cur >= r.min && max <= r.max) { + in2 = (long) i2; + } + } + if (in1 != null || in2 != null) { + combined.add(CombinedRange.from(Range.from(cur, max), in1, in2)); + } + if (next == null) { + break; + } + cur = next; + } + return combined.toArray(CombinedRange[]::new); + } + + // Helper function for combineRanges + // Return smallest range boundary that is > cur and <= next + // null represents int:MAX_VALUE + 1 + static Long nextBoundary(long cur, Range r, Long next) { + if ((r.min > cur) && (next == null || r.min < next)) { + return r.min; + } + if (r.max != MAX_VALUE) { + long i = r.max + 1; + if (i > cur && (next == null || i < next)) { + return i; + } + } + return next; + } + + public static ListMemberTypes listAtomicTypeAllMemberTypesInnerVal(ListAtomicType atomicType) { + List ranges = new ArrayList<>(); + List types = new ArrayList<>(); + + List cellInitial = atomicType.members().initial(); + int initialLength = cellInitial.size(); + + List initial = new ArrayList<>(initialLength); + for (CellSemType c : cellInitial) { + initial.add(cellInnerVal(c)); + } + + int fixedLength = atomicType.members().fixedLength(); + if (initialLength != 0) { + types.addAll(initial); + for (int i = 0; i < initialLength; i++) { + ranges.add(Range.from(i, i)); + } + if (initialLength < fixedLength) { + ranges.set(initialLength - 1, Range.from(initialLength - 1, fixedLength - 1)); + } + } + + SemType rest = cellInnerVal(atomicType.rest()); + if (!Core.isNever(rest)) { + types.add(rest); + ranges.add(Range.from(fixedLength, MAX_VALUE)); + } + + return ListMemberTypes.from(ranges, types); + } + + public static MappingAtomicType mappingAtomicType(Context cx, SemType t) { + MappingAtomicType mappingAtomicInner = MAPPING_ATOMIC_INNER; + if (t instanceof BasicTypeBitSet b) { + return b.bitset == MAPPING.bitset ? mappingAtomicInner : null; + } else { + Env env = cx.env; + if (!isSubtypeSimple(t, MAPPING)) { + return null; + } + return bddMappingAtomicType(env, + (Bdd) getComplexSubtypeData((ComplexSemType) t, BT_MAPPING), + mappingAtomicInner); + } + } + + private static MappingAtomicType bddMappingAtomicType(Env env, Bdd bdd, MappingAtomicType top) { + if (bdd instanceof BddAllOrNothing allOrNothing) { + if (allOrNothing.isAll()) { + return top; + } + return null; + } + BddNode bddNode = (BddNode) bdd; + if (bddNode instanceof BddNodeSimple bddNodeSimple) { + return env.mappingAtomType(bddNodeSimple.atom()); + } + return null; + } + + public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k) { + return diff(mappingMemberTypeInner(cx, t, k), UNDEF); + } + + // This computes the spec operation called "member type of K in T", + // for when T is a subtype of mapping, and K is either `string` or a singleton string. + // This is what Castagna calls projection. + public static SemType mappingMemberTypeInner(Context cx, SemType t, SemType k) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & MAPPING.bitset) != 0 ? VAL : UNDEF; + } else { + SubtypeData keyData = stringSubtype(k); + if (isNothingSubtype(keyData)) { + return UNDEF; + } + return bddMappingMemberTypeInner(cx, (Bdd) getComplexSubtypeData((ComplexSemType) t, BT_MAPPING), keyData, + INNER); + } + } + + public static ListAtomicType listAtomicType(Context cx, SemType t) { + ListAtomicType listAtomicInner = LIST_ATOMIC_INNER; + if (t instanceof BasicTypeBitSet b) { + return b.bitset == LIST.bitset ? listAtomicInner : null; + } else { + Env env = cx.env; + if (!isSubtypeSimple(t, LIST)) { + return null; + } + return bddListAtomicType(env, + (Bdd) getComplexSubtypeData((ComplexSemType) t, BT_LIST), + listAtomicInner); + } + } + + private static ListAtomicType bddListAtomicType(Env env, Bdd bdd, ListAtomicType top) { + if (bdd instanceof BddAllOrNothing allOrNothing) { + if (allOrNothing.isAll()) { + return top; + } + return null; + } + BddNode bddNode = (BddNode) bdd; + if (bddNode instanceof BddNodeSimple bddNodeSimple) { + return env.listAtomType(bddNodeSimple.atom()); + } + return null; + } + + public static SemType cellInnerVal(CellSemType t) { + return diff(cellInner(t), UNDEF); + } + + public static SemType cellInner(CellSemType t) { + CellAtomicType cat = cellAtomicType(t); + assert cat != null; + return cat.ty(); + } + + public static CellSemType cellContainingInnerVal(Env env, CellSemType t) { + CellAtomicType cat = cellAtomicType(t); + assert cat != null; + return cellContaining(env, diff(cat.ty(), UNDEF), cat.mut()); + } + + public static CellAtomicType cellAtomicType(SemType t) { + if (t instanceof BasicTypeBitSet) { + return PredefinedType.CELL.equals(t) ? CELL_ATOMIC_VAL : null; + } else { + if (!isSubtypeSimple(t, PredefinedType.CELL)) { + return null; + } + return bddCellAtomicType((Bdd) getComplexSubtypeData((ComplexSemType) t, BT_CELL), CELL_ATOMIC_VAL); + } + } + + static CellAtomicType bddCellAtomicType(Bdd bdd, CellAtomicType top) { + if (bdd instanceof BddAllOrNothing allOrNothing) { + if (allOrNothing.isAll()) { + return top; + } + return null; + } + BddNode bddNode = (BddNode) bdd; + if (bddNode.left().equals(BddAllOrNothing.bddAll()) && + bddNode.middle().equals(BddAllOrNothing.bddNothing()) && + bddNode.right().equals(BddAllOrNothing.bddNothing())) { + return cellAtomType(bddNode.atom()); + } + return null; + } + + public static Optional singleShape(SemType t) { + if (PredefinedType.NIL.equals(t)) { + return Optional.of(Value.from(null)); + } else if (t instanceof BasicTypeBitSet) { + return Optional.empty(); + } else if (isSubtypeSimple(t, PredefinedType.INT)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_INT); + Optional value = IntSubtype.intSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(Value.from(value.get())); + } else if (isSubtypeSimple(t, PredefinedType.FLOAT)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_FLOAT); + Optional value = FloatSubtype.floatSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(Value.from(value.get())); + } else if (isSubtypeSimple(t, PredefinedType.STRING)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_STRING); + Optional value = StringSubtype.stringSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(Value.from(value.get())); + } else if (isSubtypeSimple(t, PredefinedType.BOOLEAN)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_BOOLEAN); + Optional value = BooleanSubtype.booleanSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(Value.from(value.get())); + } else if (isSubtypeSimple(t, PredefinedType.DECIMAL)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_DECIMAL); + Optional value = DecimalSubtype.decimalSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(Value.from(value.get().toString())); + } + return Optional.empty(); + } + + public static SemType singleton(Object v) { + if (v == null) { + return PredefinedType.NIL; + } + + if (v instanceof Long lng) { + return IntSubtype.intConst(lng); + } else if (v instanceof Double d) { + return FloatSubtype.floatConst(d); + } else if (v instanceof String s) { + return StringSubtype.stringConst(s); + } else if (v instanceof Boolean b) { + return BooleanSubtype.booleanConst(b); + } else { + throw new IllegalStateException("Unsupported type: " + v.getClass().getName()); + } + } + + public static boolean containsConst(SemType t, Object v) { + if (v == null) { + return containsNil(t); + } else if (v instanceof Long lng) { + return containsConstInt(t, lng); + } else if (v instanceof Double d) { + return containsConstFloat(t, d); + } else if (v instanceof String s) { + return containsConstString(t, s); + } else if (v instanceof Boolean b) { + return containsConstBoolean(t, b); + } else { + return containsConstDecimal(t, (BigDecimal) v); + } + } + + public static boolean containsNil(SemType t) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & (1 << BT_NIL.code)) != 0; + } else { + // todo: Need to verify this behavior + AllOrNothingSubtype complexSubtypeData = + (AllOrNothingSubtype) getComplexSubtypeData((ComplexSemType) t, BT_NIL); + return complexSubtypeData.isAllSubtype(); + } + } + + + public static boolean containsConstString(SemType t, String s) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & (1 << BT_STRING.code)) != 0; + } else { + return StringSubtype.stringSubtypeContains( + getComplexSubtypeData((ComplexSemType) t, BT_STRING), s); + } + } + + public static boolean containsConstInt(SemType t, long n) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & (1 << BT_INT.code)) != 0; + } else { + return IntSubtype.intSubtypeContains( + getComplexSubtypeData((ComplexSemType) t, BT_INT), n); + } + } + + public static boolean containsConstFloat(SemType t, double n) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & (1 << BT_FLOAT.code)) != 0; + } else { + return FloatSubtype.floatSubtypeContains( + getComplexSubtypeData((ComplexSemType) t, BT_FLOAT), EnumerableFloat.from(n)); + } + } + + public static boolean containsConstDecimal(SemType t, BigDecimal n) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & (1 << BT_DECIMAL.code)) != 0; + } else { + return DecimalSubtype.decimalSubtypeContains( + getComplexSubtypeData((ComplexSemType) t, BT_DECIMAL), EnumerableDecimal.from(n)); + } + } + + public static boolean containsConstBoolean(SemType t, boolean bool) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & (1 << BT_BOOLEAN.code)) != 0; + } else { + return BooleanSubtype.booleanSubtypeContains( + getComplexSubtypeData((ComplexSemType) t, BT_BOOLEAN), bool); + } + } + + public static Optional singleNumericType(SemType semType) { + SemType numType = intersect(semType, PredefinedType.NUMBER); + if (numType instanceof BasicTypeBitSet b) { + if (b.bitset == NEVER.bitset) { + return Optional.empty(); + } + } + if (isSubtypeSimple(numType, PredefinedType.INT)) { + return Optional.of(PredefinedType.INT); + } + if (isSubtypeSimple(numType, PredefinedType.FLOAT)) { + return Optional.of(PredefinedType.FLOAT); + } + if (isSubtypeSimple(numType, PredefinedType.DECIMAL)) { + return Optional.of(PredefinedType.DECIMAL); + } + return Optional.empty(); + } + + public static SubtypeData subtypeData(SemType s, BasicTypeCode code) { + if (s instanceof BasicTypeBitSet b) { + if ((b.bitset & (1 << code.code)) != 0) { + return AllOrNothingSubtype.createAll(); + } + return AllOrNothingSubtype.createNothing(); + } else { + return getComplexSubtypeData((ComplexSemType) s, code); + } + } + + public static Context typeCheckContext(Env env) { + return Context.from(env); + } + + public static SemType createJson(Context context) { + SemType memo = context.jsonMemo; + Env env = context.env; + + if (memo != null) { + return memo; + } + ListDefinition listDef = new ListDefinition(); + MappingDefinition mapDef = new MappingDefinition(); + SemType j = union(PredefinedType.SIMPLE_OR_STRING, union(listDef.getSemType(env), mapDef.getSemType(env))); + listDef.defineListTypeWrapped(env, j); + mapDef.defineMappingTypeWrapped(env, new ArrayList<>(), j); + context.jsonMemo = j; + return j; + } + + public static SemType createAnydata(Context context) { + SemType memo = context.anydataMemo; + Env env = context.env; + + if (memo != null) { + return memo; + } + ListDefinition listDef = new ListDefinition(); + MappingDefinition mapDef = new MappingDefinition(); + SemType tableTy = TableSubtype.tableContaining(env, mapDef.getSemType(env)); + SemType ad = union(union(SIMPLE_OR_STRING, union(XML, union(REGEXP, tableTy))), + union(listDef.getSemType(env), mapDef.getSemType(env))); + listDef.defineListTypeWrapped(env, ad); + mapDef.defineMappingTypeWrapped(env, new ArrayList<>(), ad); + context.anydataMemo = ad; + return ad; + } + + public static SemType createCloneable(Context context) { + SemType memo = context.cloneableMemo; + Env env = context.env; + + if (memo != null) { + return memo; + } + ListDefinition listDef = new ListDefinition(); + MappingDefinition mapDef = new MappingDefinition(); + SemType tableTy = TableSubtype.tableContaining(env, mapDef.getSemType(env)); + SemType ad = union(VAL_READONLY, union(XML, union(listDef.getSemType(env), union(tableTy, + mapDef.getSemType(env))))); + listDef.defineListTypeWrapped(env, ad); + mapDef.defineMappingTypeWrapped(env, new ArrayList<>(), ad); + context.cloneableMemo = ad; + return ad; + } + + public static SemType createIsolatedObject(Context context) { + SemType memo = context.isolatedObjectMemo; + if (memo != null) { + return memo; + } + + ObjectQualifiers quals = new ObjectQualifiers(true, false, ObjectQualifiers.NetworkQualifier.None); + SemType isolatedObj = new ObjectDefinition().define(context.env, quals, Collections.emptyList()); + context.isolatedObjectMemo = isolatedObj; + return isolatedObj; + } + + public static SemType createServiceObject(Context context) { + SemType memo = context.serviceObjectMemo; + if (memo != null) { + return memo; + } + + ObjectQualifiers quals = new ObjectQualifiers(false, false, ObjectQualifiers.NetworkQualifier.Service); + SemType serviceObj = new ObjectDefinition().define(context.env, quals, Collections.emptyList()); + context.serviceObjectMemo = serviceObj; + return serviceObj; + } + + public static SemType createBasicSemType(BasicTypeCode typeCode, SubtypeData subtypeData) { + if (subtypeData instanceof AllOrNothingSubtype) { + if (Common.isAllSubtype(subtypeData)) { + return BasicTypeBitSet.from(1 << typeCode.code); + } else { + return BasicTypeBitSet.from(0); + } + } else { + return ComplexSemType.createComplexSemType(0, + BasicSubtype.from(typeCode, (ProperSubtypeData) subtypeData)); + } + } + + // ------------------------- Newly Introduced APIs (Does not exist in nBallerina) -------------------------------- + + // Consider map|map|...|map. This API will return all MappingAtomicTypes in the union. + public static Optional> mappingAtomicTypesInUnion(Context cx, SemType t) { + ArrayList matList = new ArrayList<>(); + MappingAtomicType mappingAtomicInner = MAPPING_ATOMIC_INNER; + if (t instanceof BasicTypeBitSet b) { + if (b.bitset == MAPPING.bitset) { + matList.add(mappingAtomicInner); + return Optional.of(matList); + } + return Optional.empty(); + } else { + Env env = cx.env; + if (!isSubtypeSimple(t, MAPPING)) { + return Optional.empty(); + } + return collectBddMappingAtomicTypesInUnion(env, + (Bdd) getComplexSubtypeData((ComplexSemType) t, BT_MAPPING), + mappingAtomicInner, matList) ? Optional.of(matList) : Optional.empty(); + } + } + + private static boolean collectBddMappingAtomicTypesInUnion(Env env, Bdd bdd, MappingAtomicType top, + List matList) { + if (bdd instanceof BddAllOrNothing allOrNothing) { + if (allOrNothing.isAll()) { + matList.add(top); + return true; + } + return false; + } + BddNode bddNode = (BddNode) bdd; + if (bddNode instanceof BddNodeSimple bddNodeSimple) { + matList.add(env.mappingAtomType(bddNodeSimple.atom())); + return true; + } + + BddNodeImpl bddNodeImpl = (BddNodeImpl) bddNode; + if (bddNodeImpl.left() instanceof BddAllOrNothing leftNode && leftNode.isAll() && + bddNodeImpl.right() instanceof BddAllOrNothing rightNode && rightNode.isNothing()) { + matList.add(env.mappingAtomType(bddNodeImpl.atom())); + return collectBddMappingAtomicTypesInUnion(env, bddNodeImpl.middle(), top, matList); + } + + return false; + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/Definition.java b/semtypes/src/main/java/io/ballerina/types/Definition.java similarity index 70% rename from semtypes/src/main/java/io/ballerina/semtype/Definition.java rename to semtypes/src/main/java/io/ballerina/types/Definition.java index da0d79a9bf6a..6b0a359083fa 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/Definition.java +++ b/semtypes/src/main/java/io/ballerina/types/Definition.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,16 +11,16 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** * Super type for type-descs. * - * @since 2.0.0 + * @since 2201.8.0 */ public interface Definition { SemType getSemType(Env env); diff --git a/semtypes/src/main/java/io/ballerina/types/EnumerableCharString.java b/semtypes/src/main/java/io/ballerina/types/EnumerableCharString.java new file mode 100644 index 000000000000..d017d133c65c --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/EnumerableCharString.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Enumerable type wrapper for string. + * + * @since 2201.8.0 + */ +public class EnumerableCharString implements EnumerableType { + // String since Java char can't hold some Unicode characters + public final String value; + + private EnumerableCharString(String value) { + this.value = value; + } + + public static EnumerableCharString from(String v) { + return new EnumerableCharString(v); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof EnumerableCharString e)) { + return false; + } + return (e.value.equals(this.value)); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/EnumerableDecimal.java b/semtypes/src/main/java/io/ballerina/types/EnumerableDecimal.java new file mode 100644 index 000000000000..09fc64e5fdf9 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/EnumerableDecimal.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.math.BigDecimal; + +/** + * Enumerable type wrapper for decimal. + * + * @since 2201.8.0 + */ +public class EnumerableDecimal implements EnumerableType { + public final BigDecimal value; + + private EnumerableDecimal(BigDecimal value) { + this.value = value; + } + + public static EnumerableDecimal from(BigDecimal d) { + return new EnumerableDecimal(d); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof EnumerableDecimal e)) { + return false; + } + return (e.value.compareTo(this.value) == 0); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/EnumerableFloat.java b/semtypes/src/main/java/io/ballerina/types/EnumerableFloat.java new file mode 100644 index 000000000000..09531897daef --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/EnumerableFloat.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Enumerable type wrapper for float. + * + * @since 2201.8.0 + */ +public class EnumerableFloat implements EnumerableType { + public final double value; + + private EnumerableFloat(double value) { + this.value = value; + } + + public static EnumerableFloat from(double d) { + return new EnumerableFloat(d); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof EnumerableFloat e)) { + return false; + } + + Double v1 = e.value; + Double v2 = this.value; + return (v1.compareTo(v2) == 0); + } + + @Override + public int hashCode() { + return Double.hashCode(value); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/EnumerableString.java b/semtypes/src/main/java/io/ballerina/types/EnumerableString.java new file mode 100644 index 000000000000..55a2c8958d4f --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/EnumerableString.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Enumerable type wrapper for string. + * + * @since 2201.8.0 + */ +public class EnumerableString implements EnumerableType { + public final String value; + + private EnumerableString(String value) { + this.value = value; + } + + public static EnumerableString from(String v) { + return new EnumerableString(v); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof EnumerableString e)) { + return false; + } + return (e.value.equals(this.value)); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/EnumerableSubtype.java b/semtypes/src/main/java/io/ballerina/types/EnumerableSubtype.java new file mode 100644 index 000000000000..68c5134ed7eb --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/EnumerableSubtype.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Objects; + +/** + * EnumerableSubtype with enumerable subtype ops. + * + * @since 2201.8.0 + */ +public abstract class EnumerableSubtype { + public static final int LT = -1; + public static final int EQ = 0; + public static final int GT = 1; + + public abstract boolean allowed(); + public abstract EnumerableType[] values(); + + public static boolean enumerableSubtypeUnion(EnumerableSubtype t1, EnumerableSubtype t2, + List result) { + boolean b1 = t1.allowed(); + boolean b2 = t2.allowed(); + boolean allowed; + if (b1 && b2) { + enumerableListUnion(t1.values(), t2.values(), result); + allowed = true; + } else if (!b1 && !b2) { + enumerableListIntersect(t1.values(), t2.values(), result); + allowed = false; + } else if (b1 && !b2) { + enumerableListDiff(t2.values(), t1.values(), result); + allowed = false; + } else { + // !b1 && b2 + enumerableListDiff(t1.values(), t2.values(), result); + allowed = false; + } + return allowed; + } + + public static boolean enumerableSubtypeIntersect(EnumerableSubtype t1, EnumerableSubtype t2, + List result) { + boolean b1 = t1.allowed(); + boolean b2 = t2.allowed(); + boolean allowed; + if (b1 && b2) { + enumerableListIntersect(t1.values(), t2.values(), result); + allowed = true; + } else if (!b1 && !b2) { + enumerableListUnion(t1.values(), t2.values(), result); + allowed = false; + } else if (b1 && !b2) { + enumerableListDiff(t1.values(), t2.values(), result); + allowed = true; + } else { + // !b1 && b2 + enumerableListDiff(t2.values(), t1.values(), result); + allowed = true; + } + return allowed; + } + + public static void enumerableListUnion(EnumerableType[] v1, EnumerableType[] v2, + List resulte) { + List result = (List) resulte; + int i1 = 0; + int i2 = 0; + int len1 = v1.length; + int len2 = v2.length; + + while (true) { + if (i1 >= len1) { + if (i2 >= len2) { + break; + } + result.add(v2[i2]); + i2 += 1; + } else if (i2 >= len2) { + result.add(v1[i1]); + i1 += 1; + } else { + EnumerableType s1 = v1[i1]; + EnumerableType s2 = v2[i2]; + switch (compareEnumerable(s1, s2)) { + case EQ: + result.add(s1); + i1 += 1; + i2 += 1; + break; + case LT: + result.add(s1); + i1 += 1; + break; + case GT: + result.add(s2); + i2 += 1; + break; + } + } + } + } + + public static void enumerableListIntersect(EnumerableType[] v1, EnumerableType[] v2, + List resulte) { + List result = (List) resulte; + int i1 = 0; + int i2 = 0; + int len1 = v1.length; + int len2 = v2.length; + + while (true) { + if (i1 >= len1 || i2 >= len2) { + break; + } else { + EnumerableType s1 = v1[i1]; + EnumerableType s2 = v2[i2]; + switch (compareEnumerable(s1, s2)) { + case EQ: + result.add(s1); + i1 += 1; + i2 += 1; + break; + case LT: + i1 += 1; + break; + case GT: + i2 += 1; + break; + } + } + } + } + + public static void enumerableListDiff(EnumerableType[] v1, EnumerableType[] v2, + List resulte) { + List result = (List) resulte; + int i1 = 0; + int i2 = 0; + int len1 = v1.length; + int len2 = v2.length; + + while (true) { + if (i1 >= len1) { + break; + } + if (i2 >= len2) { + result.add(v1[i1]); + i1 += 1; + } else { + EnumerableType s1 = v1[i1]; + EnumerableType s2 = v2[i2]; + switch (compareEnumerable(s1, s2)) { + case EQ: + i1 += 1; + i2 += 1; + break; + case LT: + result.add(s1); + i1 += 1; + break; + case GT: + i2 += 1; + break; + } + } + } + } + + public static int compareEnumerable(EnumerableType v1, EnumerableType v2) { + if (v1 instanceof EnumerableString) { + String s2 = ((EnumerableString) v2).value; + String s1 = ((EnumerableString) v1).value; + return Objects.equals(s1, s2) ? EQ : (Common.codePointCompare(s1, s2) ? LT : GT); + } else if (v1 instanceof EnumerableCharString) { + String s2 = ((EnumerableCharString) v2).value + ""; + String s1 = ((EnumerableCharString) v1).value + ""; + return Objects.equals(s1, s2) ? EQ : (Common.codePointCompare(s1, s2) ? LT : GT); + } else if (v1 instanceof EnumerableDecimal) { + BigDecimal d2 = ((EnumerableDecimal) v2).value; + BigDecimal d1 = ((EnumerableDecimal) v1).value; + return d1.compareTo(d2); + } else { + double f1 = ((EnumerableFloat) v1).value; + double f2 = ((EnumerableFloat) v2).value; + if (bFloatEq(f1, f2)) { + return EQ; + } else if (Double.isNaN(f1)) { + return LT; + } else if (Double.isNaN(f2)) { + return GT; + } else if (f1 < f2) { + return LT; + } + return GT; + } + } + + private static boolean bFloatEq(double f1, double f2) { + if (Double.isNaN(f1)) { + return Double.isNaN(f2); + } + return f1 == f2; + } +} + diff --git a/semtypes/src/main/java/io/ballerina/types/EnumerableType.java b/semtypes/src/main/java/io/ballerina/types/EnumerableType.java new file mode 100644 index 000000000000..04c46676fa85 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/EnumerableType.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Interface to indicate Enumerable types. + * + * @since 2201.8.0 + */ +public interface EnumerableType { +} diff --git a/semtypes/src/main/java/io/ballerina/types/Env.java b/semtypes/src/main/java/io/ballerina/types/Env.java new file mode 100644 index 000000000000..f9b512b72c13 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/Env.java @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Env node. + * + * @since 2201.8.0 + */ +public class Env { + + private static final int COMPACT_INDEX = 3; + final List recListAtoms; + final List recMappingAtoms; + final List recFunctionAtoms; + private final AtomicInteger distinctAtomCount; + private final Map> atomTable; + + private final LinkedHashMap types; + + public Env() { + this.atomTable = new WeakHashMap<>(); + this.recListAtoms = new ArrayList<>(); + this.recMappingAtoms = new ArrayList<>(); + this.recFunctionAtoms = new ArrayList<>(); + types = new LinkedHashMap<>(); + distinctAtomCount = new AtomicInteger(0); + + PredefinedTypeEnv.getInstance().initializeEnv(this); + } + + public int recListAtomCount() { + return this.recListAtoms.size(); + } + + public int recMappingAtomCount() { + return this.recMappingAtoms.size(); + } + + public int recFunctionAtomCount() { + return this.recFunctionAtoms.size(); + } + + public int distinctAtomCount() { + return this.distinctAtomCount.get(); + } + + public int distinctAtomCountGetAndIncrement() { + return this.distinctAtomCount.getAndIncrement(); + } + + public RecAtom recFunctionAtom() { + synchronized (this.recFunctionAtoms) { + int result = this.recFunctionAtoms.size(); + // represents adding () in nballerina + this.recFunctionAtoms.add(null); + return RecAtom.createRecAtom(result); + } + } + + public void setRecFunctionAtomType(RecAtom ra, FunctionAtomicType atomicType) { + synchronized (this.recFunctionAtoms) { + ra.setKind(Atom.Kind.FUNCTION_ATOM); + this.recFunctionAtoms.set(ra.index, atomicType); + } + } + + public FunctionAtomicType getRecFunctionAtomType(RecAtom ra) { + synchronized (this.recFunctionAtoms) { + return this.recFunctionAtoms.get(ra.index); + } + } + + public TypeAtom listAtom(ListAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + public TypeAtom mappingAtom(MappingAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + public TypeAtom functionAtom(FunctionAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + public TypeAtom cellAtom(CellAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + private TypeAtom typeAtom(AtomicType atomicType) { + synchronized (this.atomTable) { + Reference ref = this.atomTable.get(atomicType); + if (ref != null) { + TypeAtom ta = ref.get(); + if (ta != null) { + return ta; + } + } + TypeAtom result = TypeAtom.createTypeAtom(this.atomTable.size(), atomicType); + this.atomTable.put(result.atomicType(), new WeakReference<>(result)); + return result; + } + } + + public void deserializeTypeAtom(TypeAtom typeAtom) { + synchronized (this.atomTable) { + this.atomTable.put(typeAtom.atomicType(), new WeakReference<>(typeAtom)); + } + } + + public void insertRecAtomAtIndex(int index, AtomicType atomicType) { + if (atomicType instanceof MappingAtomicType mappingAtomicType) { + insertAtomAtIndexInner(index, this.recMappingAtoms, mappingAtomicType); + } else if (atomicType instanceof ListAtomicType listAtomicType) { + insertAtomAtIndexInner(index, this.recListAtoms, listAtomicType); + } else if (atomicType instanceof FunctionAtomicType functionAtomicType) { + insertAtomAtIndexInner(index, this.recFunctionAtoms, functionAtomicType); + } else { + throw new UnsupportedOperationException("Unknown atomic type " + atomicType); + } + } + + private void insertAtomAtIndexInner(int index, List atoms, E atomicType) { + // atoms are always private final fields therefore synchronizing on them should be safe. + synchronized (atoms) { + if (atoms.size() > index && atoms.get(index) != null) { + return; + } + while (atoms.size() < index + 1) { + atoms.add(null); + } + atoms.set(index, atomicType); + } + } + + public ListAtomicType listAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return getRecListAtomType(recAtom); + } else { + return (ListAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public FunctionAtomicType functionAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return getRecFunctionAtomType(recAtom); + } else { + return (FunctionAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public MappingAtomicType mappingAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return getRecMappingAtomType(recAtom); + } else { + return (MappingAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public RecAtom recListAtom() { + synchronized (this.recListAtoms) { + int result = this.recListAtoms.size(); + this.recListAtoms.add(null); + return RecAtom.createRecAtom(result); + } + } + + public RecAtom recMappingAtom() { + synchronized (this.recMappingAtoms) { + int result = this.recMappingAtoms.size(); + this.recMappingAtoms.add(null); + return RecAtom.createRecAtom(result); + } + } + + public void setRecListAtomType(RecAtom ra, ListAtomicType atomicType) { + synchronized (this.recListAtoms) { + ra.setKind(Atom.Kind.LIST_ATOM); + this.recListAtoms.set(ra.index, atomicType); + } + } + + public void setRecMappingAtomType(RecAtom ra, MappingAtomicType atomicType) { + synchronized (this.recListAtoms) { + ra.setKind(Atom.Kind.MAPPING_ATOM); + this.recMappingAtoms.set(ra.index, atomicType); + } + } + + public ListAtomicType getRecListAtomType(RecAtom ra) { + synchronized (this.recListAtoms) { + return this.recListAtoms.get(ra.index); + } + } + + public MappingAtomicType getRecMappingAtomType(RecAtom ra) { + synchronized (this.recMappingAtoms) { + return this.recMappingAtoms.get(ra.index); + } + } + + public static CellAtomicType cellAtomType(Atom atom) { + return (CellAtomicType) ((TypeAtom) atom).atomicType(); + } + + public void addTypeDef(String typeName, SemType semType) { + this.types.put(typeName, semType); + } + + public Map getTypeNameSemTypeMap() { + return new LinkedHashMap<>(this.types); + } + + public int atomCount() { + synchronized (this.atomTable) { + return this.atomTable.size(); + } + } + + // TODO: instead of compact index we should analyze the environment before serialization, but a naive bumping index + // in the BIRTypeWriter created incorrect indexes in the BIR. This is a temporary workaround. + private CompactionData compactionData = null; + + /** + * During type checking we create recursive type atoms that are not parts of the actual ballerina module which will + * not marshalled. This leaves "holes" in the rec atom lists when we unmarshall the BIR, which will then get + * propagated from one module to next. This method will return a new index corrected for such holes. + * + * @param recAtom atom for which you need the corrected index + * @return index corrected for "holes" in rec atom list + */ + public synchronized int compactRecIndex(RecAtom recAtom) { + if (compactionData == null || !compactionData.state().equals(EnvState.from(this))) { + compactionData = compaction(); + } + if (recAtom.index < COMPACT_INDEX) { + return recAtom.index; + } + return switch (recAtom.kind()) { + case LIST_ATOM -> compactionData.listMap().get(recAtom.index()); + case MAPPING_ATOM -> compactionData.mapMap().get(recAtom.index()); + case FUNCTION_ATOM -> compactionData.funcMap().get(recAtom.index()); + case CELL_ATOM, XML_ATOM, DISTINCT_ATOM -> recAtom.index; + }; + } + + private CompactionData compaction() { + EnvState state = EnvState.from(this); + Map listMap = recListCompaction(this.recListAtoms); + Map mapMap = recListCompaction(this.recMappingAtoms); + Map funcMap = recListCompaction(this.recFunctionAtoms); + return new CompactionData(state, listMap, mapMap, funcMap); + } + + private Map recListCompaction(List recAtomList) { + Map map = new HashMap<>(); + int compactIndex = COMPACT_INDEX; + for (int i = COMPACT_INDEX; i < recAtomList.size(); i++) { + if (recAtomList.get(i) != null) { + map.put(i, compactIndex); + compactIndex++; + } + } + return map; + } + + record EnvState(int recListAtomCount, int recMappingAtomCount, int recFunctionAtomCount) { + + public static EnvState from(Env env) { + return new EnvState(env.recListAtomCount(), env.recMappingAtomCount(), + env.recFunctionAtomCount()); + } + } + + private record CompactionData(EnvState state, Map listMap, Map mapMap, + Map funcMap) { + + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/Error.java b/semtypes/src/main/java/io/ballerina/types/Error.java new file mode 100644 index 000000000000..a3577b4c9f38 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/Error.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.BddNode; + +import static io.ballerina.types.BasicTypeCode.BT_ERROR; +import static io.ballerina.types.Core.subtypeData; +import static io.ballerina.types.PredefinedType.BDD_SUBTYPE_RO; +import static io.ballerina.types.PredefinedType.ERROR; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.basicSubtype; +import static io.ballerina.types.RecAtom.createDistinctRecAtom; +import static io.ballerina.types.typeops.BddCommonOps.bddAtom; +import static io.ballerina.types.typeops.BddCommonOps.bddIntersect; + +/** + * Contain functions found in error.bal file. + * + * @since 2201.8.0 + */ +public class Error { + public static SemType errorDetail(SemType detail) { + SubtypeData mappingSd = subtypeData(detail, BasicTypeCode.BT_MAPPING); + if (mappingSd instanceof AllOrNothingSubtype allOrNothingSubtype) { + if (allOrNothingSubtype.isAllSubtype()) { + return ERROR; + } else { + // XXX This should be reported as an error + return NEVER; + } + } + + SubtypeData sd = bddIntersect((Bdd) mappingSd, BDD_SUBTYPE_RO); + if (sd.equals(BDD_SUBTYPE_RO)) { + return ERROR; + } + return basicSubtype(BT_ERROR, (ProperSubtypeData) sd); + } + + public static SemType errorDistinct(int distinctId) { + assert distinctId >= 0; + BddNode bdd = bddAtom(createDistinctRecAtom(-distinctId - 1)); + return basicSubtype(BT_ERROR, bdd); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/FixedLengthArray.java b/semtypes/src/main/java/io/ballerina/types/FixedLengthArray.java new file mode 100644 index 000000000000..b12b3a08c103 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/FixedLengthArray.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Represent a fixed length semtype member list similar to a tuple. + * The length of the list is `fixedLength`, the last member of the `initial` is repeated to achieve this semantic. + * { initial: [int], fixedLength: 3, } is same as { initial: [int, int, int], fixedLength: 3 } + * { initial: [string, int], fixedLength: 100 } means `int` is repeated 99 times to get a list of 100 members. + * `fixedLength` must be `0` when `inital` is empty and the `fixedLength` must be at least `initial.length()` + * + * @param initial List of semtypes of the members of the fixes length array. If last member is repeated multiple + * times it is included only once. For example for {@code [string, string, int, int]} initial would + * be {@code [string, string, int]} + * @param fixedLength Actual length of the array. For example for {@code [string, string, int, int]} fixedLength would + * be {@code 4} + * @since 2201.8.0 + */ +public record FixedLengthArray(List initial, int fixedLength) { + + public FixedLengthArray { + initial = List.copyOf(initial); + assert fixedLength >= 0; + } + + public static FixedLengthArray from(List initial, int fixedLength) { + return new FixedLengthArray(initial, fixedLength); + } + + public List initial() { + return Collections.unmodifiableList(initial); + } + + public static FixedLengthArray empty() { + return from(new ArrayList<>(), 0); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/FunctionAtomicType.java b/semtypes/src/main/java/io/ballerina/types/FunctionAtomicType.java new file mode 100644 index 000000000000..a4bcfcd4356b --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/FunctionAtomicType.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * FunctionAtomicType node. + * + * @param paramType semtype of parameters represented as a tuple + * @param retType semtype of the return value + * @param qualifiers qualifiers of the function + * @param isGeneric atomic type represent a generic (i.e. have parameters/return type with {@code typeParam} annotation) + * @since 2201.8.0 + */ +public record FunctionAtomicType(SemType paramType, SemType retType, SemType qualifiers, boolean isGeneric) + implements AtomicType { + + public static FunctionAtomicType from(SemType paramType, SemType rest, SemType qualifiers) { + return new FunctionAtomicType(paramType, rest, qualifiers, false); + } + + public static FunctionAtomicType genericFrom(SemType paramType, SemType rest, SemType qualifiers) { + return new FunctionAtomicType(paramType, rest, qualifiers, true); + } + + @Override + public Atom.Kind atomKind() { + return Atom.Kind.FUNCTION_ATOM; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/IntSubtypeConstraints.java b/semtypes/src/main/java/io/ballerina/types/IntSubtypeConstraints.java new file mode 100644 index 000000000000..b597b70da8e4 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/IntSubtypeConstraints.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.IntSubtype; + +import static io.ballerina.types.Core.intSubtype; +import static io.ballerina.types.typeops.IntOps.intSubtypeMax; +import static io.ballerina.types.typeops.IntOps.intSubtypeMin; + +/** + * Port of: + *

+ * {@code + * // Constraints on a subtype of `int`. + * public type IntSubtypeConstraints readonly & record {| + * // all values in the subtype are >= min + * int min; + * // all values in the subtype are <= max + * int max; + * // does the subtype contain all values between min and max? + * boolean all; + * |}; + * } + * + * @since 2201.8.0 + */ +public class IntSubtypeConstraints { + final long min; + final long max; + final boolean all; + + private IntSubtypeConstraints(long min, long max, boolean all) { + this.min = min; + this.max = max; + this.all = all; + } + + // Returns `()` if `t` is not a proper, non-empty subtype of `int`. + // i.e. returns `()` if `t` contains all or non of `int`. + public static IntSubtypeConstraints intSubtypeConstraints(SemType t) { + SubtypeData st = intSubtype(t); + // JBUG can't flatten inner if-else + if (st instanceof AllOrNothingSubtype) { + return null; + } else { + IntSubtype ist = (IntSubtype) st; + int len = ist.ranges.length; + return new IntSubtypeConstraints(intSubtypeMin(ist), intSubtypeMax(ist), len == 1); + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/IsEmptyOp.java b/semtypes/src/main/java/io/ballerina/types/IsEmptyOp.java similarity index 65% rename from semtypes/src/main/java/io/ballerina/semtype/IsEmptyOp.java rename to semtypes/src/main/java/io/ballerina/types/IsEmptyOp.java index ce410b0d222f..7697284af7b9 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/IsEmptyOp.java +++ b/semtypes/src/main/java/io/ballerina/types/IsEmptyOp.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,17 +11,17 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** * Interface representing {@code isEmpty} operation. * - * @since 2.0.0 + * @since 2201.8.0 */ public interface IsEmptyOp { - boolean isEmpty(TypeCheckContext tc, SubtypeData t); + boolean isEmpty(Context cx, SubtypeData t); } diff --git a/semtypes/src/main/java/io/ballerina/types/ListAtomicType.java b/semtypes/src/main/java/io/ballerina/types/ListAtomicType.java new file mode 100644 index 000000000000..939d74b0b6fa --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/ListAtomicType.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * ListAtomicType node. + * + * @param members for a given list type this represents the required members + * @param rest for a given list type this represents the rest type. This is NEVER if the list don't have a rest type + * @since 2201.8.0 + */ +public record ListAtomicType(FixedLengthArray members, CellSemType rest) implements AtomicType { + + public ListAtomicType { + assert members != null; + assert rest != null; + } + + public static ListAtomicType from(FixedLengthArray members, CellSemType rest) { + return new ListAtomicType(members, rest); + } + + @Override + public Atom.Kind atomKind() { + return Atom.Kind.LIST_ATOM; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/ListMemberTypes.java b/semtypes/src/main/java/io/ballerina/types/ListMemberTypes.java new file mode 100644 index 000000000000..bb1dea5fb228 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/ListMemberTypes.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.Range; + +import java.util.Collections; +import java.util.List; + +/** + * Holds a pair of SemType list and Range list. + * Note: Member types at the indices that are not contained in `Range` array represent `never. + * The SemTypes in this list are not `never`. + * + * @param ranges Range array + * @param semTypes SemType array + * @since 2201.11.0 + */ +public record ListMemberTypes(List ranges, List semTypes) { + + public ListMemberTypes { + ranges = Collections.unmodifiableList(ranges); + semTypes = Collections.unmodifiableList(semTypes); + } + + @Override + public List ranges() { + return Collections.unmodifiableList(ranges); + } + + @Override + public List semTypes() { + return Collections.unmodifiableList(semTypes); + } + + public static ListMemberTypes from(List ranges, List semTypes) { + assert ranges != null && semTypes != null; + return new ListMemberTypes(ranges, semTypes); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/MappingAlternative.java b/semtypes/src/main/java/io/ballerina/types/MappingAlternative.java new file mode 100644 index 000000000000..dc668ee5163d --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/MappingAlternative.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents MappingAlternative record in core.bal. + * + * @since 2201.8.0 + */ +public class MappingAlternative { + SemType semType; + MappingAtomicType[] pos; + MappingAtomicType[] neg; + + private MappingAlternative(SemType semType, MappingAtomicType[] pos, MappingAtomicType[] neg) { + this.semType = semType; + this.pos = pos; + this.neg = neg; + } + + public MappingAlternative[] mappingAlternatives(Context cx, SemType t) { + if (t instanceof BasicTypeBitSet b) { + if ((b.bitset & PredefinedType.MAPPING.bitset) == 0) { + return new MappingAlternative[]{}; + } else { + return new MappingAlternative[]{ + from(cx, PredefinedType.MAPPING, List.of(), List.of()) + }; + } + } else { + List paths = new ArrayList<>(); + BddPath.bddPaths((Bdd) Core.getComplexSubtypeData((ComplexSemType) t, BasicTypeCode.BT_MAPPING), paths, + BddPath.from()); + List alts = new ArrayList<>(); + for (BddPath bddPath : paths) { + SemType semType = Core.createBasicSemType(BasicTypeCode.BT_MAPPING, bddPath.bdd); + if (!Core.isNever(semType)) { + alts.add(from(cx, semType, bddPath.pos, bddPath.neg)); + } + } + return alts.toArray(new MappingAlternative[]{}); + } + } + + public MappingAlternative from(Context cx, SemType semType, List pos, List neg) { + MappingAtomicType[] p = new MappingAtomicType[pos.size()]; + MappingAtomicType[] n = new MappingAtomicType[neg.size()]; + for (int i = 0; i < pos.size(); i++) { + p[i] = cx.mappingAtomType(pos.get(i)); + } + for (int i = 0; i < neg.size(); i++) { + n[i] = cx.mappingAtomType(neg.get(i)); + } + return new MappingAlternative(semType, p, n); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/MappingAtomicType.java b/semtypes/src/main/java/io/ballerina/types/MappingAtomicType.java new file mode 100644 index 000000000000..aae9c00fa5c1 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/MappingAtomicType.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +// TODO: consider switching arrays to lists so if does the element wise comparison correctly, (or override equals) + +import java.util.Arrays; +import java.util.Objects; + +/** + * MappingAtomicType node. {@code names} and {@code types} fields must be sorted. + * + * @param names names of the required members + * @param types types of the required members + * @param rest for a given mapping type this represents the rest type. This is NEVER if the mapping don't have a rest + * type + * @since 2201.8.0 + */ +public record MappingAtomicType(String[] names, CellSemType[] types, CellSemType rest) implements AtomicType { + + public MappingAtomicType(String[] names, CellSemType[] types, CellSemType rest) { + this.names = Arrays.copyOf(names, names.length); + this.types = Arrays.copyOf(types, names.length); + this.rest = rest; + } + + // TODO: we can replace these with unmodifiable lists + // (which don't create new lists after changing parameters to lists) + public String[] names() { + return Arrays.copyOf(names, names.length); + } + + public CellSemType[] types() { + return Arrays.copyOf(types, types.length); + } + + public static MappingAtomicType from(String[] names, CellSemType[] types, CellSemType rest) { + return new MappingAtomicType(names, types, rest); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof MappingAtomicType other)) { + return false; + } + return Arrays.equals(names, other.names) && + Arrays.equals(types, other.types) && + Objects.equals(rest, other.rest); + } + + @Override + public int hashCode() { + return Objects.hash(Arrays.hashCode(names), Arrays.hashCode(types), rest); + } + + @Override + public Atom.Kind atomKind() { + return Atom.Kind.MAPPING_ATOM; + } + + @Override + public String toString() { + return "MappingAtomicType{" + + "names=" + Arrays.toString(names) + + ", types=" + Arrays.toString(types) + + ", rest=" + rest + + '}'; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/OpsTable.java b/semtypes/src/main/java/io/ballerina/types/OpsTable.java new file mode 100644 index 000000000000..fae1e15ad486 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/OpsTable.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.typeops.BasicTypeOpsPanicImpl; +import io.ballerina.types.typeops.BooleanOps; +import io.ballerina.types.typeops.CellOps; +import io.ballerina.types.typeops.DecimalOps; +import io.ballerina.types.typeops.ErrorOps; +import io.ballerina.types.typeops.FloatOps; +import io.ballerina.types.typeops.FunctionOps; +import io.ballerina.types.typeops.FutureOps; +import io.ballerina.types.typeops.IntOps; +import io.ballerina.types.typeops.ListOps; +import io.ballerina.types.typeops.MappingOps; +import io.ballerina.types.typeops.ObjectOps; +import io.ballerina.types.typeops.StreamOps; +import io.ballerina.types.typeops.StringOps; +import io.ballerina.types.typeops.TableOps; +import io.ballerina.types.typeops.TypedescOps; +import io.ballerina.types.typeops.XmlOps; + +/** + * Lookup table containing subtype ops for each basic type indexed by basic type code. + * + * @since 2201.8.0 + */ +public class OpsTable { + private static final BasicTypeOpsPanicImpl PANIC_IMPL = new BasicTypeOpsPanicImpl(); + static final BasicTypeOps[] OPS; + + static { + int i = 0; + OPS = new BasicTypeOps[19]; + OPS[i++] = PANIC_IMPL; // nil + OPS[i++] = new BooleanOps(); // boolean + OPS[i++] = new IntOps(); // int + OPS[i++] = new FloatOps(); // float + OPS[i++] = new DecimalOps(); // decimal + OPS[i++] = new StringOps(); // string + OPS[i++] = new ErrorOps(); // error + OPS[i++] = new TypedescOps(); // typedesc + OPS[i++] = PANIC_IMPL; // handle + OPS[i++] = new FunctionOps(); // function + OPS[i++] = PANIC_IMPL; // regexp + OPS[i++] = new FutureOps(); // future + OPS[i++] = new StreamOps(); // stream + OPS[i++] = new ListOps(); // list + OPS[i++] = new MappingOps(); // mapping + OPS[i++] = new TableOps(); // table + OPS[i++] = new XmlOps(); // xml + OPS[i++] = new ObjectOps(); // object + OPS[i] = new CellOps(); // cell + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/PredefinedType.java b/semtypes/src/main/java/io/ballerina/types/PredefinedType.java new file mode 100644 index 000000000000..9815ee12b9e5 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/PredefinedType.java @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.StringSubtype; + +import java.util.StringJoiner; + +import static io.ballerina.types.BasicTypeCode.BT_CELL; +import static io.ballerina.types.BasicTypeCode.BT_LIST; +import static io.ballerina.types.BasicTypeCode.BT_MAPPING; +import static io.ballerina.types.BasicTypeCode.BT_OBJECT; +import static io.ballerina.types.BasicTypeCode.BT_TABLE; +import static io.ballerina.types.BasicTypeCode.BT_XML; +import static io.ballerina.types.BasicTypeCode.VT_INHERENTLY_IMMUTABLE; +import static io.ballerina.types.ComplexSemType.createComplexSemType; +import static io.ballerina.types.Core.union; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_COMMENT_RO; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_COMMENT_RW; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_ELEMENT_RO; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_ELEMENT_RW; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_PI_RO; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_PI_RW; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_TEXT; +import static io.ballerina.types.subtypedata.XmlSubtype.xmlSequence; +import static io.ballerina.types.subtypedata.XmlSubtype.xmlSingleton; +import static io.ballerina.types.typeops.BddCommonOps.bddAtom; +import static io.ballerina.types.typeops.XmlOps.XML_SUBTYPE_RO; + +/** + * Contain predefined types used for constructing other types. + * + * @since 2201.8.0 + */ +public final class PredefinedType { + + private static final PredefinedTypeEnv predefinedTypeEnv = PredefinedTypeEnv.getInstance(); + public static final BasicTypeBitSet NEVER = basicTypeUnion(0); + public static final BasicTypeBitSet NIL = basicType(BasicTypeCode.BT_NIL); + public static final BasicTypeBitSet BOOLEAN = basicType(BasicTypeCode.BT_BOOLEAN); + public static final BasicTypeBitSet INT = basicType(BasicTypeCode.BT_INT); + public static final BasicTypeBitSet FLOAT = basicType(BasicTypeCode.BT_FLOAT); + public static final BasicTypeBitSet DECIMAL = basicType(BasicTypeCode.BT_DECIMAL); + public static final BasicTypeBitSet STRING = basicType(BasicTypeCode.BT_STRING); + public static final BasicTypeBitSet ERROR = basicType(BasicTypeCode.BT_ERROR); + public static final BasicTypeBitSet LIST = basicType(BasicTypeCode.BT_LIST); + public static final BasicTypeBitSet MAPPING = basicType(BasicTypeCode.BT_MAPPING); + public static final BasicTypeBitSet TABLE = basicType(BasicTypeCode.BT_TABLE); + public static final BasicTypeBitSet CELL = basicType(BT_CELL); + public static final BasicTypeBitSet UNDEF = basicType(BasicTypeCode.BT_UNDEF); + public static final BasicTypeBitSet REGEXP = basicType(BasicTypeCode.BT_REGEXP); + + // matches all functions + public static final BasicTypeBitSet FUNCTION = basicType(BasicTypeCode.BT_FUNCTION); + public static final BasicTypeBitSet TYPEDESC = basicType(BasicTypeCode.BT_TYPEDESC); + public static final BasicTypeBitSet HANDLE = basicType(BasicTypeCode.BT_HANDLE); + + public static final BasicTypeBitSet XML = basicType(BasicTypeCode.BT_XML); + public static final BasicTypeBitSet OBJECT = basicType(BasicTypeCode.BT_OBJECT); + public static final BasicTypeBitSet STREAM = basicType(BasicTypeCode.BT_STREAM); + public static final BasicTypeBitSet FUTURE = basicType(BasicTypeCode.BT_FUTURE); + + // this is SubtypeData|error + public static final BasicTypeBitSet VAL = basicTypeUnion(BasicTypeCode.VT_MASK); + public static final BasicTypeBitSet INNER = BasicTypeBitSet.from(VAL.bitset | UNDEF.bitset); + public static final BasicTypeBitSet ANY = + basicTypeUnion(BasicTypeCode.VT_MASK & ~(1 << BasicTypeCode.BT_ERROR.code)); + + public static final BasicTypeBitSet SIMPLE_OR_STRING = + basicTypeUnion((1 << BasicTypeCode.BT_NIL.code) + | (1 << BasicTypeCode.BT_BOOLEAN.code) + | (1 << BasicTypeCode.BT_INT.code) + | (1 << BasicTypeCode.BT_FLOAT.code) + | (1 << BasicTypeCode.BT_DECIMAL.code) + | (1 << BasicTypeCode.BT_STRING.code)); + + public static final BasicTypeBitSet NUMBER = + basicTypeUnion((1 << BasicTypeCode.BT_INT.code) + | (1 << BasicTypeCode.BT_FLOAT.code) + | (1 << BasicTypeCode.BT_DECIMAL.code)); + public static final SemType BYTE = IntSubtype.intWidthUnsigned(8); + public static final SemType STRING_CHAR = StringSubtype.stringChar(); + + public static final SemType XML_ELEMENT = xmlSingleton(XML_PRIMITIVE_ELEMENT_RO | XML_PRIMITIVE_ELEMENT_RW); + public static final SemType XML_COMMENT = xmlSingleton(XML_PRIMITIVE_COMMENT_RO | XML_PRIMITIVE_COMMENT_RW); + public static final SemType XML_TEXT = xmlSequence(xmlSingleton(XML_PRIMITIVE_TEXT)); + public static final SemType XML_PI = xmlSingleton(XML_PRIMITIVE_PI_RO | XML_PRIMITIVE_PI_RW); + + public static final int BDD_REC_ATOM_READONLY = 0; + // represents both readonly & map and readonly & readonly[] + public static final BddNode BDD_SUBTYPE_RO = bddAtom(RecAtom.createRecAtom(BDD_REC_ATOM_READONLY)); + // represents (map)[] + public static final ComplexSemType MAPPING_RO = basicSubtype(BT_MAPPING, BDD_SUBTYPE_RO); + + public static final CellAtomicType CELL_ATOMIC_VAL = predefinedTypeEnv.cellAtomicVal(); + public static final TypeAtom ATOM_CELL_VAL = predefinedTypeEnv.atomCellVal(); + + public static final CellAtomicType CELL_ATOMIC_NEVER = predefinedTypeEnv.cellAtomicNever(); + public static final TypeAtom ATOM_CELL_NEVER = predefinedTypeEnv.atomCellNever(); + + public static final CellAtomicType CELL_ATOMIC_INNER = predefinedTypeEnv.cellAtomicInner(); + public static final TypeAtom ATOM_CELL_INNER = predefinedTypeEnv.atomCellInner(); + + public static final CellAtomicType CELL_ATOMIC_UNDEF = predefinedTypeEnv.cellAtomicUndef(); + public static final TypeAtom ATOM_CELL_UNDEF = predefinedTypeEnv.atomCellUndef(); + + static final CellSemType CELL_SEMTYPE_INNER = (CellSemType) basicSubtype(BT_CELL, bddAtom(ATOM_CELL_INNER)); + public static final MappingAtomicType MAPPING_ATOMIC_INNER = MappingAtomicType.from( + new String[]{}, new CellSemType[]{}, CELL_SEMTYPE_INNER + ); + public static final ListAtomicType LIST_ATOMIC_INNER = ListAtomicType.from( + FixedLengthArray.empty(), CELL_SEMTYPE_INNER + ); + + public static final CellAtomicType CELL_ATOMIC_INNER_MAPPING = predefinedTypeEnv.cellAtomicInnerMapping(); + public static final TypeAtom ATOM_CELL_INNER_MAPPING = predefinedTypeEnv.atomCellInnerMapping(); + public static final CellSemType CELL_SEMTYPE_INNER_MAPPING = (CellSemType) basicSubtype( + BT_CELL, bddAtom(ATOM_CELL_INNER_MAPPING) + ); + + public static final ListAtomicType LIST_ATOMIC_MAPPING = predefinedTypeEnv.listAtomicMapping(); + static final TypeAtom ATOM_LIST_MAPPING = predefinedTypeEnv.atomListMapping(); + // represents (map)[] + public static final BddNode LIST_SUBTYPE_MAPPING = bddAtom(ATOM_LIST_MAPPING); + + public static final CellAtomicType CELL_ATOMIC_INNER_MAPPING_RO = predefinedTypeEnv.cellAtomicInnerMappingRO(); + public static final TypeAtom ATOM_CELL_INNER_MAPPING_RO = predefinedTypeEnv.atomCellInnerMappingRO(); + + public static final CellSemType CELL_SEMTYPE_INNER_MAPPING_RO = (CellSemType) basicSubtype( + BT_CELL, bddAtom(ATOM_CELL_INNER_MAPPING_RO) + ); + + public static final ListAtomicType LIST_ATOMIC_MAPPING_RO = predefinedTypeEnv.listAtomicMappingRO(); + static final TypeAtom ATOM_LIST_MAPPING_RO = predefinedTypeEnv.atomListMappingRO(); + // represents readonly & (map)[] + static final BddNode LIST_SUBTYPE_MAPPING_RO = bddAtom(ATOM_LIST_MAPPING_RO); + + static final CellSemType CELL_SEMTYPE_VAL = (CellSemType) basicSubtype(BT_CELL, bddAtom(ATOM_CELL_VAL)); + static final CellSemType CELL_SEMTYPE_UNDEF = (CellSemType) basicSubtype(BT_CELL, bddAtom(ATOM_CELL_UNDEF)); + private static final TypeAtom ATOM_CELL_OBJECT_MEMBER_KIND = predefinedTypeEnv.atomCellObjectMemberKind(); + static final CellSemType CELL_SEMTYPE_OBJECT_MEMBER_KIND = (CellSemType) basicSubtype( + BT_CELL, bddAtom(ATOM_CELL_OBJECT_MEMBER_KIND) + ); + + private static final TypeAtom ATOM_CELL_OBJECT_MEMBER_VISIBILITY = + predefinedTypeEnv.atomCellObjectMemberVisibility(); + static final CellSemType CELL_SEMTYPE_OBJECT_MEMBER_VISIBILITY = (CellSemType) basicSubtype( + BT_CELL, bddAtom(ATOM_CELL_OBJECT_MEMBER_VISIBILITY) + ); + + public static final TypeAtom ATOM_MAPPING_OBJECT_MEMBER = predefinedTypeEnv.atomMappingObjectMember(); + + static final ComplexSemType MAPPING_SEMTYPE_OBJECT_MEMBER = + basicSubtype(BT_MAPPING, bddAtom(ATOM_MAPPING_OBJECT_MEMBER)); + + public static final TypeAtom ATOM_CELL_OBJECT_MEMBER = predefinedTypeEnv.atomCellObjectMember(); + static final CellSemType CELL_SEMTYPE_OBJECT_MEMBER = + (CellSemType) basicSubtype(BT_CELL, bddAtom(ATOM_CELL_OBJECT_MEMBER)); + + static final CellSemType CELL_SEMTYPE_OBJECT_QUALIFIER = CELL_SEMTYPE_VAL; + public static final TypeAtom ATOM_MAPPING_OBJECT = predefinedTypeEnv.atomMappingObject(); + public static final BddNode MAPPING_SUBTYPE_OBJECT = bddAtom(ATOM_MAPPING_OBJECT); + + private static final int BDD_REC_ATOM_OBJECT_READONLY = 1; + + private static final RecAtom OBJECT_RO_REC_ATOM = RecAtom.createRecAtom(BDD_REC_ATOM_OBJECT_READONLY); + + public static final BddNode MAPPING_SUBTYPE_OBJECT_RO = bddAtom(OBJECT_RO_REC_ATOM); + + public static final ComplexSemType MAPPING_ARRAY_RO = basicSubtype(BT_LIST, LIST_SUBTYPE_MAPPING_RO); + public static final TypeAtom ATOM_CELL_MAPPING_ARRAY_RO = predefinedTypeEnv.atomCellMappingArrayRO(); + public static final CellSemType CELL_SEMTYPE_LIST_SUBTYPE_MAPPING_RO = (CellSemType) basicSubtype( + BT_CELL, bddAtom(ATOM_CELL_MAPPING_ARRAY_RO) + ); + + static final TypeAtom ATOM_LIST_THREE_ELEMENT_RO = predefinedTypeEnv.atomListThreeElementRO(); + // represents [(readonly & map)[], any|error, any|error] + public static final BddNode LIST_SUBTYPE_THREE_ELEMENT_RO = bddAtom(ATOM_LIST_THREE_ELEMENT_RO); + + public static final SemType VAL_READONLY = createComplexSemType(VT_INHERENTLY_IMMUTABLE, + BasicSubtype.from(BT_LIST, BDD_SUBTYPE_RO), + BasicSubtype.from(BT_MAPPING, BDD_SUBTYPE_RO), + BasicSubtype.from(BT_TABLE, LIST_SUBTYPE_THREE_ELEMENT_RO), + BasicSubtype.from(BT_XML, XML_SUBTYPE_RO), + BasicSubtype.from(BT_OBJECT, MAPPING_SUBTYPE_OBJECT_RO) + ); + + public static final SemType INNER_READONLY = union(VAL_READONLY, UNDEF); + public static final CellAtomicType CELL_ATOMIC_INNER_RO = predefinedTypeEnv.cellAtomicInnerRO(); + public static final TypeAtom ATOM_CELL_INNER_RO = predefinedTypeEnv.atomCellInnerRO(); + public static final CellSemType CELL_SEMTYPE_INNER_RO = (CellSemType) basicSubtype( + BT_CELL, bddAtom(ATOM_CELL_INNER_RO) + ); + public static final TypeAtom ATOM_CELL_VAL_RO = predefinedTypeEnv.atomCellValRO(); + + static final CellSemType CELL_SEMTYPE_VAL_RO = + (CellSemType) basicSubtype(BT_CELL, bddAtom(ATOM_CELL_VAL_RO)); + + public static final TypeAtom ATOM_MAPPING_OBJECT_MEMBER_RO = predefinedTypeEnv.atomMappingObjectMemberRO(); + static final ComplexSemType MAPPING_SEMTYPE_OBJECT_MEMBER_RO = + basicSubtype(BT_MAPPING, bddAtom(ATOM_MAPPING_OBJECT_MEMBER_RO)); + + private static final TypeAtom ATOM_CELL_OBJECT_MEMBER_RO = predefinedTypeEnv.atomCellObjectMemberRO(); + static final CellSemType CELL_SEMTYPE_OBJECT_MEMBER_RO = + (CellSemType) basicSubtype(BT_CELL, bddAtom(ATOM_CELL_OBJECT_MEMBER_RO)); + + public static final ListAtomicType LIST_ATOMIC_TWO_ELEMENT = predefinedTypeEnv.listAtomicTwoElement(); + static final TypeAtom ATOM_LIST_TWO_ELEMENT = predefinedTypeEnv.atomListTwoElement(); + // represents [any|error, any|error] + public static final BddNode LIST_SUBTYPE_TWO_ELEMENT = bddAtom(ATOM_LIST_TWO_ELEMENT); + + public static final ComplexSemType MAPPING_ARRAY = basicSubtype(BT_LIST, LIST_SUBTYPE_MAPPING); + public static final TypeAtom ATOM_CELL_MAPPING_ARRAY = predefinedTypeEnv.atomCellMappingArray(); + public static final CellSemType CELL_SEMTYPE_LIST_SUBTYPE_MAPPING = (CellSemType) basicSubtype( + BT_CELL, bddAtom(ATOM_CELL_MAPPING_ARRAY) + ); + static final TypeAtom ATOM_LIST_THREE_ELEMENT = predefinedTypeEnv.atomListThreeElement(); + // represents [(map)[], any|error, any|error] + public static final BddNode LIST_SUBTYPE_THREE_ELEMENT = bddAtom(ATOM_LIST_THREE_ELEMENT); + + public static final MappingAtomicType MAPPING_ATOMIC_RO = predefinedTypeEnv.mappingAtomicRO(); + public static final MappingAtomicType MAPPING_ATOMIC_OBJECT_RO = predefinedTypeEnv.getMappingAtomicObjectRO(); + + public static final ListAtomicType LIST_ATOMIC_RO = predefinedTypeEnv.listAtomicRO(); + + private PredefinedType() { + } + + // Union of complete basic types + // bits is bit vector indexed by BasicTypeCode + // I would like to make the arg int:Unsigned32 + // but are language/impl bugs that make this not work well + static BasicTypeBitSet basicTypeUnion(int bitset) { + return BasicTypeBitSet.from(bitset); + } + + public static BasicTypeBitSet basicType(BasicTypeCode code) { + return BasicTypeBitSet.from(1 << code.code); + } + + public static ComplexSemType basicSubtype(BasicTypeCode code, ProperSubtypeData data) { + // TODO: We need a more efficient way to do this + if (code.equals(BT_CELL)) { + return CellSemType.from(new ProperSubtypeData[]{data}); + } + return ComplexSemType.createComplexSemType(0, BasicSubtype.from(code, data)); + } + + static String toString(int bitset) { + StringJoiner sj = new StringJoiner("|", Integer.toBinaryString(bitset) + "[", "]"); + + addIfBitSet(sj, bitset, NEVER.bitset, "never"); + addIfBitSet(sj, bitset, NIL.bitset, "nil"); + addIfBitSet(sj, bitset, BOOLEAN.bitset, "boolean"); + addIfBitSet(sj, bitset, INT.bitset, "int"); + addIfBitSet(sj, bitset, FLOAT.bitset, "float"); + addIfBitSet(sj, bitset, DECIMAL.bitset, "decimal"); + addIfBitSet(sj, bitset, STRING.bitset, "string"); + addIfBitSet(sj, bitset, ERROR.bitset, "error"); + addIfBitSet(sj, bitset, TYPEDESC.bitset, "typedesc"); + addIfBitSet(sj, bitset, HANDLE.bitset, "handle"); + addIfBitSet(sj, bitset, FUNCTION.bitset, "function"); + addIfBitSet(sj, bitset, REGEXP.bitset, "regexp"); + addIfBitSet(sj, bitset, FUTURE.bitset, "future"); + addIfBitSet(sj, bitset, STREAM.bitset, "stream"); + addIfBitSet(sj, bitset, LIST.bitset, "list"); + addIfBitSet(sj, bitset, MAPPING.bitset, "map"); + addIfBitSet(sj, bitset, TABLE.bitset, "table"); + addIfBitSet(sj, bitset, XML.bitset, "xml"); + addIfBitSet(sj, bitset, OBJECT.bitset, "object"); + addIfBitSet(sj, bitset, CELL.bitset, "cell"); + addIfBitSet(sj, bitset, UNDEF.bitset, "undef"); + return sj.toString(); + } + + private static void addIfBitSet(StringJoiner sj, int bitset, int bitToBeCheck, String strToBeAdded) { + if ((bitset & bitToBeCheck) != 0) { + sj.add(strToBeAdded); + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/PredefinedTypeEnv.java b/semtypes/src/main/java/io/ballerina/types/PredefinedTypeEnv.java new file mode 100644 index 000000000000..7ca357ba04e1 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/PredefinedTypeEnv.java @@ -0,0 +1,645 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.types; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; + +import static io.ballerina.types.Core.union; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_INNER_MAPPING; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_INNER_MAPPING_RO; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_INNER_RO; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_LIST_SUBTYPE_MAPPING; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_LIST_SUBTYPE_MAPPING_RO; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_OBJECT_MEMBER; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_OBJECT_MEMBER_KIND; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_OBJECT_MEMBER_RO; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_OBJECT_MEMBER_VISIBILITY; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_OBJECT_QUALIFIER; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_UNDEF; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_VAL; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_VAL_RO; +import static io.ballerina.types.PredefinedType.INNER; +import static io.ballerina.types.PredefinedType.INNER_READONLY; +import static io.ballerina.types.PredefinedType.MAPPING; +import static io.ballerina.types.PredefinedType.MAPPING_ARRAY; +import static io.ballerina.types.PredefinedType.MAPPING_ARRAY_RO; +import static io.ballerina.types.PredefinedType.MAPPING_RO; +import static io.ballerina.types.PredefinedType.MAPPING_SEMTYPE_OBJECT_MEMBER; +import static io.ballerina.types.PredefinedType.MAPPING_SEMTYPE_OBJECT_MEMBER_RO; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.UNDEF; +import static io.ballerina.types.PredefinedType.VAL; +import static io.ballerina.types.PredefinedType.VAL_READONLY; +import static io.ballerina.types.SemTypes.stringConst; +import static io.ballerina.types.TypeAtom.createTypeAtom; + +/** + * This is a utility class used to create various type atoms that needs to be initialized without an environment and + * common to all environments. When we construct an {@code Env}, we can use {@code initializeEnv} to populate it with + * those atoms. + * NOTE: While this class lazy initialize all the atoms technically {@code PredefinedType} will cause it initialize + * all the atoms currently. + * @since 2201.10.0 + */ +public final class PredefinedTypeEnv { + + private PredefinedTypeEnv() { + } + + // Due to some reason spot bug thinks we are returning an array via getInstance(), if this is not public + public static final PredefinedTypeEnv INSTANCE = new PredefinedTypeEnv(); + + public static PredefinedTypeEnv getInstance() { + return INSTANCE; + } + + private final List> initializedCellAtoms = new ArrayList<>(); + private final List> initializedListAtoms = new ArrayList<>(); + private final List> initializedMappingAtoms = new ArrayList<>(); + private final List initializedRecListAtoms = new ArrayList<>(); + private final List initializedRecMappingAtoms = new ArrayList<>(); + private final AtomicInteger nextAtomIndex = new AtomicInteger(0); + + // This is to avoid passing down env argument when doing cell type operations. + // Please refer to the cellSubtypeDataEnsureProper() in cell.bal + private CellAtomicType cellAtomicVal; + private CellAtomicType cellAtomicNever; + + // Represent the typeAtom required to construct equivalent subtypes of map and (any|error)[]. + private CellAtomicType callAtomicInner; + + // TypeAtoms related to (map)[]. This is to avoid passing down env argument when doing + // tableSubtypeComplement operation. + private CellAtomicType cellAtomicInnerMapping; + private ListAtomicType listAtomicMapping; + + // TypeAtoms related to readonly type. This is to avoid requiring context when referring to readonly type. + // CELL_ATOMIC_INNER_MAPPING_RO & LIST_ATOMIC_MAPPING_RO are typeAtoms required to construct + // readonly & (map)[] which is then used for readonly table type when constructing VAL_READONLY + private CellAtomicType cellAtomicInnerMappingRO; + private ListAtomicType listAtomicMappingRO; + private CellAtomicType cellAtomicInnerRO; + private ListAtomicType listAtomicThreeElementRO; + private CellAtomicType cellAtomicMappingArrayRO; + + // TypeAtoms related to [any|error, any|error]. This is to avoid passing down env argument when doing + // streamSubtypeComplement operation. + private CellAtomicType cellAtomicUndef; + private ListAtomicType listAtomicTwoElement; + + // TypeAtoms related to [(map)[], any|error, any|error]. This is to avoid passing down env argument + // when doing tableSubtypeComplement operation. + private CellAtomicType cellAtomicMappingArray; + private ListAtomicType listAtomicThreeElement; + + private CellAtomicType cellAtomicObjectMember; + private CellAtomicType cellAtomicObjectMemberKind; + private CellAtomicType cellAtomicObjectMemberRO; + private CellAtomicType cellAtomicObjectMemberVisibility; + private CellAtomicType cellAtomicValRO; + private ListAtomicType listAtomicRO; + private MappingAtomicType mappingAtomicObject; + private MappingAtomicType mappingAtomicObjectMember; + private MappingAtomicType mappingAtomicObjectMemberRO; + private MappingAtomicType mappingAtomicObjectRO; + private MappingAtomicType mappingAtomicRO; + private TypeAtom atomCellInner; + private TypeAtom atomCellInnerMapping; + private TypeAtom atomCellInnerMappingRO; + private TypeAtom atomCellInnerRO; + private TypeAtom atomCellNever; + private TypeAtom atomCellObjectMember; + private TypeAtom atomCellObjectMemberKind; + private TypeAtom atomCellObjectMemberRO; + private TypeAtom atomCellObjectMemberVisibility; + private TypeAtom atomCellUndef; + private TypeAtom atomCellVal; + private TypeAtom atomCellValRO; + private TypeAtom atomListMapping; + private TypeAtom atomListMappingRO; + private TypeAtom atomListTwoElement; + private TypeAtom atomMappingObject; + private TypeAtom atomMappingObjectMember; + private TypeAtom atomMappingObjectMemberRO; + private TypeAtom atomCellMappingArray; + private TypeAtom atomCellMappingArrayRO; + private TypeAtom atomListThreeElement; + private TypeAtom atomListThreeElementRO; + + private void addInitializedCellAtom(CellAtomicType atom) { + addInitializedAtom(initializedCellAtoms, atom); + } + + private void addInitializedListAtom(ListAtomicType atom) { + addInitializedAtom(initializedListAtoms, atom); + } + + private void addInitializedMapAtom(MappingAtomicType atom) { + addInitializedAtom(initializedMappingAtoms, atom); + } + + private void addInitializedAtom(Collection> atoms, E atom) { + atoms.add(new InitializedTypeAtom<>(atom, nextAtomIndex.getAndIncrement())); + } + + private int cellAtomIndex(CellAtomicType atom) { + return atomIndex(initializedCellAtoms, atom); + } + + private int listAtomIndex(ListAtomicType atom) { + return atomIndex(initializedListAtoms, atom); + } + + private int mappingAtomIndex(MappingAtomicType atom) { + return atomIndex(initializedMappingAtoms, atom); + } + + private int atomIndex(List> initializedAtoms, E atom) { + for (InitializedTypeAtom initializedListAtom : initializedAtoms) { + if (initializedListAtom.atomicType() == atom) { + return initializedListAtom.index(); + } + } + throw new IndexOutOfBoundsException(); + } + + synchronized CellAtomicType cellAtomicVal() { + if (cellAtomicVal == null) { + cellAtomicVal = CellAtomicType.from(VAL, CellAtomicType.CellMutability.CELL_MUT_LIMITED); + addInitializedCellAtom(cellAtomicVal); + } + return cellAtomicVal; + } + + synchronized TypeAtom atomCellVal() { + if (atomCellVal == null) { + CellAtomicType cellAtomicVal = cellAtomicVal(); + atomCellVal = createTypeAtom(cellAtomIndex(cellAtomicVal), cellAtomicVal); + } + return atomCellVal; + } + + synchronized CellAtomicType cellAtomicNever() { + if (cellAtomicNever == null) { + cellAtomicNever = CellAtomicType.from(NEVER, CellAtomicType.CellMutability.CELL_MUT_LIMITED); + addInitializedCellAtom(cellAtomicNever); + } + return cellAtomicNever; + } + + synchronized TypeAtom atomCellNever() { + if (atomCellNever == null) { + CellAtomicType cellAtomicNever = cellAtomicNever(); + atomCellNever = createTypeAtom(cellAtomIndex(cellAtomicNever), cellAtomicNever); + } + return atomCellNever; + } + + synchronized CellAtomicType cellAtomicInner() { + if (callAtomicInner == null) { + callAtomicInner = CellAtomicType.from(INNER, CellAtomicType.CellMutability.CELL_MUT_LIMITED); + addInitializedCellAtom(callAtomicInner); + } + return callAtomicInner; + } + + synchronized TypeAtom atomCellInner() { + if (atomCellInner == null) { + CellAtomicType cellAtomicInner = cellAtomicInner(); + atomCellInner = createTypeAtom(cellAtomIndex(cellAtomicInner), cellAtomicInner); + } + return atomCellInner; + } + + synchronized CellAtomicType cellAtomicInnerMapping() { + if (cellAtomicInnerMapping == null) { + cellAtomicInnerMapping = + CellAtomicType.from(union(MAPPING, UNDEF), CellAtomicType.CellMutability.CELL_MUT_LIMITED); + addInitializedCellAtom(cellAtomicInnerMapping); + } + return cellAtomicInnerMapping; + } + + synchronized TypeAtom atomCellInnerMapping() { + if (atomCellInnerMapping == null) { + CellAtomicType cellAtomicInnerMapping = cellAtomicInnerMapping(); + atomCellInnerMapping = createTypeAtom(cellAtomIndex(cellAtomicInnerMapping), cellAtomicInnerMapping); + } + return atomCellInnerMapping; + } + + synchronized CellAtomicType cellAtomicInnerMappingRO() { + if (cellAtomicInnerMappingRO == null) { + cellAtomicInnerMappingRO = + CellAtomicType.from(union(MAPPING_RO, UNDEF), CellAtomicType.CellMutability.CELL_MUT_LIMITED); + addInitializedCellAtom(cellAtomicInnerMappingRO); + } + return cellAtomicInnerMappingRO; + } + + synchronized TypeAtom atomCellInnerMappingRO() { + if (atomCellInnerMappingRO == null) { + CellAtomicType cellAtomicInnerMappingRO = cellAtomicInnerMappingRO(); + atomCellInnerMappingRO = + createTypeAtom(cellAtomIndex(cellAtomicInnerMappingRO), cellAtomicInnerMappingRO); + } + return atomCellInnerMappingRO; + } + + synchronized ListAtomicType listAtomicMapping() { + if (listAtomicMapping == null) { + listAtomicMapping = ListAtomicType.from( + FixedLengthArray.empty(), CELL_SEMTYPE_INNER_MAPPING + ); + addInitializedListAtom(listAtomicMapping); + } + return listAtomicMapping; + } + + synchronized TypeAtom atomListMapping() { + if (atomListMapping == null) { + ListAtomicType listAtomicMapping = listAtomicMapping(); + atomListMapping = createTypeAtom(listAtomIndex(listAtomicMapping), listAtomicMapping); + } + return atomListMapping; + } + + synchronized ListAtomicType listAtomicMappingRO() { + if (listAtomicMappingRO == null) { + listAtomicMappingRO = ListAtomicType.from(FixedLengthArray.empty(), CELL_SEMTYPE_INNER_MAPPING_RO); + addInitializedListAtom(listAtomicMappingRO); + } + return listAtomicMappingRO; + } + + synchronized TypeAtom atomListMappingRO() { + if (atomListMappingRO == null) { + ListAtomicType listAtomicMappingRO = listAtomicMappingRO(); + atomListMappingRO = createTypeAtom(listAtomIndex(listAtomicMappingRO), listAtomicMappingRO); + } + return atomListMappingRO; + } + + synchronized CellAtomicType cellAtomicInnerRO() { + if (cellAtomicInnerRO == null) { + cellAtomicInnerRO = CellAtomicType.from(INNER_READONLY, CellAtomicType.CellMutability.CELL_MUT_NONE); + addInitializedCellAtom(cellAtomicInnerRO); + } + return cellAtomicInnerRO; + } + + synchronized TypeAtom atomCellInnerRO() { + if (atomCellInnerRO == null) { + CellAtomicType cellAtomicInnerRO = cellAtomicInnerRO(); + atomCellInnerRO = createTypeAtom(cellAtomIndex(cellAtomicInnerRO), cellAtomicInnerRO); + } + return atomCellInnerRO; + } + + synchronized CellAtomicType cellAtomicUndef() { + if (cellAtomicUndef == null) { + cellAtomicUndef = CellAtomicType.from(UNDEF, CellAtomicType.CellMutability.CELL_MUT_NONE); + addInitializedCellAtom(cellAtomicUndef); + } + return cellAtomicUndef; + } + + synchronized TypeAtom atomCellUndef() { + if (atomCellUndef == null) { + CellAtomicType cellAtomicUndef = cellAtomicUndef(); + atomCellUndef = createTypeAtom(cellAtomIndex(cellAtomicUndef), cellAtomicUndef); + } + return atomCellUndef; + } + + synchronized ListAtomicType listAtomicTwoElement() { + if (listAtomicTwoElement == null) { + listAtomicTwoElement = + ListAtomicType.from(FixedLengthArray.from(List.of(CELL_SEMTYPE_VAL), 2), CELL_SEMTYPE_UNDEF); + addInitializedListAtom(listAtomicTwoElement); + } + return listAtomicTwoElement; + } + + synchronized TypeAtom atomListTwoElement() { + if (atomListTwoElement == null) { + ListAtomicType listAtomicTwoElement = listAtomicTwoElement(); + atomListTwoElement = createTypeAtom(listAtomIndex(listAtomicTwoElement), listAtomicTwoElement); + } + return atomListTwoElement; + } + + synchronized CellAtomicType cellAtomicValRO() { + if (cellAtomicValRO == null) { + cellAtomicValRO = CellAtomicType.from( + VAL_READONLY, CellAtomicType.CellMutability.CELL_MUT_NONE + ); + addInitializedCellAtom(cellAtomicValRO); + } + return cellAtomicValRO; + } + + synchronized TypeAtom atomCellValRO() { + if (atomCellValRO == null) { + CellAtomicType cellAtomicValRO = cellAtomicValRO(); + atomCellValRO = createTypeAtom(cellAtomIndex(cellAtomicValRO), cellAtomicValRO); + } + return atomCellValRO; + } + + synchronized MappingAtomicType mappingAtomicObjectMemberRO() { + if (mappingAtomicObjectMemberRO == null) { + mappingAtomicObjectMemberRO = MappingAtomicType.from( + new String[]{"kind", "value", "visibility"}, + new CellSemType[]{CELL_SEMTYPE_OBJECT_MEMBER_KIND, CELL_SEMTYPE_VAL_RO, + CELL_SEMTYPE_OBJECT_MEMBER_VISIBILITY}, + CELL_SEMTYPE_UNDEF); + addInitializedMapAtom(mappingAtomicObjectMemberRO); + } + return mappingAtomicObjectMemberRO; + } + + synchronized TypeAtom atomMappingObjectMemberRO() { + if (atomMappingObjectMemberRO == null) { + MappingAtomicType mappingAtomicObjectMemberRO = mappingAtomicObjectMemberRO(); + atomMappingObjectMemberRO = createTypeAtom(mappingAtomIndex(mappingAtomicObjectMemberRO), + mappingAtomicObjectMemberRO); + } + return atomMappingObjectMemberRO; + } + + synchronized CellAtomicType cellAtomicObjectMemberRO() { + if (cellAtomicObjectMemberRO == null) { + cellAtomicObjectMemberRO = CellAtomicType.from( + MAPPING_SEMTYPE_OBJECT_MEMBER_RO, CellAtomicType.CellMutability.CELL_MUT_NONE + ); + addInitializedCellAtom(cellAtomicObjectMemberRO); + } + return cellAtomicObjectMemberRO; + } + + synchronized TypeAtom atomCellObjectMemberRO() { + if (atomCellObjectMemberRO == null) { + CellAtomicType cellAtomicObjectMemberRO = cellAtomicObjectMemberRO(); + atomCellObjectMemberRO = createTypeAtom(cellAtomIndex(cellAtomicObjectMemberRO), cellAtomicObjectMemberRO); + } + return atomCellObjectMemberRO; + } + + synchronized CellAtomicType cellAtomicObjectMemberKind() { + if (cellAtomicObjectMemberKind == null) { + cellAtomicObjectMemberKind = CellAtomicType.from( + Core.union(stringConst("field"), stringConst("method")), + CellAtomicType.CellMutability.CELL_MUT_NONE + ); + addInitializedCellAtom(cellAtomicObjectMemberKind); + } + return cellAtomicObjectMemberKind; + } + + synchronized TypeAtom atomCellObjectMemberKind() { + if (atomCellObjectMemberKind == null) { + CellAtomicType cellAtomicObjectMemberKind = cellAtomicObjectMemberKind(); + atomCellObjectMemberKind = + createTypeAtom(cellAtomIndex(cellAtomicObjectMemberKind), cellAtomicObjectMemberKind); + } + return atomCellObjectMemberKind; + } + + synchronized CellAtomicType cellAtomicObjectMemberVisibility() { + if (cellAtomicObjectMemberVisibility == null) { + cellAtomicObjectMemberVisibility = CellAtomicType.from( + Core.union(stringConst("public"), stringConst("private")), + CellAtomicType.CellMutability.CELL_MUT_NONE + ); + addInitializedCellAtom(cellAtomicObjectMemberVisibility); + } + return cellAtomicObjectMemberVisibility; + } + + synchronized TypeAtom atomCellObjectMemberVisibility() { + if (atomCellObjectMemberVisibility == null) { + CellAtomicType cellAtomicObjectMemberVisibility = cellAtomicObjectMemberVisibility(); + atomCellObjectMemberVisibility = createTypeAtom(cellAtomIndex(cellAtomicObjectMemberVisibility), + cellAtomicObjectMemberVisibility); + } + return atomCellObjectMemberVisibility; + } + + synchronized MappingAtomicType mappingAtomicObjectMember() { + if (mappingAtomicObjectMember == null) { + mappingAtomicObjectMember = MappingAtomicType.from( + new String[]{"kind", "value", "visibility"}, + new CellSemType[]{CELL_SEMTYPE_OBJECT_MEMBER_KIND, CELL_SEMTYPE_VAL, + CELL_SEMTYPE_OBJECT_MEMBER_VISIBILITY}, + CELL_SEMTYPE_UNDEF); + ; + addInitializedMapAtom(mappingAtomicObjectMember); + } + return mappingAtomicObjectMember; + } + + synchronized TypeAtom atomMappingObjectMember() { + if (atomMappingObjectMember == null) { + MappingAtomicType mappingAtomicObjectMember = mappingAtomicObjectMember(); + atomMappingObjectMember = createTypeAtom(mappingAtomIndex(mappingAtomicObjectMember), + mappingAtomicObjectMember); + } + return atomMappingObjectMember; + } + + synchronized CellAtomicType cellAtomicObjectMember() { + if (cellAtomicObjectMember == null) { + cellAtomicObjectMember = CellAtomicType.from( + MAPPING_SEMTYPE_OBJECT_MEMBER, CellAtomicType.CellMutability.CELL_MUT_UNLIMITED + ); + addInitializedCellAtom(cellAtomicObjectMember); + } + return cellAtomicObjectMember; + } + + synchronized TypeAtom atomCellObjectMember() { + if (atomCellObjectMember == null) { + CellAtomicType cellAtomicObjectMember = cellAtomicObjectMember(); + atomCellObjectMember = createTypeAtom(cellAtomIndex(cellAtomicObjectMember), cellAtomicObjectMember); + } + return atomCellObjectMember; + } + + synchronized MappingAtomicType mappingAtomicObject() { + if (mappingAtomicObject == null) { + mappingAtomicObject = MappingAtomicType.from( + new String[]{"$qualifiers"}, new CellSemType[]{CELL_SEMTYPE_OBJECT_QUALIFIER}, + CELL_SEMTYPE_OBJECT_MEMBER + ); + addInitializedMapAtom(mappingAtomicObject); + } + return mappingAtomicObject; + } + + synchronized TypeAtom atomMappingObject() { + if (atomMappingObject == null) { + MappingAtomicType mappingAtomicObject = mappingAtomicObject(); + atomMappingObject = createTypeAtom(mappingAtomIndex(mappingAtomicObject), mappingAtomicObject); + } + return atomMappingObject; + } + + synchronized ListAtomicType listAtomicRO() { + if (listAtomicRO == null) { + listAtomicRO = ListAtomicType.from(FixedLengthArray.empty(), CELL_SEMTYPE_INNER_RO); + initializedRecListAtoms.add(listAtomicRO); + } + return listAtomicRO; + } + + synchronized MappingAtomicType mappingAtomicRO() { + if (mappingAtomicRO == null) { + mappingAtomicRO = MappingAtomicType.from(new String[]{}, new CellSemType[]{}, CELL_SEMTYPE_INNER_RO); + initializedRecMappingAtoms.add(mappingAtomicRO); + } + return mappingAtomicRO; + } + + synchronized MappingAtomicType getMappingAtomicObjectRO() { + if (mappingAtomicObjectRO == null) { + mappingAtomicObjectRO = MappingAtomicType.from( + new String[]{"$qualifiers"}, new CellSemType[]{CELL_SEMTYPE_OBJECT_QUALIFIER}, + CELL_SEMTYPE_OBJECT_MEMBER_RO + ); + initializedRecMappingAtoms.add(mappingAtomicObjectRO); + } + return mappingAtomicObjectRO; + } + + synchronized CellAtomicType cellAtomicMappingArray() { + if (cellAtomicMappingArray == null) { + cellAtomicMappingArray = CellAtomicType.from(MAPPING_ARRAY, CellAtomicType.CellMutability.CELL_MUT_LIMITED); + addInitializedCellAtom(cellAtomicMappingArray); + } + return cellAtomicMappingArray; + } + + synchronized TypeAtom atomCellMappingArray() { + if (atomCellMappingArray == null) { + CellAtomicType cellAtomicMappingArray = cellAtomicMappingArray(); + atomCellMappingArray = createTypeAtom(cellAtomIndex(cellAtomicMappingArray), cellAtomicMappingArray); + } + return atomCellMappingArray; + } + + synchronized CellAtomicType cellAtomicMappingArrayRO() { + if (cellAtomicMappingArrayRO == null) { + cellAtomicMappingArrayRO = CellAtomicType.from(MAPPING_ARRAY_RO, + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + addInitializedCellAtom(cellAtomicMappingArrayRO); + } + return cellAtomicMappingArrayRO; + } + + synchronized TypeAtom atomCellMappingArrayRO() { + if (atomCellMappingArrayRO == null) { + CellAtomicType cellAtomicMappingArrayRO = cellAtomicMappingArrayRO(); + atomCellMappingArrayRO = createTypeAtom(cellAtomIndex(cellAtomicMappingArrayRO), cellAtomicMappingArrayRO); + } + return atomCellMappingArrayRO; + } + + synchronized ListAtomicType listAtomicThreeElement() { + if (listAtomicThreeElement == null) { + listAtomicThreeElement = + ListAtomicType.from(FixedLengthArray.from(List.of(CELL_SEMTYPE_LIST_SUBTYPE_MAPPING, + CELL_SEMTYPE_VAL), 3), + CELL_SEMTYPE_UNDEF); + addInitializedListAtom(listAtomicThreeElement); + } + return listAtomicThreeElement; + } + + synchronized TypeAtom atomListThreeElement() { + if (atomListThreeElement == null) { + ListAtomicType listAtomicThreeElement = listAtomicThreeElement(); + atomListThreeElement = createTypeAtom(listAtomIndex(listAtomicThreeElement), listAtomicThreeElement); + } + return atomListThreeElement; + } + + synchronized ListAtomicType listAtomicThreeElementRO() { + if (listAtomicThreeElementRO == null) { + listAtomicThreeElementRO = + ListAtomicType.from(FixedLengthArray.from(List.of(CELL_SEMTYPE_LIST_SUBTYPE_MAPPING_RO, + CELL_SEMTYPE_VAL), 3), CELL_SEMTYPE_UNDEF); + addInitializedListAtom(listAtomicThreeElementRO); + } + return listAtomicThreeElementRO; + } + + synchronized TypeAtom atomListThreeElementRO() { + if (atomListThreeElementRO == null) { + ListAtomicType listAtomicThreeElementRO = listAtomicThreeElementRO(); + atomListThreeElementRO = createTypeAtom(listAtomIndex(listAtomicThreeElementRO), listAtomicThreeElementRO); + } + return atomListThreeElementRO; + } + + // Due to some reason SpotBug thinks this method is overrideable if we don't put final here as well. + final void initializeEnv(Env env) { + fillRecAtoms(env.recListAtoms, initializedRecListAtoms); + fillRecAtoms(env.recMappingAtoms, initializedRecMappingAtoms); + initializedCellAtoms.forEach(each -> env.cellAtom(each.atomicType())); + initializedListAtoms.forEach(each -> env.listAtom(each.atomicType())); + } + + private void fillRecAtoms(List envRecAtomList, List initializedRecAtoms) { + int count = reservedRecAtomCount(); + for (int i = 0; i < count; i++) { + if (i < initializedRecAtoms.size()) { + envRecAtomList.add(initializedRecAtoms.get(i)); + } else { + // This is mainly to help with bir serialization/deserialization logic. Given the number of such atoms + // will be small this shouldn't be a problem. + envRecAtomList.add(null); + } + } + } + + public int reservedRecAtomCount() { + return Integer.max(initializedRecListAtoms.size(), initializedRecMappingAtoms.size()); + } + + private record InitializedTypeAtom(E atomicType, int index) { + + } + + public Optional getPredefinedRecAtom(int index) { + // NOTE: when adding new reserved rec atoms update the bir.ksy file as well + if (isPredefinedRecAtom(index)) { + return Optional.of(RecAtom.createRecAtom(index)); + } + return Optional.empty(); + } + + public boolean isPredefinedRecAtom(int index) { + return index >= 0 && index < reservedRecAtomCount(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/ProperSubtypeData.java b/semtypes/src/main/java/io/ballerina/types/ProperSubtypeData.java similarity index 69% rename from semtypes/src/main/java/io/ballerina/semtype/ProperSubtypeData.java rename to semtypes/src/main/java/io/ballerina/types/ProperSubtypeData.java index 151c1bd1d133..5ee0e48548db 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/ProperSubtypeData.java +++ b/semtypes/src/main/java/io/ballerina/types/ProperSubtypeData.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,16 +11,16 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** * ProperSubtypeData node. * - * @since 2.0.0 + * @since 2201.8.0 */ public interface ProperSubtypeData extends SubtypeData { } diff --git a/semtypes/src/main/java/io/ballerina/semtype/README.md b/semtypes/src/main/java/io/ballerina/types/README.md similarity index 100% rename from semtypes/src/main/java/io/ballerina/semtype/README.md rename to semtypes/src/main/java/io/ballerina/types/README.md diff --git a/semtypes/src/main/java/io/ballerina/types/RecAtom.java b/semtypes/src/main/java/io/ballerina/types/RecAtom.java new file mode 100644 index 000000000000..6530e43a6588 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/RecAtom.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import static io.ballerina.types.PredefinedType.BDD_REC_ATOM_READONLY; + +/** + * Represent a recursive type atom. + * + * @since 2201.8.0 + */ +public class RecAtom implements Atom { + public final int index; + private Kind targetKind = null; + public static final RecAtom ZERO = new RecAtom(BDD_REC_ATOM_READONLY); + + private RecAtom(int index) { + this.index = index; + } + + private RecAtom(int index, Kind targetKind) { + this.index = index; + this.targetKind = targetKind; + } + + public static RecAtom createRecAtom(int index) { + if (index == BDD_REC_ATOM_READONLY) { + return ZERO; + } + return new RecAtom(index); + } + + public static RecAtom createXMLRecAtom(int index) { + return new RecAtom(index, Kind.XML_ATOM); + } + + public static RecAtom createDistinctRecAtom(int index) { + return new RecAtom(index, Kind.DISTINCT_ATOM); + } + + public void setKind(Kind targetKind) { + this.targetKind = targetKind; + } + + @Override + public int index() { + return index; + } + + @Override + public Kind kind() { + if (targetKind == null) { + throw new IllegalStateException("Target kind is not set for the recursive type atom"); + } + return targetKind; + } + + @Override + public int hashCode() { + if (targetKind == null) { + return index; + } else { + return getIdentifier().hashCode(); + } + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof RecAtom recAtom) { + return recAtom.index == this.index; + } + return false; + } + + @Override + public String toString() { + return "RecAtom{ index=" + index + ", targetKind=" + targetKind + '}'; + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/SemType.java b/semtypes/src/main/java/io/ballerina/types/SemType.java similarity index 68% rename from semtypes/src/main/java/io/ballerina/semtype/SemType.java rename to semtypes/src/main/java/io/ballerina/types/SemType.java index 98bcc7292b55..3b07310dc61e 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/SemType.java +++ b/semtypes/src/main/java/io/ballerina/types/SemType.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,16 +11,18 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** * SemType node. * - * @since 2.0.0 + * @since 2201.8.0 */ public interface SemType { + + int all(); } diff --git a/semtypes/src/main/java/io/ballerina/types/SemTypePair.java b/semtypes/src/main/java/io/ballerina/types/SemTypePair.java new file mode 100644 index 000000000000..1a30f941796a --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/SemTypePair.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Holds a pair of semtypes. + * + * @param t1 first semtype + * @param t2 second semtype + * @since 2201.11.0 + */ +public record SemTypePair(SemType t1, SemType t2) { + + public static SemTypePair from(SemType t1, SemType t2) { + assert t1 != null && t2 != null; + return new SemTypePair(t1, t2); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/SemTypes.java b/semtypes/src/main/java/io/ballerina/types/SemTypes.java new file mode 100644 index 000000000000..037c9deac740 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/SemTypes.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.definition.ObjectDefinition; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.FutureSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.StringSubtype; +import io.ballerina.types.subtypedata.TableSubtype; +import io.ballerina.types.subtypedata.TypedescSubtype; +import io.ballerina.types.subtypedata.XmlSubtype; +import io.ballerina.types.typeops.ListProj; + +import java.math.BigDecimal; + +/** + * Public API for creating type values. + * + * @since 2201.8.0 + */ +public final class SemTypes { + public static final SemType SINT8 = IntSubtype.intWidthSigned(8); + public static final SemType SINT16 = IntSubtype.intWidthSigned(16); + public static final SemType SINT32 = IntSubtype.intWidthSigned(32); + public static final SemType UINT8 = PredefinedType.BYTE; + public static final SemType UINT16 = IntSubtype.intWidthUnsigned(16); + public static final SemType UINT32 = IntSubtype.intWidthUnsigned(32); + public static final SemType CHAR = PredefinedType.STRING_CHAR; + public static final SemType XML_ELEMENT = PredefinedType.XML_ELEMENT; + public static final SemType XML_COMMENT = PredefinedType.XML_COMMENT; + public static final SemType XML_TEXT = PredefinedType.XML_TEXT; + public static final SemType XML_PI = PredefinedType.XML_PI; + + public static SemType floatConst(double v) { + return FloatSubtype.floatConst(v); + } + + public static SemType intConst(long value) { + return IntSubtype.intConst(value); + } + + public static SemType stringConst(String value) { + return StringSubtype.stringConst(value); + } + + public static SemType booleanConst(boolean value) { + return BooleanSubtype.booleanConst(value); + } + + public static SemType decimalConst(String value) { + if (value.contains("d") || value.contains("D")) { + value = value.substring(0, value.length() - 1); + } + BigDecimal d = new BigDecimal(value); + return DecimalSubtype.decimalConst(d); + } + + public static SemType union(SemType t1, SemType t2) { + return Core.union(t1, t2); + } + + public static SemType union(SemType first, SemType second, SemType... rest) { + SemType u = Core.union(first, second); + for (SemType s : rest) { + u = Core.union(u, s); + } + return u; + } + + public static SemType intersect(SemType t1, SemType t2) { + return Core.intersect(t1, t2); + } + + public static SemType intersect(SemType first, SemType second, SemType... rest) { + SemType i = Core.intersect(first, second); + for (SemType s : rest) { + i = Core.intersect(i, s); + } + return i; + } + + public static boolean isSubtype(Context context, SemType t1, SemType t2) { + return Core.isSubtype(context, t1, t2); + } + + public static boolean isSubtypeSimple(SemType t1, BasicTypeBitSet t2) { + return Core.isSubtypeSimple(t1, t2); + } + + public static boolean isSubtypeSimpleNotNever(SemType t1, BasicTypeBitSet t2) { + return !Core.isNever(t1) && Core.isSubtypeSimple(t1, t2); + } + + public static boolean containsBasicType(SemType t1, BasicTypeBitSet t2) { + return (Core.widenToBasicTypes(t1).bitset & t2.bitset) != 0; + } + + public static boolean containsType(Context context, SemType type, SemType typeToBeContained) { + return Core.isSameType(context, Core.intersect(type, typeToBeContained), typeToBeContained); + } + + public static boolean isSameType(Context context, SemType t1, SemType t2) { + return Core.isSameType(context, t1, t2); + } + + public static SemType errorDetail(SemType detail) { + return Error.errorDetail(detail); + } + + public static SemType errorDistinct(int distinctId) { + return Error.errorDistinct(distinctId); + } + + public static SemType objectDistinct(int distinctId) { + return ObjectDefinition.distinct(distinctId); + } + + public static SemType tableContaining(Env env, SemType tableConstraint) { + return TableSubtype.tableContaining(env, tableConstraint); + } + + public static SemType tableContainingKeySpecifier(Context cx, SemType tableConstraint, String[] fieldNames) { + return TableSubtype.tableContainingKeySpecifier(cx, tableConstraint, fieldNames); + } + + public static SemType tableContainingKeyConstraint(Context cx, SemType tableConstraint, SemType keyConstraint) { + return TableSubtype.tableContainingKeyConstraint(cx, tableConstraint, keyConstraint); + } + + public static SemType futureContaining(Env env, SemType constraint) { + return FutureSubtype.futureContaining(env, constraint); + } + + public static SemType typedescContaining(Env env, SemType constraint) { + return TypedescSubtype.typedescContaining(env, constraint); + } + + public static SemType mappingMemberTypeInnerVal(Context context, SemType t, SemType m) { + return Core.mappingMemberTypeInnerVal(context, t, m); + } + + public static SemType listProj(Context context, SemType t, SemType key) { + return ListProj.listProjInnerVal(context, t, key); + } + + public static SemType listMemberType(Context context, SemType t, SemType key) { + return Core.listMemberTypeInnerVal(context, t, key); + } + + public static SemType xmlSequence(SemType t) { + return XmlSubtype.xmlSequence(t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/SubtypeData.java b/semtypes/src/main/java/io/ballerina/types/SubtypeData.java similarity index 69% rename from semtypes/src/main/java/io/ballerina/semtype/SubtypeData.java rename to semtypes/src/main/java/io/ballerina/types/SubtypeData.java index 8b6834734a85..51d8a9810103 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/SubtypeData.java +++ b/semtypes/src/main/java/io/ballerina/types/SubtypeData.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,16 +11,16 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** * Represent SubtypeData type. * - * @since 2.0.0 + * @since 2201.8.0 */ public interface SubtypeData { } diff --git a/semtypes/src/main/java/io/ballerina/types/TypeAtom.java b/semtypes/src/main/java/io/ballerina/types/TypeAtom.java new file mode 100644 index 000000000000..b83a82364a2c --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/TypeAtom.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Represent a TypeAtom. + * + * @param index index of the type atom. This is unique within a given {@code Env}. {@code RecAtom}'s that refer to + * this type atom will also have the same index. + * @param atomicType atomic type representing the actual type represented by this atom. + * @since 2201.8.0 + */ +public record TypeAtom(int index, AtomicType atomicType) implements Atom { + + // Note: Whenever creating a 'TypeAtom', its 'atomicType' needs to be added to the 'Env.atomTable' + public static TypeAtom createTypeAtom(int index, AtomicType atomicType) { + assert index >= 0; + return new TypeAtom(index, atomicType); + } + + @Override + public int hashCode() { + return this.getIdentifier().hashCode(); + } + + @Override + public Kind kind() { + return atomicType.atomKind(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/BddMemo.java b/semtypes/src/main/java/io/ballerina/types/Value.java similarity index 52% rename from semtypes/src/main/java/io/ballerina/semtype/BddMemo.java rename to semtypes/src/main/java/io/ballerina/types/Value.java index 993f1670a76b..6408dd2f3383 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/BddMemo.java +++ b/semtypes/src/main/java/io/ballerina/types/Value.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,22 +11,25 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** - * Represent BddMomo type used for memoization. + * Represent `Value` type. * - * @since 2.0.0 + * @since 2201.8.0 */ -public class BddMemo { - Bdd bddNode; - MemoStatus isEmpty; +public class Value { + public final Object value; - enum MemoStatus { - NOT_SET, TRUE, FALSE + private Value(Object value) { + this.value = value; + } + + public static Value from(Object value) { + return new Value(value); } } diff --git a/semtypes/src/main/java/io/ballerina/types/definition/CellField.java b/semtypes/src/main/java/io/ballerina/types/definition/CellField.java new file mode 100644 index 000000000000..df4a40fae836 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/CellField.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.definition; + +import io.ballerina.types.CellSemType; + +/** + * Represents a cell field in a mapping type. + * + * @param name name of the field + * @param type cell-sem-type of the field + * @since 2201.10.0 + */ +public record CellField(String name, CellSemType type) { + + public static CellField from(String name, CellSemType type) { + return new CellField(name, type); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/Field.java b/semtypes/src/main/java/io/ballerina/types/definition/Field.java new file mode 100644 index 000000000000..288fc2374e2b --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/Field.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.definition; + +import io.ballerina.types.SemType; + +/** + * Represent a field in a mapping type. + * + * @param name field name + * @param ty field type + * @param ro whether the field is readonly + * @param opt whether the field is optional + * @since 2201.10.0 + */ +public record Field(String name, SemType ty, boolean ro, boolean opt) { + + public static Field from(String name, SemType type, boolean ro, boolean opt) { + return new Field(name, type, ro, opt); + } + + @Override + public String toString() { + return "Field[" + + "name=" + name + ", " + + "ty=" + ty + ", " + + "ro=" + ro + ", " + + "opt=" + opt + ']'; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/FunctionDefinition.java b/semtypes/src/main/java/io/ballerina/types/definition/FunctionDefinition.java new file mode 100644 index 000000000000..e1c6c33df23b --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/FunctionDefinition.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.definition; + +import io.ballerina.types.Atom; +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Definition; +import io.ballerina.types.Env; +import io.ballerina.types.FunctionAtomicType; +import io.ballerina.types.RecAtom; +import io.ballerina.types.SemType; +import io.ballerina.types.subtypedata.BddNode; + +import static io.ballerina.types.PredefinedType.basicSubtype; +import static io.ballerina.types.typeops.BddCommonOps.bddAtom; + +/** + * Represent function type desc. + * + * @since 2201.8.0 + */ +public final class FunctionDefinition implements Definition { + + private RecAtom rec; + private SemType semType; + + @Override + public SemType getSemType(Env env) { + if (semType != null) { + return semType; + } + RecAtom rec = env.recFunctionAtom(); + this.rec = rec; + return this.createSemType(rec); + } + + private SemType createSemType(Atom rec) { + BddNode bdd = bddAtom(rec); + ComplexSemType s = basicSubtype(BasicTypeCode.BT_FUNCTION, bdd); + this.semType = s; + return s; + } + + public SemType define(Env env, SemType args, SemType ret, FunctionQualifiers qualifiers) { + FunctionAtomicType atomicType = FunctionAtomicType.from(args, ret, qualifiers.semType()); + return defineInternal(env, atomicType); + } + + public SemType defineGeneric(Env env, SemType args, SemType ret, FunctionQualifiers qualifiers) { + FunctionAtomicType atomicType = FunctionAtomicType.genericFrom(args, ret, qualifiers.semType()); + return defineInternal(env, atomicType); + } + + private SemType defineInternal(Env env, FunctionAtomicType atomicType) { + Atom atom; + RecAtom rec = this.rec; + if (rec != null) { + atom = rec; + env.setRecFunctionAtomType(rec, atomicType); + } else { + atom = env.functionAtom(atomicType); + } + return this.createSemType(atom); + } + +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/FunctionQualifiers.java b/semtypes/src/main/java/io/ballerina/types/definition/FunctionQualifiers.java new file mode 100644 index 000000000000..17e73aac75c4 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/FunctionQualifiers.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.types.definition; + +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; + +import java.util.List; + +/** + * Wrapper class for the semtype representing the {@code function-quals} of a function. + * + * @param semType the semtype representing the function qualifiers + * @since 2201.10.0 + */ +public record FunctionQualifiers(SemType semType) { + + public FunctionQualifiers { + assert semType != null; + assert Core.isSubtypeSimple(semType, PredefinedType.LIST); + } + + public static FunctionQualifiers from(Env env, boolean isolated, boolean transactional) { + return new FunctionQualifiers(createSemType(env, isolated, transactional)); + } + + private static SemType createSemType(Env env, boolean isolated, boolean transactional) { + ListDefinition ld = new ListDefinition(); + return ld.defineListTypeWrapped(env, List.of( + isolated ? SemTypes.booleanConst(true) : PredefinedType.BOOLEAN, + transactional ? PredefinedType.BOOLEAN : SemTypes.booleanConst(false)), + 2); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/ListDefinition.java b/semtypes/src/main/java/io/ballerina/types/definition/ListDefinition.java new file mode 100644 index 000000000000..e1b22ed82b1e --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/ListDefinition.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.definition; + +import io.ballerina.types.Atom; +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.CellSemType; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Definition; +import io.ballerina.types.Env; +import io.ballerina.types.FixedLengthArray; +import io.ballerina.types.ListAtomicType; +import io.ballerina.types.RecAtom; +import io.ballerina.types.SemType; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.typeops.BddCommonOps; + +import java.util.List; + +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.Core.isNever; +import static io.ballerina.types.Core.union; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.UNDEF; +import static io.ballerina.types.PredefinedType.basicSubtype; +import static io.ballerina.types.subtypedata.CellSubtype.cellContaining; + +/** + * Represent list/tuple type desc. + * + * @since 2201.8.0 + */ +public class ListDefinition implements Definition { + + private RecAtom rec = null; + private ComplexSemType semType = null; + + @Override + public SemType getSemType(Env env) { + ComplexSemType s = this.semType; + if (s == null) { + RecAtom rec = env.recListAtom(); + this.rec = rec; + return this.createSemType(env, rec); + } else { + return s; + } + } + + public SemType tupleTypeWrapped(Env env, SemType... members) { + return defineListTypeWrapped(env, List.of(members), members.length); + } + + public SemType tupleTypeWrappedRo(Env env, SemType... members) { + return defineListTypeWrapped(env, List.of(members), members.length, NEVER, CELL_MUT_NONE); + } + + + public SemType defineListTypeWrapped(Env env, List initial, int fixedLength, SemType rest, + CellAtomicType.CellMutability mut) { + assert rest != null; + List initialCells = initial.stream().map(t -> cellContaining(env, t, mut)).toList(); + CellSemType restCell = cellContaining(env, union(rest, UNDEF), isNever(rest) ? CELL_MUT_NONE : mut); + return define(env, initialCells, fixedLength, restCell); + } + + // Overload defineListTypeWrapped method for commonly used default parameter values + public SemType defineListTypeWrapped(Env env, List initial, int size) { + return defineListTypeWrapped(env, initial, size, NEVER, CELL_MUT_LIMITED); + } + + public SemType defineListTypeWrapped(Env env, List initial, int fixedLength, SemType rest) { + return defineListTypeWrapped(env, initial, fixedLength, rest, CELL_MUT_LIMITED); + } + + public SemType defineListTypeWrapped(Env env, SemType rest) { + return defineListTypeWrapped(env, List.of(), 0, rest); + } + + public SemType defineListTypeWrapped(Env env, SemType rest, CellAtomicType.CellMutability mut) { + return defineListTypeWrapped(env, List.of(), 0, rest, mut); + } + + public SemType defineListTypeWrapped(Env env, List initial, SemType rest) { + return defineListTypeWrapped(env, initial, initial.size(), rest, CELL_MUT_LIMITED); + } + + private ComplexSemType define(Env env, List initial, int fixedLength, CellSemType rest) { + assert rest != null; + FixedLengthArray members = fixedLengthNormalize(FixedLengthArray.from(initial, fixedLength)); + ListAtomicType atomicType = ListAtomicType.from(members, rest); + Atom atom; + RecAtom rec = this.rec; + if (rec != null) { + atom = rec; + env.setRecListAtomType(rec, atomicType); + } else { + atom = env.listAtom(atomicType); + } + return this.createSemType(env, atom); + } + + private FixedLengthArray fixedLengthNormalize(FixedLengthArray array) { + List initial = array.initial(); + int i = initial.size() - 1; + if (i <= 0) { + return array; + } + SemType last = initial.get(i); + i -= 1; + while (i >= 0) { + if (last != initial.get(i)) { + break; + } + i -= 1; + } + return FixedLengthArray.from(initial.subList(0, i + 2), array.fixedLength()); + } + + private ComplexSemType createSemType(Env env, Atom atom) { + BddNode bdd = BddCommonOps.bddAtom(atom); + ComplexSemType complexSemType = basicSubtype(BasicTypeCode.BT_LIST, bdd); + this.semType = complexSemType; + return complexSemType; + } + +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/MappingDefinition.java b/semtypes/src/main/java/io/ballerina/types/definition/MappingDefinition.java new file mode 100644 index 000000000000..4c1e7bc0ee6e --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/MappingDefinition.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.definition; + +import io.ballerina.types.Atom; +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.CellSemType; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Core; +import io.ballerina.types.Definition; +import io.ballerina.types.Env; +import io.ballerina.types.MappingAtomicType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.RecAtom; +import io.ballerina.types.SemType; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.typeops.BddCommonOps; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.Core.union; +import static io.ballerina.types.PredefinedType.UNDEF; +import static io.ballerina.types.subtypedata.CellSubtype.cellContaining; + +/** + * Represent mapping type desc. + * + * @since 2201.8.0 + */ +public class MappingDefinition implements Definition { + + private RecAtom rec = null; + private SemType semType = null; + + @Override + public SemType getSemType(Env env) { + SemType s = this.semType; + if (s == null) { + RecAtom rec = env.recMappingAtom(); + this.rec = rec; + return this.createSemType(env, rec); + } else { + return s; + } + } + + /** + * This is a deviation from nBallerina. jBallerina considers `record {never x;}` as `never`. + * This method is to support jBallerina behavior. + */ + public void setSemTypeToNever() { + this.semType = PredefinedType.NEVER; + } + + public SemType define(Env env, List fields, CellSemType rest) { + SplitField sfh = splitFields(fields); + MappingAtomicType atomicType = MappingAtomicType.from(sfh.names.toArray(String[]::new), + sfh.types.toArray(CellSemType[]::new), rest); + Atom atom; + RecAtom rec = this.rec; + if (rec != null) { + atom = rec; + env.setRecMappingAtomType(rec, atomicType); + } else { + atom = env.mappingAtom(atomicType); + } + return this.createSemType(env, atom); + } + + public SemType defineMappingTypeWrapped(Env env, List fields, SemType rest) { + return defineMappingTypeWrapped(env, fields, rest, CELL_MUT_LIMITED); + } + + public SemType defineMappingTypeWrapped(Env env, List fields, SemType rest, + CellAtomicType.CellMutability mut) { + List cellFields = new ArrayList<>(fields.size()); + for (Field field : fields) { + SemType ty = field.ty(); + cellFields.add( + CellField.from(field.name(), cellContaining( + env, + field.opt() ? union(ty, UNDEF) : ty, + field.ro() ? CELL_MUT_NONE : mut + )) + ); + } + CellSemType restCell = cellContaining( + env, + union(rest, UNDEF), + Core.isNever(rest) ? CELL_MUT_NONE : mut + ); + return define(env, cellFields, restCell); + } + + private SemType createSemType(Env env, Atom atom) { + BddNode bdd = BddCommonOps.bddAtom(atom); + ComplexSemType s = PredefinedType.basicSubtype(BasicTypeCode.BT_MAPPING, bdd); + this.semType = s; + return s; + } + + private SplitField splitFields(List fields) { + CellField[] sortedFields = fields.toArray(CellField[]::new); + Arrays.sort(sortedFields, Comparator.comparing(MappingDefinition::fieldName)); + List names = new ArrayList<>(); + List types = new ArrayList<>(); + for (CellField field : sortedFields) { + names.add(field.name()); + types.add(field.type()); + } + return SplitField.from(names, types); + } + + private static String fieldName(CellField f) { + return f.name(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/Member.java b/semtypes/src/main/java/io/ballerina/types/definition/Member.java new file mode 100644 index 000000000000..f5d3f98244bb --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/Member.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.types.definition; + +import io.ballerina.types.SemType; + +import static io.ballerina.types.SemTypes.stringConst; +import static io.ballerina.types.SemTypes.union; + +/** + * Represent a member of an object type definition. + * + * @param name member name + * @param valueTy member type + * @param kind is member a field or a method + * @param visibility is member private or public + * @param immutable is member readonly. If this is set valueTy must be a subtype of readonly + * @since 2201.10.0 + */ +public record Member(String name, SemType valueTy, Kind kind, Visibility visibility, boolean immutable) { + + public Member { + assert name != null && valueTy != null && kind != null && visibility != null; + } + + // Various "tag" values associated with a member. Each of these tag values must be convertible to a Field in Map + // type for the member + @FunctionalInterface + interface MemberTag { + + Field field(); + } + + public enum Kind implements MemberTag { + Field, + Method; + + // In nBallerina these are stings since they fit small strings. Maybe consider more efficient representation + // for java + private static final Field FIELD = new Field("kind", stringConst("field"), true, false); + private static final Field METHOD = new Field("kind", stringConst("method"), true, false); + + public Field field() { + return switch (this) { + case Field -> FIELD; + case Method -> METHOD; + }; + } + } + + public enum Visibility implements MemberTag { + Public, + Private; + + private static final SemType PUBLIC_TAG = stringConst("public"); + private static final Field PUBLIC = new Field("visibility", PUBLIC_TAG, true, false); + private static final SemType PRIVATE_TAG = stringConst("private"); + private static final Field PRIVATE = new Field("visibility", PRIVATE_TAG, true, false); + static final Field ALL = new Field("visibility", union(PRIVATE_TAG, PUBLIC_TAG), true, false); + + public Field field() { + return switch (this) { + case Public -> PUBLIC; + case Private -> PRIVATE; + }; + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/ObjectDefinition.java b/semtypes/src/main/java/io/ballerina/types/definition/ObjectDefinition.java new file mode 100644 index 000000000000..1ca827f1b038 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/ObjectDefinition.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.types.definition; + +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.Bdd; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.CellSemType; +import io.ballerina.types.Core; +import io.ballerina.types.Definition; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.BddNode; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Stream; + +import static io.ballerina.types.BasicTypeCode.BT_OBJECT; +import static io.ballerina.types.Core.createBasicSemType; +import static io.ballerina.types.Core.union; +import static io.ballerina.types.PredefinedType.basicSubtype; +import static io.ballerina.types.RecAtom.createDistinctRecAtom; +import static io.ballerina.types.subtypedata.CellSubtype.cellContaining; +import static io.ballerina.types.typeops.BddCommonOps.bddAtom; + +/** + * Represent object type desc. + * + * @since 2201.10.0 + */ +public final class ObjectDefinition implements Definition { + + private final MappingDefinition mappingDefinition = new MappingDefinition(); + + public static SemType distinct(int distinctId) { + assert distinctId >= 0; + BddNode bdd = bddAtom(createDistinctRecAtom(-distinctId - 1)); + return basicSubtype(BT_OBJECT, bdd); + } + + // Each object type is represented as mapping type (with its basic type set to object) as fallows + // { + // "$qualifiers": { + // boolean isolated, + // "client"|"service" network + // }, + // [field_name]: { + // "field"|"method" kind, + // "public"|"private" visibility, + // VAL value; + // } + // ...{ + // "field" kind, + // "public"|"private" visibility, + // VAL value; + // } | { + // "method" kind, + // "public"|"private" visibility, + // FUNCTION value; + // } + // } + public SemType define(Env env, ObjectQualifiers qualifiers, Collection members) { + assert validataMembers(members); // This should never happen, so let's not run this in production + CellAtomicType.CellMutability mut = qualifiers.readonly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + Stream memberStream = members.stream() + .map(member -> memberField(env, member, mut)); + Stream qualifierStream = Stream.of(qualifiers.field(env)); + SemType mappingType = mappingDefinition.define(env, Stream.concat(memberStream, qualifierStream).toList(), + restMemberType(env, mut, qualifiers.readonly())); + return objectContaining(mappingType); + } + + private static boolean validataMembers(Collection members) { + // Check if there are two members with same name + return members.stream().map(Member::name).distinct().count() == members.size(); + } + + private SemType objectContaining(SemType mappingType) { + SubtypeData bdd = Core.subtypeData(mappingType, BasicTypeCode.BT_MAPPING); + assert bdd instanceof Bdd; + return createBasicSemType(BT_OBJECT, bdd); + } + + private CellSemType restMemberType(Env env, CellAtomicType.CellMutability mut, boolean immutable) { + MappingDefinition fieldDefn = new MappingDefinition(); + SemType fieldMemberType = fieldDefn.defineMappingTypeWrapped( + env, + List.of( + new Field("value", immutable ? PredefinedType.VAL_READONLY : PredefinedType.VAL, + immutable, false), + Member.Kind.Field.field(), + Member.Visibility.ALL + ), + PredefinedType.NEVER); + + MappingDefinition methodDefn = new MappingDefinition(); + SemType methodMemberType = methodDefn.defineMappingTypeWrapped( + env, + List.of( + new Field("value", PredefinedType.FUNCTION, true, false), + Member.Kind.Method.field(), + Member.Visibility.ALL + ), + PredefinedType.NEVER); + return cellContaining(env, union(fieldMemberType, methodMemberType), mut); + } + + private static CellField memberField(Env env, Member member, CellAtomicType.CellMutability mut) { + MappingDefinition md = new MappingDefinition(); + CellAtomicType.CellMutability fieldMut = member.immutable() ? CellAtomicType.CellMutability.CELL_MUT_NONE : mut; + SemType semtype = md.defineMappingTypeWrapped( + env, + List.of( + new Field("value", member.valueTy(), member.immutable(), false), + member.kind().field(), + member.visibility().field() + ), + PredefinedType.NEVER); + return CellField.from(member.name(), cellContaining(env, semtype, fieldMut)); + } + + @Override + public SemType getSemType(Env env) { + return objectContaining(mappingDefinition.getSemType(env)); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/ObjectQualifiers.java b/semtypes/src/main/java/io/ballerina/types/definition/ObjectQualifiers.java new file mode 100644 index 000000000000..4f7bc17fd6a1 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/ObjectQualifiers.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.types.definition; + +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; + +import java.util.List; + +import static io.ballerina.types.SemTypes.booleanConst; +import static io.ballerina.types.SemTypes.stringConst; +import static io.ballerina.types.SemTypes.union; +import static io.ballerina.types.subtypedata.CellSubtype.cellContaining; + +/** + * Represent {@code object-type-quals} in the spec. + * + * @param isolated is object isolated + * @param readonly represent {@code class readonly}. Note this is used to determining "rest" part of the object + * only {@code Member} types must be correctly set as intersection with readonly where + * applicable even with this set to true + * @param networkQualifier is object client, service or none + * @since 2201.10.0 + */ +public record ObjectQualifiers(boolean isolated, boolean readonly, NetworkQualifier networkQualifier) { + + private static final ObjectQualifiers DEFAULT = new ObjectQualifiers(false, false, NetworkQualifier.None); + + public static ObjectQualifiers defaultQualifiers() { + return DEFAULT; + } + + public static ObjectQualifiers from(boolean isolated, boolean readonly, NetworkQualifier networkQualifier) { + if (networkQualifier == NetworkQualifier.None && !isolated) { + return defaultQualifiers(); + } + return new ObjectQualifiers(isolated, readonly, networkQualifier); + } + + public enum NetworkQualifier { + Client, + Service, + None; + + private static final SemType CLIENT_TAG = stringConst("client"); + private static final Field CLIENT = new Field("network", CLIENT_TAG, true, false); + + private static final SemType SERVICE_TAG = stringConst("service"); + private static final Field SERVICE = new Field("network", SERVICE_TAG, true, false); + + // Object can't be both client and service, which is enforced by the enum. We are using a union here so that + // if this is none it matches both + private static final Field NONE = new Field("network", union(CLIENT_TAG, SERVICE_TAG), true, false); + + private Field field() { + return switch (this) { + case Client -> CLIENT; + case Service -> SERVICE; + case None -> NONE; + }; + } + } + + public CellField field(Env env) { + MappingDefinition md = new MappingDefinition(); + Field isolatedField = + new Field("isolated", isolated ? booleanConst(true) : PredefinedType.BOOLEAN, true, false); + Field networkField = networkQualifier.field(); + SemType ty = md.defineMappingTypeWrapped(env, List.of(isolatedField, networkField), PredefinedType.NEVER, + CellAtomicType.CellMutability.CELL_MUT_NONE); + + return CellField.from("$qualifiers", cellContaining(env, ty)); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/SplitField.java b/semtypes/src/main/java/io/ballerina/types/definition/SplitField.java new file mode 100644 index 000000000000..14a56ee7bafa --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/SplitField.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.definition; + +import io.ballerina.types.CellSemType; + +import java.util.List; + +/** + * Holds the [string[], CellSemType[]] return type. + * + * @since 2201.10.0 + */ +public class SplitField { + public final List names; + public final List types; + + private SplitField(List strings, List semTypes) { + this.names = strings; + this.types = semTypes; + } + + public static SplitField from(List strings, List semTypes) { + return new SplitField(strings, semTypes); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/StreamDefinition.java b/semtypes/src/main/java/io/ballerina/types/definition/StreamDefinition.java new file mode 100644 index 000000000000..dc700a5ecad4 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/StreamDefinition.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.definition; + +import io.ballerina.types.Bdd; +import io.ballerina.types.Definition; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; + +import static io.ballerina.types.BasicTypeCode.BT_LIST; +import static io.ballerina.types.BasicTypeCode.BT_STREAM; +import static io.ballerina.types.Core.createBasicSemType; +import static io.ballerina.types.Core.subtypeData; + +/** + * Represent stream type desc. + * + * @since 2201.10.0 + */ +public final class StreamDefinition implements Definition { + + private final ListDefinition listDefinition = new ListDefinition(); + + @Override + public SemType getSemType(Env env) { + return streamContaining((listDefinition.getSemType(env))); + } + + public SemType define(Env env, SemType valueTy, SemType completionTy) { + if (PredefinedType.VAL.equals(completionTy) && PredefinedType.VAL.equals(valueTy)) { + return PredefinedType.STREAM; + } + SemType tuple = listDefinition.tupleTypeWrapped(env, valueTy, completionTy); + return streamContaining(tuple); + } + + private static SemType streamContaining(SemType tupleType) { + SubtypeData bdd = subtypeData(tupleType, BT_LIST); + assert bdd instanceof Bdd; + return createBasicSemType(BT_STREAM, bdd); + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/AllOrNothingSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/AllOrNothingSubtype.java similarity index 68% rename from semtypes/src/main/java/io/ballerina/semtype/subtypedata/AllOrNothingSubtype.java rename to semtypes/src/main/java/io/ballerina/types/subtypedata/AllOrNothingSubtype.java index b14487953317..40099994dddc 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/AllOrNothingSubtype.java +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/AllOrNothingSubtype.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,33 +11,36 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype.subtypedata; +package io.ballerina.types.subtypedata; -import io.ballerina.semtype.SubtypeData; +import io.ballerina.types.SubtypeData; /** * A subtype representing either all subtypes or nothing. * This is the Java representation of the `boolean` found in `SubtypeData` type in Ballerina impl. * - * @since 2.0.0 + * @since 2201.8.0 */ public class AllOrNothingSubtype implements SubtypeData { private final boolean isAll; + private static final AllOrNothingSubtype all = new AllOrNothingSubtype(true); + private static final AllOrNothingSubtype nothing = new AllOrNothingSubtype(false); + private AllOrNothingSubtype(boolean isAll) { this.isAll = isAll; } public static AllOrNothingSubtype createAll() { - return new AllOrNothingSubtype(true); + return all; } public static AllOrNothingSubtype createNothing() { - return new AllOrNothingSubtype(false); + return nothing; } public boolean isAllSubtype() { diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/BddAllOrNothing.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddAllOrNothing.java new file mode 100644 index 000000000000..56ce186ae170 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddAllOrNothing.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.Bdd; + +/** + * Represent boolean subtype of Bdd type. + * + * @since 2201.8.0 + */ +public class BddAllOrNothing implements Bdd { + private final boolean isAll; + + private static final BddAllOrNothing all = new BddAllOrNothing(true); + private static final BddAllOrNothing nothing = new BddAllOrNothing(false); + + private BddAllOrNothing(boolean isAll) { + this.isAll = isAll; + } + + public static BddAllOrNothing bddAll() { + return all; + } + + public static BddAllOrNothing bddNothing() { + return nothing; + } + + public boolean isAll() { + return this.isAll; + } + + public boolean isNothing() { + return !this.isAll; + } + + public BddAllOrNothing complement() { + if (isAll) { + return nothing; + } + return all; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof BddAllOrNothing allOrNothing) { + return this.isAll == allOrNothing.isAll; + } + + return false; + } + + @Override + public int hashCode() { + return 0xa11084 + (isAll ? 1 : 0); + } + + @Override + public String toString() { + return Boolean.toString(isAll); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNode.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNode.java new file mode 100644 index 000000000000..c4c61efede1a --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNode.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.types.subtypedata; + +import io.ballerina.types.Atom; +import io.ballerina.types.Bdd; + +/** + * Internal node of a BDD, which represents a disjunction of conjunctions of atoms. + */ +public interface BddNode extends Bdd { + + static BddNode create(Atom atom, Bdd left, Bdd middle, Bdd right) { + if (isSimpleNode(left, middle, right)) { + return new BddNodeSimple(atom); + } + return new BddNodeImpl(atom, left, middle, right); + } + + private static boolean isSimpleNode(Bdd left, Bdd middle, Bdd right) { + return left instanceof BddAllOrNothing leftNode && leftNode.isAll() && + middle instanceof BddAllOrNothing middleNode && middleNode.isNothing() && + right instanceof BddAllOrNothing rightNode && rightNode.isNothing(); + } + + Atom atom(); + + Bdd left(); + + Bdd middle(); + + Bdd right(); +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNodeImpl.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNodeImpl.java new file mode 100644 index 000000000000..f2bea68b01ef --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNodeImpl.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.Atom; +import io.ballerina.types.Bdd; + +/** + * Actual implementation of a generic Bdd node. + * + * @param atom the atom that this node represents + * @param left path that include this node's atom positively + * @param middle path that doesn't include this node's atom + * @param right path that include this node's atom negatively + * @since 2201.10.0 + */ +public record BddNodeImpl(Atom atom, Bdd left, Bdd middle, Bdd right) implements BddNode { + +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNodeSimple.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNodeSimple.java new file mode 100644 index 000000000000..7ead5fbeb3f3 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNodeSimple.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.Atom; +import io.ballerina.types.Bdd; + +/** + * Represent a Bdd node that contains a single atom as positive. This is used to reduce the memory overhead of + * BddNodeImpl in representing such nodes + * + * @param atom Atom this node represents + * @since 2201.10.0 + */ +public record BddNodeSimple(Atom atom) implements BddNode { + + @Override + public Bdd left() { + return BddAllOrNothing.bddAll(); + } + + @Override + public Bdd middle() { + return BddAllOrNothing.bddNothing(); + } + + @Override + public Bdd right() { + return BddAllOrNothing.bddNothing(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/BooleanSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/BooleanSubtype.java new file mode 100644 index 000000000000..4eaa4f4f5e1b --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/BooleanSubtype.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; + +import java.util.Optional; + +/** + * Represent BooleanSubtype. + * + * @since 2201.8.0 + */ +public class BooleanSubtype implements ProperSubtypeData { + public final boolean value; + + private BooleanSubtype(boolean value) { + this.value = value; + } + + public static BooleanSubtype from(boolean value) { + return new BooleanSubtype(value); + } + + public static boolean booleanSubtypeContains(SubtypeData d, boolean b) { + if (d instanceof AllOrNothingSubtype allOrNothingSubtype) { + return allOrNothingSubtype.isAllSubtype(); + } + BooleanSubtype r = (BooleanSubtype) d; + return r.value == b; + } + + public static SemType booleanConst(boolean value) { + BooleanSubtype t = BooleanSubtype.from(value); + return PredefinedType.basicSubtype(BasicTypeCode.BT_BOOLEAN, t); + } + + public static Optional booleanSubtypeSingleValue(SubtypeData d) { + if (d instanceof AllOrNothingSubtype) { + return Optional.empty(); + } + BooleanSubtype b = (BooleanSubtype) d; + return Optional.of(b.value); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/CellSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/CellSubtype.java new file mode 100644 index 000000000000..e7dcae506dd6 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/CellSubtype.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.CellSemType; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.TypeAtom; + +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.typeops.BddCommonOps.bddAtom; + +/** + * CellSubType. + * + * @since 2201.10.0 + */ +public class CellSubtype { + + // TODO: cache common cells such as cell containing NEVER + public static CellSemType cellContaining(Env env, SemType ty) { + return cellContaining(env, ty, CELL_MUT_LIMITED); + } + + public static CellSemType roCellContaining(Env env, SemType ty) { + return cellContaining(env, ty, CELL_MUT_NONE); + } + + public static CellSemType cellContaining(Env env, SemType ty, CellAtomicType.CellMutability mut) { + assert Core.isNever(ty) || !Core.isSubtypeSimple(ty, PredefinedType.CELL); + CellAtomicType atomicCell = CellAtomicType.from(ty, mut); + TypeAtom atom = env.cellAtom(atomicCell); + BddNode bdd = bddAtom(atom); + ComplexSemType complexSemType = PredefinedType.basicSubtype(BasicTypeCode.BT_CELL, bdd); + return CellSemType.from(complexSemType.subtypeDataList()); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/CharStringSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/CharStringSubtype.java new file mode 100644 index 000000000000..b8f9a7bcbe18 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/CharStringSubtype.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.EnumerableCharString; +import io.ballerina.types.EnumerableSubtype; +import io.ballerina.types.EnumerableType; + +/** + * Represent CharStringSubtype. + * + * @since 2201.8.0 + */ +public class CharStringSubtype extends EnumerableSubtype { + + public boolean allowed; + public EnumerableCharString[] values; + + private CharStringSubtype(boolean allowed, EnumerableCharString[] values) { + this.allowed = allowed; + this.values = values; + } + + public static CharStringSubtype from(boolean allowed, EnumerableCharString[] values) { + return new CharStringSubtype(allowed, values); + } + + @Override + public boolean allowed() { + return allowed; + } + + @Override + public EnumerableType[] values() { + return values; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/DecimalSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/DecimalSubtype.java new file mode 100644 index 000000000000..22b5fb7483c6 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/DecimalSubtype.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.EnumerableDecimal; +import io.ballerina.types.EnumerableSubtype; +import io.ballerina.types.EnumerableType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; + +import java.math.BigDecimal; +import java.util.Optional; +import java.util.StringJoiner; + +/** + * Represent DecimalSubtype. + * + * @since 2201.8.0 + */ +public class DecimalSubtype extends EnumerableSubtype implements ProperSubtypeData { + public boolean allowed; + public EnumerableDecimal[] values; + + private DecimalSubtype(boolean allowed, EnumerableDecimal value) { + this(allowed, new EnumerableDecimal[]{value}); + } + + private DecimalSubtype(boolean allowed, EnumerableDecimal[] values) { + this.allowed = allowed; + this.values = values; + } + + public static SemType decimalConst(BigDecimal value) { + return PredefinedType.basicSubtype(BasicTypeCode.BT_DECIMAL, + new DecimalSubtype(true, EnumerableDecimal.from(value))); + } + + public static Optional decimalSubtypeSingleValue(SubtypeData d) { + if (d instanceof AllOrNothingSubtype) { + return Optional.empty(); + } + + DecimalSubtype f = (DecimalSubtype) d; + if (!f.allowed) { + return Optional.empty(); + } + + EnumerableDecimal[] values = f.values; + if (values.length != 1) { + return Optional.empty(); + } + return Optional.of(values[0].value); + } + + public static boolean decimalSubtypeContains(SubtypeData d, EnumerableDecimal f) { + if (d instanceof AllOrNothingSubtype allOrNothingSubtype) { + return allOrNothingSubtype.isAllSubtype(); + } + + DecimalSubtype v = (DecimalSubtype) d; + for (EnumerableType val : v.values) { + if (val.equals(f)) { + return v.allowed; + } + } + return !v.allowed; + } + + public static SubtypeData createDecimalSubtype(boolean allowed, EnumerableDecimal[] values) { + if (values.length == 0) { + return allowed ? AllOrNothingSubtype.createNothing() : AllOrNothingSubtype.createAll(); + } + return new DecimalSubtype(allowed, values); + } + + @Override + public boolean allowed() { + return allowed; + } + + @Override + public EnumerableType[] values() { + return values; + } + + @Override + public String toString() { + StringJoiner j = new StringJoiner(", ", "DecimalSubtype:" + (allowed ? "allowed[" : "disallowed["), "]"); + for (EnumerableDecimal value : values) { + j.add(String.valueOf(value.value)); + } + return j.toString(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/FloatSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/FloatSubtype.java new file mode 100644 index 000000000000..992b47b0030f --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/FloatSubtype.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.EnumerableFloat; +import io.ballerina.types.EnumerableSubtype; +import io.ballerina.types.EnumerableType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; + +import java.util.Optional; +import java.util.StringJoiner; + +/** + * Represent FloatSubtype. + * + * @since 2201.8.0 + */ +public class FloatSubtype extends EnumerableSubtype implements ProperSubtypeData { + public boolean allowed; + public EnumerableFloat[] values; + + private FloatSubtype(boolean allowed, EnumerableFloat value) { + this(allowed, new EnumerableFloat[]{value}); + } + + private FloatSubtype(boolean allowed, EnumerableFloat[] values) { + this.allowed = allowed; + this.values = values; + } + + public static SemType floatConst(double value) { + return PredefinedType.basicSubtype(BasicTypeCode.BT_FLOAT, new FloatSubtype(true, + EnumerableFloat.from(value))); + } + + public static Optional floatSubtypeSingleValue(SubtypeData d) { + if (d instanceof AllOrNothingSubtype) { + return Optional.empty(); + } + + FloatSubtype f = (FloatSubtype) d; + if (!f.allowed) { + return Optional.empty(); + } + + EnumerableFloat[] values = f.values; + if (values.length != 1) { + return Optional.empty(); + } + return Optional.of(values[0].value); + } + + public static boolean floatSubtypeContains(SubtypeData d, EnumerableFloat f) { + if (d instanceof AllOrNothingSubtype allOrNothingSubtype) { + return allOrNothingSubtype.isAllSubtype(); + } + + FloatSubtype v = (FloatSubtype) d; + for (EnumerableType val : v.values) { + if (val.equals(f)) { + return v.allowed; + } + } + return !v.allowed; + } + + public static SubtypeData createFloatSubtype(boolean allowed, EnumerableFloat[] values) { + if (values.length == 0) { + return allowed ? AllOrNothingSubtype.createNothing() : AllOrNothingSubtype.createAll(); + } + return new FloatSubtype(allowed, values); + } + + @Override + public boolean allowed() { + return allowed; + } + + @Override + public EnumerableType[] values() { + return values; + } + + @Override + public String toString() { + StringJoiner j = new StringJoiner(", ", "FloatSubtype:" + (allowed ? "allowed[" : "disallowed["), "]"); + for (EnumerableFloat value : values) { + j.add(String.valueOf(value.value)); + } + return j.toString(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/FutureSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/FutureSubtype.java new file mode 100644 index 000000000000..f7135617ffdf --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/FutureSubtype.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.Bdd; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.definition.MappingDefinition; + +import java.util.List; + +import static io.ballerina.types.BasicTypeCode.BT_FUTURE; +import static io.ballerina.types.BasicTypeCode.BT_MAPPING; +import static io.ballerina.types.Core.createBasicSemType; +import static io.ballerina.types.Core.subtypeData; + +/** + * Represent future subtype. + * + * @since 2201.10.0 + */ +public final class FutureSubtype { + + private FutureSubtype() { + } + + public static SemType futureContaining(Env env, SemType constraint) { + if (PredefinedType.VAL.equals(constraint)) { + return PredefinedType.FUTURE; + } + + MappingDefinition mappingDef = new MappingDefinition(); + SemType mappingType = mappingDef.defineMappingTypeWrapped(env, List.of(), constraint); + Bdd bdd = (Bdd) subtypeData(mappingType, BT_MAPPING); + return createBasicSemType(BT_FUTURE, bdd); + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/IntSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/IntSubtype.java similarity index 63% rename from semtypes/src/main/java/io/ballerina/semtype/subtypedata/IntSubtype.java rename to semtypes/src/main/java/io/ballerina/types/subtypedata/IntSubtype.java index 475640212fca..e977b77cdfd5 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/IntSubtype.java +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/IntSubtype.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,29 +11,30 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype.subtypedata; +package io.ballerina.types.subtypedata; -import io.ballerina.semtype.PredefinedType; -import io.ballerina.semtype.ProperSubtypeData; -import io.ballerina.semtype.SemType; -import io.ballerina.semtype.SubtypeData; -import io.ballerina.semtype.UniformTypeCode; +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; import java.util.Arrays; import java.util.Optional; +import java.util.StringJoiner; /** * Represent IntSubtype. * - * @since 2.0.0 + * @since 2201.8.0 */ public class IntSubtype implements ProperSubtypeData { - private final Range[] ranges; + public final Range[] ranges; public IntSubtype(Range[] ranges) { this.ranges = Arrays.copyOf(ranges, ranges.length); @@ -48,7 +49,7 @@ public static IntSubtype createSingleRangeSubtype(long min, long max) { } public static SemType intConst(long value) { - return PredefinedType.uniformSubtype(UniformTypeCode.UT_INT, createSingleRangeSubtype(value, value)); + return PredefinedType.basicSubtype(BasicTypeCode.BT_INT, createSingleRangeSubtype(value, value)); } static void validIntWidth(boolean signed, long bits) { @@ -84,17 +85,17 @@ public static SemType intWidthSigned(long bits) { return PredefinedType.INT; } IntSubtype t = createSingleRangeSubtype(-(1L << (bits - 1L)), (1L << (bits - 1L)) - 1L); - return PredefinedType.uniformSubtype(UniformTypeCode.UT_INT, t); + return PredefinedType.basicSubtype(BasicTypeCode.BT_INT, t); } public static SemType intWidthUnsigned(int bits) { validIntWidth(false, bits); IntSubtype t = createSingleRangeSubtype(0L, (1L << bits) - 1L); - return PredefinedType.uniformSubtype(UniformTypeCode.UT_INT, t); + return PredefinedType.basicSubtype(BasicTypeCode.BT_INT, t); } // Widen to UnsignedN - public SubtypeData intSubtypeWidenUnsigned(SubtypeData d) { + public static SubtypeData intSubtypeWidenUnsigned(SubtypeData d) { if (d instanceof AllOrNothingSubtype) { return d; } @@ -105,10 +106,10 @@ public SubtypeData intSubtypeWidenUnsigned(SubtypeData d) { } Range r = v.ranges[v.ranges.length - 1]; - int i = 8; - while (i <= 32) { + long i = 8L; + while (i <= 32L) { if (r.max < (1L << i)) { - IntSubtype w = createSingleRangeSubtype(0L, i); + IntSubtype w = createSingleRangeSubtype(0L, (1L << i) - 1); return w; } i = i * 2; @@ -116,7 +117,7 @@ public SubtypeData intSubtypeWidenUnsigned(SubtypeData d) { return AllOrNothingSubtype.createAll(); } - public Optional intSubtypeSingleValue(SubtypeData d) { + public static Optional intSubtypeSingleValue(SubtypeData d) { if (d instanceof AllOrNothingSubtype) { return Optional.empty(); } @@ -133,16 +134,32 @@ public Optional intSubtypeSingleValue(SubtypeData d) { return Optional.of(min); } - /** - * Int Range node. - */ - static class Range { - final long min; - final long max; + public static boolean intSubtypeContains(SubtypeData d, long n) { + if (d instanceof AllOrNothingSubtype allOrNothingSubtype) { + return allOrNothingSubtype.isAllSubtype(); + } + IntSubtype v = (IntSubtype) d; + for (Range r : v.ranges) { + if (r.min <= n && n <= r.max) { + return true; + } + } + return false; + } + + @Override + public String toString() { + StringJoiner j = new StringJoiner(", ", "Int:Range[", "]"); + for (Range r : ranges) { + j.add(minusIndi(r.min) + "-" + minusIndi(r.max)); + } + return j.toString(); + } - public Range(long min, long max) { - this.min = min; - this.max = max; + private String minusIndi(long i) { + if (i < 0) { + return "(" + i + ")"; } + return String.valueOf(i); } } diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/NonCharStringSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/NonCharStringSubtype.java new file mode 100644 index 000000000000..3d686bd4fcc4 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/NonCharStringSubtype.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.EnumerableString; +import io.ballerina.types.EnumerableSubtype; +import io.ballerina.types.EnumerableType; + +/** + * Represent NonCharStringSubtype. + * + * @since 2201.8.0 + */ +public class NonCharStringSubtype extends EnumerableSubtype { + + public boolean allowed; + public EnumerableString[] values; + + private NonCharStringSubtype(boolean allowed, EnumerableString[] values) { + this.allowed = allowed; + this.values = values; + } + + public static NonCharStringSubtype from(boolean allowed, EnumerableString[] values) { + return new NonCharStringSubtype(allowed, values); + } + + @Override + public boolean allowed() { + return allowed; + } + + @Override + public EnumerableType[] values() { + return values; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/Range.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/Range.java new file mode 100644 index 000000000000..f34ca9f3a501 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/Range.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +/** + * Int Range node. + * + * @since 2201.8.0 + */ +public class Range { + public final long min; + public final long max; + + public Range(long min, long max) { + this.min = min; + this.max = max; + } + + public static Range from(long min, long max) { + return new Range(min, max); + } + + @Override + public String toString() { + return "Range[" + min + ", " + max + "]"; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/RangeUnion.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/RangeUnion.java new file mode 100644 index 000000000000..cc7cf4846769 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/RangeUnion.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +/** + * Holds a range if there is a single range representing the union/intersect of r1 and r1. + * status -1 means union/intersect is empty because r1 is before r2, with no overlap + * status 1 means union/intersect is empty because r2 is before r1 with no overlap + * Precondition r1 and r2 are non-empty. + * + * @since 2201.8.0 + */ +public class RangeUnion { + public final int status; // -1, 1, default to zero when there is a range + public final Range range; + + private RangeUnion(int status, Range range) { + this.status = status; + this.range = range; + } + + public static RangeUnion from(int status) { + return new RangeUnion(status, null); + } + + public static RangeUnion from(Range range) { + return new RangeUnion(0, range); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/StringSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/StringSubtype.java new file mode 100644 index 000000000000..edd0c4dad869 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/StringSubtype.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.EnumerableCharString; +import io.ballerina.types.EnumerableString; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; + +import java.util.Arrays; +import java.util.Optional; + +/** + * Represent StringSubtype. + * + * @since 2201.8.0 + */ +public class StringSubtype implements ProperSubtypeData { + + private static final EnumerableString[] EMPTY_STRING_ARR = {}; + private static final EnumerableCharString[] EMPTY_CHAR_ARR = {}; + CharStringSubtype charData; + NonCharStringSubtype nonCharData; + + public CharStringSubtype getChar() { + return charData; + } + + public NonCharStringSubtype getNonChar() { + return nonCharData; + } + + private StringSubtype(CharStringSubtype charData, NonCharStringSubtype nonCharData) { + this.charData = charData; + this.nonCharData = nonCharData; + } + + public static StringSubtype from(CharStringSubtype chara, NonCharStringSubtype nonChar) { + return new StringSubtype(chara, nonChar); + } + + public static boolean stringSubtypeContains(SubtypeData d, String s) { + if (d instanceof AllOrNothingSubtype allOrNothingSubtype) { + return allOrNothingSubtype.isAllSubtype(); + } + StringSubtype st = (StringSubtype) d; + CharStringSubtype chara = st.charData; + NonCharStringSubtype nonChar = st.nonCharData; + if (s.length() == 1) { + return Arrays.asList(chara.values).contains(EnumerableCharString.from(s)) == chara.allowed; + } + return Arrays.asList(nonChar.values).contains(EnumerableString.from(s)) == nonChar.allowed; + } + + public static SubtypeData createStringSubtype(CharStringSubtype chara, NonCharStringSubtype nonChar) { + if (chara.values.length == 0 && nonChar.values.length == 0) { + if (!chara.allowed && !nonChar.allowed) { + return AllOrNothingSubtype.createAll(); + } else if (chara.allowed && nonChar.allowed) { + return AllOrNothingSubtype.createNothing(); + } + } + return StringSubtype.from(chara, nonChar); + } + + public static Optional stringSubtypeSingleValue(SubtypeData d) { + if (d instanceof AllOrNothingSubtype) { + return Optional.empty(); + } + StringSubtype st = (StringSubtype) d; + CharStringSubtype chara = st.charData; + NonCharStringSubtype nonChar = st.nonCharData; + int charCount = chara.allowed ? chara.values.length : 2; + int nonCharCount = nonChar.allowed ? nonChar.values.length : 2; + if (charCount + nonCharCount == 1) { + return charCount != 0 ? + Optional.of(chara.values[0].value) : Optional.of(nonChar.values[0].value); + } + return Optional.empty(); + } + + public static SemType stringConst(String value) { + CharStringSubtype chara; + NonCharStringSubtype nonChar; + if (value.codePointCount(0, value.length()) == 1) { + chara = CharStringSubtype.from(true, + new EnumerableCharString[]{EnumerableCharString.from(value)}); + nonChar = NonCharStringSubtype.from(true, EMPTY_STRING_ARR); + } else { + chara = CharStringSubtype.from(true, EMPTY_CHAR_ARR); + nonChar = NonCharStringSubtype.from(true, new EnumerableString[]{EnumerableString.from(value)}); + } + return PredefinedType.basicSubtype(BasicTypeCode.BT_STRING, new StringSubtype(chara, nonChar)); + } + + public static SemType stringChar() { + StringSubtype st = new StringSubtype( + CharStringSubtype.from(false, EMPTY_CHAR_ARR), + NonCharStringSubtype.from(true, EMPTY_STRING_ARR)); + return PredefinedType.basicSubtype(BasicTypeCode.BT_STRING, st); + } + + /** + * Describes the relationship between a StringSubtype and a list of strings + * How the StringSubtype covers the list and vice versa. + * type StringSubtypeListCoverage record {| + * // true if the StringSubtype is a subtype of the type containing the strings in the lists + * boolean isSubtype; + * // contains the index in order of each member of the list that is in the StringSubtype + * int[] indices; + * |}; + */ + public static class StringSubtypeListCoverage { + public final boolean isSubtype; + public final int[] indices; + + public StringSubtypeListCoverage(boolean isSubtype, int[] indices) { + this.isSubtype = isSubtype; + this.indices = indices; + } + + public static StringSubtypeListCoverage from(boolean isSubtype, int[] indices) { + return new StringSubtypeListCoverage(isSubtype, indices); + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/TableSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/TableSubtype.java new file mode 100644 index 000000000000..680065c91ff9 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/TableSubtype.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.Bdd; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.FixedLengthArray; +import io.ballerina.types.ListAtomicType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.definition.ListDefinition; + +import static io.ballerina.types.BasicTypeCode.BT_LIST; +import static io.ballerina.types.BasicTypeCode.BT_TABLE; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.Core.createBasicSemType; +import static io.ballerina.types.Core.subtypeData; + +/** + * TableSubtype. + * + * @since 2201.8.0 + */ +public final class TableSubtype { + + private TableSubtype() { + } + + public static SemType tableContainingKeyConstraint(Context cx, SemType tableConstraint, SemType keyConstraint) { + SemType normalizedKc; + ListAtomicType lat = Core.listAtomicType(cx, keyConstraint); + if (lat != null && PredefinedType.CELL_ATOMIC_UNDEF.equals(Core.cellAtomicType(lat.rest()))) { + FixedLengthArray members = lat.members(); + normalizedKc = switch (members.fixedLength()) { + case 0 -> PredefinedType.VAL; + case 1 -> Core.cellAtomicType(members.initial().get(0)).ty(); + default -> keyConstraint; + }; + } else { + normalizedKc = keyConstraint; + } + return tableContaining(cx.env, tableConstraint, normalizedKc, PredefinedType.VAL); + } + + public static SemType tableContainingKeySpecifier(Context cx, SemType tableConstraint, String[] fieldNames) { + SemType[] fieldNameSingletons = new SemType[fieldNames.length]; + SemType[] fieldTypes = new SemType[fieldNames.length]; + for (int i = 0; i < fieldNames.length; i++) { + SemType key = SemTypes.stringConst(fieldNames[i]); + fieldNameSingletons[i] = key; + fieldTypes[i] = Core.mappingMemberTypeInnerVal(cx, tableConstraint, key); + } + + SemType normalizedKs = new ListDefinition().tupleTypeWrapped(cx.env, fieldNameSingletons); + + SemType normalizedKc; + if (fieldTypes.length > 1) { + ListDefinition ld = new ListDefinition(); + normalizedKc = ld.tupleTypeWrapped(cx.env, fieldTypes); + } else { + normalizedKc = fieldTypes[0]; + } + return tableContaining(cx.env, tableConstraint, normalizedKc, normalizedKs); + } + + public static SemType tableContaining(Env env, SemType tableConstraint) { + return tableContaining(env, tableConstraint, CELL_MUT_LIMITED); + } + + public static SemType tableContaining(Env env, SemType tableConstraint, CellAtomicType.CellMutability mut) { + SemType normalizedKc = PredefinedType.VAL; // TODO: Ideally this should be anydata + SemType normalizedKs = PredefinedType.VAL; // TODO: Ideally this should be string[] + return tableContaining(env, tableConstraint, normalizedKc, normalizedKs, mut); + } + + private static SemType tableContaining(Env env, SemType tableConstraint, + SemType normalizedKc, SemType normalizedKs, + CellAtomicType.CellMutability mut) { + + assert SemTypes.isSubtypeSimple(tableConstraint, PredefinedType.MAPPING); + ListDefinition typeParamArrDef = new ListDefinition(); + SemType typeParamArray = typeParamArrDef.defineListTypeWrapped(env, tableConstraint, mut); + + ListDefinition listDef = new ListDefinition(); + SemType tupleType = listDef.tupleTypeWrapped(env, typeParamArray, normalizedKc, normalizedKs); + Bdd bdd = (Bdd) subtypeData(tupleType, BT_LIST); + return createBasicSemType(BT_TABLE, bdd); + } + + private static SemType tableContaining(Env env, SemType tableConstraint, + SemType normalizedKc, SemType normalizedKs) { + return tableContaining(env, tableConstraint, normalizedKc, normalizedKs, CELL_MUT_LIMITED); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/TypedescSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/TypedescSubtype.java new file mode 100644 index 000000000000..a56171f854c1 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/TypedescSubtype.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.Bdd; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.definition.MappingDefinition; + +import java.util.List; + +import static io.ballerina.types.BasicTypeCode.BT_MAPPING; +import static io.ballerina.types.BasicTypeCode.BT_TYPEDESC; +import static io.ballerina.types.Core.createBasicSemType; +import static io.ballerina.types.Core.subtypeData; + +/** + * Represent typedesc subtype. + * + * @since 2201.10.0 + */ +public final class TypedescSubtype { + + private TypedescSubtype() { + } + + public static SemType typedescContaining(Env env, SemType constraint) { + if (PredefinedType.VAL.equals(constraint)) { + return PredefinedType.TYPEDESC; + } + + MappingDefinition mappingDef = new MappingDefinition(); + SemType mappingType = mappingDef.defineMappingTypeWrapped(env, List.of(), constraint, + CellAtomicType.CellMutability.CELL_MUT_NONE); + Bdd bdd = (Bdd) subtypeData(mappingType, BT_MAPPING); + return createBasicSemType(BT_TYPEDESC, bdd); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/XmlSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/XmlSubtype.java new file mode 100644 index 000000000000..1991dd0eb86f --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/XmlSubtype.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.Bdd; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.RecAtom; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.typeops.BddCommonOps; + +/** + * Implementation specific to basic type xml. + * + * @since 2201.8.0 + */ +public class XmlSubtype implements ProperSubtypeData { + // This is the bitwise-or of above XML_PRIMITIVE_* fields. + // If the XML_PRIMITIVE_NEVER bit is set, then the empty XML sequence belongs to the type. + // If one of the other XML_PRIMITVE_* bits is set, then the type contains the + // corresponding singleton type. + public final int primitives; + // This is a logical combination of the allowed sequences types. The `atom` field of + // the `BddNode` is a bitwise-or of XML_PRIMTIVE_* (except for XML_PRIMITIVE_NEVER). + // It represents a sequence of two or more singletons, where the allowed singletons + // are those whose bit is set in the `atom` field. + public final Bdd sequence; + + public static final int XML_PRIMITIVE_NEVER = 1; + public static final int XML_PRIMITIVE_TEXT = 1 << 1; + public static final int XML_PRIMITIVE_ELEMENT_RO = 1 << 2; + public static final int XML_PRIMITIVE_PI_RO = 1 << 3; + public static final int XML_PRIMITIVE_COMMENT_RO = 1 << 4; + public static final int XML_PRIMITIVE_ELEMENT_RW = 1 << 5; + public static final int XML_PRIMITIVE_PI_RW = 1 << 6; + public static final int XML_PRIMITIVE_COMMENT_RW = 1 << 7; + + public static final int XML_PRIMITIVE_RO_SINGLETON = XML_PRIMITIVE_TEXT | XML_PRIMITIVE_ELEMENT_RO + | XML_PRIMITIVE_PI_RO | XML_PRIMITIVE_COMMENT_RO; + public static final int XML_PRIMITIVE_RO_MASK = XML_PRIMITIVE_NEVER | XML_PRIMITIVE_RO_SINGLETON; + public static final int XML_PRIMITIVE_RW_MASK = XML_PRIMITIVE_ELEMENT_RW | XML_PRIMITIVE_PI_RW + | XML_PRIMITIVE_COMMENT_RW; + public static final int XML_PRIMITIVE_SINGLETON = XML_PRIMITIVE_RO_SINGLETON | XML_PRIMITIVE_RW_MASK; + public static final int XML_PRIMITIVE_ALL_MASK = XML_PRIMITIVE_RO_MASK | XML_PRIMITIVE_RW_MASK; + + private XmlSubtype(int primitives, Bdd sequence) { + this.primitives = primitives; + this.sequence = sequence; + } + + public static XmlSubtype from(int primitives, Bdd sequence) { + return new XmlSubtype(primitives, sequence); + } + + public static SemType xmlSingleton(int primitives) { + return createXmlSemtype(createXmlSubtype(primitives, BddAllOrNothing.bddNothing())); + } + + public static SemType xmlSequence(SemType constituentType) { + // It is a precondition that constituentType is a subtype of XML + assert Core.isSubtypeSimple(constituentType, PredefinedType.XML); + + if (Core.isNever(constituentType)) { + return xmlSequence(xmlSingleton(XML_PRIMITIVE_NEVER)); + } + if (constituentType instanceof BasicTypeBitSet) { + return constituentType; + } else { + ComplexSemType cct = (ComplexSemType) constituentType; + SubtypeData xmlSubtype = Core.getComplexSubtypeData(cct, BasicTypeCode.BT_XML); + xmlSubtype = (xmlSubtype instanceof AllOrNothingSubtype) ? + xmlSubtype : makeXmlSequence((XmlSubtype) xmlSubtype); + return createXmlSemtype(xmlSubtype); + } + } + + private static SubtypeData makeXmlSequence(XmlSubtype d) { + int primitives = XML_PRIMITIVE_NEVER | d.primitives; + int atom = d.primitives & XML_PRIMITIVE_SINGLETON; + Bdd sequence = BddCommonOps.bddUnion(BddCommonOps.bddAtom(RecAtom.createXMLRecAtom(atom)), d.sequence); + return createXmlSubtype(primitives, sequence); + } + + public static SemType createXmlSemtype(SubtypeData xmlSubtype) { + if (xmlSubtype instanceof AllOrNothingSubtype allOrNothingSubtype) { + return allOrNothingSubtype.isAllSubtype() ? PredefinedType.XML : PredefinedType.NEVER; + } else { + return PredefinedType.basicSubtype(BasicTypeCode.BT_XML, (ProperSubtypeData) xmlSubtype); + } + } + + public static SubtypeData createXmlSubtype(int primitives, Bdd sequence) { + int p = primitives & XML_PRIMITIVE_ALL_MASK; + if (sequence instanceof BddAllOrNothing allOrNothing && allOrNothing.isAll() && + p == XML_PRIMITIVE_ALL_MASK) { + return AllOrNothingSubtype.createAll(); + } + return createXmlSubtypeOrEmpty(p, sequence); + } + + public static SubtypeData createXmlSubtypeOrEmpty(int primitives, Bdd sequence) { + if (sequence instanceof BddAllOrNothing allOrNothing && allOrNothing.isNothing() && primitives == 0) { + return AllOrNothingSubtype.createNothing(); + } + return from(primitives, sequence); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/BasicTypeOpsPanicImpl.java b/semtypes/src/main/java/io/ballerina/types/typeops/BasicTypeOpsPanicImpl.java new file mode 100644 index 000000000000..923b7b154f8d --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/BasicTypeOpsPanicImpl.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; + +/** + * Default implementation for basic subtypes that does not need type-ops. + * + * @since 2201.8.0 + */ +public class BasicTypeOpsPanicImpl implements BasicTypeOps { + @Override + public SubtypeData union(SubtypeData t1, SubtypeData t2) { + throw new IllegalStateException("Binary operation should not be called"); + } + + @Override + public SubtypeData intersect(SubtypeData t1, SubtypeData t2) { + throw new IllegalStateException("Binary operation should not be called"); + } + + @Override + public SubtypeData diff(SubtypeData t1, SubtypeData t2) { + throw new IllegalStateException("Binary operation should not be called"); + } + + @Override + public SubtypeData complement(SubtypeData t) { + throw new IllegalStateException("Unary operation should not be called"); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + throw new IllegalStateException("Unary boolean operation should not be called"); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/BddCommonOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/BddCommonOps.java new file mode 100644 index 000000000000..6de40258eee1 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/BddCommonOps.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.Atom; +import io.ballerina.types.Bdd; +import io.ballerina.types.RecAtom; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; + +/** + * Contain common BDD operations found in bdd.bal file. + * + * @since 2201.8.0 + */ +public abstract class BddCommonOps { + + public static BddNode bddAtom(Atom atom) { + return BddNode.create(atom, + BddAllOrNothing.bddAll(), + BddAllOrNothing.bddNothing(), + BddAllOrNothing.bddNothing()); + } + + public static Bdd bddUnion(Bdd b1, Bdd b2) { + if (b1 == b2) { + return b1; + } else if (b1 instanceof BddAllOrNothing) { + return ((BddAllOrNothing) b1).isAll() ? BddAllOrNothing.bddAll() : b2; + } else if (b2 instanceof BddAllOrNothing) { + return ((BddAllOrNothing) b2).isAll() ? BddAllOrNothing.bddAll() : b1; + } else { + BddNode b1Bdd = (BddNode) b1; + BddNode b2Bdd = (BddNode) b2; + long cmp = atomCmp(b1Bdd.atom(), b2Bdd.atom()); + if (cmp < 0L) { + return bddCreate(b1Bdd.atom(), + b1Bdd.left(), + bddUnion(b1Bdd.middle(), b2), + b1Bdd.right()); + } else if (cmp > 0L) { + return bddCreate(b2Bdd.atom(), + b2Bdd.left(), + bddUnion(b1, b2Bdd.middle()), + b2Bdd.right()); + } else { + return bddCreate(b1Bdd.atom(), + bddUnion(b1Bdd.left(), b2Bdd.left()), + bddUnion(b1Bdd.middle(), b2Bdd.middle()), + bddUnion(b1Bdd.right(), b2Bdd.right())); + } + } + } + + public static Bdd bddIntersect(Bdd b1, Bdd b2) { + if (b1 == b2) { + return b1; + } else if (b1 instanceof BddAllOrNothing) { + return ((BddAllOrNothing) b1).isAll() ? b2 : BddAllOrNothing.bddNothing(); + } else if (b2 instanceof BddAllOrNothing) { + return ((BddAllOrNothing) b2).isAll() ? b1 : BddAllOrNothing.bddNothing(); + } else { + BddNode b1Bdd = (BddNode) b1; + BddNode b2Bdd = (BddNode) b2; + long cmp = atomCmp(b1Bdd.atom(), b2Bdd.atom()); + if (cmp < 0L) { + return bddCreate(b1Bdd.atom(), + bddIntersect(b1Bdd.left(), b2), + bddIntersect(b1Bdd.middle(), b2), + bddIntersect(b1Bdd.right(), b2)); + } else if (cmp > 0L) { + return bddCreate(b2Bdd.atom(), + bddIntersect(b1, b2Bdd.left()), + bddIntersect(b1, b2Bdd.middle()), + bddIntersect(b1, b2Bdd.right())); + } else { + return bddCreate(b1Bdd.atom(), + bddIntersect( + bddUnion(b1Bdd.left(), b1Bdd.middle()), + bddUnion(b2Bdd.left(), b2Bdd.middle())), + BddAllOrNothing.bddNothing(), + bddIntersect( + bddUnion(b1Bdd.right(), b1Bdd.middle()), + bddUnion(b2Bdd.right(), b2Bdd.middle()))); + } + } + } + + public static Bdd bddDiff(Bdd b1, Bdd b2) { + if (b1 == b2) { + return BddAllOrNothing.bddNothing(); + } else if (b2 instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? BddAllOrNothing.bddNothing() : b1; + } else if (b1 instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? bddComplement(b2) : BddAllOrNothing.bddNothing(); + } else { + BddNode b1Bdd = (BddNode) b1; + BddNode b2Bdd = (BddNode) b2; + long cmp = atomCmp(b1Bdd.atom(), b2Bdd.atom()); + if (cmp < 0L) { + return bddCreate(b1Bdd.atom(), + bddDiff(bddUnion(b1Bdd.left(), b1Bdd.middle()), b2), + BddAllOrNothing.bddNothing(), + bddDiff(bddUnion(b1Bdd.right(), b1Bdd.middle()), b2)); + } else if (cmp > 0L) { + return bddCreate(b2Bdd.atom(), + bddDiff(b1, bddUnion(b2Bdd.left(), b2Bdd.middle())), + BddAllOrNothing.bddNothing(), + bddDiff(b1, bddUnion(b2Bdd.right(), b2Bdd.middle()))); + } else { + // There is an error in the Castagna paper for this formula. + // The union needs to be materialized here. + // The original formula does not work in a case like (a0|a1) - a0. + // Castagna confirms that the following formula is the correct one. + return bddCreate(b1Bdd.atom(), + bddDiff(bddUnion(b1Bdd.left(), b1Bdd.middle()), + bddUnion(b2Bdd.left(), b2Bdd.middle())), + BddAllOrNothing.bddNothing(), + bddDiff(bddUnion(b1Bdd.right(), b1Bdd.middle()), + bddUnion(b2Bdd.right(), b2Bdd.middle()))); + } + } + } + + public static Bdd bddComplement(Bdd b) { + if (b instanceof BddAllOrNothing) { + return ((BddAllOrNothing) b).complement(); + } else { + return bddNodeComplement((BddNode) b); + } + } + + public static Bdd bddNodeComplement(BddNode b) { + BddAllOrNothing bddNothing = BddAllOrNothing.bddNothing(); + if (b.right().equals(bddNothing)) { + return bddCreate(b.atom(), + bddNothing, + bddComplement(bddUnion(b.left(), b.middle())), + bddComplement(b.middle())); + } else if (b.left().equals(bddNothing)) { + return bddCreate(b.atom(), + bddComplement(b.middle()), + bddComplement(bddUnion(b.right(), b.middle())), + bddNothing); + } else if (b.middle().equals(bddNothing)) { + return bddCreate(b.atom(), + bddComplement(b.left()), + bddComplement(bddUnion(b.left(), b.right())), + bddComplement(b.right())); + } else { + // There is a typo in the Frisch PhD thesis for this formula. + // (It has left and right swapped.) + // Castagna (the PhD supervisor) confirms that this is the correct formula. + return bddCreate(b.atom(), + bddComplement(bddUnion(b.left(), b.middle())), + bddNothing, + bddComplement(bddUnion(b.right(), b.middle()))); + } + } + + public static Bdd bddCreate(Atom atom, Bdd left, Bdd middle, Bdd right) { + if (middle instanceof BddAllOrNothing && ((BddAllOrNothing) middle).isAll()) { + return middle; + } + if (left.equals(right)) { + return bddUnion(left, right); + } + + return BddNode.create(atom, left, middle, right); + } + + // order RecAtom < TypeAtom + public static long atomCmp(Atom a1, Atom a2) { + if (a1 instanceof RecAtom r1) { + if (a2 instanceof RecAtom r2) { + return r1.index() - r2.index(); + } else { + return -1L; + } + } else if (a2 instanceof RecAtom) { + return 1L; + } else { + return a1.index() - a2.index(); + } + } + + // This is for debugging purposes. + // It uses the Frisch/Castagna notation. + public static String bddToString(Bdd b, boolean inner) { + if (b instanceof BddAllOrNothing) { + return ((BddAllOrNothing) b).isAll() ? "1" : "0"; + } else { + String str; + BddNode bdd = (BddNode) b; + Atom a = bdd.atom(); + + if (a instanceof RecAtom) { + str = "r" + a; + } else { + str = "a" + a.index(); + } + str += "?" + bddToString(bdd.left(), true) + ":" + bddToString(bdd.middle(), true) + + ":" + bddToString(bdd.right(), true); + if (inner) { + str = "(" + str + ")"; + } + return str; + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/BooleanOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/BooleanOps.java new file mode 100644 index 000000000000..6268a16fc00b --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/BooleanOps.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Common; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.BooleanSubtype; + +/** + * Basic type ops for boolean type. + * + * @since 2201.8.0 + */ +public class BooleanOps implements BasicTypeOps { + @Override + public SubtypeData union(SubtypeData d1, SubtypeData d2) { + BooleanSubtype v1 = (BooleanSubtype) d1; + BooleanSubtype v2 = (BooleanSubtype) d2; + return v1.value == v2.value ? v1 : AllOrNothingSubtype.createAll(); + } + + @Override + public SubtypeData intersect(SubtypeData d1, SubtypeData d2) { + BooleanSubtype v1 = (BooleanSubtype) d1; + BooleanSubtype v2 = (BooleanSubtype) d2; + return v1.value == v2.value ? v1 : AllOrNothingSubtype.createNothing(); + } + + @Override + public SubtypeData diff(SubtypeData d1, SubtypeData d2) { + BooleanSubtype v1 = (BooleanSubtype) d1; + BooleanSubtype v2 = (BooleanSubtype) d2; + return v1.value == v2.value ? AllOrNothingSubtype.createNothing() : v1; + } + + @Override + public SubtypeData complement(SubtypeData d) { + BooleanSubtype v = (BooleanSubtype) d; + BooleanSubtype t = BooleanSubtype.from(!v.value); + return t; + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return Common.notIsEmpty(cx, t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/CellOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/CellOps.java new file mode 100644 index 000000000000..9a561c33ed5e --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/CellOps.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.Atom; +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Common; +import io.ballerina.types.Conjunction; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.AllOrNothingSubtype; + +import java.util.Collections; +import java.util.EnumSet; + +import static io.ballerina.types.Common.bddSubtypeComplement; +import static io.ballerina.types.Common.bddSubtypeDiff; +import static io.ballerina.types.Common.bddSubtypeIntersect; +import static io.ballerina.types.Common.bddSubtypeUnion; +import static io.ballerina.types.Core.cellAtomType; +import static io.ballerina.types.PredefinedType.ATOM_CELL_NEVER; +import static io.ballerina.types.PredefinedType.ATOM_CELL_VAL; +import static io.ballerina.types.typeops.BddCommonOps.bddAtom; + +/** + * Basic type ops for cell type. + * + * @since 2201.10.0 + */ +public class CellOps extends CommonOps implements BasicTypeOps { + + private static boolean cellFormulaIsEmpty(Context cx, SubtypeData t) { + return Common.bddEvery(cx, (Bdd) t, null, null, CellOps::cellFormulaIsEmpty); + } + + private static boolean cellFormulaIsEmpty(Context cx, Conjunction posList, Conjunction negList) { + CellAtomicType combined; + if (posList == null) { + combined = CellAtomicType.from(PredefinedType.VAL, CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + } else { + combined = cellAtomType(posList.atom); + Conjunction p = posList.next; + while (p != null) { + combined = intersectCellAtomicType(combined, cellAtomType(p.atom)); + p = p.next; + } + } + return !cellInhabited(cx, combined, negList); + } + + private static boolean cellInhabited(Context cx, CellAtomicType posCell, Conjunction negList) { + SemType pos = posCell.ty(); + if (Core.isEmpty(cx, pos)) { + return false; + } + return switch (posCell.mut()) { + case CELL_MUT_NONE -> cellMutNoneInhabited(cx, pos, negList); + case CELL_MUT_LIMITED -> cellMutLimitedInhabited(cx, pos, negList); + default -> cellMutUnlimitedInhabited(cx, pos, negList); + }; + } + + private static boolean cellMutNoneInhabited(Context cx, SemType pos, Conjunction negList) { + SemType negListUnionResult = cellNegListUnion(negList); + // We expect `isNever` condition to be `true` when there are no negative atoms. + // Otherwise, we do `isEmpty` to conclude on the inhabitance. + return Core.isNever(negListUnionResult) || !Core.isEmpty(cx, Core.diff(pos, negListUnionResult)); + } + + private static SemType cellNegListUnion(Conjunction negList) { + SemType negUnion = PredefinedType.NEVER; + Conjunction neg = negList; + while (neg != null) { + negUnion = Core.union(negUnion, cellAtomType(neg.atom).ty()); + neg = neg.next; + } + return negUnion; + } + + private static boolean cellMutLimitedInhabited(Context cx, SemType pos, Conjunction negList) { + if (negList == null) { + return true; + } + CellAtomicType negAtomicCell = cellAtomType(negList.atom); + if ((negAtomicCell.mut().compareTo(CellAtomicType.CellMutability.CELL_MUT_LIMITED) >= 0) && + Core.isEmpty(cx, Core.diff(pos, negAtomicCell.ty()))) { + return false; + } + return cellMutLimitedInhabited(cx, pos, negList.next); + } + + private static boolean cellMutUnlimitedInhabited(Context cx, SemType pos, Conjunction negList) { + Conjunction neg = negList; + while (neg != null) { + if (cellAtomType(neg.atom).mut() == CellAtomicType.CellMutability.CELL_MUT_LIMITED && + Core.isSameType(cx, PredefinedType.VAL, cellAtomType(neg.atom).ty())) { + return false; + } + neg = neg.next; + } + SemType negListUnionResult = cellNegListUnlimitedUnion(negList); + // We expect `isNever` condition to be `true` when there are no negative atoms with unlimited mutability. + // Otherwise, we do `isEmpty` to conclude on the inhabitance. + return Core.isNever(negListUnionResult) || !Core.isEmpty(cx, Core.diff(pos, negListUnionResult)); + } + + private static SemType cellNegListUnlimitedUnion(Conjunction negList) { + SemType negUnion = PredefinedType.NEVER; + Conjunction neg = negList; + while (neg != null) { + if (cellAtomType(neg.atom).mut() == CellAtomicType.CellMutability.CELL_MUT_UNLIMITED) { + negUnion = Core.union(negUnion, cellAtomType(neg.atom).ty()); + } + neg = neg.next; + } + return negUnion; + } + + public static CellAtomicType intersectCellAtomicType(CellAtomicType c1, CellAtomicType c2) { + SemType ty = Core.intersect(c1.ty(), c2.ty()); + CellAtomicType.CellMutability mut = Collections.min(EnumSet.of(c1.mut(), c2.mut())); + return CellAtomicType.from(ty, mut); + } + + private static ProperSubtypeData cellSubtypeUnion(SubtypeData t1, SubtypeData t2) { + return cellSubtypeDataEnsureProper(bddSubtypeUnion(t1, t2)); + } + + private static ProperSubtypeData cellSubtypeIntersect(SubtypeData t1, SubtypeData t2) { + return cellSubtypeDataEnsureProper(bddSubtypeIntersect(t1, t2)); + } + + private static ProperSubtypeData cellSubtypeDiff(SubtypeData t1, SubtypeData t2) { + return cellSubtypeDataEnsureProper(bddSubtypeDiff(t1, t2)); + } + + private static ProperSubtypeData cellSubtypeComplement(SubtypeData t) { + return cellSubtypeDataEnsureProper(bddSubtypeComplement(t)); + } + + /** + * SubtypeData being a boolean would result in a BasicTypeBitSet which could either be 0 or 1 << BT_CELL. + * This is to avoid cell type being a BasicTypeBitSet, as we don't want to lose the cell wrapper. + */ + private static ProperSubtypeData cellSubtypeDataEnsureProper(SubtypeData subtypeData) { + if (subtypeData instanceof AllOrNothingSubtype allOrNothingSubtype) { + Atom atom; + if (allOrNothingSubtype.isAllSubtype()) { + atom = ATOM_CELL_VAL; + } else { + atom = ATOM_CELL_NEVER; + } + return bddAtom(atom); + } else { + return (ProperSubtypeData) subtypeData; + } + } + + @Override + public SubtypeData union(SubtypeData t1, SubtypeData t2) { + return cellSubtypeUnion(t1, t2); + } + + @Override + public SubtypeData intersect(SubtypeData t1, SubtypeData t2) { + return cellSubtypeIntersect(t1, t2); + } + + @Override + public SubtypeData diff(SubtypeData t1, SubtypeData t2) { + return cellSubtypeDiff(t1, t2); + } + + @Override + public SubtypeData complement(SubtypeData t) { + return cellSubtypeComplement(t); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return cellFormulaIsEmpty(cx, t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/typeops/CommonOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/CommonOps.java similarity index 57% rename from semtypes/src/main/java/io/ballerina/semtype/typeops/CommonOps.java rename to semtypes/src/main/java/io/ballerina/types/typeops/CommonOps.java index 328686d429ef..30254d0a12ab 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/typeops/CommonOps.java +++ b/semtypes/src/main/java/io/ballerina/types/typeops/CommonOps.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,38 +11,39 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype.typeops; +package io.ballerina.types.typeops; -import io.ballerina.semtype.CommonUniformTypeOps; -import io.ballerina.semtype.SubtypeData; +import io.ballerina.types.Bdd; +import io.ballerina.types.CommonBasicTypeOps; +import io.ballerina.types.SubtypeData; /** * Common methods operate on SubtypeData. * - * @since 2.0.0 + * @since 2201.8.0 */ -public abstract class CommonOps implements CommonUniformTypeOps { +public abstract class CommonOps implements CommonBasicTypeOps { @Override public SubtypeData union(SubtypeData t1, SubtypeData t2) { - throw new AssertionError(); + return BddCommonOps.bddUnion((Bdd) t1, (Bdd) t2); } @Override public SubtypeData intersect(SubtypeData t1, SubtypeData t2) { - throw new AssertionError(); + return BddCommonOps.bddIntersect((Bdd) t1, (Bdd) t2); } @Override public SubtypeData diff(SubtypeData t1, SubtypeData t2) { - throw new AssertionError(); + return BddCommonOps.bddDiff((Bdd) t1, (Bdd) t2); } @Override public SubtypeData complement(SubtypeData t) { - throw new AssertionError(); + return BddCommonOps.bddComplement((Bdd) t); } } diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/DecimalOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/DecimalOps.java new file mode 100644 index 000000000000..9e5fb7ff7fe4 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/DecimalOps.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Common; +import io.ballerina.types.Context; +import io.ballerina.types.EnumerableDecimal; +import io.ballerina.types.EnumerableSubtype; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.DecimalSubtype; + +import java.util.ArrayList; + +/** + * Decimal specific methods operate on SubtypeData. + * + * @since 2201.8.0 + */ +public class DecimalOps extends CommonOps implements BasicTypeOps { + @Override + public SubtypeData union(SubtypeData t1, SubtypeData t2) { + ArrayList values = new ArrayList<>(); + boolean allowed = EnumerableSubtype.enumerableSubtypeUnion((DecimalSubtype) t1, (DecimalSubtype) t2, values); + EnumerableDecimal[] valueArray = new EnumerableDecimal[values.size()]; + return DecimalSubtype.createDecimalSubtype(allowed, values.toArray(valueArray)); + } + + @Override + public SubtypeData intersect(SubtypeData t1, SubtypeData t2) { + ArrayList values = new ArrayList<>(); + boolean allowed = EnumerableSubtype.enumerableSubtypeIntersect((DecimalSubtype) t1, + (DecimalSubtype) t2, values); + return DecimalSubtype.createDecimalSubtype(allowed, values.toArray(new EnumerableDecimal[]{})); + } + + @Override + public SubtypeData diff(SubtypeData t1, SubtypeData t2) { + return intersect(t1, complement(t2)); + } + + @Override + public SubtypeData complement(SubtypeData t) { + DecimalSubtype s = (DecimalSubtype) t; + return DecimalSubtype.createDecimalSubtype(!s.allowed, (EnumerableDecimal[]) s.values); + } + + @Override + public boolean isEmpty(Context tc, SubtypeData t) { + return Common.notIsEmpty(tc, t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/ErrorOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/ErrorOps.java new file mode 100644 index 000000000000..654d36988c50 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/ErrorOps.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; + +import static io.ballerina.types.Common.bddEveryPositive; +import static io.ballerina.types.Common.bddPosMaybeEmpty; +import static io.ballerina.types.Common.bddSubtypeDiff; +import static io.ballerina.types.Common.memoSubtypeIsEmpty; +import static io.ballerina.types.PredefinedType.BDD_SUBTYPE_RO; +import static io.ballerina.types.typeops.BddCommonOps.bddIntersect; + +/** + * Basic type ops for error type. + * + * @since 2201.8.0 + */ +public class ErrorOps extends CommonOps implements BasicTypeOps { + + private static SubtypeData errorSubtypeComplement(SubtypeData t) { + return bddSubtypeDiff(BDD_SUBTYPE_RO, t); + } + + private static boolean errorSubtypeIsEmpty(Context cx, SubtypeData t) { + Bdd b = (Bdd) t; + // The goal of this is to ensure that mappingFormulaIsEmpty call in errorBddIsEmpty beneath + // does not get an empty posList, because it will interpret that + // as `map` rather than `readonly & map`. + b = bddPosMaybeEmpty(b) ? bddIntersect(b, BDD_SUBTYPE_RO) : b; + return memoSubtypeIsEmpty(cx, cx.mappingMemo, ErrorOps::errorBddIsEmpty, b); + } + + private static boolean errorBddIsEmpty(Context cx, Bdd b) { + return bddEveryPositive(cx, b, null, null, MappingOps::mappingFormulaIsEmpty); + } + + @Override + public SubtypeData complement(SubtypeData d) { + return errorSubtypeComplement(d); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return errorSubtypeIsEmpty(cx, t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/FieldPair.java b/semtypes/src/main/java/io/ballerina/types/typeops/FieldPair.java new file mode 100644 index 000000000000..7f93864922e2 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/FieldPair.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.CellSemType; + +/** + * Represent the FieldPair record. + * + * @param name name of the field + * @param type1 type of the field in the first mapping + * @param type2 type of the field in the second mapping + * @param index1 index of the field in the first mapping + * @param index2 index of the field in the second mapping + * @since 2201.10.0 + */ +public record FieldPair(String name, CellSemType type1, CellSemType type2, Integer index1, Integer index2) { + + public static FieldPair create(String name, CellSemType type1, CellSemType type2, Integer index1, + Integer index2) { + return new FieldPair(name, type1, type2, index1, index2); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/FieldPairs.java b/semtypes/src/main/java/io/ballerina/types/typeops/FieldPairs.java new file mode 100644 index 000000000000..bd85a0f99082 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/FieldPairs.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.MappingAtomicType; + +import java.util.Iterator; + +/** + * Ballerina iterator is similar to an iterable in Java. + * This class implements the iterable for `MappingPairing` + * + * @since 2201.8.0 + */ +public class FieldPairs implements Iterable { + + MappingAtomicType m1; + MappingAtomicType m2; + + public FieldPairs(MappingAtomicType m1, MappingAtomicType m2) { + this.m1 = m1; + this.m2 = m2; + } + + @Override + public Iterator iterator() { + return new MappingPairIterator(m1, m2); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/FloatOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/FloatOps.java new file mode 100644 index 000000000000..1264bbcf9671 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/FloatOps.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Common; +import io.ballerina.types.Context; +import io.ballerina.types.EnumerableFloat; +import io.ballerina.types.EnumerableSubtype; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.FloatSubtype; + +import java.util.ArrayList; + +/** + * Float specific methods operate on SubtypeData. + * + * @since 2201.8.0 + */ +public class FloatOps extends CommonOps implements BasicTypeOps { + @Override + public SubtypeData union(SubtypeData t1, SubtypeData t2) { + ArrayList values = new ArrayList<>(); + boolean allowed = EnumerableSubtype.enumerableSubtypeUnion((FloatSubtype) t1, (FloatSubtype) t2, values); + EnumerableFloat[] valueArray = new EnumerableFloat[values.size()]; + return FloatSubtype.createFloatSubtype(allowed, values.toArray(valueArray)); + } + + @Override + public SubtypeData intersect(SubtypeData t1, SubtypeData t2) { + ArrayList values = new ArrayList<>(); + boolean allowed = EnumerableSubtype.enumerableSubtypeIntersect((FloatSubtype) t1, (FloatSubtype) t2, values); + return FloatSubtype.createFloatSubtype(allowed, values.toArray(new EnumerableFloat[]{})); + } + + @Override + public SubtypeData diff(SubtypeData t1, SubtypeData t2) { + return intersect(t1, complement(t2)); + } + + @Override + public SubtypeData complement(SubtypeData t) { + FloatSubtype s = (FloatSubtype) t; + return FloatSubtype.createFloatSubtype(!s.allowed, s.values); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return Common.notIsEmpty(cx, t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/FunctionOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/FunctionOps.java new file mode 100644 index 000000000000..11ce0296a305 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/FunctionOps.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.Common; +import io.ballerina.types.Conjunction; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.FunctionAtomicType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; + +import java.io.PrintStream; + +import static io.ballerina.types.Common.memoSubtypeIsEmpty; + +/** + * Function specific methods operate on SubtypeData. + * + * @since 2201.8.0 + */ +public class FunctionOps extends CommonOps implements BasicTypeOps { + + private static final PrintStream console = System.out; + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return memoSubtypeIsEmpty(cx, cx.functionMemo, + (context, bdd) -> Common.bddEvery(context, bdd, null, null, FunctionOps::functionFormulaIsEmpty), + (Bdd) t); + } + + private static boolean functionFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { + return functionPathIsEmpty(cx, functionIntersectRet(cx, pos), functionUnionParams(cx, pos), + functionUnionQualifiers(cx, pos), pos, neg); + } + + private static boolean functionPathIsEmpty(Context cx, SemType rets, SemType params, SemType qualifiers, + Conjunction pos, Conjunction neg) { + if (neg == null) { + return false; + } else { + FunctionAtomicType t = cx.functionAtomType(neg.atom); + SemType t0 = t.paramType(); + SemType t1 = t.retType(); + SemType t2 = t.qualifiers(); + if (t.isGeneric()) { + // TODO: this is not correct. Ideally we should either resolve generic function calls to concrete + // function or properly support generics. However in order to support former we need some sort of + // monomorphization (at least for types) since generics can contain calls to other generics, and + // in order to do latter we need to define generics properly in spec. Untill that we are going to use + // a hack just to make sure internal libraries type checks passes + return (Core.isSubtype(cx, qualifiers, t2) && Core.isSubtype(cx, params, t0) && + Core.isSubtype(cx, rets, t1)) + || functionPathIsEmpty(cx, rets, params, qualifiers, pos, neg.next); + } + return (Core.isSubtype(cx, qualifiers, t2) && Core.isSubtype(cx, t0, params) && + functionPhi(cx, t0, Core.complement(t1), pos)) + || functionPathIsEmpty(cx, rets, params, qualifiers, pos, neg.next); + } + } + + private static boolean functionPhi(Context cx, SemType t0, SemType t1, Conjunction pos) { + if (pos == null) { + // t0 is NEVER only for function top types with qualifiers + return !Core.isNever(t0) && (Core.isEmpty(cx, t0) || Core.isEmpty(cx, t1)); + } + return functionPhiInner(cx, t0, t1, pos); + } + + private static boolean functionPhiInner(Context cx, SemType t0, SemType t1, Conjunction pos) { + if (pos == null) { + return Core.isEmpty(cx, t0) || Core.isEmpty(cx, t1); + } else { + FunctionAtomicType s = cx.functionAtomType(pos.atom); + SemType s0 = s.paramType(); + SemType s1 = s.retType(); + return (Core.isSubtype(cx, t0, s0) + || Core.isSubtype(cx, functionIntersectRet(cx, pos.next), Core.complement(t1))) + && functionPhiInner(cx, t0, Core.intersect(t1, s1), pos.next) + && functionPhiInner(cx, Core.diff(t0, s0), t1, pos.next); + } + } + + + private static SemType functionUnionParams(Context cx, Conjunction pos) { + if (pos == null) { + return PredefinedType.NEVER; + } + return Core.union(cx.functionAtomType(pos.atom).paramType(), functionUnionParams(cx, pos.next)); + } + + private static SemType functionUnionQualifiers(Context cx, Conjunction pos) { + if (pos == null) { + return PredefinedType.NEVER; + } + return Core.union(cx.functionAtomType(pos.atom).qualifiers(), functionUnionQualifiers(cx, pos.next)); + } + + + private static SemType functionIntersectRet(Context cx, Conjunction pos) { + if (pos == null) { + return PredefinedType.VAL; + } + return Core.intersect(cx.functionAtomType(pos.atom).retType(), functionIntersectRet(cx, pos.next)); + } + + private boolean functionTheta(Context cx, SemType t0, SemType t1, Conjunction pos) { + if (pos == null) { + return Core.isEmpty(cx, t0) || Core.isEmpty(cx, t1); + } else { + // replaces the SemType[2] [s0, s1] in nballerina where s0 = paramType, s1 = retType + FunctionAtomicType s = cx.functionAtomType(pos.atom); + SemType s0 = s.paramType(); + SemType s1 = s.retType(); + return (Core.isSubtype(cx, t0, s0) || functionTheta(cx, Core.diff(s0, t0), s1, pos.next)) + && (Core.isSubtype(cx, t1, Core.complement(s1)) + || functionTheta(cx, s0, Core.intersect(s1, t1), pos.next)); + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/FutureOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/FutureOps.java new file mode 100644 index 000000000000..ad4114f41706 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/FutureOps.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; + +import static io.ballerina.types.typeops.MappingOps.mappingSubtypeIsEmpty; + +/** + * Basic type ops for future type. + * + * @since 2201.10.0 + */ +public class FutureOps extends CommonOps implements BasicTypeOps { + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return mappingSubtypeIsEmpty(cx, t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/IntOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/IntOps.java new file mode 100644 index 000000000000..accaf8640732 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/IntOps.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Common; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.Range; +import io.ballerina.types.subtypedata.RangeUnion; + +import java.util.ArrayList; +import java.util.List; + +import static java.lang.Long.MAX_VALUE; +import static java.lang.Long.MIN_VALUE; + +/** + * Basic subtype ops for int type. + * + * @since 2201.8.0 + */ +public class IntOps implements BasicTypeOps { + + private static IntOps intOpsInstance = new IntOps(); + + @Override + public SubtypeData union(SubtypeData d1, SubtypeData d2) { + IntSubtype v1 = (IntSubtype) d1; + IntSubtype v2 = (IntSubtype) d2; + Range[] v = rangeListUnion(v1.ranges, v2.ranges); + if (v.length == 1 && v[0].min == MIN_VALUE && v[0].max == MAX_VALUE) { + return AllOrNothingSubtype.createAll(); + } + return IntSubtype.createIntSubtype(v); + } + + @Override + public SubtypeData intersect(SubtypeData d1, SubtypeData d2) { + IntSubtype v1 = (IntSubtype) d1; + IntSubtype v2 = (IntSubtype) d2; + Range[] v = rangeListIntersect(v1.ranges, v2.ranges); + if (v.length == 0) { + return AllOrNothingSubtype.createNothing(); + } + return IntSubtype.createIntSubtype(v); + } + + @Override + public SubtypeData diff(SubtypeData d1, SubtypeData d2) { + IntSubtype v1 = (IntSubtype) d1; + IntSubtype v2 = (IntSubtype) d2; + Range[] v = rangeListIntersect(v1.ranges, rangeListComplement(v2.ranges)); + if (v.length == 0) { + return AllOrNothingSubtype.createNothing(); + } + return IntSubtype.createIntSubtype(v); + } + + @Override + public SubtypeData complement(SubtypeData d) { + IntSubtype v = (IntSubtype) d; + return IntSubtype.createIntSubtype(rangeListComplement(v.ranges)); + } + + static boolean intSubtypeOverlapRange(IntSubtype subtype, Range range) { + SubtypeData subtypeData = intOpsInstance.intersect(subtype, IntSubtype.createIntSubtype(range)); + return !(subtypeData instanceof AllOrNothingSubtype allOrNothingSubtype && + allOrNothingSubtype.isNothingSubtype()); + } + + public static long intSubtypeMax(IntSubtype subtype) { + return subtype.ranges[subtype.ranges.length - 1].max; + } + + public static long intSubtypeMin(IntSubtype subtype) { + return subtype.ranges[0].min; + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return Common.notIsEmpty(cx, t); + } + + private Range[] rangeListUnion(Range[] v1, Range[] v2) { + List result = new ArrayList<>(); + int i1 = 0; + int i2 = 0; + int len1 = v1.length; + int len2 = v2.length; + + while (true) { + if (i1 >= len1) { + if (i2 >= len2) { + break; + } + rangeUnionPush(result, v2[i2]); + i2 += 1; + } else if (i2 >= len2) { + rangeUnionPush(result, v1[i1]); + i1 += 1; + } else { + Range r1 = v1[i1]; + Range r2 = v2[i2]; + RangeUnion combined = rangeUnion(r1, r2); + if (combined.status == 0) { + rangeUnionPush(result, combined.range); + i1 += 1; + i2 += 1; + } else if (combined.status < 0) { + rangeUnionPush(result, r1); + i1 += 1; + } else { + rangeUnionPush(result, r2); + i2 += 1; + } + } + } + Range[] rangeList = new Range[result.size()]; + return result.toArray(rangeList); + } + + private void rangeUnionPush(List ranges, Range next) { + int lastIndex = ranges.size() - 1; + if (lastIndex < 0) { + ranges.add(next); + return; + } + RangeUnion combined = rangeUnion(ranges.get(lastIndex), next); + if (combined.status == 0) { + ranges.set(lastIndex, combined.range); + } else { + ranges.add(next); + } + } + /* [from nballerina] Returns a range if there is a single range representing the union of r1 and r1. + -1 means union is empty because r1 is before r2, with no overlap + 1 means union is empty because r2 is before r1 with no overlap + Precondition r1 and r2 are non-empty */ + private RangeUnion rangeUnion(Range r1, Range r2) { + if (r1.max < r2.min) { + if (r1.max + 1 != r2.min) { + return RangeUnion.from(-1); + } + } + if (r2.max < r1.min) { + if (r2.max + 1 != r1.min) { + return RangeUnion.from(1); + } + } + return RangeUnion.from(new Range(Long.min(r1.min, r2.min), Long.max(r1.max, r2.max))); + } + + private Range[] rangeListIntersect(Range[] v1, Range[] v2) { + List result = new ArrayList<>(); + int i1 = 0; + int i2 = 0; + int len1 = v1.length; + int len2 = v2.length; + while (true) { + if (i1 >= len1 || i2 >= len2) { + break; + } else { + Range r1 = v1[i1]; + Range r2 = v2[i2]; + RangeUnion combined = rangeIntersect(r1, r2); + if (combined.status == 0) { + result.add(combined.range); + i1 += 1; + i2 += 1; + } else if (combined.status < 0) { + i1 += 1; + } else { + i2 += 1; + } + } + } + Range[] rangeList = new Range[result.size()]; + return result.toArray(rangeList); + } + + /* [from nballerina] When Range is returned, it is non-empty and the intersection of r1 and r2 + -1 means empty intersection because r1 before r2 + 1 means empty intersection because r1 after r2 */ + private RangeUnion rangeIntersect(Range r1, Range r2) { + if (r1.max < r2.min) { + return RangeUnion.from(-1); + } + if (r2.max < r1.min) { + return RangeUnion.from(1); + } + return RangeUnion.from(new Range(Long.max(r1.min, r2.min), Long.min(r1.max, r2.max))); + } + + private Range[] rangeListComplement(Range[] v) { + List result = new ArrayList<>(); + int len = v.length; + long min = v[0].min; + if (min > MIN_VALUE) { + result.add(new Range(MIN_VALUE, min - 1)); + } + for (int i = 1; i < len; i++) { + result.add(new Range(v[i - 1].max + 1, v[i].min - 1)); + } + long max = v[v.length - 1].max; + if (max < MAX_VALUE) { + result.add(new Range(max + 1, MAX_VALUE)); + } + return result.toArray(new Range[]{}); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/ListOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/ListOps.java new file mode 100644 index 000000000000..406bd48e91e3 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/ListOps.java @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.Atom; +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.CellSemType; +import io.ballerina.types.Common; +import io.ballerina.types.Conjunction; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.FixedLengthArray; +import io.ballerina.types.ListAtomicType; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.Range; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.IntStream; + +import static io.ballerina.types.Common.bddSubtypeComplement; +import static io.ballerina.types.Common.bddSubtypeDiff; +import static io.ballerina.types.Common.bddSubtypeIntersect; +import static io.ballerina.types.Common.bddSubtypeUnion; +import static io.ballerina.types.Common.memoSubtypeIsEmpty; +import static io.ballerina.types.Core.cellContainingInnerVal; +import static io.ballerina.types.Core.cellInner; +import static io.ballerina.types.Core.cellInnerVal; +import static io.ballerina.types.Core.intersectMemberSemTypes; +import static io.ballerina.types.PredefinedType.LIST_ATOMIC_INNER; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.UNDEF; +import static io.ballerina.types.subtypedata.IntSubtype.intSubtypeContains; +import static io.ballerina.types.typeops.IntOps.intSubtypeMax; +import static io.ballerina.types.typeops.IntOps.intSubtypeOverlapRange; + +/** + * Basic type ops for list type. + * + * @since 2201.8.0 + */ +public class ListOps extends CommonOps implements BasicTypeOps { + + static boolean listSubtypeIsEmpty(Context cx, SubtypeData t) { + return memoSubtypeIsEmpty(cx, cx.listMemo, + (context, bdd) -> Common.bddEvery(context, bdd, null, null, ListOps::listFormulaIsEmpty), + (Bdd) t); + } + + private static boolean listFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { + FixedLengthArray members; + CellSemType rest; + if (pos == null) { + ListAtomicType atom = LIST_ATOMIC_INNER; + members = atom.members(); + rest = atom.rest(); + } else { + // combine all the positive tuples using intersection + ListAtomicType lt = cx.listAtomType(pos.atom); + members = lt.members(); + rest = lt.rest(); + Conjunction p = pos.next; + // the neg case is in case we grow the array in listInhabited + if (p != null || neg != null) { + // Jbal note: we don't need this as we already created copies when converting from array to list. + // Just keeping this for the sake of source similarity between Bal code and Java. + members = fixedArrayShallowCopy(members); + } + while (true) { + if (p == null) { + break; + } else { + Atom d = p.atom; + p = p.next; + lt = cx.listAtomType(d); + TwoTuple + intersected = listIntersectWith(cx.env, members, rest, lt.members(), lt.rest()); + if (intersected == null) { + return true; + } + members = intersected.item1; + rest = intersected.item2; + } + } + if (fixedArrayAnyEmpty(cx, members)) { + return true; + } + } + List indices = listSamples(cx, members, rest, neg); + TwoTuple, Integer> sampleTypes = listSampleTypes(cx, members, rest, indices); + return !listInhabited(cx, indices.toArray(new Integer[0]), + sampleTypes.item1.toArray(SemType[]::new), + sampleTypes.item2, neg); + } + + public static TwoTuple, Integer> listSampleTypes(Context cx, FixedLengthArray members, + CellSemType rest, List indices) { + List memberTypes = new ArrayList<>(); + int nRequired = 0; + for (int i = 0; i < indices.size(); i++) { + int index = indices.get(i); + CellSemType t = cellContainingInnerVal(cx.env, listMemberAt(members, rest, index)); + if (Core.isEmpty(cx, t)) { + break; + } + memberTypes.add(t); + if (index < members.fixedLength()) { + nRequired = i + 1; + } + } + return TwoTuple.from(memberTypes, nRequired); + } + + // Return a list of sample indices for use as second argument of `listInhabited`. + // The positive list type P is represented by `members` and `rest`. + // The negative list types N are represented by `neg` + // The `indices` list (first member of return value) is constructed in two stages. + // First, the set of all non-negative integers is partitioned so that two integers are + // in different partitions if they get different types as an index in P or N. + // Second, we choose a number of samples from each partition. It doesn't matter + // which sample we choose, but (this is the key point) we need at least as many samples + // as there are negatives in N, so that for each negative we can freely choose a type for the sample + // to avoid being matched by that negative. + static List listSamples(Context cx, FixedLengthArray members, SemType rest, Conjunction neg) { + int maxInitialLength = members.initial().size(); + List fixedLengths = new ArrayList<>(); + fixedLengths.add(members.fixedLength()); + Conjunction tem = neg; + int nNeg = 0; + while (true) { + if (tem != null) { + ListAtomicType lt = cx.listAtomType(tem.atom); + FixedLengthArray m = lt.members(); + maxInitialLength = Integer.max(maxInitialLength, m.initial().size()); + if (m.fixedLength() > maxInitialLength) { + fixedLengths.add(m.fixedLength()); + } + nNeg += 1; + tem = tem.next; + } else { + break; + } + } + Collections.sort(fixedLengths); + // `boundaries` partitions the non-negative integers + // Construct `boundaries` from `fixedLengths` and `maxInitialLength` + // An index b is a boundary point if indices < b are different from indices >= b + //int[] boundaries = from int i in 1 ... maxInitialLength select i; + List boundaries = new ArrayList<>(); + for (int i = 1; i <= maxInitialLength; i++) { + boundaries.add(i); + } + for (int n : fixedLengths) { + // this also removes duplicates + if (boundaries.size() == 0 || n > boundaries.get(boundaries.size() - 1)) { + boundaries.add(n); + } + } + // Now construct the list of indices by taking nNeg samples from each partition. + List indices = new ArrayList<>(); + int lastBoundary = 0; + if (nNeg == 0) { + // this is needed for when this is used in listProj + nNeg = 1; + } + for (int b : boundaries) { + int segmentLength = b - lastBoundary; + // Cannot have more samples than are in the parition. + int nSamples = Integer.min(segmentLength, nNeg); + for (int i = b - nSamples; i < b; i++) { + indices.add(i); + } + lastBoundary = b; + } + for (int i = 0; i < nNeg; i++) { + // Be careful to avoid integer overflow. + if (lastBoundary > Integer.MAX_VALUE - i) { + break; + } + indices.add(lastBoundary + i); + } + return indices; + } + + static TwoTuple listIntersectWith(Env env, FixedLengthArray members1, + CellSemType rest1, FixedLengthArray members2, + CellSemType rest2) { + if (listLengthsDisjoint(members1, rest1, members2, rest2)) { + return null; + } + // This is different from nBallerina, but I think assuming we have normalized the FixedLengthArrays we must + // consider fixedLengths not the size of initial members. For example consider any[4] and + // [int, string, float...]. If we don't consider the fixedLength in the initial part we'll consider only the + // first two elements and rest will compare essentially 5th element, meaning we are ignoring 3 and 4 elements + int max = Integer.max(members1.fixedLength(), members2.fixedLength()); + List initial = + IntStream.range(0, max) + .mapToObj(i -> intersectMemberSemTypes(env, listMemberAt(members1, rest1, i), + listMemberAt(members2, rest2, i))) + .toList(); + return TwoTuple.from(FixedLengthArray.from(initial, + Integer.max(members1.fixedLength(), members2.fixedLength())), + intersectMemberSemTypes(env, rest1, rest2)); + } + + static FixedLengthArray fixedArrayShallowCopy(FixedLengthArray array) { + return FixedLengthArray.from(array.initial(), array.fixedLength()); + } + + // This function determines whether a list type P & N is inhabited. + // where P is a positive list type and N is a list of negative list types. + // With just straightforward fixed-length tuples we can consider every index of the tuple. + // But we cannot do this in general because of rest types and fixed length array types e.g. `byte[10000000]`. + // So we consider instead a collection of indices that is sufficient for us to determine inhabitation, + // given the types of P and N. + // `indices` is this list of sample indices: these are indicies into members of the list type. + // We don't represent P directly. Instead P is represented by `memberTypes` and `nRequired`: + // `memberTypes[i]` is the type that P gives to `indices[i]`; + // `nRequired` is the number of members of `memberTypes` that are required by P. + // `neg` represents N. + static boolean listInhabited(Context cx, Integer[] indices, SemType[] memberTypes, int nRequired, Conjunction neg) { + // TODO: Way `listInhabited` method works is essentially removing negative atoms one by one until either we + // run-out of negative atoms or there is nothing left in the positive (intersection) atom. While correctness + // of this method is independent of the order of negative atoms, the performance of this method is dependent + // on the order. For example consider positive atom is int[] and we have negative atoms int[1], int[2], + // int[3], any[]. If we start with any[] we immediately know it is not inhabited whereas in any other order we + // have to evaluate until we come to any[] to figure this out. We say any[] is larger than others since it + // covers more values than them. According to Frisch section 7.3.1 we should use this size as an heuristic to + // sort negative atoms. However it is not clear to me how to estimate the size correctly + if (neg == null) { + return true; + } else { + final ListAtomicType nt = cx.listAtomType(neg.atom); + if (nRequired > 0 && Core.isNever(listMemberAtInnerVal(nt.members(), nt.rest(), indices[nRequired - 1]))) { + // Skip this negative if it is always shorter than the minimum required by the positive + return listInhabited(cx, indices, memberTypes, nRequired, neg.next); + } + int negLen = nt.members().fixedLength(); + if (negLen > 0) { + // If we have isEmpty(T1 & S1) or isEmpty(T2 & S2) then we have [T1, T2] / [S1, S2] = [T1, T2]. + // Therefore, we can skip the negative + for (int i = 0; i < memberTypes.length; i++) { + int index = indices[i]; + if (index >= negLen) { + break; + } + SemType negMemberType = listMemberAt(nt.members(), nt.rest(), index); + SemType common = Core.intersect(memberTypes[i], negMemberType); + if (Core.isEmpty(cx, common)) { + return listInhabited(cx, indices, memberTypes, nRequired, neg.next); + } + } + // Consider cases we can avoid this negative by having a sufficiently short list + int len = memberTypes.length; + if (len < indices.length && indices[len] < negLen) { + return listInhabited(cx, indices, memberTypes, nRequired, neg.next); + } + for (int i = nRequired; i < memberTypes.length; i++) { + if (indices[i] >= negLen) { + break; + } + // TODO: avoid creating new arrays here + SemType[] t = Arrays.copyOfRange(memberTypes, 0, i); + if (listInhabited(cx, indices, t, nRequired, neg.next)) { + return true; + } + } + } + // Now we need to explore the possibility of shapes with length >= neglen + // This is the heart of the algorithm. + // For [v0, v1] not to be in [t0,t1], there are two possibilities + // (1) v0 is not in t0, or + // (2) v1 is not in t1 + // Case (1) + // For v0 to be in s0 but not t0, d0 must not be empty. + // We must then find a [v0,v1] satisfying the remaining negated tuples, + // such that v0 is in d0. + // SemType d0 = diff(s[0], t[0]); + // if !isEmpty(cx, d0) && tupleInhabited(cx, [d0, s[1]], neg.rest) { + // return true; + // } + // Case (2) + // For v1 to be in s1 but not t1, d1 must not be empty. + // We must then find a [v0,v1] satisfying the remaining negated tuples, + // such that v1 is in d1. + // SemType d1 = diff(s[1], t[1]); + // return !isEmpty(cx, d1) && tupleInhabited(cx, [s[0], d1], neg.rest); + // We can generalize this to tuples of arbitrary length. + for (int i = 0; i < memberTypes.length; i++) { + SemType d = Core.diff(memberTypes[i], listMemberAt(nt.members(), nt.rest(), indices[i])); + if (!Core.isEmpty(cx, d)) { + SemType[] t = memberTypes.clone(); + t[i] = d; + // We need to make index i be required + if (listInhabited(cx, indices, t, Integer.max(nRequired, i + 1), neg.next)) { + return true; + } + } + } + // This is correct for length 0, because we know that the length of the + // negative is 0, and [] - [] is empty. + return false; + } + } + + static SemType listMemberAtInnerVal(FixedLengthArray fixedArray, CellSemType rest, int index) { + return cellInnerVal(listMemberAt(fixedArray, rest, index)); + } + + private static boolean listLengthsDisjoint(FixedLengthArray members1, CellSemType rest1, + FixedLengthArray members2, CellSemType rest2) { + int len1 = members1.fixedLength(); + int len2 = members2.fixedLength(); + if (len1 < len2) { + return Core.isNever(cellInnerVal(rest1)); + } + if (len2 < len1) { + return Core.isNever(cellInnerVal(rest2)); + } + return false; + } + + static CellSemType listMemberAt(FixedLengthArray fixedArray, CellSemType rest, int index) { + if (index < fixedArray.fixedLength()) { + return fixedArrayGet(fixedArray, index); + } + return rest; + } + + static boolean fixedArrayAnyEmpty(Context cx, FixedLengthArray array) { + for (var t : array.initial()) { + if (Core.isEmpty(cx, t)) { + return true; + } + } + return false; + } + + private static CellSemType fixedArrayGet(FixedLengthArray members, int index) { + int memberLen = members.initial().size(); + int i = Integer.min(index, memberLen - 1); + return members.initial().get(i); + } + + static SemType listAtomicMemberTypeInnerVal(ListAtomicType atomic, SubtypeData key) { + return Core.diff(listAtomicMemberTypeInner(atomic, key), UNDEF); + } + + private static SemType listAtomicMemberTypeInner(ListAtomicType atomic, SubtypeData key) { + return listAtomicMemberTypeAtInner(atomic.members(), atomic.rest(), key); + } + + static SemType listAtomicMemberTypeAtInner(FixedLengthArray fixedArray, CellSemType rest, SubtypeData key) { + if (key instanceof IntSubtype intSubtype) { + SemType m = NEVER; + int initLen = fixedArray.initial().size(); + int fixedLen = fixedArray.fixedLength(); + if (fixedLen != 0) { + for (int i = 0; i < initLen; i++) { + if (intSubtypeContains(key, i)) { + m = Core.union(m, cellInner(fixedArrayGet(fixedArray, i))); + } + } + if (intSubtypeOverlapRange(intSubtype, Range.from(initLen, fixedLen - 1))) { + m = Core.union(m, cellInner(fixedArrayGet(fixedArray, fixedLen - 1))); + } + } + if (fixedLen == 0 || intSubtypeMax((IntSubtype) key) > fixedLen - 1) { + m = Core.union(m, cellInner(rest)); + } + return m; + } + SemType m = cellInner(rest); + if (fixedArray.fixedLength() > 0) { + for (CellSemType ty : fixedArray.initial()) { + m = Core.union(m, cellInner(ty)); + } + } + return m; + } + + public static SemType bddListMemberTypeInnerVal(Context cx, Bdd b, SubtypeData key, SemType accum) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? accum : NEVER; + } else { + BddNode bddNode = (BddNode) b; + return Core.union(bddListMemberTypeInnerVal(cx, bddNode.left(), key, + Core.intersect(listAtomicMemberTypeInnerVal(cx.listAtomType(bddNode.atom()), key), accum)), + Core.union(bddListMemberTypeInnerVal(cx, bddNode.middle(), key, accum), + bddListMemberTypeInnerVal(cx, bddNode.right(), key, accum))); + } + } + + @Override + public SubtypeData union(SubtypeData d1, SubtypeData d2) { + return bddSubtypeUnion(d1, d2); + } + + @Override + public SubtypeData intersect(SubtypeData d1, SubtypeData d2) { + return bddSubtypeIntersect(d1, d2); + } + + @Override + public SubtypeData diff(SubtypeData d1, SubtypeData d2) { + return bddSubtypeDiff(d1, d2); + } + + @Override + public SubtypeData complement(SubtypeData d) { + return bddSubtypeComplement(d); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData d) { + return listSubtypeIsEmpty(cx, d); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/ListProj.java b/semtypes/src/main/java/io/ballerina/types/typeops/ListProj.java new file mode 100644 index 000000000000..379da6baeae0 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/ListProj.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.Atom; +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.Bdd; +import io.ballerina.types.CellSemType; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Conjunction; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.FixedLengthArray; +import io.ballerina.types.ListAtomicType; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.subtypedata.CellSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.Range; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +import static io.ballerina.types.BasicTypeCode.BT_LIST; +import static io.ballerina.types.Common.isNothingSubtype; +import static io.ballerina.types.Conjunction.and; +import static io.ballerina.types.Core.cellInnerVal; +import static io.ballerina.types.Core.diff; +import static io.ballerina.types.Core.getComplexSubtypeData; +import static io.ballerina.types.Core.isEmpty; +import static io.ballerina.types.Core.isNever; +import static io.ballerina.types.Core.union; +import static io.ballerina.types.PredefinedType.LIST; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.UNDEF; +import static io.ballerina.types.PredefinedType.VAL; +import static io.ballerina.types.subtypedata.CellSubtype.cellContaining; +import static io.ballerina.types.subtypedata.IntSubtype.intSubtypeContains; +import static io.ballerina.types.typeops.ListOps.fixedArrayAnyEmpty; +import static io.ballerina.types.typeops.ListOps.fixedArrayShallowCopy; +import static io.ballerina.types.typeops.ListOps.listIntersectWith; +import static io.ballerina.types.typeops.ListOps.listMemberAtInnerVal; + +/** + * Class to hold functions ported from `listProj.bal` file. + * + * @since 2201.8.0 + */ +public class ListProj { + // Untested full implementation of list projection. + + // Based on listMemberType + public static SemType listProjInnerVal(Context cx, SemType t, SemType k) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & LIST.bitset) != 0 ? VAL : NEVER; + } else { + SubtypeData keyData = Core.intSubtype(k); + if (isNothingSubtype(keyData)) { + return NEVER; + } + return listProjBddInnerVal(cx, keyData, (Bdd) getComplexSubtypeData((ComplexSemType) t, BT_LIST), null, + null); + } + } + + // Based on bddEvery + static SemType listProjBddInnerVal(Context cx, SubtypeData k, Bdd b, Conjunction pos, Conjunction neg) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? listProjPathInnerVal(cx, k, pos, neg) : NEVER; + } else { + BddNode bddNode = (BddNode) b; + return union(listProjBddInnerVal(cx, k, bddNode.left(), and(bddNode.atom(), pos), neg), + union(listProjBddInnerVal(cx, k, bddNode.middle(), pos, neg), + listProjBddInnerVal(cx, k, bddNode.right(), pos, and(bddNode.atom(), neg)))); + } + } + + // Based on listFormulaIsEmpty + static SemType listProjPathInnerVal(Context cx, SubtypeData k, Conjunction pos, Conjunction neg) { + FixedLengthArray members; + CellSemType rest; + if (pos == null) { + members = FixedLengthArray.empty(); + rest = cellContaining(cx.env, union(VAL, UNDEF)); + } else { + // combine all the positive tuples using intersection + ListAtomicType lt = cx.listAtomType(pos.atom); + members = lt.members(); + rest = lt.rest(); + Conjunction p = pos.next; + // the neg case is in case we grow the array in listInhabited + if (p != null || neg != null) { + members = fixedArrayShallowCopy(members); + } + + while (true) { + if (p == null) { + break; + } else { + Atom d = p.atom; + p = p.next; + lt = cx.listAtomType(d); + TwoTuple + intersected = listIntersectWith(cx.env, members, rest, lt.members(), lt.rest()); + if (intersected == null) { + return NEVER; + } + members = intersected.item1; + rest = intersected.item2; + } + } + if (fixedArrayAnyEmpty(cx, members)) { + return NEVER; + } + // Ensure that we can use isNever on rest in listInhabited + if (!Core.isNever(cellInnerVal(rest)) && isEmpty(cx, rest)) { + rest = CellSubtype.roCellContaining(cx.env, NEVER); + } + } + // return listProjExclude(cx, k, members, rest, listConjunction(cx, neg)); + List indices = ListOps.listSamples(cx, members, rest, neg); + TwoTuple, List> projSamples = listProjSamples(indices, k); + indices = projSamples.item1; + TwoTuple, Integer> sampleTypes = ListOps.listSampleTypes(cx, members, rest, indices); + return listProjExcludeInnerVal(cx, projSamples.item1.toArray(Integer[]::new), + projSamples.item2.toArray(Integer[]::new), + sampleTypes.item1.toArray(CellSemType[]::new), + sampleTypes.item2, neg); + } + + // In order to adapt listInhabited to do projection, we need + // to know which samples correspond to keys and to ensure that + // every equivalence class that overlaps with a key has a sample in the + // intersection. + // Here we add samples for both ends of each range. This doesn't handle the + // case where the key is properly within a partition: but that is handled + // because we already have a sample of the end of the partition. + private static TwoTuple, List> listProjSamples(List indices, SubtypeData k) { + List> v = new ArrayList<>(); + for (int i : indices) { + v.add(TwoTuple.from(i, intSubtypeContains(k, i))); + } + if (k instanceof IntSubtype intSubtype) { + for (Range range : intSubtype.ranges) { + long max = range.max; + if (range.max >= 0) { + v.add(TwoTuple.from((int) max, true)); + int min = Integer.max(0, (int) range.min); + if (min < max) { + v.add(TwoTuple.from(min, true)); + } + } + } + } + v.sort(Comparator.comparingInt(p -> p.item1)); + List indices1 = new ArrayList<>(); + List keyIndices = new ArrayList<>(); + for (var ib : v) { + if (indices1.isEmpty() || !Objects.equals(ib.item1, indices1.get(indices1.size() - 1))) { + if (ib.item2) { + keyIndices.add(indices1.size()); + } + indices1.add(ib.item1); + } + } + return TwoTuple.from(indices1, keyIndices); + } + + // `keyIndices` are the indices in `memberTypes` of those samples that belong to the key type. + // Based on listInhabited + // Corresponds to phi^x in AMK tutorial generalized for list types. + static SemType listProjExcludeInnerVal(Context cx, Integer[] indices, Integer[] keyIndices, + CellSemType[] memberTypes, int nRequired, Conjunction neg) { + SemType p = NEVER; + if (neg == null) { + int len = memberTypes.length; + for (int k : keyIndices) { + if (k < len) { + p = union(p, cellInnerVal(memberTypes[k])); + } + } + } else { + final ListAtomicType nt = cx.listAtomType(neg.atom); + if (nRequired > 0 && isNever(listMemberAtInnerVal(nt.members(), nt.rest(), indices[nRequired - 1]))) { + return listProjExcludeInnerVal(cx, indices, keyIndices, memberTypes, nRequired, neg.next); + } + int negLen = nt.members().fixedLength(); + if (negLen > 0) { + int len = memberTypes.length; + if (len < indices.length && indices[len] < negLen) { + return listProjExcludeInnerVal(cx, indices, keyIndices, memberTypes, nRequired, neg.next); + } + for (int i = nRequired; i < memberTypes.length; i++) { + if (indices[i] >= negLen) { + break; + } + // TODO: think about a way to avoid this allocation here and instead create view and pass it in + CellSemType[] t = Arrays.copyOfRange(memberTypes, 0, i); + p = union(p, listProjExcludeInnerVal(cx, indices, keyIndices, t, nRequired, neg.next)); + } + } + for (int i = 0; i < memberTypes.length; i++) { + SemType d = + diff(cellInnerVal(memberTypes[i]), listMemberAtInnerVal(nt.members(), nt.rest(), indices[i])); + if (!Core.isEmpty(cx, d)) { + CellSemType[] t = memberTypes.clone(); + t[i] = cellContaining(cx.env, d); + // We need to make index i be required + p = union(p, listProjExcludeInnerVal(cx, indices, keyIndices, t, Integer.max(nRequired, i + 1), + neg.next)); + } + } + } + return p; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/MappingOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/MappingOps.java new file mode 100644 index 000000000000..17305851e6cd --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/MappingOps.java @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.CellSemType; +import io.ballerina.types.Common; +import io.ballerina.types.Conjunction; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.MappingAtomicType; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.subtypedata.StringSubtype; + +import java.util.ArrayList; +import java.util.List; + +import static io.ballerina.types.Common.bddSubtypeComplement; +import static io.ballerina.types.Common.bddSubtypeDiff; +import static io.ballerina.types.Common.bddSubtypeIntersect; +import static io.ballerina.types.Common.bddSubtypeUnion; +import static io.ballerina.types.Common.isAllSubtype; +import static io.ballerina.types.Common.memoSubtypeIsEmpty; +import static io.ballerina.types.PredefinedType.MAPPING_ATOMIC_INNER; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.UNDEF; +import static io.ballerina.types.typeops.StringOps.stringSubtypeListCoverage; + +/** + * Basic type ops for mapping type. + * + * @since 2201.8.0 + */ +public class MappingOps extends CommonOps implements BasicTypeOps { + // This works the same as the tuple case, except that instead of + // just comparing the lengths of the tuples we compare the sorted list of field names + public static boolean mappingFormulaIsEmpty(Context cx, Conjunction posList, Conjunction negList) { + MappingAtomicType combined; + if (posList == null) { + combined = MAPPING_ATOMIC_INNER; + } else { + // combine all the positive atoms using intersection + combined = cx.mappingAtomType(posList.atom); + Conjunction p = posList.next; + while (true) { + if (p == null) { + break; + } else { + MappingAtomicType m = intersectMapping(cx.env, combined, cx.mappingAtomType(p.atom)); + if (m == null) { + return true; + } else { + combined = m; + } + p = p.next; + } + } + for (SemType t : combined.types()) { + if (Core.isEmpty(cx, t)) { + return true; + } + } + + } + return !mappingInhabited(cx, combined, negList); + } + + private static boolean mappingInhabited(Context cx, MappingAtomicType pos, Conjunction negList) { + if (negList == null) { + return true; + } else { + MappingAtomicType neg = cx.mappingAtomType(negList.atom); + + FieldPairs pairing = new FieldPairs(pos, neg); + if (!Core.isEmpty(cx, Core.diff(pos.rest(), neg.rest()))) { + return mappingInhabited(cx, pos, negList.next); + } + for (FieldPair fieldPair : pairing) { + SemType intersect = Core.intersect(fieldPair.type1(), fieldPair.type2()); + // if types of at least one field are disjoint, the neg atom will not contribute to the next iteration. + // Therefore, we can skip the current neg atom. + // i.e. if we have isEmpty(T1 & S1) or isEmpty(T2 & S2) then, + // record { T1 f1; T2 f2; } / record { S1 f1; S2 f2; } = record { T1 f1; T2 f2; } + if (Core.isEmpty(cx, intersect)) { + return mappingInhabited(cx, pos, negList.next); + } + + CellSemType d = (CellSemType) Core.diff(fieldPair.type1(), fieldPair.type2()); + if (!Core.isEmpty(cx, d)) { + MappingAtomicType mt; + if (fieldPair.index1() == null) { + // the posType came from the rest type + mt = insertField(pos, fieldPair.name(), d); + } else { + CellSemType[] posTypes = pos.types(); + posTypes[fieldPair.index1()] = d; + mt = MappingAtomicType.from(pos.names(), posTypes, pos.rest()); + } + if (mappingInhabited(cx, mt, negList.next)) { + return true; + } + } + } + return false; + } + } + + private static MappingAtomicType insertField(MappingAtomicType m, String name, CellSemType t) { + String[] orgNames = m.names(); + String[] names = Common.shallowCopyStrings(orgNames, orgNames.length + 1); + CellSemType[] orgTypes = m.types(); + CellSemType[] types = Common.shallowCopyCellTypes(orgTypes, orgTypes.length + 1); + int i = orgNames.length; + while (true) { + if (i == 0 || Common.codePointCompare(names[i - 1], name)) { + names[i] = name; + types[i] = t; + break; + } + names[i] = names[i - 1]; + types[i] = types[i - 1]; + i -= 1; + } + return MappingAtomicType.from(names, types, m.rest()); + } + + private static MappingAtomicType intersectMapping(Env env, MappingAtomicType m1, MappingAtomicType m2) { + List names = new ArrayList<>(); + List types = new ArrayList<>(); + FieldPairs pairing = new FieldPairs(m1, m2); + for (FieldPair fieldPair : pairing) { + names.add(fieldPair.name()); + CellSemType t = Core.intersectMemberSemTypes(env, fieldPair.type1(), fieldPair.type2()); + if (Core.isNever(Core.cellInner(fieldPair.type1()))) { + return null; + } + types.add(t); + } + CellSemType rest = Core.intersectMemberSemTypes(env, m1.rest(), m2.rest()); + return MappingAtomicType.from(names.toArray(new String[]{}), types.toArray(new CellSemType[]{}), rest); + } + + public static boolean mappingSubtypeIsEmpty(Context cx, SubtypeData t) { + return memoSubtypeIsEmpty(cx, cx.mappingMemo, + (context, bdd) -> Common.bddEvery(context, bdd, null, null, MappingOps::mappingFormulaIsEmpty), + (Bdd) t); + } + + public static SemType bddMappingMemberTypeInner(Context cx, Bdd b, SubtypeData key, SemType accum) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? accum : NEVER; + } else { + BddNode bdd = (BddNode) b; + return Core.union( + bddMappingMemberTypeInner(cx, bdd.left(), key, + Core.intersect(mappingAtomicMemberTypeInner(cx.mappingAtomType(bdd.atom()), key), + accum)), + Core.union(bddMappingMemberTypeInner(cx, bdd.middle(), key, accum), + bddMappingMemberTypeInner(cx, bdd.right(), key, accum))); + } + } + + static SemType mappingAtomicMemberTypeInner(MappingAtomicType atomic, SubtypeData key) { + SemType memberType = null; + for (SemType ty : mappingAtomicApplicableMemberTypesInner(atomic, key)) { + if (memberType == null) { + memberType = ty; + } else { + memberType = Core.union(memberType, ty); + } + } + return memberType == null ? UNDEF : memberType; + } + + static List mappingAtomicApplicableMemberTypesInner(MappingAtomicType atomic, SubtypeData key) { + List types = new ArrayList<>(atomic.types().length); + for (CellSemType t : atomic.types()) { + types.add(Core.cellInner(t)); + } + + List memberTypes = new ArrayList<>(); + SemType rest = Core.cellInner(atomic.rest()); + if (isAllSubtype(key)) { + memberTypes.addAll(types); + memberTypes.add(rest); + } else { + StringSubtype.StringSubtypeListCoverage coverage = stringSubtypeListCoverage((StringSubtype) key, + atomic.names()); + for (int index : coverage.indices) { + memberTypes.add(types.get(index)); + } + if (!coverage.isSubtype) { + memberTypes.add(rest); + } + } + return memberTypes; + } + + @Override + public SubtypeData union(SubtypeData d1, SubtypeData d2) { + return bddSubtypeUnion(d1, d2); + } + + @Override + public SubtypeData intersect(SubtypeData d1, SubtypeData d2) { + return bddSubtypeIntersect(d1, d2); + } + + @Override + public SubtypeData diff(SubtypeData d1, SubtypeData d2) { + return bddSubtypeDiff(d1, d2); + } + + @Override + public SubtypeData complement(SubtypeData d) { + return bddSubtypeComplement(d); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData d) { + return mappingSubtypeIsEmpty(cx, d); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/MappingPairIterator.java b/semtypes/src/main/java/io/ballerina/types/typeops/MappingPairIterator.java new file mode 100644 index 000000000000..f929daacf2d5 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/MappingPairIterator.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.CellSemType; +import io.ballerina.types.Common; +import io.ballerina.types.MappingAtomicType; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; + +/** + * Iteration implementation of `MappingPairIterator`. + * + * @since 2201.8.0 + */ +public class MappingPairIterator implements Iterator { + private final String[] names1; + private final String[] names2; + private final CellSemType[] types1; + private final CellSemType[] types2; + private final int len1; + private final int len2; + private int i1 = 0; + private int i2 = 0; + private final CellSemType rest1; + private final CellSemType rest2; + + private boolean doneIteration = false; + private boolean shouldCalculate = true; + private FieldPair cache = null; + + public MappingPairIterator(MappingAtomicType m1, MappingAtomicType m2) { + this.names1 = m1.names(); + this.len1 = this.names1.length; + this.types1 = m1.types(); + this.rest1 = m1.rest(); + this.names2 = m2.names(); + this.len2 = this.names2.length; + this.types2 = m2.types(); + this.rest2 = m2.rest(); + } + + @Override + public boolean hasNext() { + if (this.doneIteration) { + return false; + } + if (this.shouldCalculate) { + FieldPair cache = internalNext(); + if (cache == null) { + this.doneIteration = true; + } + this.cache = cache; + this.shouldCalculate = false; + } + return !this.doneIteration; + } + + @Override + public FieldPair next() { + if (this.doneIteration) { + throw new NoSuchElementException("Exhausted iterator"); + } + + if (this.shouldCalculate) { + FieldPair cache = internalNext(); + if (cache == null) { + // this.doneIteration = true; + throw new IllegalStateException(); + } + this.cache = cache; + } + this.shouldCalculate = true; + return this.cache; + } + + /* + * This method corresponds to `next` method of MappingPairing. + */ + private FieldPair internalNext() { + FieldPair p; + if (this.i1 >= this.len1) { + if (this.i2 >= this.len2) { + return null; + } + p = FieldPair.create(curName2(), this.rest1, curType2(), null, this.i2); + this.i2 += 1; + } else if (this.i2 >= this.len2) { + p = FieldPair.create(curName1(), curType1(), this.rest2, this.i1, null); + this.i1 += 1; + } else { + String name1 = curName1(); + String name2 = curName2(); + if (Common.codePointCompare(name1, name2)) { + p = FieldPair.create(name1, curType1(), this.rest2, this.i1, null); + this.i1 += 1; + } else if (Common.codePointCompare(name2, name1)) { + p = FieldPair.create(name2, this.rest1, curType2(), null, this.i2); + this.i2 += 1; + } else { + p = FieldPair.create(name1, curType1(), curType2(), this.i1, this.i2); + this.i1 += 1; + this.i2 += 1; + } + } + return p; + } + + private CellSemType curType1() { + return this.types1[this.i1]; + } + + private String curName1() { + return this.names1[this.i1]; + } + + private CellSemType curType2() { + return this.types2[this.i2]; + } + + private String curName2() { + return this.names2[this.i2]; + } + + public void reset() { + this.i1 = 0; + this.i2 = 0; + } + + public Optional index1(String name) { + int i1Prev = this.i1 - 1; + return i1Prev >= 0 && Objects.equals(this.names1[i1Prev], name) ? Optional.of(i1Prev) : Optional.empty(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/ObjectOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/ObjectOps.java new file mode 100644 index 000000000000..47518f536d1b --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/ObjectOps.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; + +import static io.ballerina.types.Common.bddEveryPositive; +import static io.ballerina.types.Common.bddSubtypeDiff; +import static io.ballerina.types.Common.memoSubtypeIsEmpty; +import static io.ballerina.types.PredefinedType.MAPPING_SUBTYPE_OBJECT; + +public final class ObjectOps extends CommonOps implements BasicTypeOps { + + @Override + public SubtypeData complement(SubtypeData t) { + return objectSubTypeComplement(t); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return objectSubTypeIsEmpty(cx, t); + } + + private static boolean objectSubTypeIsEmpty(Context cx, SubtypeData t) { + return memoSubtypeIsEmpty(cx, cx.mappingMemo, ObjectOps::objectBddIsEmpty, (Bdd) t); + } + + private static boolean objectBddIsEmpty(Context cx, Bdd b) { + return bddEveryPositive(cx, b, null, null, MappingOps::mappingFormulaIsEmpty); + } + + private SubtypeData objectSubTypeComplement(SubtypeData t) { + return bddSubtypeDiff(MAPPING_SUBTYPE_OBJECT, t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/StreamOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/StreamOps.java new file mode 100644 index 000000000000..17948aaecc6c --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/StreamOps.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; + +import static io.ballerina.types.Common.bddPosMaybeEmpty; +import static io.ballerina.types.Common.bddSubtypeDiff; +import static io.ballerina.types.PredefinedType.LIST_SUBTYPE_TWO_ELEMENT; +import static io.ballerina.types.typeops.BddCommonOps.bddIntersect; +import static io.ballerina.types.typeops.ListOps.listSubtypeIsEmpty; + +/** + * Basic type ops for stream type. + * + * @since 2201.10.0 + */ +public class StreamOps extends CommonOps implements BasicTypeOps { + + private static SubtypeData streamSubtypeComplement(SubtypeData t) { + return bddSubtypeDiff(LIST_SUBTYPE_TWO_ELEMENT, t); + } + + private static boolean streamSubtypeIsEmpty(Context cx, SubtypeData t) { + Bdd b = (Bdd) t; + // The goal of this is to ensure that listSubtypeIsEmpty call beneath does + // not get an empty posList, because it will interpret that + // as `[any|error...]` rather than `[any|error, any|error]`. + b = bddPosMaybeEmpty(b) ? bddIntersect(b, LIST_SUBTYPE_TWO_ELEMENT) : b; + return listSubtypeIsEmpty(cx, b); + } + + @Override + public SubtypeData complement(SubtypeData t) { + return streamSubtypeComplement(t); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return streamSubtypeIsEmpty(cx, t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/StringOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/StringOps.java new file mode 100644 index 000000000000..fd345ce17d6c --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/StringOps.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Common; +import io.ballerina.types.Context; +import io.ballerina.types.EnumerableCharString; +import io.ballerina.types.EnumerableString; +import io.ballerina.types.EnumerableSubtype; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.CharStringSubtype; +import io.ballerina.types.subtypedata.NonCharStringSubtype; +import io.ballerina.types.subtypedata.StringSubtype; + +import java.util.ArrayList; +import java.util.List; + +import static io.ballerina.types.EnumerableSubtype.EQ; +import static io.ballerina.types.EnumerableSubtype.GT; +import static io.ballerina.types.EnumerableSubtype.LT; + +/** + * Basic subtype ops for string type. + * + * @since 2201.8.0 + */ +public class StringOps implements BasicTypeOps { + @Override + public SubtypeData union(SubtypeData d1, SubtypeData d2) { + //List values = new ArrayList<>(); + //boolean allowed = EnumerableSubtype.enumerableSubtypeUnion((StringSubtype) d1, (StringSubtype) d2, values); + //EnumerableString[] valueArray = new EnumerableString[values.size()]; + //return StringSubtype.createStringSubtype(allowed, values.toArray(valueArray)); + StringSubtype sd1 = (StringSubtype) d1; + StringSubtype sd2 = (StringSubtype) d2; + ArrayList chars = new ArrayList<>(); + ArrayList nonChars = new ArrayList<>(); + boolean charsAllowed = EnumerableSubtype.enumerableSubtypeUnion(sd1.getChar(), sd2.getChar(), chars); + boolean nonCharsAllowed = EnumerableSubtype.enumerableSubtypeUnion(sd1.getNonChar(), + sd2.getNonChar(), nonChars); + + return StringSubtype.createStringSubtype(CharStringSubtype.from(charsAllowed, + chars.toArray(new EnumerableCharString[]{})), + NonCharStringSubtype.from(nonCharsAllowed, nonChars.toArray(new EnumerableString[]{}))); + } + + @Override + public SubtypeData intersect(SubtypeData d1, SubtypeData d2) { + if (d1 instanceof AllOrNothingSubtype) { + return ((AllOrNothingSubtype) d1).isAllSubtype() ? d2 : AllOrNothingSubtype.createNothing(); + } + if (d2 instanceof AllOrNothingSubtype) { + return ((AllOrNothingSubtype) d2).isAllSubtype() ? d1 : AllOrNothingSubtype.createNothing(); + } + + StringSubtype sd1 = (StringSubtype) d1; + StringSubtype sd2 = (StringSubtype) d2; + ArrayList chars = new ArrayList<>(); + ArrayList nonChars = new ArrayList<>(); + boolean charsAllowed = EnumerableSubtype.enumerableSubtypeIntersect(sd1.getChar(), sd2.getChar(), chars); + boolean nonCharsAllowed = EnumerableSubtype.enumerableSubtypeIntersect(sd1.getNonChar(), + sd2.getNonChar(), nonChars); + + return StringSubtype.createStringSubtype(CharStringSubtype.from(charsAllowed, + chars.toArray(new EnumerableCharString[]{})), + NonCharStringSubtype.from(nonCharsAllowed, nonChars.toArray(new EnumerableString[]{}))); + } + + @Override + public SubtypeData diff(SubtypeData d1, SubtypeData d2) { + return intersect(d1, complement(d2)); + } + + @Override + public SubtypeData complement(SubtypeData d) { + StringSubtype st = (StringSubtype) d; + if (st.getChar().values.length == 0 && st.getNonChar().values.length == 0) { + if (st.getChar().allowed && st.getNonChar().allowed) { + return AllOrNothingSubtype.createAll(); + } else if (!st.getChar().allowed && !st.getNonChar().allowed) { + return AllOrNothingSubtype.createNothing(); + } + } + + return StringSubtype.createStringSubtype(CharStringSubtype.from(!st.getChar().allowed, st.getChar().values), + NonCharStringSubtype.from(!st.getNonChar().allowed, st.getNonChar().values)); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return Common.notIsEmpty(cx, t); + } + + // Returns a description of the relationship between a StringSubtype and a list of strings + // `values` must be ordered. + static StringSubtype.StringSubtypeListCoverage stringSubtypeListCoverage(StringSubtype subtype, String[] values) { + List indices = new ArrayList<>(); + CharStringSubtype ch = subtype.getChar(); + NonCharStringSubtype nonChar = subtype.getNonChar(); + int stringConsts = 0; + if (ch.allowed) { + stringListIntersect(values, toStringArray(ch.values), indices); + stringConsts = ch.values.length; + } else if (ch.values.length == 0) { + for (int i = 0; i < values.length; i++) { + if (values[i].length() == 1) { + indices.add(i); + } + } + } + if (nonChar.allowed) { + stringListIntersect(values, toStringArray(nonChar.values), indices); + stringConsts += nonChar.values.length; + } else if (nonChar.values.length == 0) { + for (int i = 0; i < values.length; i++) { + if (values[i].length() != 1) { + indices.add(i); + } + } + } + int[] inds = indices.stream().mapToInt(i -> i).toArray(); + return StringSubtype.StringSubtypeListCoverage.from(stringConsts == indices.size(), inds); + } + + private static String[] toStringArray(EnumerableCharString[] ar) { + String[] strings = new String[ar.length]; + for (int i = 0; i < ar.length; i++) { + strings[i] = ar[i].value; + } + return strings; + } + + private static String[] toStringArray(EnumerableString[] ar) { + String[] strings = new String[ar.length]; + for (int i = 0; i < ar.length; i++) { + strings[i] = ar[i].value; + } + return strings; + } + + static void stringListIntersect(String[] values, String[] target, List indices) { + int i1 = 0; + int i2 = 0; + int len1 = values.length; + int len2 = target.length; + while (true) { + if (i1 >= len1 || i2 >= len2) { + break; + } else { + int comp = EnumerableSubtype.compareEnumerable(EnumerableString.from(values[i1]), + EnumerableString.from(target[i2])); + switch (comp) { + case EQ: + indices.add(i1); + i1 += 1; + i2 += 1; + break; + case LT: + i1 += 1; + break; + case GT: + i2 += 1; + break; + default: + throw new AssertionError("Invalid comparison value!"); + } + } + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePair.java b/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePair.java new file mode 100644 index 000000000000..fa21b980e681 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePair.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.ProperSubtypeData; + +/** + * Represent a 3-tuple containing paired-up subtype data. + * + * @since 2201.8.0 + */ +public class SubtypePair { + public final BasicTypeCode basicTypeCode; + public final ProperSubtypeData subtypeData1; + public final ProperSubtypeData subtypeData2; + + private SubtypePair(BasicTypeCode basicTypeCode, ProperSubtypeData subtypeData1, + ProperSubtypeData subtypeData2) { + this.basicTypeCode = basicTypeCode; + this.subtypeData1 = subtypeData1; + this.subtypeData2 = subtypeData2; + } + + public static SubtypePair create(BasicTypeCode basicTypeCode, + ProperSubtypeData subtypeData1, ProperSubtypeData subtypeData2) { + return new SubtypePair(basicTypeCode, subtypeData1, subtypeData2); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePairIterator.java b/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePairIterator.java new file mode 100644 index 000000000000..913f1ddc6ee0 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePairIterator.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicSubtype; +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.SemType; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * Iteration implementation of `SubtypePairIterator`. + * + * @since 2201.8.0 + */ +public class SubtypePairIterator implements Iterator { + private int i1; + private int i2; + private final List t1; + private final List t2; + private final BasicTypeBitSet bits; + + private boolean doneIteration = false; + private boolean shouldCalculate = true; + private SubtypePair cache = null; + + public SubtypePairIterator(SemType t1, SemType t2, BasicTypeBitSet bits) { + this.i1 = 0; + this.i2 = 0; + this.t1 = unpackToBasicSubtypes(t1); + this.t2 = unpackToBasicSubtypes(t2); + this.bits = bits; + } + + private List unpackToBasicSubtypes(SemType type) { + if (type instanceof BasicTypeBitSet) { + return new ArrayList<>(); + } + return UnpackComplexSemType.unpack((ComplexSemType) type); + } + + private boolean include(BasicTypeCode code) { + return (this.bits.bitset & (1 << code.code)) != 0; + } + + private BasicSubtype get1() { + return this.t1.get(this.i1); + } + + private BasicSubtype get2() { + return this.t2.get(this.i2); + } + + @Override + public boolean hasNext() { + if (this.doneIteration) { + return false; + } + if (this.shouldCalculate) { + SubtypePair cache = internalNext(); + if (cache == null) { + this.doneIteration = true; + } + this.cache = cache; + this.shouldCalculate = false; + } + return !this.doneIteration; + } + + @Override + public SubtypePair next() { + if (this.doneIteration) { + throw new NoSuchElementException("Exhausted iterator"); + } + + if (this.shouldCalculate) { + SubtypePair cache = internalNext(); + if (cache == null) { + // this.doneIteration = true; + throw new IllegalStateException(); + } + this.cache = cache; + } + this.shouldCalculate = true; + return this.cache; + } + + /* + * This method corresponds to `next` method of SubtypePairIteratorImpl. + */ + private SubtypePair internalNext() { + while (true) { + if (this.i1 >= this.t1.size()) { + if (this.i2 >= this.t2.size()) { + break; + } + BasicSubtype t = get2(); + BasicTypeCode code = t.basicTypeCode; + ProperSubtypeData data2 = t.subtypeData; + this.i2 += 1; + if (include(code)) { + return SubtypePair.create(code, null, data2); + } + } else if (this.i2 >= this.t2.size()) { + BasicSubtype t = this.get1(); + this.i1 += 1; + BasicTypeCode code = t.basicTypeCode; + ProperSubtypeData data1 = t.subtypeData; + if (include(code)) { + return SubtypePair.create(code, data1, null); + } + } else { + BasicSubtype t1 = get1(); + BasicTypeCode code1 = t1.basicTypeCode; + ProperSubtypeData data1 = t1.subtypeData; + + BasicSubtype t2 = get2(); + BasicTypeCode code2 = t2.basicTypeCode; + ProperSubtypeData data2 = t2.subtypeData; + + if (code1.code == code2.code) { + this.i1 += 1; + this.i2 += 1; + if (include(code1)) { + return SubtypePair.create(code1, data1, data2); + } + } else if (code1.code < code2.code) { + this.i1 += 1; + if (include(code1)) { + return SubtypePair.create(code1, data1, null); + } + } else { + this.i2 += 1; + if (include(code2)) { + return SubtypePair.create(code2, null, data2); + } + } + } + } + return null; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePairs.java b/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePairs.java new file mode 100644 index 000000000000..d703591efafa --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePairs.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.SemType; + +import java.util.Iterator; + +/** + * Ballerina iterator is similar to an iterable in Java. + * This class implements the iterable for `SubtypePairIteratorImpl` + * + * @since 2201.8.0 + */ +public class SubtypePairs implements Iterable { + + private final SemType t1; + private final SemType t2; + private final BasicTypeBitSet bits; + + public SubtypePairs(SemType t1, SemType t2, BasicTypeBitSet bits) { + this.t1 = t1; + this.t2 = t2; + this.bits = bits; + } + + @Override + public Iterator iterator() { + return new SubtypePairIterator(t1, t2, bits); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/TableOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/TableOps.java new file mode 100644 index 000000000000..8d3f3430ad78 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/TableOps.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; + +import static io.ballerina.types.Common.bddPosMaybeEmpty; +import static io.ballerina.types.Common.bddSubtypeDiff; +import static io.ballerina.types.PredefinedType.LIST_SUBTYPE_THREE_ELEMENT; +import static io.ballerina.types.typeops.BddCommonOps.bddIntersect; +import static io.ballerina.types.typeops.ListOps.listSubtypeIsEmpty; + +/** + * Basic type ops for table type. + * + * @since 2201.8.0 + */ +public class TableOps extends CommonOps implements BasicTypeOps { + + private static SubtypeData tableSubtypeComplement(SubtypeData t) { + return bddSubtypeDiff(LIST_SUBTYPE_THREE_ELEMENT, t); + } + + private static boolean tableSubtypeIsEmpty(Context cx, SubtypeData t) { + Bdd b = (Bdd) t; + // The goal of this is to ensure that listSubtypeIsEmpty call beneath does + // not get an empty posList, because it will interpret that + // as `(any|error)[]` rather than `[(map)[], any|error, any|error]`. + b = bddPosMaybeEmpty(b) ? bddIntersect(b, LIST_SUBTYPE_THREE_ELEMENT) : b; + return listSubtypeIsEmpty(cx, b); + } + + @Override + public SubtypeData complement(SubtypeData d) { + return tableSubtypeComplement(d); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData d) { + return tableSubtypeIsEmpty(cx, d); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/TwoTuple.java b/semtypes/src/main/java/io/ballerina/types/typeops/TwoTuple.java new file mode 100644 index 000000000000..4490e3f23ed6 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/TwoTuple.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +/** + * Used to return two values from a method. + * + * @param Type of first item + * @param Type of second item + * @since 2201.8.0 + */ +public final class TwoTuple { + + final E1 item1; + final E2 item2; + + private TwoTuple(E1 item1, E2 item2) { + this.item1 = item1; + this.item2 = item2; + } + + public static TwoTuple from(E1 item1, E2 item2) { + return new TwoTuple<>(item1, item2); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/TypedescOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/TypedescOps.java new file mode 100644 index 000000000000..26f283b35a8f --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/TypedescOps.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; + +import static io.ballerina.types.Common.bddPosMaybeEmpty; +import static io.ballerina.types.Common.bddSubtypeDiff; +import static io.ballerina.types.PredefinedType.BDD_SUBTYPE_RO; +import static io.ballerina.types.typeops.BddCommonOps.bddIntersect; +import static io.ballerina.types.typeops.MappingOps.mappingSubtypeIsEmpty; + +/** + * Basic type ops for typedesc type. + * + * @since 2201.10.0 + */ +public class TypedescOps extends CommonOps implements BasicTypeOps { + + private static SubtypeData typedescSubtypeComplement(SubtypeData t) { + return bddSubtypeDiff(BDD_SUBTYPE_RO, t); + } + + private static boolean typedescSubtypeIsEmpty(Context cx, SubtypeData t) { + Bdd b = (Bdd) t; + // The goal of this is to ensure that mappingSubtypeIsEmpty call beneath does + // not get an empty posList, because it will interpret that + // as `map` rather than `readonly & map)`. + b = bddPosMaybeEmpty(b) ? bddIntersect(b, BDD_SUBTYPE_RO) : b; + return mappingSubtypeIsEmpty(cx, b); + } + + @Override + public SubtypeData complement(SubtypeData d) { + return typedescSubtypeComplement(d); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData d) { + return typedescSubtypeIsEmpty(cx, d); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/UnpackComplexSemType.java b/semtypes/src/main/java/io/ballerina/types/typeops/UnpackComplexSemType.java new file mode 100644 index 000000000000..7cf850a46540 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/UnpackComplexSemType.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicSubtype; +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.ProperSubtypeData; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represent `unpackComplexSemType` function. + * + * @since 2201.8.0 + */ +public class UnpackComplexSemType { + private UnpackComplexSemType() { + } + + public static List unpack(ComplexSemType t) { + int some = t.some(); + List subtypeList = new ArrayList<>(); + for (ProperSubtypeData data : t.subtypeDataList()) { + int code = Integer.numberOfTrailingZeros(some); + subtypeList.add(BasicSubtype.from(BasicTypeCode.from(code), data)); + some ^= (1 << code); + } + return subtypeList; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/XmlOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/XmlOps.java new file mode 100644 index 000000000000..4bd166c5a42e --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/XmlOps.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.Common; +import io.ballerina.types.Conjunction; +import io.ballerina.types.Context; +import io.ballerina.types.RecAtom; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.XmlSubtype; + +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_ALL_MASK; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_RO_MASK; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_RO_SINGLETON; + +/** + * Basic subtype ops for xml type. + * + * @since 2201.8.0 + */ +public class XmlOps implements BasicTypeOps { + + public static final XmlSubtype XML_SUBTYPE_RO = XmlSubtype.from(XML_PRIMITIVE_RO_MASK, + BddCommonOps.bddAtom(RecAtom.createXMLRecAtom(XML_PRIMITIVE_RO_SINGLETON))); + public static final XmlSubtype XML_SUBTYPE_TOP = XmlSubtype.from(XML_PRIMITIVE_ALL_MASK, BddAllOrNothing.bddAll()); + + @Override + public SubtypeData union(SubtypeData d1, SubtypeData d2) { + XmlSubtype v1 = (XmlSubtype) d1; + XmlSubtype v2 = (XmlSubtype) d2; + int primitives = v1.primitives | v2.primitives; + return XmlSubtype.createXmlSubtype(primitives, BddCommonOps.bddUnion(v1.sequence, v2.sequence)); + } + + @Override + public SubtypeData intersect(SubtypeData d1, SubtypeData d2) { + XmlSubtype v1 = (XmlSubtype) d1; + XmlSubtype v2 = (XmlSubtype) d2; + int primitives = v1.primitives & v2.primitives; + return XmlSubtype.createXmlSubtypeOrEmpty(primitives, BddCommonOps.bddIntersect(v1.sequence, v2.sequence)); + } + + @Override + public SubtypeData diff(SubtypeData d1, SubtypeData d2) { + XmlSubtype v1 = (XmlSubtype) d1; + XmlSubtype v2 = (XmlSubtype) d2; + int primitives = v1.primitives & ~v2.primitives; + return XmlSubtype.createXmlSubtypeOrEmpty(primitives, BddCommonOps.bddDiff(v1.sequence, v2.sequence)); + } + + @Override + public SubtypeData complement(SubtypeData d) { + return diff(XML_SUBTYPE_TOP, d); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + XmlSubtype sd = (XmlSubtype) t; + if (sd.primitives != 0) { + return false; + } + return xmlBddEmpty(cx, sd.sequence); + } + + boolean xmlBddEmpty(Context cx, Bdd bdd) { + return Common.bddEvery(cx, bdd, null, null, XmlOps::xmlFormulaIsEmpty); + } + + private static boolean xmlFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { + int allPosBits = collectAllPrimitives(pos) & XmlSubtype.XML_PRIMITIVE_ALL_MASK; + return xmlHasTotalNegative(allPosBits, neg); + } + + public static int collectAllPrimitives(Conjunction con) { + int bits = 0; + Conjunction current = con; + while (current != null) { + bits &= getIndex(current); + current = current.next; + } + return bits; + } + + public static boolean xmlHasTotalNegative(int allBits, Conjunction con) { + if (allBits == 0) { + return true; + } + + Conjunction n = con; + while (n != null) { + if ((allBits & ~getIndex(con)) == 0) { + return true; + } + n = n.next; + } + return false; + } + + private static int getIndex(Conjunction con) { + return ((RecAtom) con.atom).index; + } +} diff --git a/semtypes/src/main/java/module-info.java b/semtypes/src/main/java/module-info.java index 5ae00697cfae..40129cf5658d 100644 --- a/semtypes/src/main/java/module-info.java +++ b/semtypes/src/main/java/module-info.java @@ -1,3 +1,5 @@ module io.ballerina.semtype { - + exports io.ballerina.types; + exports io.ballerina.types.definition; + exports io.ballerina.types.subtypedata; } diff --git a/semtypes/src/test/java/io/ballerina/semtype/SemTypeTest.java b/semtypes/src/test/java/io/ballerina/semtype/SemTypeTest.java deleted file mode 100644 index a4a16c8e6031..000000000000 --- a/semtypes/src/test/java/io/ballerina/semtype/SemTypeTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -import org.testng.Assert; -import org.testng.annotations.Test; - -/** - * Tests if placeholder is created. - * - */ -public class SemTypeTest { - - @SuppressWarnings("ConstantValue") - @Test - public void testSemType() { - SemTypeMock s1 = new UniformTypeBitSetMock(0x00); - SemTypeMock s2 = new ComplexSemTypeMock(); - Assert.assertTrue(s1 instanceof UniformTypeBitSetMock); - Assert.assertTrue(s2 instanceof ComplexSemTypeMock); - } -} diff --git a/semtypes/src/test/java/io/ballerina/types/CellTypeTest.java b/semtypes/src/test/java/io/ballerina/types/CellTypeTest.java new file mode 100644 index 000000000000..8da46f516ca3 --- /dev/null +++ b/semtypes/src/test/java/io/ballerina/types/CellTypeTest.java @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.CellSubtype; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_UNLIMITED; + +/** + * Tests subtyping rules of cell basic type. + * + * @since 2201.10.0 + */ +public class CellTypeTest { + + Context ctx; + + public enum Relation { + EQUAL("="), + SUBTYPE("<"), + NO_RELATION("<>"); + + final String value; + + Relation(String value) { + this.value = value; + } + } + + @BeforeClass + public void beforeClass() { + ctx = Context.from(new Env()); + } + + private CellSemType cell(SemType ty, CellAtomicType.CellMutability mut) { + return CellSubtype.cellContaining(ctx.env, ty, mut); + } + + private void assertSemTypeRelation(SemType t1, SemType t2, Relation relation) { + Relation actual = getSemTypeRelation(t1, t2); + Assert.assertEquals(actual, relation); + } + + private Relation getSemTypeRelation(SemType t1, SemType t2) { + boolean s1 = Core.isSubtype(ctx, t1, t2); + boolean s2 = Core.isSubtype(ctx, t2, t1); + if (s1 && s2) { + return Relation.EQUAL; + } else if (s1) { + return Relation.SUBTYPE; + } else if (s2) { + throw new IllegalStateException("'>' relation found which can be converted to a '<' relation"); + } else { + return Relation.NO_RELATION; + } + } + + @Test(description = "Test T and cell(T) having no relation", dataProvider = "typeCellDisparityDataProvider") + public void testTypeCellDisparity(SemType t1, SemType t2, Relation relation) { + assertSemTypeRelation(t1, t2, relation); + } + + @DataProvider(name = "typeCellDisparityDataProvider") + public Object[][] createTypeCellDisparityTestData() { + return new Object[][]{ + {PredefinedType.INT, cell(PredefinedType.INT, CELL_MUT_NONE), Relation.NO_RELATION}, + {PredefinedType.INT, cell(PredefinedType.INT, CELL_MUT_LIMITED), Relation.NO_RELATION}, + {PredefinedType.INT, cell(PredefinedType.INT, CELL_MUT_UNLIMITED), Relation.NO_RELATION}, + }; + } + + @Test(description = "Test basic cell subtyping", dataProvider = "basicCellSubtypingDataProvider") + public void testBasicCellSubtyping(SemType t1, SemType t2, Relation[] relations) { + assert relations.length == 3; + Relation[] actual = new Relation[3]; + + CellAtomicType.CellMutability[] values = CellAtomicType.CellMutability.values(); + // Obtaining relation for each mutability kind + for (int i = 0; i < values.length; i++) { + CellAtomicType.CellMutability mut = values[i]; + CellSemType c1 = cell(t1, mut); + CellSemType c2 = cell(t2, mut); + actual[i] = getSemTypeRelation(c1, c2); + } + + Assert.assertEquals(actual, relations); + } + + @DataProvider(name = "basicCellSubtypingDataProvider") + public Object[][] createBasicCellSubtypingTestData() { + // This contains some of nBallerina 'cell-1.typetest' test data + return new Object[][]{ + { + PredefinedType.INT, PredefinedType.INT, + new Relation[]{ + Relation.EQUAL, Relation.EQUAL, Relation.EQUAL + } + }, + { + PredefinedType.BOOLEAN, PredefinedType.BOOLEAN, + new Relation[]{ + Relation.EQUAL, Relation.EQUAL, Relation.EQUAL + } + }, + { + PredefinedType.BYTE, PredefinedType.INT, + new Relation[]{ + Relation.SUBTYPE, Relation.SUBTYPE, Relation.SUBTYPE + } + }, + { + PredefinedType.BOOLEAN, PredefinedType.INT, + new Relation[]{ + Relation.NO_RELATION, Relation.NO_RELATION, Relation.NO_RELATION + } + }, + { + PredefinedType.BOOLEAN, Core.union(PredefinedType.INT, PredefinedType.BOOLEAN), + new Relation[]{ + Relation.SUBTYPE, Relation.SUBTYPE, Relation.SUBTYPE + } + } + }; + } + + @Test(dataProvider = "cellSubtypeDataProvider1") + public void testCellSubtyping1(SemType t1, SemType t2, Relation relation) { + assertSemTypeRelation(t1, t2, relation); + } + + @DataProvider(name = "cellSubtypeDataProvider1") + public Object[][] createCellSubtypeData1() { + // This contains some of nBallerina 'cell-1.typetest' test data + return new Object[][]{ + // Set 1 + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.BOOLEAN, CELL_MUT_NONE) + ), + cell(SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN), CELL_MUT_NONE), + Relation.EQUAL + }, + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_LIMITED), + cell(PredefinedType.BOOLEAN, CELL_MUT_LIMITED) + ), + cell(SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN), CELL_MUT_LIMITED), + Relation.SUBTYPE + }, + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_UNLIMITED), + cell(PredefinedType.BOOLEAN, CELL_MUT_UNLIMITED) + ), + cell(SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN), CELL_MUT_UNLIMITED), + Relation.EQUAL + }, + // Set 2 + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.BOOLEAN, CELL_MUT_NONE), + cell(PredefinedType.STRING, CELL_MUT_NONE) + ), + cell(SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN, PredefinedType.STRING), + CELL_MUT_NONE), + Relation.EQUAL + }, + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_LIMITED), + cell(PredefinedType.BOOLEAN, CELL_MUT_LIMITED), + cell(PredefinedType.STRING, CELL_MUT_LIMITED) + ), + cell(SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN, PredefinedType.STRING), + CELL_MUT_LIMITED), + Relation.SUBTYPE + }, + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_UNLIMITED), + cell(PredefinedType.BOOLEAN, CELL_MUT_UNLIMITED), + cell(PredefinedType.STRING, CELL_MUT_UNLIMITED) + ), + cell(SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN, PredefinedType.STRING), + CELL_MUT_UNLIMITED), + Relation.EQUAL + }, + // Set 3 + { + SemTypes.union( + cell(TypeTestUtils.roTuple(ctx.env, PredefinedType.INT), CELL_MUT_NONE), + cell(TypeTestUtils.roTuple(ctx.env, PredefinedType.BOOLEAN), CELL_MUT_NONE) + ), + cell(TypeTestUtils.roTuple(ctx.env, SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN)), + CELL_MUT_NONE), + Relation.EQUAL + }, + { + SemTypes.union( + cell(TypeTestUtils.tuple(ctx.env, PredefinedType.INT), CELL_MUT_LIMITED), + cell(TypeTestUtils.tuple(ctx.env, PredefinedType.BOOLEAN), CELL_MUT_LIMITED) + ), + cell(TypeTestUtils.tuple(ctx.env, SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN)), + CELL_MUT_LIMITED), + Relation.SUBTYPE + }, + { + SemTypes.union( + cell(TypeTestUtils.tuple(ctx.env, PredefinedType.INT), CELL_MUT_UNLIMITED), + cell(TypeTestUtils.tuple(ctx.env, PredefinedType.BOOLEAN), CELL_MUT_UNLIMITED) + ), + cell(TypeTestUtils.tuple(ctx.env, SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN)), + CELL_MUT_UNLIMITED), + Relation.SUBTYPE + }, + }; + } + + @Test(dataProvider = "cellSubtypeDataProvider2") + public void testCellSubtyping2(SemType t1, SemType t2, Relation relation) { + assertSemTypeRelation(t1, t2, relation); + } + + @DataProvider(name = "cellSubtypeDataProvider2") + public Object[][] createCellSubtypeData2() { + // This contains nBallerina 'cell-2.typetest' test data + return new Object[][]{ + // test 1 + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.BOOLEAN, CELL_MUT_UNLIMITED), + cell(PredefinedType.STRING, CELL_MUT_LIMITED) + ), + cell( + SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN, PredefinedType.STRING), + CELL_MUT_UNLIMITED + ), + Relation.SUBTYPE + }, + // test 2 + { + cell( + SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN, PredefinedType.STRING), + CELL_MUT_NONE + ), + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.BOOLEAN, CELL_MUT_UNLIMITED), + cell(PredefinedType.STRING, CELL_MUT_LIMITED) + ), + Relation.SUBTYPE + }, + // test 3 + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.BOOLEAN, CELL_MUT_UNLIMITED), + cell(PredefinedType.STRING, CELL_MUT_LIMITED) + ), + cell( + SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN, PredefinedType.STRING), + CELL_MUT_LIMITED + ), + Relation.NO_RELATION + }, + // test 4 + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.INT, CELL_MUT_LIMITED), + cell(PredefinedType.INT, CELL_MUT_UNLIMITED) + ), + cell(PredefinedType.INT, CELL_MUT_UNLIMITED), + Relation.EQUAL + }, + // test 5 + { + SemTypes.intersect( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.INT, CELL_MUT_LIMITED), + cell(PredefinedType.INT, CELL_MUT_UNLIMITED) + ), + cell(PredefinedType.INT, CELL_MUT_UNLIMITED), + Relation.SUBTYPE + }, + // test 6 + { + SemTypes.intersect( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.INT, CELL_MUT_LIMITED), + cell(PredefinedType.INT, CELL_MUT_UNLIMITED) + ), + cell(PredefinedType.INT, CELL_MUT_LIMITED), + Relation.SUBTYPE + }, + // test 7 + { + SemTypes.intersect( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.INT, CELL_MUT_LIMITED), + cell(PredefinedType.INT, CELL_MUT_UNLIMITED) + ), + cell(PredefinedType.INT, CELL_MUT_NONE), + Relation.EQUAL + }, + // test 8 + { + SemTypes.intersect( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.INT, CELL_MUT_LIMITED), + cell(PredefinedType.BYTE, CELL_MUT_LIMITED) + ), + cell(PredefinedType.BYTE, CELL_MUT_LIMITED), + Relation.SUBTYPE + }, + // test 9 + { + SemTypes.intersect( + cell(PredefinedType.INT, CELL_MUT_NONE), + SemTypes.union( + cell(PredefinedType.BYTE, CELL_MUT_LIMITED), + cell(PredefinedType.BOOLEAN, CELL_MUT_LIMITED) + ) + ), + cell(PredefinedType.BYTE, CELL_MUT_NONE), + Relation.EQUAL + }, + }; + } + + @AfterClass + public void afterClass() { + ctx = null; + } +} diff --git a/semtypes/src/test/java/io/ballerina/types/EnvInitTest.java b/semtypes/src/test/java/io/ballerina/types/EnvInitTest.java new file mode 100644 index 000000000000..1cf69e55251e --- /dev/null +++ b/semtypes/src/test/java/io/ballerina/types/EnvInitTest.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.lang.ref.Reference; +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import static io.ballerina.types.Core.union; + +/** + * Test class for {@link Env} initialization. + * + * @since 2201.10.0 + */ +public class EnvInitTest { + + @BeforeClass + public void setup() { + // All the types in PredefinedTypeEnv are populated by the loading of PredefinedType class. + try { + Class.forName("io.ballerina.types.PredefinedType"); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + @Test + public void testEnvInitAtomTable() throws NoSuchFieldException, IllegalAccessException { + final Env env = new Env(); + + // Access the private field atomTable using reflection + Field atomTableField = Env.class.getDeclaredField("atomTable"); + atomTableField.setAccessible(true); + Map> atomTable = (Map) atomTableField.get(env); + + // Check that the atomTable contains the expected entries + Assert.assertEquals(atomTable.size(), 19); + + CellAtomicType cellAtomicVal = CellAtomicType.from( + PredefinedType.VAL, CellAtomicType.CellMutability.CELL_MUT_LIMITED + ); + + TypeAtom typeAtom0 = atomTable.get(cellAtomicVal).get(); + Assert.assertNotNull(typeAtom0); + Assert.assertEquals(typeAtom0.atomicType(), cellAtomicVal); + + CellAtomicType cellAtomicNever = CellAtomicType.from( + PredefinedType.NEVER, CellAtomicType.CellMutability.CELL_MUT_LIMITED + ); + + TypeAtom typeAtom1 = atomTable.get(cellAtomicNever).get(); + Assert.assertNotNull(typeAtom1); + Assert.assertEquals(typeAtom1.atomicType(), cellAtomicNever); + + CellAtomicType cellAtomicInner = CellAtomicType.from( + PredefinedType.INNER, CellAtomicType.CellMutability.CELL_MUT_LIMITED + ); + + TypeAtom typeAtom2 = atomTable.get(cellAtomicInner).get(); + Assert.assertNotNull(typeAtom2); + Assert.assertEquals(typeAtom2.atomicType(), cellAtomicInner); + + CellAtomicType cellAtomicInnerMapping = CellAtomicType.from( + union(PredefinedType.MAPPING, PredefinedType.UNDEF), + CellAtomicType.CellMutability.CELL_MUT_LIMITED + ); + + TypeAtom typeAtom3 = atomTable.get(cellAtomicInnerMapping).get(); + Assert.assertNotNull(typeAtom3); + Assert.assertEquals(typeAtom3.atomicType(), cellAtomicInnerMapping); + + ListAtomicType listAtomicMapping = ListAtomicType.from( + FixedLengthArray.empty(), PredefinedType.CELL_SEMTYPE_INNER_MAPPING + ); + + TypeAtom typeAtom4 = atomTable.get(listAtomicMapping).get(); + Assert.assertNotNull(typeAtom4); + Assert.assertEquals(typeAtom4.atomicType(), listAtomicMapping); + + TypeAtom typeAtom5 = atomTable.get(PredefinedType.CELL_ATOMIC_INNER_MAPPING_RO).get(); + Assert.assertNotNull(typeAtom5); + Assert.assertEquals(typeAtom5.atomicType(), PredefinedType.CELL_ATOMIC_INNER_MAPPING_RO); + + ListAtomicType listAtomicMappingRo = ListAtomicType.from( + FixedLengthArray.empty(), PredefinedType.CELL_SEMTYPE_INNER_MAPPING_RO + ); + + TypeAtom typeAtom6 = atomTable.get(listAtomicMappingRo).get(); + Assert.assertNotNull(typeAtom6); + Assert.assertEquals(typeAtom6.atomicType(), listAtomicMappingRo); + + CellAtomicType cellAtomicInnerRo = CellAtomicType.from( + PredefinedType.INNER_READONLY, CellAtomicType.CellMutability.CELL_MUT_NONE + ); + + TypeAtom typeAtom7 = atomTable.get(cellAtomicInnerRo).get(); + Assert.assertNotNull(typeAtom7); + Assert.assertEquals(typeAtom7.atomicType(), cellAtomicInnerRo); + + CellAtomicType cellAtomicUndef = CellAtomicType.from( + PredefinedType.UNDEF, CellAtomicType.CellMutability.CELL_MUT_NONE + ); + + TypeAtom typeAtom8 = atomTable.get(cellAtomicUndef).get(); + Assert.assertNotNull(typeAtom8); + Assert.assertEquals(typeAtom8.atomicType(), cellAtomicUndef); + + ListAtomicType listAtomicTwoElement = ListAtomicType.from( + FixedLengthArray.from(List.of(PredefinedType.CELL_SEMTYPE_VAL), 2), + PredefinedType.CELL_SEMTYPE_UNDEF + ); + + TypeAtom typeAtom9 = atomTable.get(listAtomicTwoElement).get(); + Assert.assertNotNull(typeAtom8); + Assert.assertEquals(typeAtom9.atomicType(), listAtomicTwoElement); + } + + @Test + public void testTypeAtomIndices() throws NoSuchFieldException, IllegalAccessException { + Env env = new Env(); + Field atomTableField = Env.class.getDeclaredField("atomTable"); + atomTableField.setAccessible(true); + Map> recListAtoms = + (Map>) atomTableField.get(env); + Collection indices = new HashSet<>(); + for (var each : recListAtoms.values()) { + Assert.assertTrue(indices.add(each.get().index()), "Duplicate index found: " + each.get().index()); + } + } + + @Test + public void testEnvInitRecAtoms() throws NoSuchFieldException, IllegalAccessException { + Env env = new Env(); + Field recListAtomsField = Env.class.getDeclaredField("recListAtoms"); + recListAtomsField.setAccessible(true); + List recListAtoms = (List) recListAtomsField.get(env); + Assert.assertEquals(recListAtoms.size(), 2); + ListAtomicType listAtomicRo = ListAtomicType.from( + FixedLengthArray.empty(), PredefinedType.CELL_SEMTYPE_INNER_RO + ); + Assert.assertEquals(recListAtoms.get(0), listAtomicRo); + Assert.assertNull(recListAtoms.get(1)); + + Field recMappingAtomsField = Env.class.getDeclaredField("recMappingAtoms"); + recMappingAtomsField.setAccessible(true); + List recMappingAtoms = (List) recMappingAtomsField.get(env); + Assert.assertEquals(recMappingAtoms.size(), 2); + Assert.assertEquals(recMappingAtoms.get(0), PredefinedType.MAPPING_ATOMIC_RO); + Assert.assertEquals(recMappingAtoms.get(1), PredefinedType.MAPPING_ATOMIC_OBJECT_RO); + + Field recFunctionAtomsField = Env.class.getDeclaredField("recFunctionAtoms"); + recFunctionAtomsField.setAccessible(true); + List recFunctionAtoms = (List) recFunctionAtomsField.get(env); + Assert.assertEquals(recFunctionAtoms.size(), 0); + } +} diff --git a/semtypes/src/test/java/io/ballerina/types/SemTypeBddTest.java b/semtypes/src/test/java/io/ballerina/types/SemTypeBddTest.java new file mode 100644 index 000000000000..0653955dd696 --- /dev/null +++ b/semtypes/src/test/java/io/ballerina/types/SemTypeBddTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.typeops.BddCommonOps; +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * Tests Bdd of Semtypes. + * + * @since 2201.8.0 + */ +public class SemTypeBddTest { + + @Test + public void bddTest() { + Bdd b1 = BddCommonOps.bddAtom(RecAtom.createRecAtom(1)); + Bdd b2 = BddCommonOps.bddAtom(RecAtom.createRecAtom(2)); + Bdd b1and2 = BddCommonOps.bddIntersect(b1, b2); + Bdd r = BddCommonOps.bddDiff(b1and2, b1); + Assert.assertFalse(((BddAllOrNothing) r).isAll()); + } +} diff --git a/semtypes/src/test/java/io/ballerina/types/SemTypeCoreTest.java b/semtypes/src/test/java/io/ballerina/types/SemTypeCoreTest.java new file mode 100644 index 000000000000..b9b15d2b1953 --- /dev/null +++ b/semtypes/src/test/java/io/ballerina/types/SemTypeCoreTest.java @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.definition.FunctionDefinition; +import io.ballerina.types.definition.FunctionQualifiers; +import io.ballerina.types.definition.ListDefinition; +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.Range; +import io.ballerina.types.subtypedata.StringSubtype; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.function.BiFunction; + +import static io.ballerina.types.TypeTestUtils.roTuple; +import static io.ballerina.types.TypeTestUtils.tuple; +import static io.ballerina.types.subtypedata.StringSubtype.stringConst; + +/** + * Tests Core functions of Semtypes. + * + * @since 2201.8.0 + */ +public class SemTypeCoreTest { + + @Test + public void testSubtypeSimple() { + Assert.assertTrue(Core.isSubtypeSimple(PredefinedType.NIL, PredefinedType.ANY)); + Assert.assertTrue(Core.isSubtypeSimple(PredefinedType.INT, PredefinedType.VAL)); + Assert.assertTrue(Core.isSubtypeSimple(PredefinedType.ANY, PredefinedType.VAL)); + Assert.assertFalse(Core.isSubtypeSimple(PredefinedType.INT, PredefinedType.BOOLEAN)); + Assert.assertFalse(Core.isSubtypeSimple(PredefinedType.ERROR, PredefinedType.ANY)); + } + + @Test + public void testSingleNumericType() { + Assert.assertEquals(Core.singleNumericType(PredefinedType.INT), Optional.of(PredefinedType.INT)); + Assert.assertEquals(Core.singleNumericType(PredefinedType.BOOLEAN), Optional.empty()); + Core.singleNumericType(Core.singleton(1L)); + Assert.assertEquals(Core.singleNumericType(Core.singleton(1L)), Optional.of(PredefinedType.INT)); + Assert.assertEquals(Core.singleNumericType(Core.union(PredefinedType.INT, PredefinedType.FLOAT)), + Optional.empty()); + } + + @Test + public void testBitTwiddling() { + Assert.assertEquals(Long.numberOfTrailingZeros(0x10), 4); + Assert.assertEquals(Long.numberOfTrailingZeros(0x100), 8); + Assert.assertEquals(Long.numberOfTrailingZeros(0x1), 0); + Assert.assertEquals(Long.numberOfTrailingZeros(0x0), 64); + Assert.assertEquals(Integer.bitCount(0x10000), 1); + Assert.assertEquals(Integer.bitCount(0), 0); + Assert.assertEquals(Integer.bitCount(1), 1); + Assert.assertEquals(Integer.bitCount(0x10010010), 3); + } + + @Test + public void test1() { + Env env = new Env(); + disjoint(Core.typeCheckContext(env), PredefinedType.STRING, PredefinedType.INT); + disjoint(Core.typeCheckContext(env), PredefinedType.INT, PredefinedType.NIL); + SemType t1 = createTupleType(env, PredefinedType.INT, PredefinedType.INT); + disjoint(Core.typeCheckContext(env), t1, PredefinedType.INT); + SemType t2 = createTupleType(env, PredefinedType.STRING, PredefinedType.STRING); + disjoint(Core.typeCheckContext(env), PredefinedType.NIL, t2); + } + + private void disjoint(Context cx, SemType t1, SemType t2) { + Assert.assertFalse(Core.isSubtype(cx, t1, t2)); + Assert.assertFalse(Core.isSubtype(cx, t2, t1)); + Assert.assertTrue(Core.isEmpty(cx, Core.intersect(t1, t2))); + } + + @Test + public void test2() { + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(new Env()), PredefinedType.INT, PredefinedType.VAL)); + } + + @Test + public void test3() { + Env env = new Env(); + SemType s = roTuple(env, PredefinedType.INT, Core.union(PredefinedType.INT, + PredefinedType.STRING)); + SemType t = Core.union(roTuple(env, PredefinedType.INT, PredefinedType.INT), + roTuple(env, PredefinedType.INT, PredefinedType.STRING)); + equiv(env, s, t); + } + + private void equiv(Env env, SemType s, SemType t) { + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), s, t)); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), t, s)); + } + + @Test + public void test4() { + Env env = new Env(); + SemType isT = createTupleType(env, PredefinedType.INT, PredefinedType.STRING); + SemType itT = createTupleType(env, PredefinedType.INT, PredefinedType.VAL); + SemType tsT = createTupleType(env, PredefinedType.VAL, PredefinedType.STRING); + SemType iiT = createTupleType(env, PredefinedType.INT, PredefinedType.INT); + SemType ttT = createTupleType(env, PredefinedType.VAL, PredefinedType.VAL); + Context cx = Core.typeCheckContext(env); + Assert.assertTrue(Core.isSubtype(cx, isT, itT)); + Assert.assertTrue(Core.isSubtype(cx, isT, tsT)); + Assert.assertTrue(Core.isSubtype(cx, iiT, ttT)); + } + + @Test + public void test5() { + Env env = new Env(); + SemType s = roTuple(env, PredefinedType.INT, Core.union(PredefinedType.NIL, + Core.union(PredefinedType.INT, PredefinedType.STRING))); + SemType t = Core.union(roTuple(env, PredefinedType.INT, PredefinedType.INT), + Core.union(roTuple(env, PredefinedType.INT, PredefinedType.NIL), + roTuple(env, PredefinedType.INT, PredefinedType.STRING))); + equiv(env, s, t); + } + + @Test + public void test6() { + Env env = new Env(); + SemType s = tuple(env, PredefinedType.INT, Core.union(PredefinedType.NIL, + Core.union(PredefinedType.INT, PredefinedType.STRING))); + SemType t = Core.union(tuple(env, PredefinedType.INT, PredefinedType.INT), + Core.union(tuple(env, PredefinedType.INT, PredefinedType.NIL), + tuple(env, PredefinedType.INT, PredefinedType.STRING))); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), t, s)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), s, t)); + } + + @Test + public void test7() { + Env env = new Env(); + SemType s = tuple(env, PredefinedType.INT, Core.union(PredefinedType.INT, + PredefinedType.STRING)); + SemType t = Core.union(tuple(env, PredefinedType.INT, PredefinedType.INT), + tuple(env, PredefinedType.INT, PredefinedType.STRING)); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), t, s)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), s, t)); + } + + @Test + public void tupleTest1() { + Env env = new Env(); + SemType s = createTupleType(env, PredefinedType.INT, PredefinedType.STRING, PredefinedType.NIL); + SemType t = createTupleType(env, PredefinedType.VAL, PredefinedType.VAL, PredefinedType.VAL); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), s, t)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), t, s)); + } + + @Test + public void tupleTest2() { + Env env = new Env(); + SemType s = createTupleType(env, PredefinedType.INT, PredefinedType.STRING, PredefinedType.NIL); + SemType t = createTupleType(env, PredefinedType.VAL, PredefinedType.VAL); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), s, t)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), t, s)); + } + + @Test + public void tupleTest3() { + Env env = new Env(); + SemType z1 = createTupleType(env); + SemType z2 = createTupleType(env); + SemType t = createTupleType(env, PredefinedType.INT); + Assert.assertTrue(!Core.isEmpty(Core.typeCheckContext(env), z1)); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), z1, z2)); + Assert.assertTrue(Core.isEmpty(Core.typeCheckContext(env), Core.diff(z1, z2))); + Assert.assertFalse(Core.isEmpty(Core.typeCheckContext(env), Core.diff(z1, PredefinedType.INT))); + Assert.assertFalse(Core.isEmpty(Core.typeCheckContext(env), Core.diff(PredefinedType.INT, z1))); + } + + @Test + public void tupleTest4() { + Env env = new Env(); + SemType s = createTupleType(env, PredefinedType.INT, PredefinedType.INT); + SemType t = createTupleType(env, PredefinedType.INT, PredefinedType.INT, PredefinedType.INT); + Assert.assertFalse(Core.isEmpty(Core.typeCheckContext(env), s)); + Assert.assertFalse(Core.isEmpty(Core.typeCheckContext(env), t)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), s, t)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), t, s)); + Assert.assertTrue(Core.isEmpty(Core.typeCheckContext(env), Core.intersect(s, t))); + } + + private SemType func(Env env, SemType args, SemType ret) { + FunctionDefinition def = new FunctionDefinition(); + return def.define(env, args, ret, FunctionQualifiers.from(env, false, false)); + } + + @Test + public void funcTest1() { + Env env = new Env(); + SemType s = func(env, PredefinedType.INT, PredefinedType.INT); + SemType t = func(env, PredefinedType.INT, Core.union(PredefinedType.NIL, PredefinedType.INT)); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), s, t)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), t, s)); + } + + @Test + public void funcTest2() { + Env env = new Env(); + SemType s = func(env, Core.union(PredefinedType.NIL, PredefinedType.INT), PredefinedType.INT); + SemType t = func(env, PredefinedType.INT, PredefinedType.INT); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), s, t)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), t, s)); + } + + @Test + public void funcTest3() { + Env env = new Env(); + SemType s = func(env, createTupleType(env, Core.union(PredefinedType.NIL, PredefinedType.INT)), + PredefinedType.INT); + SemType t = func(env, createTupleType(env, PredefinedType.INT), PredefinedType.INT); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), s, t)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), t, s)); + } + + @Test + public void funcTest4() { + Env env = new Env(); + SemType s = func(env, createTupleType(env, Core.union(PredefinedType.NIL, PredefinedType.INT)), + PredefinedType.INT); + SemType t = func(env, createTupleType(env, PredefinedType.INT), + Core.union(PredefinedType.NIL, PredefinedType.INT)); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), s, t)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), t, s)); + } + + @Test + public void stringTest() { + List result = new ArrayList<>(); + // TODO may have to assert lists by converting the output to a string list + + EnumerableSubtype.enumerableListUnion(new EnumerableString[]{EnumerableString.from("a"), + EnumerableString.from("b"), EnumerableString.from("d")}, + new EnumerableString[]{EnumerableString.from("c")}, result); + Assert.assertEquals(result.get(0).value, "a"); + Assert.assertEquals(result.get(1).value, "b"); + Assert.assertEquals(result.get(2).value, "c"); + Assert.assertEquals(result.get(3).value, "d"); + + result = new ArrayList<>(); + EnumerableSubtype.enumerableListIntersect(new EnumerableString[]{EnumerableString.from("a"), + EnumerableString.from("b"), EnumerableString.from("d")}, + new EnumerableString[]{EnumerableString.from("d")}, result); + Assert.assertEquals(result.get(0).value, "d"); + + result = new ArrayList<>(); + EnumerableSubtype.enumerableListDiff(new EnumerableString[]{EnumerableString.from("a"), + EnumerableString.from("b"), EnumerableString.from("c"), EnumerableString.from("d")}, + new EnumerableString[]{EnumerableString.from("a"), EnumerableString.from("c")}, result); + Assert.assertEquals(result.get(0).value, "b"); + Assert.assertEquals(result.get(1).value, "d"); + } + + @Test + public void roListTest() { + SemType t1 = Core.intersect(PredefinedType.LIST, PredefinedType.VAL_READONLY); + Env env = new Env(); + ListDefinition ld = new ListDefinition(); + SemType t2 = ld.defineListTypeWrapped(env, new ArrayList<>(), 0, PredefinedType.VAL); + SemType t = Core.diff(t1, t2); + Context cx = Core.typeCheckContext(env); + boolean b = Core.isEmpty(cx, t); + Assert.assertTrue(b); + } + + @Test + public void testIntSubtypeWidenUnsigned() { + Assert.assertTrue(((AllOrNothingSubtype) IntSubtype.intSubtypeWidenUnsigned(AllOrNothingSubtype.createAll())) + .isAllSubtype()); + Assert.assertTrue(((AllOrNothingSubtype) IntSubtype.intSubtypeWidenUnsigned( + IntSubtype.createIntSubtype(new Range(-1L, 10L)))).isAllSubtype()); + IntSubtype intType1 = (IntSubtype) IntSubtype.intSubtypeWidenUnsigned( + IntSubtype.createIntSubtype(new Range(0L, 0L))); + Assert.assertEquals(intType1.ranges[0].min, 0L); + Assert.assertEquals(intType1.ranges[0].max, 255L); + IntSubtype intType2 = (IntSubtype) IntSubtype.intSubtypeWidenUnsigned( + IntSubtype.createIntSubtype(new Range(0L, 257L))); + Assert.assertEquals(intType2.ranges[0].min, 0L); + Assert.assertEquals(intType2.ranges[0].max, 65535L); + } + + public SemType recursiveTuple(Env env, BiFunction> f) { + ListDefinition def = new ListDefinition(); + SemType t = def.getSemType(env); + List members = f.apply(env, t); + return def.defineListTypeWrapped(env, members, members.size()); + } + + @Test + public void recTest() { + Env env = new Env(); + SemType t1 = recursiveTuple(env, + (e, t) -> Arrays.asList(PredefinedType.INT, Core.union(t, PredefinedType.NIL))); + SemType t2 = recursiveTuple(env, (e, t) -> Arrays.asList(Core.union(PredefinedType.INT, PredefinedType.STRING), + Core.union(t, PredefinedType.NIL))); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), t1, t2)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), t2, t1)); + } + + @Test + public void recTest2() { + Env env = new Env(); + SemType t1 = Core.union(PredefinedType.NIL, recursiveTuple(env, + (e, t) -> Arrays.asList(PredefinedType.INT, Core.union(t, PredefinedType.NIL)))); + SemType t2 = recursiveTuple(env, + (e, t) -> Arrays.asList(PredefinedType.INT, Core.union(t, PredefinedType.NIL))); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), t2, t1)); + } + + @Test + public void recTest3() { + Env env = new Env(); + SemType t1 = recursiveTuple(env, + (e, t) -> Arrays.asList(PredefinedType.INT, Core.union(t, PredefinedType.NIL))); + SemType t2 = recursiveTuple(env, (e, t) -> Arrays.asList(PredefinedType.INT, Core.union(PredefinedType.NIL, + createTupleType(e, PredefinedType.INT, Core.union(PredefinedType.NIL, t))))); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), t1, t2)); + } + + @Test + public void testStringCharSubtype() { + ComplexSemType st = (ComplexSemType) stringConst("a"); + Assert.assertEquals(st.subtypeDataList().length, 1); + StringSubtype subType = (StringSubtype) st.subtypeDataList()[0]; + Assert.assertEquals(subType.getChar().values.length, 1); + Assert.assertEquals(subType.getChar().values[0].value, "a"); + Assert.assertEquals(subType.getChar().allowed, true); + Assert.assertEquals(subType.getNonChar().values.length, 0); + Assert.assertEquals(subType.getNonChar().allowed, true); + } + + @Test + public void testStringNonCharSubtype() { + ComplexSemType st = (ComplexSemType) stringConst("abc"); + Assert.assertEquals(st.subtypeDataList().length, 1); + StringSubtype subType = (StringSubtype) st.subtypeDataList()[0]; + Assert.assertEquals(subType.getChar().values.length, 0); + Assert.assertEquals(subType.getChar().allowed, true); + Assert.assertEquals(subType.getNonChar().values.length, 1); + Assert.assertEquals(subType.getNonChar().values[0].value, "abc"); + Assert.assertEquals(subType.getNonChar().allowed, true); + } + + @Test + public void testStringSubtypeSingleValue() { + ComplexSemType abc = (ComplexSemType) stringConst("abc"); + StringSubtype abcSD = (StringSubtype) abc.subtypeDataList()[0]; + Assert.assertEquals(StringSubtype.stringSubtypeSingleValue(abcSD).get(), "abc"); + + ComplexSemType a = (ComplexSemType) stringConst("a"); + StringSubtype aSD = (StringSubtype) a.subtypeDataList()[0]; + Assert.assertEquals(StringSubtype.stringSubtypeSingleValue(aSD).get(), "a"); + + ComplexSemType aAndAbc = (ComplexSemType) Core.union(a, abc); + Assert.assertEquals(StringSubtype.stringSubtypeSingleValue(aAndAbc.subtypeDataList()[0]), + Optional.empty()); + + ComplexSemType intersect1 = (ComplexSemType) Core.intersect(aAndAbc, a); + Assert.assertEquals(StringSubtype.stringSubtypeSingleValue(intersect1.subtypeDataList()[0]).get(), "a"); + ComplexSemType intersect2 = (ComplexSemType) Core.intersect(aAndAbc, abc); + Assert.assertEquals(StringSubtype.stringSubtypeSingleValue(intersect2.subtypeDataList()[0]).get(), "abc"); + SemType intersect3 = Core.intersect(a, abc); + Assert.assertEquals(intersect3.toString(), PredefinedType.NEVER.toString()); + } + + private static SemType createTupleType(Env env, SemType... members) { + ListDefinition ld = new ListDefinition(); + return ld.tupleTypeWrapped(env, members); + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/UniformTypeBitSet.java b/semtypes/src/test/java/io/ballerina/types/TypeTestUtils.java similarity index 50% rename from semtypes/src/main/java/io/ballerina/semtype/UniformTypeBitSet.java rename to semtypes/src/test/java/io/ballerina/types/TypeTestUtils.java index dc3621d7d0d3..09b3161140e4 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/UniformTypeBitSet.java +++ b/semtypes/src/test/java/io/ballerina/types/TypeTestUtils.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -15,17 +15,21 @@ * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; -/** - * UniformTypeBitSet node. - * - * @since 2.0.0 - */ -public class UniformTypeBitSet implements SemType { - int bitset; +package io.ballerina.types; + +import io.ballerina.types.definition.ListDefinition; + +enum TypeTestUtils { + ; + + static SemType tuple(Env env, SemType... ty) { + ListDefinition ld = new ListDefinition(); + return ld.tupleTypeWrapped(env, ty); + } - public UniformTypeBitSet(int bitset) { - this.bitset = bitset; + static SemType roTuple(Env env, SemType... ty) { + ListDefinition ld = new ListDefinition(); + return ld.tupleTypeWrappedRo(env, ty); } } diff --git a/semtypes/src/test/resources/testng.xml b/semtypes/src/test/resources/testng.xml new file mode 100644 index 000000000000..018c612564da --- /dev/null +++ b/semtypes/src/test/resources/testng.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + diff --git a/settings.gradle b/settings.gradle index 21d05ef00350..1e0010e5f193 100644 --- a/settings.gradle +++ b/settings.gradle @@ -51,6 +51,7 @@ include(':ballerina-bindgen') include(':maven-resolver') include(':jballerina-unit-test') include(':jballerina-semtype-test') +include(':jballerina-semtype-port-test') include(':jballerina-benchmark-test') include(':ballerina-compiler-plugin-test') include(':ballerina-cli') @@ -216,6 +217,7 @@ project(':maven-resolver').projectDir = file('misc/maven-resolver') project(':jballerina-unit-test').projectDir = file('tests/jballerina-unit-test') project(':jballerina-semtype-test').projectDir = file('tests/jballerina-semtype-test') project(':jballerina-benchmark-test').projectDir = file('tests/jballerina-benchmark-test') +project(':jballerina-semtype-port-test').projectDir = file('tests/jballerina-semtype-port-test') project(':ballerina-compiler-plugin-test').projectDir = file('tests/ballerina-compiler-plugin-test') project(':central-client').projectDir = file('cli/central-client') project(':ballerina-cli').projectDir = file('cli/ballerina-cli') diff --git a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/ResourceSignatureTest.java b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/ResourceSignatureTest.java new file mode 100644 index 000000000000..6d48c821f508 --- /dev/null +++ b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/ResourceSignatureTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://wso2.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.ballerina.semantic.api.test; + +import io.ballerina.compiler.api.SemanticModel; +import io.ballerina.compiler.api.symbols.ResourceMethodSymbol; +import io.ballerina.compiler.api.symbols.resourcepath.ResourcePath; +import io.ballerina.projects.Document; +import io.ballerina.projects.Project; +import org.ballerinalang.test.BCompileUtil; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import static io.ballerina.semantic.api.test.util.SemanticAPITestUtils.getDefaultModulesSemanticModel; +import static io.ballerina.semantic.api.test.util.SemanticAPITestUtils.getDocumentForSingleSource; +import static io.ballerina.tools.text.LinePosition.from; +import static org.testng.Assert.assertEquals; + +/** + * Test cases for class symbols. + * + * @since 2.0.0 + */ +public class ResourceSignatureTest { + + private SemanticModel model; + private Document srcFile; + + @BeforeClass + public void setup() { + Project project = BCompileUtil.loadProject("test-src/resource_signature_test.bal"); + model = getDefaultModulesSemanticModel(project); + srcFile = getDocumentForSingleSource(project); + } + + @Test + public void testResourceSignature() { + ResourceMethodSymbol method = (ResourceMethodSymbol) model.symbol(srcFile, from(17, 22)).get(); + assertEquals(method.resourcePath().kind(), ResourcePath.Kind.PATH_SEGMENT_LIST); + assertEquals(method.resourcePath().signature(), "store/'order"); + assertEquals(method.signature(), "resource function get store/'order () returns string"); + } +} diff --git a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/TypedescriptorTest.java b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/TypedescriptorTest.java index 3ff673aa836f..e88942c82171 100644 --- a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/TypedescriptorTest.java +++ b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/TypedescriptorTest.java @@ -1128,10 +1128,10 @@ private Object[][] getSingletonType() { {353, 10, STRING, "\"abc\""}, {354, 8, FLOAT, "1.2"}, {355, 9, FLOAT, "3.4"}, - {356, 8, BYTE, "10"}, + {356, 8, INT, "10"}, {357, 11, INT, "46575"}, - {358, 12, FLOAT, "0xA1.B5p0"}, - {359, 14, FLOAT, "0xB2.8Fp1"}, + {358, 12, FLOAT, "161.70703125"}, + {359, 14, FLOAT, "357.1171875"}, {360, 8, STRING, "\"a\""}, {361, 8, STRING, "\"RED\""}, }; diff --git a/tests/ballerina-compiler-api-test/src/test/resources/test-src/resource_signature_test.bal b/tests/ballerina-compiler-api-test/src/test/resources/test-src/resource_signature_test.bal new file mode 100644 index 000000000000..a376cfe6cdf1 --- /dev/null +++ b/tests/ballerina-compiler-api-test/src/test/resources/test-src/resource_signature_test.bal @@ -0,0 +1,21 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +service class ServiceClass { + resource function get store/'order() returns string { + return self.message + "dot"; + } +} diff --git a/tests/ballerina-compiler-api-test/src/test/resources/testng.xml b/tests/ballerina-compiler-api-test/src/test/resources/testng.xml index ccc371762d38..5da3658012f2 100644 --- a/tests/ballerina-compiler-api-test/src/test/resources/testng.xml +++ b/tests/ballerina-compiler-api-test/src/test/resources/testng.xml @@ -44,6 +44,7 @@ + diff --git a/tests/ballerina-test-utils/build.gradle b/tests/ballerina-test-utils/build.gradle index 366be3b9ec98..9647982c1d06 100644 --- a/tests/ballerina-test-utils/build.gradle +++ b/tests/ballerina-test-utils/build.gradle @@ -22,6 +22,7 @@ plugins { dependencies { implementation project(':ballerina-tools-api') + implementation project(':ballerina-parser') implementation project(':ballerina-lang') implementation project(':ballerina-runtime') implementation project(':ballerina-cli') diff --git a/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/BCompileUtil.java b/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/BCompileUtil.java index ba140c78fc3f..3cf4bd961375 100644 --- a/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/BCompileUtil.java +++ b/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/BCompileUtil.java @@ -17,9 +17,12 @@ */ package org.ballerinalang.test; +import io.ballerina.compiler.syntax.tree.SyntaxTree; import io.ballerina.projects.BuildOptions; +import io.ballerina.projects.DocumentId; import io.ballerina.projects.JBallerinaBackend; import io.ballerina.projects.JvmTarget; +import io.ballerina.projects.Module; import io.ballerina.projects.NullBackend; import io.ballerina.projects.Package; import io.ballerina.projects.PackageCompilation; @@ -36,6 +39,7 @@ import org.slf4j.LoggerFactory; import org.wso2.ballerinalang.compiler.bir.model.BIRNode; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BPackageSymbol; +import org.wso2.ballerinalang.compiler.tree.BLangPackage; import org.wso2.ballerinalang.programfile.CompiledBinaryFile; import java.io.IOException; @@ -118,6 +122,15 @@ public static CompileResult compileOffline(String sourceFilePath) { return compileResult; } + public static PackageSyntaxTreePair compileSemType(String sourceFilePath) { + Project project = loadProject(sourceFilePath); + Package currentPackage = project.currentPackage(); + Module module = currentPackage.getDefaultModule(); + DocumentId docId = module.documentIds().iterator().next(); + return new PackageSyntaxTreePair(currentPackage.getCompilation().defaultModuleBLangPackage(), + module.document(docId).syntaxTree()); + } + public static BIRCompileResult generateBIR(String sourceFilePath) { Project project = loadProject(sourceFilePath); NullBackend nullBackend = NullBackend.from(project.currentPackage().getCompilation()); @@ -323,4 +336,20 @@ public byte[] getActualBIR() { public static String getPlatformFromBala(String balaName, String packageName, String version) { return balaName.split(packageName + "-")[1].split("-" + version)[0]; } + + /** + * Contain compiled {@code BLangPackage} and Syntax tree. + * This result is used to test sem-type relationships. + * + * @since 3.0.0 + */ + public static class PackageSyntaxTreePair { + public final BLangPackage bLangPackage; + public final SyntaxTree syntaxTree; + + public PackageSyntaxTreePair(BLangPackage bLangPackage, SyntaxTree syntaxTree) { + this.bLangPackage = bLangPackage; + this.syntaxTree = syntaxTree; + } + } } diff --git a/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/context/BServerInstance.java b/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/context/BServerInstance.java index dcc709041ed0..91e5d95ac2fc 100644 --- a/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/context/BServerInstance.java +++ b/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/context/BServerInstance.java @@ -387,7 +387,7 @@ private void runBuildTool(String command, String[] args, Map env env.put(entry.getKey(), entry.getValue()); } } - process = processBuilder.start(); + Process process = processBuilder.start(); serverInfoLogReader = new ServerLogReader("inputStream", process.getInputStream()); tmpInfoLeechers.forEach(leecher -> serverInfoLogReader.addLeecher(leecher)); @@ -503,7 +503,7 @@ private void executeJarFile(String jarPath, String[] args, Map e for (Map.Entry entry : envProperties.entrySet()) { env.put(entry.getKey(), entry.getValue()); } - process = processBuilder.start(); + Process process = processBuilder.start(); serverInfoLogReader = new ServerLogReader("inputStream", process.getInputStream()); tmpInfoLeechers.forEach(leecher -> serverInfoLogReader.addLeecher(leecher)); diff --git a/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/context/ServerLogReader.java b/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/context/ServerLogReader.java index dd18161b5a1d..b9a1eb6399ac 100644 --- a/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/context/ServerLogReader.java +++ b/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/context/ServerLogReader.java @@ -55,7 +55,8 @@ public ServerLogReader(String name, InputStream inputStream) { * Start reading the stream. */ public void start() { - Thread.startVirtualThread(this); + Thread thread = new Thread(this); + thread.start(); } /** @@ -124,7 +125,7 @@ public void run() { } feedAndPrint(s); } else { - TimeUnit.MILLISECONDS.sleep(1); + TimeUnit.MICROSECONDS.sleep(1); } } String s = bufferedReader.readLine(); diff --git a/tests/jballerina-debugger-integration-test/src/test/java/org/ballerinalang/debugger/test/adapter/variables/VariableVisibilityTest.java b/tests/jballerina-debugger-integration-test/src/test/java/org/ballerinalang/debugger/test/adapter/variables/VariableVisibilityTest.java index 9db72a68f1fb..df0fdbaee897 100644 --- a/tests/jballerina-debugger-integration-test/src/test/java/org/ballerinalang/debugger/test/adapter/variables/VariableVisibilityTest.java +++ b/tests/jballerina-debugger-integration-test/src/test/java/org/ballerinalang/debugger/test/adapter/variables/VariableVisibilityTest.java @@ -35,6 +35,7 @@ import java.util.Map; import static org.ballerinalang.debugger.test.utils.DebugTestRunner.VariableScope; + /** * Test class for variable visibility. */ @@ -48,13 +49,14 @@ public class VariableVisibilityTest extends BaseTestCase { @Override @BeforeClass public void setup() { - String testProjectName = "variable-tests"; - String testModuleFileName = "main.bal"; - debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); } @Test(description = "Variable visibility test at the beginning(first line) of the main() method") public void initialVariableVisibilityTest() throws BallerinaTestException { + String testProjectName = "variable-tests"; + String testModuleFileName = "main.bal"; + debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 123)); debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN); debugHitInfo = debugTestRunner.waitForDebugHit(25000); @@ -70,6 +72,10 @@ public void initialVariableVisibilityTest() throws BallerinaTestException { @Test(description = "Variable visibility test in the middle of the main() method for a new variable") public void newVariableVisibilityTest() throws BallerinaTestException { + String testProjectName = "variable-tests"; + String testModuleFileName = "main.bal"; + debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 245)); debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 316)); debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN); @@ -105,6 +111,10 @@ public void newVariableVisibilityTest() throws BallerinaTestException { @Test(description = "Variable visibility test in control flows") public void controlFlowVariableVisibilityTest() throws BallerinaTestException { + String testProjectName = "variable-tests"; + String testModuleFileName = "main.bal"; + debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 266)); debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 270)); debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 277)); @@ -177,6 +187,10 @@ public void controlFlowVariableVisibilityTest() throws BallerinaTestException { @Test(description = "Variable visibility test for global variables") public void globalVariableVisibilityTest() throws BallerinaTestException { + String testProjectName = "variable-tests"; + String testModuleFileName = "main.bal"; + debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 352)); debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 327)); debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN); @@ -215,6 +229,10 @@ public void globalVariableVisibilityTest() throws BallerinaTestException { @Test(description = "Variable visibility test for local variables at the last line of main() method") public void localVariableVisibilityTest() throws BallerinaTestException { + String testProjectName = "variable-tests"; + String testModuleFileName = "main.bal"; + debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 327)); debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 360)); debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN); @@ -339,6 +357,10 @@ public void localVariableVisibilityTest() throws BallerinaTestException { @Test(enabled = false, description = "Child variable visibility test for local variables at the last line of main" + "() method") public void localVariableChildrenVisibilityTest() throws BallerinaTestException { + String testProjectName = "variable-tests"; + String testModuleFileName = "main.bal"; + debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 327)); debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN); debugHitInfo = debugTestRunner.waitForDebugHit(25000); @@ -352,18 +374,18 @@ public void localVariableChildrenVisibilityTest() throws BallerinaTestException // xml attributes child variable visibility test Map xmlAttributesChildVariables = - debugTestRunner.fetchChildVariables(xmlChildVariables.get("attributes")); + debugTestRunner.fetchChildVariables(xmlChildVariables.get("attributes")); debugTestRunner.assertVariable(xmlAttributesChildVariables, "gender", "\"male\"", "string"); // xml children variable visibility test Map xmlChildrenVariables = - debugTestRunner.fetchChildVariables(xmlChildVariables.get("children")); + debugTestRunner.fetchChildVariables(xmlChildVariables.get("children")); debugTestRunner.assertVariable(xmlChildrenVariables, "[0]", "XMLElement", "xml"); debugTestRunner.assertVariable(xmlChildrenVariables, "[1]", "XMLElement", "xml"); // xml grand children variable visibility test Map xmlGrandChildrenVariables = - debugTestRunner.fetchChildVariables(xmlChildrenVariables.get("[0]")); + debugTestRunner.fetchChildVariables(xmlChildrenVariables.get("[0]")); debugTestRunner.assertVariable(xmlGrandChildrenVariables, "children", "XMLSequence (size = 1)", "xml"); // array child variable visibility test @@ -391,7 +413,7 @@ public void localVariableChildrenVisibilityTest() throws BallerinaTestException // record child variable visibility test (Student record) Map studentRecordChildVariables = - debugTestRunner.fetchChildVariables(localVariables.get("recordVar")); + debugTestRunner.fetchChildVariables(localVariables.get("recordVar")); debugTestRunner.assertVariable(studentRecordChildVariables, "1st_name", "\"John Doe\"", "string"); debugTestRunner.assertVariable(studentRecordChildVariables, "grades", "Grades", "record"); debugTestRunner.assertVariable(studentRecordChildVariables, "Ȧɢέ_ /:@[`{~π", "20", "int"); @@ -399,7 +421,7 @@ public void localVariableChildrenVisibilityTest() throws BallerinaTestException // record child variable visibility test (Grades record) Map gradesChildVariables = - debugTestRunner.fetchChildVariables(studentRecordChildVariables.get("grades")); + debugTestRunner.fetchChildVariables(studentRecordChildVariables.get("grades")); debugTestRunner.assertVariable(gradesChildVariables, "chemistry", "65", "int"); debugTestRunner.assertVariable(gradesChildVariables, "maths", "80", "int"); debugTestRunner.assertVariable(gradesChildVariables, "physics", "75", "int"); @@ -407,7 +429,7 @@ public void localVariableChildrenVisibilityTest() throws BallerinaTestException // anonymous record child variable visibility test Map recordChildVariables = - debugTestRunner.fetchChildVariables(localVariables.get("anonRecord")); + debugTestRunner.fetchChildVariables(localVariables.get("anonRecord")); debugTestRunner.assertVariable(recordChildVariables, "city", "\"London\"", "string"); debugTestRunner.assertVariable(recordChildVariables, "country", "\"UK\"", "string"); @@ -418,18 +440,18 @@ public void localVariableChildrenVisibilityTest() throws BallerinaTestException // error details child variable visibility test Map errorDetailsChildVariables = - debugTestRunner.fetchChildVariables(errorChildVariables.get("details")); + debugTestRunner.fetchChildVariables(errorChildVariables.get("details")); debugTestRunner.assertVariable(errorDetailsChildVariables, "message", "\"Simple error occurred\"", "string"); // future child variable visibility test Map futureChildVariables = - debugTestRunner.fetchChildVariables(localVariables.get("futureVar")); + debugTestRunner.fetchChildVariables(localVariables.get("futureVar")); debugTestRunner.assertVariable(futureChildVariables, "isDone", "true", "boolean"); debugTestRunner.assertVariable(futureChildVariables, "result", "90", "int"); // object child variable visibility test (Person object) Map personObjectChildVariables = - debugTestRunner.fetchChildVariables(localVariables.get("objectVar")); + debugTestRunner.fetchChildVariables(localVariables.get("objectVar")); debugTestRunner.assertVariable(personObjectChildVariables, "1st_name", "\"John\"", "string"); debugTestRunner.assertVariable(personObjectChildVariables, "address", "\"No 20, Palm grove\"", "string"); debugTestRunner.assertVariable(personObjectChildVariables, "parent", "()", "nil"); @@ -438,7 +460,7 @@ public void localVariableChildrenVisibilityTest() throws BallerinaTestException // anonymous object child variable visibility test (AnonPerson object) Map anonObjectChildVariables = - debugTestRunner.fetchChildVariables(localVariables.get("anonObjectVar")); + debugTestRunner.fetchChildVariables(localVariables.get("anonObjectVar")); debugTestRunner.assertVariable(anonObjectChildVariables, "1st_name", "\"John\"", "string"); debugTestRunner.assertVariable(anonObjectChildVariables, "address", "\"No 20, Palm grove\"", "string"); debugTestRunner.assertVariable(anonObjectChildVariables, "parent", "()", "nil"); @@ -459,21 +481,21 @@ public void localVariableChildrenVisibilityTest() throws BallerinaTestException // table with key child variable visibility test Map tableWithKeyChildVariables = - debugTestRunner.fetchChildVariables(localVariables.get("tableWithKeyVar")); + debugTestRunner.fetchChildVariables(localVariables.get("tableWithKeyVar")); debugTestRunner.assertVariable(tableWithKeyChildVariables, "[0]", "Employee", "record"); debugTestRunner.assertVariable(tableWithKeyChildVariables, "[1]", "Employee", "record"); debugTestRunner.assertVariable(tableWithKeyChildVariables, "[2]", "Employee", "record"); // table without key child variable visibility test Map tableWithoutKeyChildVariables = - debugTestRunner.fetchChildVariables(localVariables.get("tableWithoutKeyVar")); + debugTestRunner.fetchChildVariables(localVariables.get("tableWithoutKeyVar")); debugTestRunner.assertVariable(tableWithoutKeyChildVariables, "[0]", "Employee", "record"); debugTestRunner.assertVariable(tableWithoutKeyChildVariables, "[1]", "Employee", "record"); debugTestRunner.assertVariable(tableWithoutKeyChildVariables, "[2]", "Employee", "record"); // service child variable visibility test Map serviceChildVariables = - debugTestRunner.fetchChildVariables(localVariables.get("serviceVar")); + debugTestRunner.fetchChildVariables(localVariables.get("serviceVar")); debugTestRunner.assertVariable(serviceChildVariables, "i", "5", "int"); } @@ -510,7 +532,8 @@ public void objectVariableVisibilityTest() throws BallerinaTestException { debugTestRunner.assertVariable(selfChildVariables, "name", "\"John\"", "string"); } - @Test(description = "Worker related variable visibility test") + // Need to be enabled after fixing https://github.com/ballerina-platform/ballerina-lang/issues/43636 + @Test(description = "Worker related variable visibility test", enabled = false) public void workerVariableVisibilityTest() throws BallerinaTestException { String testProjectName = "worker-tests"; String testModuleFileName = "main.bal"; @@ -550,6 +573,68 @@ public void workerVariableVisibilityTest() throws BallerinaTestException { } } + @Test(description = "Binding pattern variables related visibility test") + public void bindingPatternVariableVisibilityTest() throws BallerinaTestException { + String testProjectName = "variable-tests-2"; + String testModuleFileName = "main.bal"; + debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); + + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 35)); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 40)); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 43)); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 46)); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 49)); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 80)); + + debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN); + Pair debugHitInfo = debugTestRunner.waitForDebugHit(25000); + + // simple binding pattern variables + localVariables = debugTestRunner.fetchVariables(debugHitInfo.getRight(), DebugTestRunner.VariableScope.LOCAL); + debugTestRunner.assertVariable(localVariables, "profession", "\"Software Engineer\"", "string"); + + // list binding pattern variables + debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugTestRunner.DebugResumeKind.NEXT_BREAKPOINT); + debugHitInfo = debugTestRunner.waitForDebugHit(10000); + // TODO: enable after fixing runtime issue https://github.com/ballerina-platform/ballerina-lang/issues/43623 +// localVariables = debugTestRunner.fetchVariables(debugHitInfo.getRight(), DebugTestRunner.VariableScope.LOCAL); +// debugTestRunner.assertVariable(localVariables, "id", "1234", "int"); +// debugTestRunner.assertVariable(localVariables, "firstName", "\"John Doe\"", "string"); + + // mapping binding pattern variables + debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugTestRunner.DebugResumeKind.NEXT_BREAKPOINT); + debugHitInfo = debugTestRunner.waitForDebugHit(10000); + // TODO: enable after fixing runtime issue https://github.com/ballerina-platform/ballerina-lang/issues/43623 +// localVariables = debugTestRunner.fetchVariables(debugHitInfo.getRight(), DebugTestRunner.VariableScope.LOCAL); +// debugTestRunner.assertVariable(localVariables, "givenName", "\"Anne\"", "string"); +// debugTestRunner.assertVariable(localVariables, "surName", "\"Frank\"", "string"); + + // error binding pattern variables + debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugTestRunner.DebugResumeKind.NEXT_BREAKPOINT); + debugHitInfo = debugTestRunner.waitForDebugHit(10000); + // TODO: enable after fixing runtime issue https://github.com/ballerina-platform/ballerina-lang/issues/43623 +// localVariables = debugTestRunner.fetchVariables(debugHitInfo.getRight(), DebugTestRunner.VariableScope.LOCAL); +// debugTestRunner.assertVariable(localVariables, "cause", "\"Database Error\"", "error"); +// debugTestRunner.assertVariable(localVariables, "code", "20", "int"); +// debugTestRunner.assertVariable(localVariables, "reason", "\"deadlock condition\"", "string"); + + // list binding pattern inside foreach statement + debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugTestRunner.DebugResumeKind.NEXT_BREAKPOINT); + debugHitInfo = debugTestRunner.waitForDebugHit(10000); + localVariables = debugTestRunner.fetchVariables(debugHitInfo.getRight(), DebugTestRunner.VariableScope.LOCAL); + debugTestRunner.assertVariable(localVariables, "name", "\"John\"", "string"); + debugTestRunner.assertVariable(localVariables, "age", "30", "int"); + + // list binding patterns inside match statement + debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugTestRunner.DebugResumeKind.NEXT_BREAKPOINT); + debugHitInfo = debugTestRunner.waitForDebugHit(10000); + // TODO: enable after fixing runtime issue https://github.com/ballerina-platform/ballerina-lang/issues/43623 +// localVariables = debugTestRunner.fetchVariables(debugHitInfo.getRight(), DebugTestRunner.VariableScope.LOCAL); +// debugTestRunner.assertVariable(localVariables, "remove", "Remove", "string"); +// debugTestRunner.assertVariable(localVariables, "all", "*", "string"); +// debugTestRunner.assertVariable(localVariables, "isDir", "true", "boolean"); + } + @Override @AfterMethod(alwaysRun = true) public void cleanUp() { diff --git a/tests/jballerina-debugger-integration-test/src/test/resources/project-based-tests/variable-tests-2/Ballerina.toml b/tests/jballerina-debugger-integration-test/src/test/resources/project-based-tests/variable-tests-2/Ballerina.toml new file mode 100644 index 000000000000..7352e4440b5f --- /dev/null +++ b/tests/jballerina-debugger-integration-test/src/test/resources/project-based-tests/variable-tests-2/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "debug_test_resources" +name = "variable_tests_2" +version = "0.1.0" diff --git a/tests/jballerina-debugger-integration-test/src/test/resources/project-based-tests/variable-tests-2/main.bal b/tests/jballerina-debugger-integration-test/src/test/resources/project-based-tests/variable-tests-2/main.bal new file mode 100644 index 000000000000..12fefe845eb4 --- /dev/null +++ b/tests/jballerina-debugger-integration-test/src/test/resources/project-based-tests/variable-tests-2/main.bal @@ -0,0 +1,99 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +type Person record {| + int id; + string fname; + string lname; +|}; + +type SampleErrorData record {| + int code; + string reason; +|}; + +type SampleError error; + +public function main() { + // 1. simple binding pattern + var profession = "Software Engineer"; + + // 2. list binding pattern + [int, [string, string]] [id, [firstName, _]] = getDetails(); + + // 3. mapping binding pattern + string givenName; + string surname; + {fname: givenName, lname: surname} = getPerson(); + + // 4. error binding pattern + var error(_, cause, code = code, reason = reason) = getSampleError(); + + // 5. binding patterns inside a foreach statement + string names = ""; + [string, int][] personInfoList = getPersonInfo(); + foreach [string, int] [name, age] in personInfoList { + names += " " + name; + } + + // 6. binding patterns inside a match statement + matchCommand(["Remove", "*", true]); +} + +function getDetails() returns [int, [string, string]] { + return [ + 1234, + ["John", "Doe"] + ]; +} + +function getPerson() returns Person { + Person person = {id: 1001, fname: "Anne", lname: "Frank"}; + return person; +} + +function getSampleError() returns SampleError { + return error("Transaction Failure", error("Database Error"), code = 20, reason = "deadlock condition"); +} + +function matchCommand(any commands) { + match commands { + var [show] => { + string name = "show"; + } + // The list binding pattern below binds lists that contain three list items + // where the third element in the list is the boolean value `true`. + var [remove, all, isDir] if isDir is true => { + string name = "remove"; + } + // The list binding pattern below binds lists that contain three list items. + var [remove, all, _] => { + string name = "remove"; + } + // The list binding pattern below binds lists that contain two list items, + // in which the second list item is also a list of two items. + var [copy, [file1, file2]] => { + string name = "copy"; + } + _ => { + string name = "unknown"; + } + } +} + +function getPersonInfo() returns [string, int][] { + return [["John", 30]]; +} diff --git a/tests/jballerina-integration-test/src/test/resources/testng.xml b/tests/jballerina-integration-test/src/test/resources/testng.xml index 323d222cd163..dffc5fb44a00 100644 --- a/tests/jballerina-integration-test/src/test/resources/testng.xml +++ b/tests/jballerina-integration-test/src/test/resources/testng.xml @@ -20,7 +20,7 @@ - + diff --git a/tests/jballerina-semtype-port-test/build.gradle b/tests/jballerina-semtype-port-test/build.gradle new file mode 100644 index 000000000000..983e476c2b7d --- /dev/null +++ b/tests/jballerina-semtype-port-test/build.gradle @@ -0,0 +1,28 @@ +plugins { + id 'javaProject' + id 'ballerinaLangLibLoad' +} +dependencies { + implementation 'org.slf4j:slf4j-api' + implementation project(':ballerina-lang') + implementation project(':testerina:testerina-core') + implementation project(':ballerina-cli') + implementation project(':ballerina-lang:jballerina.java') + implementation project(':ballerina-lang-test') + implementation project(':ballerina-runtime') + implementation project(':docerina') + + testImplementation 'org.testng:testng' + testImplementation project(path: ':ballerina-test-utils', configuration: 'shadow') +} + +description = 'JBallerina Semtyp port - Unit Test Module' + +test { + // Add additional system property to distinguish tests requiring all basic types + systemProperty "ballerina.semtype.all.types.test", "true" + + useTestNG() { + suites 'src/test/resources/testng.xml' + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerSemTypeResolver.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerSemTypeResolver.java new file mode 100644 index 000000000000..ec8f4641d79b --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerSemTypeResolver.java @@ -0,0 +1,779 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.semtype.port.test; + +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.Definition; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.definition.Field; +import io.ballerina.types.definition.FunctionDefinition; +import io.ballerina.types.definition.FunctionQualifiers; +import io.ballerina.types.definition.ListDefinition; +import io.ballerina.types.definition.MappingDefinition; +import io.ballerina.types.definition.Member; +import io.ballerina.types.definition.ObjectDefinition; +import io.ballerina.types.definition.ObjectQualifiers; +import io.ballerina.types.definition.StreamDefinition; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.TableSubtype; +import org.ballerinalang.model.elements.Flag; +import org.ballerinalang.model.tree.IdentifierNode; +import org.ballerinalang.model.tree.NodeKind; +import org.ballerinalang.model.tree.types.ArrayTypeNode; +import org.ballerinalang.model.tree.types.TypeNode; +import org.ballerinalang.model.types.TypeKind; +import org.jetbrains.annotations.NotNull; +import org.wso2.ballerinalang.compiler.tree.BLangFunction; +import org.wso2.ballerinalang.compiler.tree.BLangNode; +import org.wso2.ballerinalang.compiler.tree.BLangResourceFunction; +import org.wso2.ballerinalang.compiler.tree.BLangSimpleVariable; +import org.wso2.ballerinalang.compiler.tree.BLangTypeDefinition; +import org.wso2.ballerinalang.compiler.tree.BLangVariable; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangConstant; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; +import org.wso2.ballerinalang.compiler.tree.types.BLangArrayType; +import org.wso2.ballerinalang.compiler.tree.types.BLangBuiltInRefTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangConstrainedType; +import org.wso2.ballerinalang.compiler.tree.types.BLangErrorType; +import org.wso2.ballerinalang.compiler.tree.types.BLangFiniteTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangFunctionTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangIntersectionTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangObjectTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangRecordTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangStreamType; +import org.wso2.ballerinalang.compiler.tree.types.BLangTableTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangTupleTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangType; +import org.wso2.ballerinalang.compiler.tree.types.BLangUnionTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangUserDefinedType; +import org.wso2.ballerinalang.compiler.tree.types.BLangValueType; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +import static org.ballerinalang.model.tree.NodeKind.CONSTANT; +import static org.wso2.ballerinalang.compiler.semantics.analyzer.SymbolEnter.getTypeOrClassName; + +/** + * Resolves sem-types for module definitions using compiler side semtype implementation. + * + * @since 2201.11.0 + */ +public class CompilerSemTypeResolver extends SemTypeResolver { + + private final Map attachedDefinitions = new HashMap<>(); + + public void defineSemTypes(List moduleDefs, TypeTestContext cx) { + Map modTable = new LinkedHashMap<>(); + for (BLangNode typeAndClassDef : moduleDefs) { + modTable.put(getTypeOrClassName(typeAndClassDef), typeAndClassDef); + } + modTable = Collections.unmodifiableMap(modTable); + + for (BLangNode def : moduleDefs) { + if (def.getKind() == NodeKind.CLASS_DEFN) { + throw new UnsupportedOperationException("Semtype are not supported for class definitions yet"); + } else if (def.getKind() == CONSTANT) { + resolveConstant(cx, modTable, (BLangConstant) def); + } else { + BLangTypeDefinition typeDefinition = (BLangTypeDefinition) def; + resolveTypeDefn(cx, modTable, typeDefinition, 0); + } + } + } + + @Override + protected void resolveConstant(TypeTestContext cx, Map modTable, + BLangConstant constant) { + SemType semtype = evaluateConst(constant); + addSemTypeBType(constant.getTypeNode(), semtype); + cx.getEnv().addTypeDef(constant.name.value, semtype); + } + + private SemType evaluateConst(BLangConstant constant) { + switch (constant.symbol.value.type.getKind()) { + case INT: + return SemTypes.intConst((long) constant.symbol.value.value); + case BOOLEAN: + return SemTypes.booleanConst((boolean) constant.symbol.value.value); + case STRING: + return SemTypes.stringConst((String) constant.symbol.value.value); + case FLOAT: + return SemTypes.floatConst((double) constant.symbol.value.value); + default: + throw new UnsupportedOperationException("Expression type not implemented for const semtype"); + } + } + + @Override + protected void resolveTypeDefn(TypeTestContext cx, Map mod, + BLangTypeDefinition defn) { + resolveTypeDefn(cx, mod, defn, 0); + } + + private SemType resolveTypeDefn(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth) { + if (defn.semType != null) { + return defn.semType; + } + + if (depth == defn.semCycleDepth) { + throw new IllegalStateException("cyclic type definition: " + defn.name.value); + } + defn.semCycleDepth = depth; + SemType s = resolveTypeDesc(cx, mod, defn, depth, defn.typeNode); + addSemTypeBType(defn.getTypeNode(), s); + if (defn.semType == null) { + defn.semType = s; + defn.semCycleDepth = -1; + cx.getEnv().addTypeDef(defn.name.value, s); + return s; + } else { + return s; + } + } + + private void addSemTypeBType(BLangType typeNode, SemType semType) { + if (typeNode != null) { + typeNode.getBType().semType(semType); + } + } + + public SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, TypeNode td) { + if (td == null) { + return null; + } + switch (td.getKind()) { + case VALUE_TYPE: + return resolveTypeDesc(cx, (BLangValueType) td); + case BUILT_IN_REF_TYPE: + return resolveTypeDesc(cx, (BLangBuiltInRefTypeNode) td); + case RECORD_TYPE: + return resolveTypeDesc(cx, (BLangRecordTypeNode) td, mod, depth, defn); + case CONSTRAINED_TYPE: // map and typedesc + return resolveTypeDesc(cx, (BLangConstrainedType) td, mod, depth, defn); + case UNION_TYPE_NODE: + return resolveTypeDesc(cx, (BLangUnionTypeNode) td, mod, depth, defn); + case INTERSECTION_TYPE_NODE: + return resolveTypeDesc(cx, (BLangIntersectionTypeNode) td, mod, depth, defn); + case USER_DEFINED_TYPE: + return resolveTypeDesc(cx, (BLangUserDefinedType) td, mod, depth); + case FINITE_TYPE_NODE: + return resolveSingletonType((BLangFiniteTypeNode) td); + case ARRAY_TYPE: + return resolveTypeDesc(cx, mod, defn, depth, (BLangArrayType) td); + case TUPLE_TYPE_NODE: + return resolveTypeDesc(cx, mod, defn, depth, (BLangTupleTypeNode) td); + case FUNCTION_TYPE: + return resolveTypeDesc(cx, mod, defn, depth, (BLangFunctionTypeNode) td); + case TABLE_TYPE: + return resolveTypeDesc(cx, mod, defn, depth, (BLangTableTypeNode) td); + case ERROR_TYPE: + return resolveTypeDesc(cx, mod, defn, depth, (BLangErrorType) td); + case OBJECT_TYPE: + return resolveTypeDesc(cx, mod, defn, depth, (BLangObjectTypeNode) td); + case STREAM_TYPE: + return resolveTypeDesc(cx, mod, defn, depth, (BLangStreamType) td); + default: + throw new UnsupportedOperationException("type not implemented: " + td.getKind()); + } + } + + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, BLangObjectTypeNode td) { + SemType innerType = resolveNonDistinctObject(cx, mod, defn, depth, td); + if (td.flagSet.contains(Flag.DISTINCT)) { + return getDistinctObjectType((Env) cx.getInnerEnv(), innerType); + } + return innerType; + } + + private static SemType getDistinctObjectType(Env env, SemType innerType) { + return Core.intersect(SemTypes.objectDistinct(env.distinctAtomCountGetAndIncrement()), innerType); + } + + private SemType resolveNonDistinctObject(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, + int depth, BLangObjectTypeNode td) { + Env env = (Env) cx.getInnerEnv(); + if (td.defn != null) { + return td.defn.getSemType(env); + } + ObjectDefinition od = new ObjectDefinition(); + Stream fieldStream = td.fields.stream().map(field -> { + Set flags = field.flagSet; + Member.Visibility visibility = flags.contains(Flag.PUBLIC) ? Member.Visibility.Public : + Member.Visibility.Private; + SemType ty = resolveTypeDesc(cx, mod, defn, depth + 1, field.typeNode); + return new Member(field.name.value, ty, Member.Kind.Field, visibility, flags.contains(Flag.READONLY)); + }); + Stream methodStream = td.getFunctions().stream().map(method -> { + Member.Visibility visibility = method.flagSet.contains(Flag.PUBLIC) ? Member.Visibility.Public : + Member.Visibility.Private; + SemType ty = resolveTypeDesc(cx, mod, defn, depth + 1, method); + return new Member(method.name.value, ty, Member.Kind.Method, visibility, true); + }); + td.defn = od; + List members = Stream.concat(fieldStream, methodStream).toList(); + ObjectQualifiers qualifiers = getQualifiers(td); + return od.define(env, qualifiers, members); + } + + private static ObjectQualifiers getQualifiers(BLangObjectTypeNode td) { + Set flags = td.symbol.getFlags(); + ObjectQualifiers.NetworkQualifier networkQualifier; + assert !(flags.contains(Flag.CLIENT) && flags.contains(Flag.SERVICE)) : + "object can't be both client and service"; + if (flags.contains(Flag.CLIENT)) { + networkQualifier = ObjectQualifiers.NetworkQualifier.Client; + } else if (flags.contains(Flag.SERVICE)) { + networkQualifier = ObjectQualifiers.NetworkQualifier.Service; + } else { + networkQualifier = ObjectQualifiers.NetworkQualifier.None; + } + return new ObjectQualifiers(flags.contains(Flag.ISOLATED), flags.contains(Flag.READONLY), networkQualifier); + } + + // TODO: should we make definition part of BLangFunction as well? + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, BLangFunction functionType) { + Definition attached = attachedDefinitions.get(functionType); + Env env = (Env) cx.getInnerEnv(); + if (attached != null) { + return attached.getSemType(env); + } + FunctionDefinition fd = new FunctionDefinition(); + attachedDefinitions.put(functionType, fd); + Map paramScope = new HashMap<>(); + List params = getParameters(cx, mod, paramScope, defn, depth, functionType); + SemType rest; + if (functionType.getRestParameters() == null) { + rest = PredefinedType.NEVER; + } else { + ArrayTypeNode arrayType = (ArrayTypeNode) functionType.getRestParameters().getTypeNode(); + rest = resolveTypeDesc(cx, mod, defn, depth + 1, arrayType.getElementType()); + } + SemType returnType = resolveReturnType(cx, mod, paramScope, defn, depth + 1, functionType.getReturnTypeNode()); + ListDefinition paramListDefinition = new ListDefinition(); + FunctionQualifiers qualifiers = FunctionQualifiers.from(env, functionType.flagSet.contains(Flag.ISOLATED), + functionType.flagSet.contains(Flag.TRANSACTIONAL)); + return fd.define(env, paramListDefinition.defineListTypeWrapped(env, params, params.size(), rest, + CellAtomicType.CellMutability.CELL_MUT_NONE), returnType, qualifiers); + } + + @NotNull + private List getParameters(TypeTestContext cx, Map mod, + Map paramScope, BLangTypeDefinition defn, int depth, + BLangFunction functionType) { + List params = new ArrayList<>(); + if (functionType instanceof BLangResourceFunction resourceFunctionType) { + params.add(SemTypes.stringConst(resourceFunctionType.methodName.value)); + for (var each : resourceFunctionType.resourcePathSegments) { + params.add(resolveTypeDesc(cx, mod, defn, depth + 1, each.typeNode)); + } + } + for (BLangSimpleVariable paramVar : functionType.getParameters()) { + SemType semType = resolveTypeDesc(cx, mod, defn, depth + 1, paramVar.typeNode); + if (Core.isSubtypeSimple(semType, PredefinedType.TYPEDESC)) { + paramScope.put(paramVar.name.value, paramVar); + } + params.add(semType); + } + return params; + } + + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, BLangFunctionTypeNode td) { + Env env = (Env) cx.getInnerEnv(); + if (isFunctionTop(td)) { + if (td.flagSet.contains(Flag.ISOLATED) || td.flagSet.contains(Flag.TRANSACTIONAL)) { + FunctionQualifiers qualifiers = FunctionQualifiers.from(env, td.flagSet.contains(Flag.ISOLATED), + td.flagSet.contains(Flag.TRANSACTIONAL)); + // I think param type here is wrong. It should be the intersection of all list types, but I think + // never is close enough + return new FunctionDefinition().define(env, PredefinedType.NEVER, PredefinedType.VAL, qualifiers); + } + return PredefinedType.FUNCTION; + } + if (td.defn != null) { + return td.defn.getSemType((Env) cx.getInnerEnv()); + } + FunctionDefinition fd = new FunctionDefinition(); + td.defn = fd; + Map tdScope = new HashMap<>(); + List params = new ArrayList<>(td.params.size()); + for (BLangSimpleVariable param : td.params) { + SemType paramType = resolveTypeDesc(cx, mod, defn, depth + 1, param.typeNode); + params.add(paramType); + if (Core.isSubtypeSimple(paramType, PredefinedType.TYPEDESC)) { + tdScope.put(param.name.value, param); + } + } + SemType rest; + if (td.restParam == null) { + rest = PredefinedType.NEVER; + } else { + BLangArrayType restArrayType = (BLangArrayType) td.restParam.typeNode; + rest = resolveTypeDesc(cx, mod, defn, depth + 1, restArrayType.elemtype); + } + SemType returnType = resolveReturnType(cx, mod, tdScope, defn, depth + 1, td.returnTypeNode); + ListDefinition paramListDefinition = new ListDefinition(); + FunctionQualifiers qualifiers = FunctionQualifiers.from(env, td.flagSet.contains(Flag.ISOLATED), + td.flagSet.contains(Flag.TRANSACTIONAL)); + return fd.define(env, paramListDefinition.defineListTypeWrapped(env, params, params.size(), rest, + CellAtomicType.CellMutability.CELL_MUT_NONE), returnType, qualifiers); + } + + private SemType resolveReturnType(TypeTestContext cx, Map mod, + Map mayBeDependentlyTypeNodes, BLangTypeDefinition defn, + int depth, BLangType returnTypeNode) { + if (returnTypeNode == null) { + return PredefinedType.NIL; + } + SemType innerType; + // Dependently typed function are quite rare so doing it via exception handling should be faster than actually + // checking if it is a dependently typed one. + boolean isDependentlyType; + try { + innerType = resolveTypeDesc(cx, mod, defn, depth + 1, returnTypeNode); + isDependentlyType = false; + } catch (IndexOutOfBoundsException err) { + innerType = + resolveDependentlyTypedReturnType(cx, mod, mayBeDependentlyTypeNodes, defn, depth, returnTypeNode); + isDependentlyType = true; + } + ListDefinition ld = new ListDefinition(); + return ld.tupleTypeWrapped((Env) cx.getInnerEnv(), + !isDependentlyType ? PredefinedType.BOOLEAN : SemTypes.booleanConst(true), innerType); + } + + private SemType resolveDependentlyTypedReturnType(TypeTestContext cx, Map mod, + Map mayBeDependentlyTypeNodes, + BLangTypeDefinition defn, int depth, + TypeNode returnTypeNode) { + Map combined = new HashMap<>(mod); + combined.putAll(mayBeDependentlyTypeNodes); + return resolveTypeDesc(cx, combined, defn, depth + 1, returnTypeNode); + } + + private boolean isFunctionTop(BLangFunctionTypeNode td) { + return td.params.isEmpty() && td.restParam == null && td.returnTypeNode == null; + } + + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, BLangArrayType td) { + if (td.defn != null) { + return td.defn.getSemType((Env) cx.getInnerEnv()); + } + ListDefinition ld = new ListDefinition(); + td.defn = ld; + + int dimensions = td.dimensions; + SemType accum = resolveTypeDesc(cx, mod, defn, depth + 1, td.elemtype); + for (int i = 0; i < dimensions; i++) { + int size = from(mod, td.sizes.get(i)); + if (i == dimensions - 1) { + accum = resolveListInner(cx, ld, size, accum); + } else { + accum = resolveListInner(cx, size, accum); + } + } + return accum; + } + + private SemType resolveListInner(TypeTestContext cx, int size, SemType eType) { + ListDefinition ld = new ListDefinition(); + return resolveListInner(cx, ld, size, eType); + } + + private static SemType resolveListInner(TypeTestContext cx, ListDefinition ld, int size, SemType eType) { + Env env = (Env) cx.getInnerEnv(); + if (size != -1) { + return ld.defineListTypeWrapped(env, List.of(eType), Math.abs(size), PredefinedType.NEVER, + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + } else { + return ld.defineListTypeWrapped(env, List.of(), 0, eType, + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + } + } + + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, + BLangTupleTypeNode td) { + Env env = (Env) cx.getInnerEnv(); + if (td.defn != null) { + return td.defn.getSemType(env); + } + ListDefinition ld = new ListDefinition(); + td.defn = ld; + List memberSemTypes = + td.members.stream().map(member -> resolveTypeDesc(cx, mod, defn, depth + 1, member.typeNode)) + .toList(); + SemType rest = td.restParamType != null ? resolveTypeDesc(cx, mod, defn, depth + 1, td.restParamType) : + PredefinedType.NEVER; + return ld.defineListTypeWrapped(env, memberSemTypes, memberSemTypes.size(), rest); + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangValueType td) { + Context innerContext = (Context) cx.getInnerContext(); + switch (td.typeKind) { + case NIL: + return PredefinedType.NIL; + case BOOLEAN: + return PredefinedType.BOOLEAN; + case BYTE: + return PredefinedType.BYTE; + case INT: + return PredefinedType.INT; + case FLOAT: + return PredefinedType.FLOAT; + case DECIMAL: + return PredefinedType.DECIMAL; + case STRING: + return PredefinedType.STRING; + case TYPEDESC: + return PredefinedType.TYPEDESC; + case ERROR: + return PredefinedType.ERROR; + case HANDLE: + return PredefinedType.HANDLE; + case XML: + return PredefinedType.XML; + case ANY: + return PredefinedType.ANY; + case READONLY: + return PredefinedType.VAL_READONLY; + case ANYDATA: + return Core.createAnydata(innerContext); + case JSON: + return Core.createJson(innerContext); + default: + throw new IllegalStateException("Unknown type: " + td); + } + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangBuiltInRefTypeNode td) { + return switch (td.typeKind) { + case NEVER -> PredefinedType.NEVER; + case XML -> PredefinedType.XML; + case JSON -> Core.createJson((Context) cx.getInnerContext()); + case FUTURE -> PredefinedType.FUTURE; + default -> throw new UnsupportedOperationException("Built-in ref type not implemented: " + td.typeKind); + }; + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangConstrainedType td, Map mod, + int depth, BLangTypeDefinition defn) { + TypeKind typeKind = ((BLangBuiltInRefTypeNode) td.getType()).getTypeKind(); + return switch (typeKind) { + case MAP -> resolveMapTypeDesc(td, cx, mod, depth, defn); + case XML -> resolveXmlTypeDesc(td, cx, mod, depth, defn); + case FUTURE -> resolveFutureTypeDesc(td, cx, mod, depth, defn); + case TYPEDESC -> resolveTypedescTypeDesc(td, cx, mod, depth, defn); + default -> throw new IllegalStateException("Unexpected constrained type: " + typeKind); + }; + } + + private SemType resolveMapTypeDesc(BLangConstrainedType td, TypeTestContext cx, Map mod, + int depth, BLangTypeDefinition typeDefinition) { + Env env = (Env) cx.getInnerEnv(); + if (td.defn != null) { + return td.defn.getSemType(env); + } + + MappingDefinition d = new MappingDefinition(); + td.defn = d; + + SemType rest = resolveTypeDesc(cx, mod, typeDefinition, depth + 1, td.constraint); + return d.defineMappingTypeWrapped(env, Collections.emptyList(), rest == null ? PredefinedType.NEVER : rest); + } + + private SemType resolveXmlTypeDesc(BLangConstrainedType td, TypeTestContext cx, Map mod, + int depth, + BLangTypeDefinition defn) { + SemType constraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + return SemTypes.xmlSequence(constraint); + } + + private SemType resolveFutureTypeDesc(BLangConstrainedType td, TypeTestContext cx, + Map mod, int depth, + BLangTypeDefinition defn) { + SemType constraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + return SemTypes.futureContaining((Env) cx.getInnerEnv(), constraint); + } + + private SemType resolveTypedescTypeDesc(BLangConstrainedType td, TypeTestContext cx, + Map mod, int depth, + BLangTypeDefinition defn) { + SemType constraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + return SemTypes.typedescContaining((Env) cx.getInnerEnv(), constraint); + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangRecordTypeNode td, Map mod, + int depth, BLangTypeDefinition typeDefinition) { + if (td.defn != null) { + return td.defn.getSemType((Env) cx.getInnerEnv()); + } + + MappingDefinition d = new MappingDefinition(); + td.defn = d; + + List fields = new ArrayList<>(); + for (BLangSimpleVariable field : td.fields) { + SemType ty = resolveTypeDesc(cx, mod, typeDefinition, depth + 1, field.typeNode); + if (Core.isNever(ty)) { + throw new IllegalStateException("record field can't be never"); + } + fields.add(Field.from(field.name.value, ty, false, field.flagSet.contains(Flag.OPTIONAL))); + } + + SemType rest; + if (!td.isSealed() && td.getRestFieldType() == null) { + rest = Core.createAnydata((Context) cx.getInnerContext()); + } else { + rest = resolveTypeDesc(cx, mod, typeDefinition, depth + 1, td.restFieldType); + } + + return d.defineMappingTypeWrapped((Env) cx.getInnerEnv(), fields, rest == null ? PredefinedType.NEVER : rest); + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangUnionTypeNode td, Map mod, + int depth, + BLangTypeDefinition defn) { + Iterator iterator = td.memberTypeNodes.iterator(); + SemType u = resolveTypeDesc(cx, mod, defn, depth, iterator.next()); + while (iterator.hasNext()) { + u = Core.union(u, resolveTypeDesc(cx, mod, defn, depth, iterator.next())); + } + return u; + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangIntersectionTypeNode td, + Map mod, int depth, + BLangTypeDefinition defn) { + Iterator iterator = td.constituentTypeNodes.iterator(); + SemType i = resolveTypeDesc(cx, mod, defn, depth, iterator.next()); + while (iterator.hasNext()) { + i = Core.intersect(i, resolveTypeDesc(cx, mod, defn, depth, iterator.next())); + } + return i; + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangUserDefinedType td, Map mod, + int depth) { + String name = td.typeName.value; + // Need to replace this with a real package lookup + if (td.pkgAlias.value.equals("int")) { + return resolveIntSubtype(name); + } else if (td.pkgAlias.value.equals("string") && name.equals("Char")) { + return SemTypes.CHAR; + } else if (td.pkgAlias.value.equals("xml")) { + return resolveXmlSubtype(name); + } else if (td.pkgAlias.value.equals("regexp") && name.equals("RegExp")) { + return PredefinedType.REGEXP; + } + + BLangNode moduleLevelDef = mod.get(name); + if (moduleLevelDef == null) { + throw new IndexOutOfBoundsException("unknown type " + name); + } + + switch (moduleLevelDef.getKind()) { + case TYPE_DEFINITION -> { + SemType ty = resolveTypeDefn(cx, mod, (BLangTypeDefinition) moduleLevelDef, depth); + if (td.flagSet.contains(Flag.DISTINCT)) { + return getDistinctSemType(cx, ty); + } + return ty; + } + case CONSTANT -> { + BLangConstant constant = (BLangConstant) moduleLevelDef; + return resolveTypeDefn(cx, mod, constant.getAssociatedTypeDefinition(), depth); + } + case VARIABLE -> { + // This happens when the type is a parameter of a dependently typed function + BLangVariable variable = (BLangVariable) moduleLevelDef; + BLangConstrainedType typeDescType = (BLangConstrainedType) variable.getTypeNode(); + return resolveTypeDesc(cx, mod, null, depth, typeDescType.constraint); + } + default -> throw new UnsupportedOperationException("class defns not implemented"); + } + } + + private SemType getDistinctSemType(TypeTestContext cx, SemType innerType) { + Env env = (Env) cx.getInnerEnv(); + if (Core.isSubtypeSimple(innerType, PredefinedType.OBJECT)) { + return CompilerSemTypeResolver.getDistinctObjectType(env, innerType); + } else if (Core.isSubtypeSimple(innerType, PredefinedType.ERROR)) { + return getDistinctErrorType(env, innerType); + } + throw new IllegalArgumentException("Distinct type not supported for: " + innerType); + } + + private SemType resolveIntSubtype(String name) { + // TODO: support MAX_VALUE + return switch (name) { + case "Signed8" -> SemTypes.SINT8; + case "Signed16" -> SemTypes.SINT16; + case "Signed32" -> SemTypes.SINT32; + case "Unsigned8" -> SemTypes.UINT8; + case "Unsigned16" -> SemTypes.UINT16; + case "Unsigned32" -> SemTypes.UINT32; + default -> throw new UnsupportedOperationException("Unknown int subtype: " + name); + }; + } + + private SemType resolveXmlSubtype(String name) { + return switch (name) { + case "Element" -> SemTypes.XML_ELEMENT; + case "Comment" -> SemTypes.XML_COMMENT; + case "Text" -> SemTypes.XML_TEXT; + case "ProcessingInstruction" -> SemTypes.XML_PI; + default -> throw new IllegalStateException("Unknown XML subtype: " + name); + }; + } + + private SemType resolveSingletonType(BLangFiniteTypeNode td) { + return resolveSingletonType(td.valueSpace); + } + + private SemType resolveSingletonType(List valueSpace) { + List types = new ArrayList<>(); + for (BLangExpression bLangExpression : valueSpace) { + types.add(resolveSingletonType((BLangLiteral) bLangExpression)); + } + + Iterator iter = types.iterator(); + SemType u = iter.next(); + while (iter.hasNext()) { + u = SemTypes.union(u, iter.next()); + } + return u; + } + + private SemType resolveSingletonType(BLangLiteral literal) { + return resolveSingletonType(literal.value, literal.getDeterminedType().getKind()); + } + + private SemType resolveSingletonType(Object value, TypeKind targetTypeKind) { + return switch (targetTypeKind) { + case NIL -> PredefinedType.NIL; + case BOOLEAN -> SemTypes.booleanConst((Boolean) value); + case INT, BYTE -> { + assert !(value instanceof Byte); + yield SemTypes.intConst(((Number) value).longValue()); + } + case FLOAT -> { + double doubleVal; + if (value instanceof Long longValue) { + doubleVal = longValue.doubleValue(); + } else if (value instanceof Double doubleValue) { + doubleVal = doubleValue; + } else { + // literal value will be a string if it wasn't within the bounds of what is supported by Java Long + // or Double when it was parsed in BLangNodeBuilder. + try { + doubleVal = Double.parseDouble((String) value); + } catch (NumberFormatException e) { + // We reach here when there is a syntax error. Mock the flow with default float value. + yield FloatSubtype.floatConst(0); + } + } + yield SemTypes.floatConst(doubleVal); + // literal value will be a string if it wasn't within the bounds of what is supported by Java Long + // or Double when it was parsed in BLangNodeBuilder. + // We reach here when there is a syntax error. Mock the flow with default float value. + } + case DECIMAL -> SemTypes.decimalConst((String) value); + case STRING -> SemTypes.stringConst((String) value); + default -> throw new UnsupportedOperationException("Finite type not implemented for: " + targetTypeKind); + }; + } + + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, BLangTableTypeNode td) { + SemType tableConstraint = resolveTypeDesc(cx, mod, defn, depth, td.constraint); + Context context = (Context) cx.getInnerContext(); + if (td.tableKeySpecifier != null) { + List fieldNameIdentifierList = td.tableKeySpecifier.fieldNameIdentifierList; + String[] fieldNames = fieldNameIdentifierList.stream().map(IdentifierNode::getValue).toArray(String[]::new); + return TableSubtype.tableContainingKeySpecifier(context, tableConstraint, fieldNames); + } + + if (td.tableKeyTypeConstraint != null) { + SemType keyConstraint = resolveTypeDesc(cx, mod, defn, depth, td.tableKeyTypeConstraint.keyType); + return TableSubtype.tableContainingKeyConstraint(context, tableConstraint, keyConstraint); + } + + return TableSubtype.tableContaining(context.env, tableConstraint); + } + + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, + BLangErrorType td) { + SemType err; + if (td.detailType == null) { + err = PredefinedType.ERROR; + } else { + SemType detail = resolveTypeDesc(cx, mod, defn, depth, td.detailType); + err = SemTypes.errorDetail(detail); + } + + if (td.flagSet.contains(Flag.DISTINCT)) { + Env env = (Env) cx.getInnerEnv(); + err = getDistinctErrorType(env, err); + } + return err; + } + + private static SemType getDistinctErrorType(Env env, SemType err) { + return Core.intersect(SemTypes.errorDistinct(env.distinctAtomCountGetAndIncrement()), err); + } + + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, BLangStreamType td) { + if (td.constraint == null) { + return PredefinedType.STREAM; + } + Env env = (Env) cx.getInnerEnv(); + if (td.defn != null) { + return td.defn.getSemType(env); + } + StreamDefinition d = new StreamDefinition(); + td.defn = d; + + SemType valueType = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + SemType completionType = td.error == null ? + PredefinedType.NIL : resolveTypeDesc(cx, mod, defn, depth + 1, td.error); + return d.define(env, valueType, completionType); + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerTypeTestAPI.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerTypeTestAPI.java new file mode 100644 index 000000000000..71870e480870 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerTypeTestAPI.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.Context; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; + +public final class CompilerTypeTestAPI implements TypeTestAPI { + + private static final CompilerTypeTestAPI INSTANCE = new CompilerTypeTestAPI(); + + private CompilerTypeTestAPI() { + } + + public static CompilerTypeTestAPI getInstance() { + return INSTANCE; + } + + @Override + public boolean isSubtype(TypeTestContext cx, SemType t1, SemType t2) { + return SemTypes.isSubtype(form(cx), t1, t2); + } + + private static Context form(TypeTestContext cx) { + return (Context) cx.getInnerContext(); + } + + @Override + public boolean isSubtypeSimple(SemType t1, SemType t2) { + return SemTypes.isSubtypeSimple(t1, (BasicTypeBitSet) t2); + } + + @Override + public boolean isListType(SemType t) { + return SemTypes.isSubtypeSimple(t, PredefinedType.LIST); + } + + @Override + public boolean isMapType(SemType t) { + return SemTypes.isSubtypeSimple(t, PredefinedType.MAPPING); + } + + @Override + public SemType intConst(long l) { + return SemTypes.intConst(l); + } + + @Override + public SemType mappingMemberTypeInnerVal(TypeTestContext context, SemType semType, SemType m) { + return SemTypes.mappingMemberTypeInnerVal((Context) context.getInnerContext(), semType, m); + } + + @Override + public SemType listProj(TypeTestContext context, SemType t, SemType key) { + return SemTypes.listProj((Context) context.getInnerContext(), t, key); + } + + @Override + public SemType listMemberType(TypeTestContext context, SemType t, SemType key) { + return SemTypes.listMemberType((Context) context.getInnerContext(), t, key); + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerTypeTestEnv.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerTypeTestEnv.java new file mode 100644 index 000000000000..116d114445be --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerTypeTestEnv.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +import io.ballerina.types.Env; +import io.ballerina.types.SemType; + +import java.util.Map; + +public class CompilerTypeTestEnv implements TypeTestEnv { + + private final Env env; + + private CompilerTypeTestEnv(Env env) { + this.env = env; + } + + public static synchronized CompilerTypeTestEnv from(Env env) { + return new CompilerTypeTestEnv(env); + } + + @Override + public Map getTypeNameSemTypeMap() { + return env.getTypeNameSemTypeMap(); + } + + @Override + public void addTypeDef(String value, SemType semtype) { + env.addTypeDef(value, semtype); + } + + @Override + public Object getInnerEnv() { + return env; + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/ComplierTypeTestContext.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/ComplierTypeTestContext.java new file mode 100644 index 000000000000..ceafd5eddba1 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/ComplierTypeTestContext.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +import io.ballerina.types.Context; +import io.ballerina.types.SemType; + +public class ComplierTypeTestContext implements TypeTestContext { + + private final Context context; + private final TypeTestEnv env; + + private ComplierTypeTestContext(Context context) { + this.context = context; + env = CompilerTypeTestEnv.from(context.env); + } + + public static synchronized ComplierTypeTestContext from(Context context) { + return new ComplierTypeTestContext(context); + } + + @Override + public TypeTestEnv getEnv() { + return env; + } + + @Override + public Object getInnerEnv() { + return context.env; + } + + @Override + public Object getInnerContext() { + return context; + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeSemTypeResolver.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeSemTypeResolver.java new file mode 100644 index 000000000000..14644cc3e289 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeSemTypeResolver.java @@ -0,0 +1,738 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.Definition; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.ErrorUtils; +import io.ballerina.runtime.internal.types.semtype.FunctionDefinition; +import io.ballerina.runtime.internal.types.semtype.FunctionQualifiers; +import io.ballerina.runtime.internal.types.semtype.FutureUtils; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import io.ballerina.runtime.internal.types.semtype.MappingDefinition; +import io.ballerina.runtime.internal.types.semtype.Member; +import io.ballerina.runtime.internal.types.semtype.ObjectDefinition; +import io.ballerina.runtime.internal.types.semtype.ObjectQualifiers; +import io.ballerina.runtime.internal.types.semtype.StreamDefinition; +import io.ballerina.runtime.internal.types.semtype.TableUtils; +import io.ballerina.runtime.internal.types.semtype.TypedescUtils; +import io.ballerina.runtime.internal.types.semtype.XmlUtils; +import org.ballerinalang.model.elements.Flag; +import org.ballerinalang.model.tree.IdentifierNode; +import org.ballerinalang.model.tree.types.ArrayTypeNode; +import org.ballerinalang.model.tree.types.TypeNode; +import org.ballerinalang.model.types.TypeKind; +import org.wso2.ballerinalang.compiler.tree.BLangFunction; +import org.wso2.ballerinalang.compiler.tree.BLangNode; +import org.wso2.ballerinalang.compiler.tree.BLangResourceFunction; +import org.wso2.ballerinalang.compiler.tree.BLangSimpleVariable; +import org.wso2.ballerinalang.compiler.tree.BLangTypeDefinition; +import org.wso2.ballerinalang.compiler.tree.BLangVariable; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangConstant; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; +import org.wso2.ballerinalang.compiler.tree.types.BLangArrayType; +import org.wso2.ballerinalang.compiler.tree.types.BLangBuiltInRefTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangConstrainedType; +import org.wso2.ballerinalang.compiler.tree.types.BLangErrorType; +import org.wso2.ballerinalang.compiler.tree.types.BLangFiniteTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangFunctionTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangIntersectionTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangObjectTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangRecordTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangStreamType; +import org.wso2.ballerinalang.compiler.tree.types.BLangTableTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangTupleTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangType; +import org.wso2.ballerinalang.compiler.tree.types.BLangUnionTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangUserDefinedType; +import org.wso2.ballerinalang.compiler.tree.types.BLangValueType; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; + +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED16_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED16_MIN_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED32_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED32_MIN_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED8_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED8_MIN_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED16_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED32_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED8_MAX_VALUE; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; + +/** + * Resolves sem-types for module definitions using runtime side semtype implementation. + * + * @since 2201.11.0 + */ +class RuntimeSemTypeResolver extends SemTypeResolver { + + private static final SemType[] EMPTY_SEMTYPE_ARR = {}; + Map attachedSemType = new HashMap<>(); + Map semTypeMemo = new HashMap<>(); + Map attachedDefinitions = new HashMap<>(); + + @Override + public void resolveTypeDefn(TypeTestContext cx, Map modTable, + BLangTypeDefinition typeDefinition) { + resolveTypeDefnRec(cx, modTable, typeDefinition, 0); + } + + @Override + public void resolveConstant(TypeTestContext cx, Map modTable, BLangConstant constant) { + SemType semtype = evaluateConst(constant); + attachToBType(constant.typeNode, semtype); + cx.getEnv().addTypeDef(constant.name.value, semtype); + } + + private SemType resolveTypeDefnRec(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth) { + SemType memo = semTypeMemo.get(defn); + if (memo != null) { + return memo; + } + if (depth == defn.semCycleDepth) { + throw new IllegalStateException("cyclic type definition: " + defn.name.value); + } + + defn.semCycleDepth = depth; + SemType s = resolveTypeDesc(cx, mod, defn, depth, defn.typeNode); + attachToBType(defn.getTypeNode(), s); + if (!semTypeMemo.containsKey(defn)) { + semTypeMemo.put(defn, s); + defn.semCycleDepth = -1; + cx.getEnv().addTypeDef(defn.name.value, s); + return s; + } else { + return s; + } + } + + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, TypeNode td) { + if (td == null) { + return null; + } + return switch (td.getKind()) { + case VALUE_TYPE -> resolveTypeDesc(cx, (BLangValueType) td); + case BUILT_IN_REF_TYPE -> resolveTypeDesc((BLangBuiltInRefTypeNode) td); + case INTERSECTION_TYPE_NODE -> resolveTypeDesc(cx, (BLangIntersectionTypeNode) td, mod, depth, defn); + case UNION_TYPE_NODE -> resolveTypeDesc(cx, (BLangUnionTypeNode) td, mod, depth, defn); + case USER_DEFINED_TYPE -> resolveTypeDesc(cx, (BLangUserDefinedType) td, mod, depth); + case FINITE_TYPE_NODE -> resolveSingletonType((BLangFiniteTypeNode) td); + case ARRAY_TYPE -> resolveArrayTypeDesc(cx, mod, defn, depth, (BLangArrayType) td); + case TUPLE_TYPE_NODE -> resolveTupleTypeDesc(cx, mod, defn, depth, (BLangTupleTypeNode) td); + case CONSTRAINED_TYPE -> resolveConstrainedTypeDesc(cx, mod, defn, depth, (BLangConstrainedType) td); + case RECORD_TYPE -> resolveRecordTypeDesc(cx, mod, defn, depth, (BLangRecordTypeNode) td); + case FUNCTION_TYPE -> resolveFunctionTypeDesc(cx, mod, defn, depth, (BLangFunctionTypeNode) td); + case OBJECT_TYPE -> resolveObjectTypeDesc(cx, mod, defn, depth, (BLangObjectTypeNode) td); + case ERROR_TYPE -> resolveErrorTypeDesc(cx, mod, defn, depth, (BLangErrorType) td); + case TABLE_TYPE -> resolveTableTypeDesc(cx, mod, defn, depth, (BLangTableTypeNode) td); + case STREAM_TYPE -> resolveStreamTypeDesc(cx, mod, defn, depth, (BLangStreamType) td); + default -> throw new UnsupportedOperationException("type not implemented: " + td.getKind()); + }; + } + + private SemType resolveStreamTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangStreamType td) { + if (td.constraint == null) { + return Builder.getStreamType(); + } + Env env = (Env) cx.getInnerEnv(); + Definition attachedDefinition = attachedDefinitions.get(td); + if (attachedDefinition != null) { + return attachedDefinition.getSemType(env); + } + StreamDefinition sd = new StreamDefinition(); + attachedDefinitions.put(td, sd); + + SemType valueType = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + SemType completionType = td.error == null ? Builder.getNilType() : + resolveTypeDesc(cx, mod, defn, depth + 1, td.error); + return sd.define(env, valueType, completionType); + } + + private SemType resolveTableTypeDesc(TypeTestContext cx, + Map mod, BLangTypeDefinition defn, + int depth, BLangTableTypeNode td) { + SemType tableConstraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + Context context = (Context) cx.getInnerContext(); + if (td.tableKeySpecifier != null) { + List fieldNameIdentifierList = td.tableKeySpecifier.fieldNameIdentifierList; + String[] fieldNames = fieldNameIdentifierList.stream().map(IdentifierNode::getValue).toArray(String[]::new); + return TableUtils.tableContainingKeySpecifier(context, tableConstraint, fieldNames); + } + if (td.tableKeyTypeConstraint != null) { + SemType keyConstraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.tableKeyTypeConstraint.keyType); + return TableUtils.tableContainingKeyConstraint(context, tableConstraint, keyConstraint); + } + return TableUtils.tableContaining(context.env, tableConstraint); + } + + + private SemType resolveErrorTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangErrorType td) { + SemType innerType = createErrorType(cx, mod, defn, depth, td); + if (td.flagSet.contains(Flag.DISTINCT)) { + Env env = (Env) cx.getInnerEnv(); + return getDistinctErrorType(env, innerType); + } + return innerType; + } + + private static SemType getDistinctErrorType(Env env, SemType innerType) { + return Core.intersect(ErrorUtils.errorDistinct(env.distinctAtomCountGetAndIncrement()), innerType); + } + + private SemType createErrorType(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, BLangErrorType td) { + if (td.detailType == null) { + return Builder.getErrorType(); + } else { + SemType detailType = resolveTypeDesc(cx, mod, defn, depth + 1, td.detailType); + return ErrorUtils.errorDetail(detailType); + } + } + + private SemType resolveObjectTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangObjectTypeNode td) { + SemType innerType = resolveNonDistinctObject(cx, mod, defn, depth, td); + if (td.flagSet.contains(Flag.DISTINCT)) { + return getDistinctObjectType((Env) cx.getInnerEnv(), innerType); + } + return innerType; + } + + private SemType resolveNonDistinctObject(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangObjectTypeNode td) { + Env env = (Env) cx.getInnerEnv(); + Definition attachedDefinition = attachedDefinitions.get(td); + if (attachedDefinition != null) { + return attachedDefinition.getSemType(env); + } + ObjectDefinition od = new ObjectDefinition(); + attachedDefinitions.put(td, od); + Stream fieldStream = td.fields.stream().map(field -> { + Set flags = field.flagSet; + Member.Visibility visibility = flags.contains(Flag.PUBLIC) ? Member.Visibility.Public : + Member.Visibility.Private; + SemType ty = resolveTypeDesc(cx, mod, defn, depth + 1, field.typeNode); + return new Member(field.name.value, ty, Member.Kind.Field, visibility, flags.contains(Flag.READONLY)); + }); + Stream methodStream = td.getFunctions().stream().map(method -> { + Member.Visibility visibility = method.flagSet.contains(Flag.PUBLIC) ? Member.Visibility.Public : + Member.Visibility.Private; + SemType ty = resolveFunctionType(cx, mod, defn, depth + 1, method); + return new Member(method.name.value, ty, Member.Kind.Method, visibility, true); + }); + List members = Stream.concat(fieldStream, methodStream).toList(); + ObjectQualifiers qualifiers = getQualifiers(td); + return od.define(env, qualifiers, members, qualifiers.readonly() ? CELL_MUT_NONE : + CELL_MUT_LIMITED); + } + + private ObjectQualifiers getQualifiers(BLangObjectTypeNode td) { + Set flags = td.symbol.getFlags(); + ObjectQualifiers.NetworkQualifier networkQualifier; + assert !(flags.contains(Flag.CLIENT) && flags.contains(Flag.SERVICE)) : + "object can't be both client and service"; + if (flags.contains(Flag.CLIENT)) { + networkQualifier = ObjectQualifiers.NetworkQualifier.Client; + } else if (flags.contains(Flag.SERVICE)) { + networkQualifier = ObjectQualifiers.NetworkQualifier.Service; + } else { + networkQualifier = ObjectQualifiers.NetworkQualifier.None; + } + return new ObjectQualifiers(flags.contains(Flag.ISOLATED), flags.contains(Flag.READONLY), networkQualifier); + } + + private SemType resolveFunctionType(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, + int depth, BLangFunction functionType) { + Env env = (Env) cx.getInnerEnv(); + Definition attached = attachedDefinitions.get(functionType); + if (attached != null) { + return attached.getSemType(env); + } + FunctionDefinition fd = new FunctionDefinition(); + attachedDefinitions.put(functionType, fd); + Map paramScope = new HashMap<>(); + SemType[] params = getParameters(cx, mod, paramScope, defn, depth, functionType); + SemType rest; + if (functionType.getRestParameters() == null) { + rest = Builder.getNeverType(); + } else { + ArrayTypeNode arrayType = (ArrayTypeNode) functionType.getRestParameters().getTypeNode(); + rest = resolveTypeDesc(cx, mod, defn, depth + 1, arrayType.getElementType()); + } + SemType returnType = resolveReturnType(cx, mod, paramScope, defn, depth + 1, functionType.getReturnTypeNode()); + ListDefinition paramListDefinition = new ListDefinition(); + FunctionQualifiers qualifiers = FunctionQualifiers.create(functionType.flagSet.contains(Flag.ISOLATED), + functionType.flagSet.contains(Flag.TRANSACTIONAL)); + return fd.define(env, paramListDefinition.defineListTypeWrapped(env, params, params.length, rest, + CellAtomicType.CellMutability.CELL_MUT_NONE), returnType, qualifiers); + } + + private SemType[] getParameters(TypeTestContext cx, Map mod, + Map paramScope, BLangTypeDefinition defn, int depth, + BLangFunction functionType) { + List params = new ArrayList<>(); + if (functionType instanceof BLangResourceFunction resourceFunctionType) { + params.add(Builder.getStringConst(resourceFunctionType.methodName.value)); + for (var each : resourceFunctionType.resourcePathSegments) { + params.add(resolveTypeDesc(cx, mod, defn, depth + 1, each.typeNode)); + } + } + for (BLangSimpleVariable paramVar : functionType.getParameters()) { + SemType semType = resolveTypeDesc(cx, mod, defn, depth + 1, paramVar.typeNode); + if (Core.isSubtypeSimple(semType, Builder.getTypeDescType())) { + paramScope.put(paramVar.name.value, paramVar); + } + params.add(semType); + } + return params.toArray(SemType[]::new); + } + + private SemType getDistinctObjectType(Env env, SemType innerType) { + return Core.intersect(ObjectDefinition.distinct(env.distinctAtomCountGetAndIncrement()), innerType); + } + + private SemType resolveFunctionTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangFunctionTypeNode td) { + Env env = (Env) cx.getInnerEnv(); + if (isFunctionTop(td)) { + if (td.flagSet.contains(Flag.ISOLATED) || td.flagSet.contains(Flag.TRANSACTIONAL)) { + FunctionDefinition fd = new FunctionDefinition(); + return fd.define(env, Builder.getNeverType(), Builder.getValType(), + FunctionQualifiers.create( + td.flagSet.contains(Flag.ISOLATED), + td.flagSet.contains(Flag.TRANSACTIONAL))); + } + return Builder.getFunctionType(); + } + Definition attachedDefinition = attachedDefinitions.get(td); + if (attachedDefinition != null) { + return attachedDefinition.getSemType(env); + } + FunctionDefinition fd = new FunctionDefinition(); + attachedDefinitions.put(td, fd); + + Map tdScope = new HashMap<>(); + List params = new ArrayList<>(td.params.size()); + for (BLangSimpleVariable param : td.params) { + SemType paramType = resolveTypeDesc(cx, mod, defn, depth + 1, param.typeNode); + params.add(paramType); + if (Core.isSubtypeSimple(paramType, Builder.getTypeDescType())) { + tdScope.put(param.name.value, param); + } + } + SemType rest; + if (td.restParam == null) { + rest = Builder.getNeverType(); + } else { + BLangArrayType restArrayType = (BLangArrayType) td.restParam.typeNode; + rest = resolveTypeDesc(cx, mod, defn, depth + 1, restArrayType.elemtype); + } + SemType returnType = resolveReturnType(cx, mod, tdScope, defn, depth + 1, td.returnTypeNode); + ListDefinition paramListDefinition = new ListDefinition(); + return fd.define(env, + paramListDefinition.defineListTypeWrapped(env, params.toArray(SemType[]::new), params.size(), rest, + CELL_MUT_NONE), + returnType, + FunctionQualifiers.create(td.flagSet.contains(Flag.ISOLATED), td.flagSet.contains(Flag.TRANSACTIONAL))); + } + + private SemType resolveReturnType(TypeTestContext cx, + Map mod, + Map mayBeDependentlyTypeNodes, + BLangTypeDefinition defn, + int depth, BLangType returnTypeNode) { + if (returnTypeNode == null) { + return Builder.getNilType(); + } + SemType innerType; + // Dependently typed function are quite rare so doing it via exception handling should be faster than actually + // checking if it is a dependently typed one. + boolean isDependentlyType; + try { + innerType = resolveTypeDesc(cx, mod, defn, depth + 1, returnTypeNode); + isDependentlyType = false; + } catch (IndexOutOfBoundsException err) { + innerType = + resolveDependentlyTypedReturnType(cx, mod, mayBeDependentlyTypeNodes, defn, depth, returnTypeNode); + isDependentlyType = true; + } + ListDefinition ld = new ListDefinition(); + return ld.defineListTypeWrapped((Env) cx.getInnerEnv(), + new SemType[]{!isDependentlyType ? Builder.getBooleanType() : Builder.getBooleanConst(true), + innerType}, 2, Builder.getNeverType(), + CELL_MUT_LIMITED); + } + + private SemType resolveDependentlyTypedReturnType(TypeTestContext cx, + Map mod, + Map mayBeDependentlyTypeNodes, + BLangTypeDefinition defn, int depth, + TypeNode returnTypeNode) { + Map combined = new HashMap<>(mod); + combined.putAll(mayBeDependentlyTypeNodes); + return resolveTypeDesc(cx, combined, defn, depth + 1, returnTypeNode); + } + + private boolean isFunctionTop(BLangFunctionTypeNode td) { + return td.params.isEmpty() && td.restParam == null && td.returnTypeNode == null; + } + + private SemType resolveRecordTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangRecordTypeNode td) { + Env env = (Env) cx.getInnerEnv(); + Definition attachedDefinition = attachedDefinitions.get(td); + if (attachedDefinition != null) { + return attachedDefinition.getSemType(env); + } + + MappingDefinition md = new MappingDefinition(); + attachedDefinitions.put(td, md); + + MappingDefinition.Field[] fields = new MappingDefinition.Field[td.fields.size()]; + for (int i = 0; i < td.fields.size(); i++) { + BLangSimpleVariable field = td.fields.get(i); + SemType type = resolveTypeDesc(cx, mod, defn, depth + 1, field.typeNode); + if (Core.isNever(type)) { + throw new IllegalStateException("record field can't be never"); + } + fields[i] = + new MappingDefinition.Field(field.name.value, type, field.flagSet.contains(Flag.READONLY), + field.flagSet.contains(Flag.OPTIONAL)); + } + + SemType rest; + if (!td.isSealed() && td.getRestFieldType() == null) { + rest = Builder.getAnyDataType(); + } else { + rest = resolveTypeDesc(cx, mod, defn, depth + 1, td.getRestFieldType()); + } + if (rest == null) { + rest = Builder.getNeverType(); + } + return md.defineMappingTypeWrapped((Env) cx.getInnerEnv(), fields, rest, CELL_MUT_LIMITED); + } + + private SemType resolveConstrainedTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangConstrainedType td) { + BLangBuiltInRefTypeNode refTypeNode = (BLangBuiltInRefTypeNode) td.getType(); + return switch (refTypeNode.typeKind) { + case MAP -> resolveMapTypeDesc(cx, mod, defn, depth, td); + case FUTURE -> resolveFutureTypeDesc(cx, mod, defn, depth, td); + case XML -> resolveXmlTypeDesc(cx, mod, defn, depth, td); + case TYPEDESC -> resolveTypedescTypeDesc(cx, mod, defn, depth, td); + default -> throw new UnsupportedOperationException( + "Constrained type not implemented: " + refTypeNode.typeKind); + }; + } + + private SemType resolveTypedescTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangConstrainedType td) { + SemType constraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + return TypedescUtils.typedescContaining((Env) cx.getInnerEnv(), constraint); + } + + private SemType resolveFutureTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangConstrainedType td) { + SemType constraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + return FutureUtils.futureContaining((Env) cx.getInnerEnv(), constraint); + } + + private SemType resolveXmlTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangConstrainedType td) { + SemType constraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + return XmlUtils.xmlSequence(constraint); + } + + private SemType resolveMapTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangConstrainedType td) { + Env env = (Env) cx.getInnerEnv(); + Definition attachedDefinition = attachedDefinitions.get(td); + if (attachedDefinition != null) { + return attachedDefinition.getSemType(env); + } + + MappingDefinition md = new MappingDefinition(); + attachedDefinitions.put(td, md); + + SemType rest = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + + return md.defineMappingTypeWrapped(env, new MappingDefinition.Field[0], rest, CELL_MUT_LIMITED); + } + + private SemType resolveTupleTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangTupleTypeNode td) { + Env env = (Env) cx.getInnerEnv(); + Definition attachedDefinition = attachedDefinitions.get(td); + if (attachedDefinition != null) { + return attachedDefinition.getSemType(env); + } + ListDefinition ld = new ListDefinition(); + attachedDefinitions.put(td, ld); + SemType[] memberSemTypes = td.members.stream() + .map(member -> resolveTypeDesc(cx, mod, defn, depth + 1, member.typeNode)) + .toArray(SemType[]::new); + SemType rest = + td.restParamType != null ? resolveTypeDesc(cx, mod, defn, depth + 1, td.restParamType) : + Builder.getNeverType(); + return ld.defineListTypeWrapped(env, memberSemTypes, memberSemTypes.length, rest, CELL_MUT_LIMITED); + } + + private SemType resolveArrayTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangArrayType td) { + Definition attachedDefinition = attachedDefinitions.get(td); + if (attachedDefinition != null) { + return attachedDefinition.getSemType((Env) cx.getInnerEnv()); + } + + ListDefinition ld = new ListDefinition(); + attachedDefinitions.put(td, ld); + + int dimensions = td.dimensions; + SemType accum = resolveTypeDesc(cx, mod, defn, depth + 1, td.elemtype); + for (int i = 0; i < dimensions; i++) { + int size = from(mod, td.sizes.get(i)); + if (i == dimensions - 1) { + accum = resolveListInner(cx, ld, size, accum); + } else { + accum = resolveListInner(cx, size, accum); + } + } + return accum; + } + + private SemType resolveListInner(TypeTestContext cx, int size, SemType eType) { + ListDefinition ld = new ListDefinition(); + return resolveListInner(cx, ld, size, eType); + } + + private static SemType resolveListInner(TypeTestContext cx, ListDefinition ld, int size, SemType eType) { + Env env = (Env) cx.getInnerEnv(); + if (size != -1) { + SemType[] members = {eType}; + return ld.defineListTypeWrapped(env, members, Math.abs(size), Builder.getNeverType(), CELL_MUT_LIMITED); + } else { + return ld.defineListTypeWrapped(env, EMPTY_SEMTYPE_ARR, 0, eType, CELL_MUT_LIMITED); + } + } + + + private SemType resolveSingletonType(BLangFiniteTypeNode td) { + return td.valueSpace.stream().map(each -> (BLangLiteral) each) + .map(literal -> resolveSingletonType(literal.value, literal.getDeterminedType().getKind()).get()) + .reduce(Builder.getNeverType(), Core::union); + } + + private Optional resolveSingletonType(Object value, TypeKind targetTypeKind) { + return switch (targetTypeKind) { + case NIL -> Optional.of(Builder.getNilType()); + case BOOLEAN -> Optional.of(Builder.getBooleanConst((Boolean) value)); + case INT, BYTE -> { + assert !(value instanceof Byte); + yield Optional.of(Builder.getIntConst(((Number) value).longValue())); + } + case FLOAT -> { + double doubleVal; + if (value instanceof Long longValue) { + doubleVal = longValue.doubleValue(); + } else if (value instanceof Double doubleValue) { + doubleVal = doubleValue; + } else { + // literal value will be a string if it wasn't within the bounds of what is supported by Java Long + // or Double when it was parsed in BLangNodeBuilder. + try { + doubleVal = Double.parseDouble((String) value); + } catch (NumberFormatException e) { + yield Optional.empty(); + } + } + yield Optional.of(Builder.getFloatConst(doubleVal)); + } + case DECIMAL -> { + String repr = (String) value; + if (repr.contains("d") || repr.contains("D")) { + repr = repr.substring(0, repr.length() - 1); + } + yield Optional.of(Builder.getDecimalConst(new BigDecimal(repr))); + } + case STRING -> Optional.of(Builder.getStringConst((String) value)); + case HANDLE -> Optional.of(Builder.getHandleType()); + default -> Optional.empty(); + }; + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangUnionTypeNode td, Map mod, + int depth, BLangTypeDefinition defn) { + + Iterator iterator = td.memberTypeNodes.iterator(); + SemType res = resolveTypeDesc(cx, mod, defn, depth, iterator.next()); + while (iterator.hasNext()) { + res = Core.union(res, resolveTypeDesc(cx, mod, defn, depth, iterator.next())); + } + return res; + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangUserDefinedType td, Map mod, + int depth) { + String name = td.typeName.value; + // Need to replace this with a real package lookup + if (td.pkgAlias.value.equals("int")) { + return resolveIntSubtype(name); + } else if (td.pkgAlias.value.equals("string") && name.equals("Char")) { + return Builder.getCharType(); + } else if (td.pkgAlias.value.equals("xml")) { + return resolveXmlSubType(name); + } else if (td.pkgAlias.value.equals("regexp") && name.equals("RegExp")) { + return Builder.getRegexType(); + } + + BLangNode moduleLevelDef = mod.get(name); + if (moduleLevelDef == null) { + throw new IndexOutOfBoundsException("unknown type " + name); + } + + switch (moduleLevelDef.getKind()) { + case TYPE_DEFINITION -> { + SemType ty = resolveTypeDefnRec(cx, mod, (BLangTypeDefinition) moduleLevelDef, depth); + if (td.flagSet.contains(Flag.DISTINCT)) { + return getDistinctSemType(cx, ty); + } + return ty; + } + case CONSTANT -> { + BLangConstant constant = (BLangConstant) moduleLevelDef; + return resolveTypeDefnRec(cx, mod, constant.getAssociatedTypeDefinition(), depth); + } + case VARIABLE -> { + // This happens when the type is a parameter of a dependently typed function + BLangVariable variable = (BLangVariable) moduleLevelDef; + BLangConstrainedType typeDescType = (BLangConstrainedType) variable.getTypeNode(); + return resolveTypeDesc(cx, mod, null, depth, typeDescType.constraint); + } + default -> throw new UnsupportedOperationException("class defns not implemented"); + } + } + + private SemType resolveXmlSubType(String name) { + return switch (name) { + case "Element" -> Builder.getXmlElementType(); + case "Comment" -> Builder.getXmlCommentType(); + case "Text" -> Builder.getXmlTextType(); + case "ProcessingInstruction" -> Builder.getXmlPIType(); + default -> throw new IllegalStateException("Unknown XML subtype: " + name); + }; + } + + private SemType getDistinctSemType(TypeTestContext cx, SemType innerType) { + Env env = (Env) cx.getInnerEnv(); + if (Core.isSubtypeSimple(innerType, Builder.getObjectType())) { + return getDistinctObjectType(env, innerType); + } else if (Core.isSubtypeSimple(innerType, Builder.getErrorType())) { + return getDistinctErrorType(env, innerType); + } + throw new IllegalArgumentException("Distinct type not supported for: " + innerType); + } + + private SemType resolveIntSubtype(String name) { + // TODO: support MAX_VALUE + return switch (name) { + case "Signed8" -> Builder.createIntRange(SIGNED8_MIN_VALUE, SIGNED8_MAX_VALUE); + case "Signed16" -> Builder.createIntRange(SIGNED16_MIN_VALUE, SIGNED16_MAX_VALUE); + case "Signed32" -> Builder.createIntRange(SIGNED32_MIN_VALUE, SIGNED32_MAX_VALUE); + case "Unsigned8" -> Builder.createIntRange(0, UNSIGNED8_MAX_VALUE); + case "Unsigned16" -> Builder.createIntRange(0, UNSIGNED16_MAX_VALUE); + case "Unsigned32" -> Builder.createIntRange(0, UNSIGNED32_MAX_VALUE); + default -> throw new UnsupportedOperationException("Unknown int subtype: " + name); + }; + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangIntersectionTypeNode td, + Map mod, int depth, BLangTypeDefinition defn) { + Iterator iterator = td.constituentTypeNodes.iterator(); + SemType res = resolveTypeDesc(cx, mod, defn, depth, iterator.next()); + while (iterator.hasNext()) { + res = Core.intersect(res, resolveTypeDesc(cx, mod, defn, depth, iterator.next())); + } + return res; + } + + private SemType resolveTypeDesc(BLangBuiltInRefTypeNode td) { + return switch (td.typeKind) { + case NEVER -> Builder.getNeverType(); + case XML -> Builder.getXmlType(); + case FUTURE -> Builder.getFutureType(); + // TODO: implement json type + + default -> throw new UnsupportedOperationException("Built-in ref type not implemented: " + td.typeKind); + }; + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangValueType td) { + return switch (td.typeKind) { + case NIL -> Builder.getNilType(); + case BOOLEAN -> Builder.getBooleanType(); + case BYTE -> Builder.createIntRange(0, UNSIGNED8_MAX_VALUE); + case INT -> Builder.getIntType(); + case FLOAT -> Builder.getFloatType(); + case DECIMAL -> Builder.getDecimalType(); + case STRING -> Builder.getStringType(); + case READONLY -> Builder.getReadonlyType(); + case ANY -> Builder.getAnyType(); + case ANYDATA -> Builder.getAnyDataType(); + case ERROR -> Builder.getErrorType(); + case XML -> Builder.getXmlType(); + case HANDLE -> Builder.getHandleType(); + case TYPEDESC -> Builder.getTypeDescType(); + default -> throw new IllegalStateException("Unknown type: " + td); + }; + } + + private SemType evaluateConst(BLangConstant constant) { + return switch (constant.symbol.value.type.getKind()) { + case INT -> Builder.getIntConst((long) constant.symbol.value.value); + case BOOLEAN -> Builder.getBooleanConst((boolean) constant.symbol.value.value); + case STRING -> Builder.getStringConst((String) constant.symbol.value.value); + case FLOAT -> Builder.getFloatConst((double) constant.symbol.value.value); + default -> throw new UnsupportedOperationException("Expression type not implemented for const semtype"); + }; + } + + private void attachToBType(BLangType bType, SemType semType) { + attachedSemType.put(bType, semType); + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestAPI.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestAPI.java new file mode 100644 index 000000000000..cd2d6e0671ae --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestAPI.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.ListProj; +import io.ballerina.runtime.api.types.semtype.MappingProj; +import io.ballerina.runtime.api.types.semtype.SemType; + +public class RuntimeTypeTestAPI implements TypeTestAPI { + + private static final RuntimeTypeTestAPI INSTANCE = new RuntimeTypeTestAPI(); + + private RuntimeTypeTestAPI() { + } + + public static RuntimeTypeTestAPI getInstance() { + return INSTANCE; + } + + @Override + public boolean isSubtype(TypeTestContext cx, SemType t1, SemType t2) { + return Core.isSubType(from(cx), t1, t2); + } + + private static Context from(TypeTestContext cx) { + return (Context) cx.getInnerContext(); + } + + @Override + public boolean isSubtypeSimple(SemType t1, SemType t2) { + return Core.isSubtypeSimple(t1, t2); + } + + @Override + public boolean isListType(SemType t) { + return Core.isSubtypeSimple(t, Builder.getListType()); + } + + @Override + public boolean isMapType(SemType t) { + return Core.isSubtypeSimple(t, Builder.getMappingType()); + } + + @Override + public SemType intConst(long l) { + return Builder.getIntConst(l); + } + + @Override + public SemType mappingMemberTypeInnerVal(TypeTestContext context, SemType t, SemType key) { + return MappingProj.mappingMemberTypeInnerVal(from(context), t, key); + } + + @Override + public SemType listProj(TypeTestContext context, SemType t, SemType key) { + return ListProj.listProjInnerVal(from(context), t, key); + } + + @Override + public SemType listMemberType(TypeTestContext context, SemType t, SemType key) { + return Core.listMemberTypeInnerVal(from(context), t, key); + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestContext.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestContext.java new file mode 100644 index 000000000000..add06c8db8c5 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestContext.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +public final class RuntimeTypeTestContext implements TypeTestContext { + + private final TypeTestEnv env; + private final Context cx; + + private RuntimeTypeTestContext(TypeTestEnv env) { + this.env = env; + this.cx = Context.from((Env) env.getInnerEnv()); + } + + public static synchronized RuntimeTypeTestContext from(TypeTestEnv env) { + return new RuntimeTypeTestContext(env); + } + + @Override + public TypeTestEnv getEnv() { + return env; + } + + @Override + public Object getInnerEnv() { + return env.getInnerEnv(); + } + + @Override + public Object getInnerContext() { + return cx; + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestEnv.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestEnv.java new file mode 100644 index 000000000000..2592d6ba5565 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestEnv.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.HashMap; +import java.util.Map; + +class RuntimeTypeTestEnv implements TypeTestEnv { + + private final Map typeMap = new HashMap<>(); + private final Env env; + + private RuntimeTypeTestEnv(Env env) { + this.env = env; + } + + public static synchronized RuntimeTypeTestEnv from(Env env) { + return new RuntimeTypeTestEnv(env); + } + + @Override + public Map getTypeNameSemTypeMap() { + return typeMap; + } + + @Override + public void addTypeDef(String value, SemType semtype) { + typeMap.put(value, semtype); + } + + @Override + public Object getInnerEnv() { + return env; + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeAssertionTransformer.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeAssertionTransformer.java new file mode 100644 index 000000000000..b43a4fa84f33 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeAssertionTransformer.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.semtype.port.test; + +import io.ballerina.compiler.syntax.tree.Minutiae; +import io.ballerina.compiler.syntax.tree.MinutiaeList; +import io.ballerina.compiler.syntax.tree.ModuleMemberDeclarationNode; +import io.ballerina.compiler.syntax.tree.ModulePartNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeVisitor; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.compiler.syntax.tree.SyntaxTree; +import io.ballerina.compiler.syntax.tree.Token; +import org.testng.Assert; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +/** + * Extract semtype assertions in the below form. + * @param which semtype (runtime or compiler) used in the assertions. + * // @type A < B + * // @type B = C + * // @type c <> D + * @param Actual semtype definition on which assertion is defined on (runtime or compile time) + * @since 3.0.0 + */ +public final class SemTypeAssertionTransformer extends NodeVisitor { + + private final String fileName; + private final SyntaxTree syntaxTree; + private final TypeTestEnv semtypeEnv; + private final TypeTestContext context; + private final List list; + private final TypeTestAPI semtypeAPI; + + private SemTypeAssertionTransformer(String fileName, SyntaxTree syntaxTree, TypeTestEnv semtypeEnv, + TypeTestContext context, TypeTestAPI semtypeAPI) { + this.fileName = fileName; + this.syntaxTree = syntaxTree; + this.semtypeEnv = semtypeEnv; + this.context = context; + list = new ArrayList<>(); + this.semtypeAPI = semtypeAPI; + } + + public static List> getTypeAssertionsFrom(String fileName, + SyntaxTree syntaxTree, + TypeTestEnv semtypeEnv, + TypeTestContext context, + TypeTestAPI api) { + final SemTypeAssertionTransformer t = + new SemTypeAssertionTransformer<>(fileName, syntaxTree, semtypeEnv, context, api); + return t.getTypeAssertions(); + } + + private List> getTypeAssertions() { + syntaxTree.rootNode().accept(this); + List> assertions = new ArrayList<>(); + for (String str : list) { + String[] parts = splitAssertion(str); + if (parts == null) { + continue; + } + SemType lhs = toSemType(parts[0]); + RelKind kind = RelKind.fromString(parts[1], str); + SemType rhs = toSemType(parts[2]); + String text = parts[0] + " " + parts[1] + " " + parts[2]; + assertions.add(new TypeAssertion<>(this.context, this.fileName, lhs, rhs, kind, text)); + } + return assertions; + } + + private SemType toSemType(String typeExpr) { + // Simple type reference + int leftBracketPos = typeExpr.indexOf('['); + final Map typeNameSemTypeMap = semtypeEnv.getTypeNameSemTypeMap(); + if (leftBracketPos == -1) { + SemType referredType = typeNameSemTypeMap.get(typeExpr); + if (referredType == null) { + throw new IllegalArgumentException("No such type: " + typeExpr); + } + return referredType; + } + int rightBracketPos = typeExpr.indexOf(']'); + String typeRef = typeExpr.substring(0, leftBracketPos); + String memberAccessExpr = typeExpr.substring(leftBracketPos + 1, rightBracketPos); + + SemType type = typeNameSemTypeMap.get(typeRef); + if (semtypeAPI.isListType(type)) { + SemType m; + try { + long l = Long.parseLong(memberAccessExpr); + m = semtypeAPI.intConst(l); + } catch (Exception e) { + // parsing number failed, access must be a type-reference + m = typeNameSemTypeMap.get(memberAccessExpr); + } + return listProj(context, type, m); + } else if (semtypeAPI.isMapType(type)) { + SemType m = typeNameSemTypeMap.get(memberAccessExpr); + return semtypeAPI.mappingMemberTypeInnerVal(context, type, m); + } + throw new IllegalStateException("Unsupported type test: " + typeExpr); + } + + private SemType listProj(TypeTestContext context, SemType t, SemType m) { + SemType s1 = semtypeAPI.listProj(context, t, m); + SemType s2 = semtypeAPI.listMemberType(context, t, m); + if (!semtypeAPI.isSubtype(context, s1, s2)) { + Assert.fail("listProj result is not a subtype of listMemberType"); + } + return s1; + } + + private String[] splitAssertion(String str) { + if (ignoredCommentSet().contains(str)) { + return null; + } + String[] parts = str.split(" "); + + if (parts[1].equals("-@type")) { + // TODO: remove this check once diff operator is supported + return null; + } + + // Only accept the form: `//` `@type` T1 REL T2 + if (!parts[1].equals("@type") || parts.length != 5) { + throw new IllegalStateException("Invalid type assertion '" + str + + "', expected in form: '// @type T1 REL T2'"); + } + return Arrays.copyOfRange(parts, 2, 5); + } + + /** + * Returns a set of comments that are to be ignored during the processing. + * These comments include non-essential information or comments that do not conform to the expected format. + * + * @return Set of comments to be ignored. + */ + public final HashSet ignoredCommentSet() { + HashSet hashSet = new HashSet<>(); + hashSet.add("// the order of type defns are intentional"); + return hashSet; + } + + @Override + protected void visitSyntaxNode(Node node) { + addComments(node.leadingMinutiae()); + addComments(node.trailingMinutiae()); + } + + @Override + public void visit(Token token) { + addComments(token.leadingMinutiae()); + addComments(token.trailingMinutiae()); + } + + private void addComments(MinutiaeList minutiaes) { + for (Minutiae minutiae : minutiaes) { + if (minutiae.kind() == SyntaxKind.COMMENT_MINUTIAE) { + String text = minutiae.text(); + if (text.length() > 2) { + list.add(text); + } + } + } + } + + @Override + public void visit(ModulePartNode modulePartNode) { + for (ModuleMemberDeclarationNode member : modulePartNode.members()) { + member.accept(this); + } + modulePartNode.eofToken().accept(this); + } + + /** + * Relationship to be asserted. + * + * @since 3.0.0 + */ + public enum RelKind { + SUB, SAME, NON; + + static RelKind fromString(String rel, String assertion) { + switch (rel) { + case "<": + return SUB; + case "=": + return SAME; + case "<>": + return NON; + default: + throw new AssertionError("Unknown type relationship in assertion: " + rel + "in: " + assertion); + } + } + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeResolver.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeResolver.java new file mode 100644 index 000000000000..288983862e21 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeResolver.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +import org.ballerinalang.model.tree.NodeKind; +import org.wso2.ballerinalang.compiler.tree.BLangNode; +import org.wso2.ballerinalang.compiler.tree.BLangTypeDefinition; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangConstant; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangSimpleVarRef; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static org.wso2.ballerinalang.compiler.semantics.analyzer.SymbolEnter.getTypeOrClassName; + +/** + * Abstract implementation of a type resolver that can be used for type tests. + * + * @param SemType implementation used + * @since 2201.11.0 + */ +public abstract class SemTypeResolver { + + protected static int from(Map mod, BLangNode expr) { + if (expr instanceof BLangLiteral literal) { + return SemTypeResolver.listSize((Number) literal.value); + } else if (expr instanceof BLangSimpleVarRef varRef) { + String varName = varRef.variableName.value; + return SemTypeResolver.from(mod, mod.get(varName)); + } else if (expr instanceof BLangConstant constant) { + return SemTypeResolver.listSize((Number) constant.symbol.value.value); + } + throw new UnsupportedOperationException("Unsupported expr kind " + expr.getKind()); + } + + private static int listSize(Number size) { + if (size.longValue() > Integer.MAX_VALUE) { + throw new IllegalArgumentException("list sizes greater than " + Integer.MAX_VALUE + " not yet supported"); + } + return size.intValue(); + } + + public void defineSemTypes(List moduleDefs, TypeTestContext cx) { + Map modTable = new LinkedHashMap<>(); + for (BLangNode typeAndClassDef : moduleDefs) { + modTable.put(getTypeOrClassName(typeAndClassDef), typeAndClassDef); + } + modTable = Collections.unmodifiableMap(modTable); + + for (BLangNode def : moduleDefs) { + if (def.getKind() == NodeKind.CLASS_DEFN) { + throw new UnsupportedOperationException("Semtype are not supported for class definitions yet"); + } else if (def.getKind() == NodeKind.CONSTANT) { + resolveConstant(cx, modTable, (BLangConstant) def); + } else { + BLangTypeDefinition typeDefinition = (BLangTypeDefinition) def; + resolveTypeDefn(cx, modTable, typeDefinition); + } + } + } + + protected abstract void resolveConstant(TypeTestContext cx, + Map modTable, BLangConstant constant); + + protected abstract void resolveTypeDefn(TypeTestContext cx, + Map mod, BLangTypeDefinition defn); +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeTest.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeTest.java new file mode 100644 index 000000000000..9ee71aaffbc9 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeTest.java @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.semtype.port.test; + +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.internal.utils.ValueComparisonUtils; +import io.ballerina.tools.diagnostics.Diagnostic; +import io.ballerina.tools.diagnostics.DiagnosticSeverity; +import io.ballerina.types.Context; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import org.ballerinalang.test.BCompileUtil; +import org.jetbrains.annotations.NotNull; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.wso2.ballerinalang.compiler.semantics.model.Scope; +import org.wso2.ballerinalang.compiler.tree.BLangNode; +import org.wso2.ballerinalang.compiler.tree.BLangPackage; +import org.wso2.ballerinalang.compiler.util.Name; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringJoiner; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Test semtypes using compiler front-end for parsing. + * + * @since 2201.10.0 + */ +public class SemTypeTest { + + @DataProvider(name = "dataDirFileNameProvider") + public Object[] dataDirFileNameProvider() { + File dataDir = resolvePath("test-src/data").toFile(); + List testFiles = Arrays.stream(dataDir.listFiles()) + .map(File::getAbsolutePath) + .filter(name -> name.endsWith(".bal") && + !dataDirSkipList().contains(name.substring(name.lastIndexOf(File.separator) + 1))) + .collect(Collectors.toList()); + + include(testFiles, + "test-src/simple-type/float-altered.bal", + "test-src/simple-type/function-altered.bal", + "test-src/simple-type/int-singleton-altered.bal", + "test-src/simple-type/list-type-test.bal", + "test-src/simple-type/map-type-test.bal", + "test-src/simple-type/type-test.bal" + ); + + return testFiles.toArray(String[]::new); + //return new Object[]{"test-src/data/error2.bal"}; + } + + public final HashSet dataDirSkipList() { + HashSet hashSet = new HashSet<>(); + return hashSet; + } + + @DataProvider(name = "fileNameProviderFunc") + public Object[] fileNameProviderFunc() { + File dataDir = resolvePath("test-src/localVar").toFile(); + List testFiles = Arrays.stream(dataDir.listFiles()) + .map(File::getAbsolutePath) + .filter(name -> name.endsWith(".bal")) + .toList(); + + return testFiles.toArray(new String[0]); + } + + @DataProvider(name = "type-rel-provider") + public Object[] typeRelTestFileNameProvider() { + File dataDir = resolvePath("test-src/type-rel").toFile(); + List balFiles = new ArrayList<>(); + listAllBalFiles(dataDir, balFiles); + Collections.sort(balFiles); + + Collection> tests = new ArrayList<>(); + for (File file : balFiles) { + TypeCheckData utils = compilerTypeResolverUtilsFromFile(file); + List> assertions = + getTypeAssertions(file, utils.resolver(), utils.context(), utils.env(), + CompilerTypeTestAPI.getInstance(), utils.pair()); + tests.addAll(assertions); + } + return tests.toArray(); + } + + @NotNull + private static List> getTypeAssertions(File file, + SemTypeResolver typeResolver, + TypeTestContext typeCheckContext, + TypeTestEnv typeTestEnv, + TypeTestAPI api, + BCompileUtil.PackageSyntaxTreePair pair) { + String fileName = file.getAbsolutePath(); + BLangPackage pkgNode = pair.bLangPackage; + + List typeAndClassDefs = new ArrayList<>(); + typeAndClassDefs.addAll(pkgNode.constants); + typeAndClassDefs.addAll(pkgNode.typeDefinitions); + + try { + typeResolver.defineSemTypes(typeAndClassDefs, typeCheckContext); + return SemTypeAssertionTransformer.getTypeAssertionsFrom(fileName, pair.syntaxTree, typeTestEnv, + typeCheckContext, api); + } catch (Exception e) { + return List.of(new TypeAssertion<>( + null, fileName, null, null, null, e.getMessage() + )); + } + } + + public void listAllBalFiles(File file, List balFiles) { + if (file.isFile()) { + return; + } + for (File f : file.listFiles()) { + if (f.isDirectory()) { + listAllBalFiles(f, balFiles); + } + String fileName = f.getName(); + if (fileName.endsWith(".bal") && !typeRelDirSkipList().contains(fileName)) { + balFiles.add(f); + } + } + } + + public final HashSet typeRelDirSkipList() { + HashSet hashSet = new HashSet<>(); + // intersection with negative (!) atom + hashSet.add("proj7-tv.bal"); + hashSet.add("proj8-tv.bal"); + hashSet.add("proj9-tv.bal"); + hashSet.add("proj10-tv.bal"); + + // Not a type test. This is an error test + hashSet.add("xml-te.bal"); + return hashSet; + } + + private void include(List testFiles, String... fileNames) { + for (int i = 0; i < fileNames.length; i++) { + testFiles.add(i, fileNames[i]); + } + } + + @Test(dataProvider = "dataDirFileNameProvider") + public void verifyAllSubtypeRelationships(String fileName) { + List subtypeRels = getSubtypeRels(fileName); + List expectedRels = extractSubtypeRelations(fileName); + // Commented code will get expected content for this test to pass. + // Useful for taking a diff. + // String text = toText(subtypeRels); + Assert.assertEquals(subtypeRels, expectedRels); + } + + @Test(dataProvider = "fileNameProviderFunc") + public void funcTest(String fileName) { + BCompileUtil.PackageSyntaxTreePair packageSyntaxTreePair = BCompileUtil.compileSemType(fileName); + BLangPackage bLangPackage = packageSyntaxTreePair.bLangPackage; + ensureNoErrors(bLangPackage); + List vars = extractVarTypes(fileName); + Context tc = Context.from(bLangPackage.semtypeEnv); + Scope globalScope = bLangPackage.symbol.scope; + bLangPackage.functions.forEach(func -> { + Scope scope = func.getBody().scope; + vars.forEach(v -> { + if (v.length != 2) { + Assert.fail("test structure should be `variable = Type`"); + } + SemType t1 = scope.lookup(new Name(v[0])).symbol.type.semType(); + SemType t2 = globalScope.lookup(new Name(v[1])).symbol.type.semType(); + + String msg = "semtype in local scope is different from global scope"; + Assert.assertTrue(SemTypes.isSubtype(tc, t1, t2), msg); + Assert.assertTrue(SemTypes.isSubtype(tc, t2, t1), msg); + }); + }); + } + + @Test(expectedExceptions = AssertionError.class) + public void shouldFailForIncorrectTestStructure() { + File wrongAssertionFile = resolvePath("test-src/type-rel-wrong.bal").toFile(); + TypeCheckData utils = compilerTypeResolverUtilsFromFile(wrongAssertionFile); + List> typeAssertions = getTypeAssertions(wrongAssertionFile, + utils.resolver(), utils.context(), utils.env(), CompilerTypeTestAPI.getInstance(), utils.pair() + ); + testSemTypeAssertions(typeAssertions.get(0)); + } + + @DataProvider(name = "runtimeFileNameProviderFunc") + public Object[] runtimeFileNameProviderFunc() { + File dataDir = resolvePath("test-src/type-rel").toFile(); + List balFiles = new ArrayList<>(); + listAllBalFiles(dataDir, balFiles); + Collections.sort(balFiles); + return balFiles.stream() + .map(File::getAbsolutePath).toArray(); + } + + private static Predicate createRuntimeFileNameFilter(Set skipList) { + return file -> file.getName().endsWith(".bal") && !skipList.contains(file.getName()); + } + + @Test(dataProvider = "runtimeFileNameProviderFunc") + public void testRuntimeSemTypes(String fileName) { + File file = resolvePath(fileName).toFile(); + var utils = runtimeTypeResolverUtilsFromFile(file); + RuntimeTypeTestAPI api = RuntimeTypeTestAPI.getInstance(); + getTypeAssertions(file, + utils.resolver(), utils.context(), utils.env(), api, utils.pair()) + .forEach(a -> testAssertion(a, api)); + } + + private static TypeCheckData compilerTypeResolverUtilsFromFile(File file) { + String fileName = file.getAbsolutePath(); + BCompileUtil.PackageSyntaxTreePair pair = BCompileUtil.compileSemType(fileName); + BLangPackage pkgNode = pair.bLangPackage; + TypeTestContext context = ComplierTypeTestContext.from(Context.from(pkgNode.semtypeEnv)); + TypeTestEnv env = CompilerTypeTestEnv.from(pkgNode.semtypeEnv); + SemTypeResolver resolver = new CompilerSemTypeResolver(); + return new TypeCheckData<>(pair, context, env, resolver); + } + + private static TypeCheckData runtimeTypeResolverUtilsFromFile( + File file) { + String fileName = file.getAbsolutePath(); + BCompileUtil.PackageSyntaxTreePair pair = BCompileUtil.compileSemType(fileName); + TypeTestEnv env = RuntimeTypeTestEnv.from(Env.getInstance()); + TypeTestContext context = RuntimeTypeTestContext.from(env); + SemTypeResolver resolver = new RuntimeSemTypeResolver(); + return new TypeCheckData<>(pair, context, env, resolver); + } + + private record TypeCheckData(BCompileUtil.PackageSyntaxTreePair pair, TypeTestContext context, + TypeTestEnv env, SemTypeResolver resolver) { + + } + + @Test(expectedExceptions = AssertionError.class) + public void shouldFailForTooLargeLists() { + File wrongAssertionFile = resolvePath("test-src/fixed-length-array-too-large-te.bal").toFile(); + TypeCheckData utils = compilerTypeResolverUtilsFromFile(wrongAssertionFile); + List> typeAssertions = getTypeAssertions(wrongAssertionFile, + utils.resolver(), utils.context(), utils.env(), CompilerTypeTestAPI.getInstance(), utils.pair() + ); + testSemTypeAssertions(typeAssertions.get(0)); + } + + @Test(dataProvider = "type-rel-provider") + public void testSemTypeAssertions(TypeAssertion typeAssertion) { + if (typeAssertion.kind() == null) { + Assert.fail( + "Exception thrown in " + typeAssertion.fileName() + System.lineSeparator() + typeAssertion.text()); + } + testAssertion(typeAssertion, CompilerTypeTestAPI.getInstance()); + } + + private void testAssertion(TypeAssertion typeAssertion, + TypeTestAPI semTypes) { + switch (typeAssertion.kind()) { + case NON: + Assert.assertFalse( + semTypes.isSubtype(typeAssertion.context(), typeAssertion.lhs(), typeAssertion.rhs()), + formatFailingAssertionDescription(typeAssertion)); + Assert.assertFalse( + semTypes.isSubtype(typeAssertion.context(), typeAssertion.rhs(), typeAssertion.lhs()), + formatFailingAssertionDescription(typeAssertion)); + break; + case SUB: + Assert.assertTrue(semTypes.isSubtype(typeAssertion.context(), typeAssertion.lhs(), typeAssertion.rhs()), + formatFailingAssertionDescription(typeAssertion)); + Assert.assertFalse( + semTypes.isSubtype(typeAssertion.context(), typeAssertion.rhs(), typeAssertion.lhs()), + formatFailingAssertionDescription(typeAssertion)); + break; + case SAME: + Assert.assertTrue(semTypes.isSubtype(typeAssertion.context(), typeAssertion.lhs(), typeAssertion.rhs()), + formatFailingAssertionDescription(typeAssertion)); + Assert.assertTrue(semTypes.isSubtype(typeAssertion.context(), typeAssertion.rhs(), typeAssertion.lhs()), + formatFailingAssertionDescription(typeAssertion)); + } + } + + @NotNull + private String formatFailingAssertionDescription(TypeAssertion typeAssertion) { + return typeAssertion.text() + "\n in: " + typeAssertion.fileName(); + } + + + private String toText(List expectedRels) { + StringJoiner joiner = new StringJoiner("\n// ", "// ", ""); + for (String rel : expectedRels) { + joiner.add(rel); + } + return joiner.toString(); + } + + private List getSubtypeRels(String sourceFilePath) { + BLangPackage pkgNode = BCompileUtil.compileSemType(sourceFilePath).bLangPackage; + // xxxx-e.bal pattern is used to test bal files where jBallerina type checking doesn't support type operations + // such as intersection. Make sure not to use nBallerina type negation (!) with this as jBallerina compiler + // front end doesn't generate AST from those. + if (!sourceFilePath.endsWith("-e.bal")) { + ensureNoErrors(pkgNode); + } + + List typeAndClassDefs = new ArrayList<>(); + typeAndClassDefs.addAll(pkgNode.constants); + typeAndClassDefs.addAll(pkgNode.typeDefinitions); + + SemTypeResolver typeResolver = new CompilerSemTypeResolver(); + TypeTestContext typeCheckContext = + ComplierTypeTestContext.from(Context.from(pkgNode.semtypeEnv)); + typeResolver.defineSemTypes(typeAndClassDefs, typeCheckContext); + Map typeMap = pkgNode.semtypeEnv.getTypeNameSemTypeMap(); + TypeTestAPI api = CompilerTypeTestAPI.getInstance(); + List subtypeRelations = new ArrayList<>(); + List typeNameList = typeMap.keySet().stream() + .filter(n -> !n.startsWith("$anon")) + .sorted(SemTypeTest::ballerinaStringCompare) + .toList(); + int size = typeNameList.size(); + for (int i = 0; i < size; i++) { + for (int j = i + 1; j < size; j++) { + String name1 = typeNameList.get(i); + String name2 = typeNameList.get(j); + + SemType t1 = typeMap.get(name1); + SemType t2 = typeMap.get(name2); + if (api.isSubtype(typeCheckContext, t1, t2)) { + subtypeRelations.add(TypeRel.rel(name1, name2)); + } + if (api.isSubtype(typeCheckContext, t2, t1)) { + subtypeRelations.add(TypeRel.rel(name2, name1)); + } + } + } + + return subtypeRelations.stream() + .map(TypeRel::toString) + .sorted(SemTypeTest::ballerinaStringCompare) + .collect(Collectors.toList()); + } + + private void ensureNoErrors(BLangPackage bLangPackage) { + List errors = bLangPackage.getDiagnostics().stream() + .filter(d -> d.diagnosticInfo().severity() == DiagnosticSeverity.ERROR) + .toList(); + if (!errors.isEmpty()) { + Assert.fail(errors.stream() + .map(Diagnostic::toString) + .reduce("", (a, b) -> a + "\n" + b)); + } + } + + private static int ballerinaStringCompare(String o1, String o2) { + return ValueComparisonUtils.compareValues(StringUtils.fromString(o1), StringUtils.fromString(o2), ""); + } + + List extractSubtypeRelations(String fileName) { + try { + Path path = resolvePath(fileName); + Stream lines = Files.lines(Path.of(path.toString())); + return lines.filter(s -> s.startsWith("// ") && s.contains("<:")) + .map(s -> s.substring(3).strip()) + .sorted() + .collect(Collectors.toList()); + } catch (IOException e) { + Assert.fail(e.toString()); + } + return null; + } + + List extractVarTypes(String fileName) { + try { + Path path = resolvePath(fileName); + Stream lines = Files.lines(Path.of(path.toString())); + return lines.filter(s -> s.startsWith("// ")) + .map(s -> s.substring(3).replaceAll("\\s", "").split("=")) + .collect(Collectors.toList()); + } catch (IOException e) { + Assert.fail(e.toString()); + } + return null; + } + + private Path resolvePath(String fileName) { + return Paths.get("src/test/resources").resolve(fileName); + } + + /** + * Represent subtype relationship. + * + * @param subType subtype name + * @param superType super type name + * @since 2201.10.0 + */ + private record TypeRel(String subType, String superType) { + + public static TypeRel rel(String sub, String sup) { + return new TypeRel(sub, sup); + } + + @Override + public String toString() { + return subType + "<:" + superType; + } + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeAssertion.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeAssertion.java new file mode 100644 index 000000000000..e4452711b6c1 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeAssertion.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +import java.nio.file.Paths; + +/** + * Subtype test. + * + * @param Which semtype (runtime or compiler) is used for type assertion. + * @param context Type context under which {@code SemTypes} were defined. + * @param fileName Name of the file in which types were defined in. + * @param lhs Resolved {@code SemType} for the Left-hand side of the subtype test. + * @param rhs Resolved {@code SemType} for the Right-hand side of the subtype test. + * @param kind Relationship between the two types. + * @param text Text that will be shown in case of assertion failure. + * @since 3.0.0 + */ +public record TypeAssertion(TypeTestContext context, String fileName, SemType lhs, SemType rhs, + SemTypeAssertionTransformer.RelKind kind, String text) { + + public TypeAssertion { + if (kind != null) { + assert lhs != null; + assert rhs != null; + } + } + + @Override + public String toString() { + return Paths.get(fileName).getFileName().toString() + ": " + text; + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestAPI.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestAPI.java new file mode 100644 index 000000000000..60dc1190abfb --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestAPI.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +public interface TypeTestAPI { + + boolean isSubtype(TypeTestContext cx, SemType t1, SemType t2); + + boolean isSubtypeSimple(SemType t1, SemType t2); + + boolean isListType(SemType t); + + boolean isMapType(SemType t); + + SemType intConst(long l); + + SemType mappingMemberTypeInnerVal(TypeTestContext context, SemType type, SemType m); + + SemType listProj(TypeTestContext context, SemType t, SemType key); + + SemType listMemberType(TypeTestContext context, SemType t, SemType key); +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestContext.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestContext.java new file mode 100644 index 000000000000..86402244cc03 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestContext.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +public interface TypeTestContext { + + TypeTestEnv getEnv(); + + Object getInnerEnv(); + + Object getInnerContext(); +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/BooleanSubtype.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestEnv.java similarity index 60% rename from semtypes/src/main/java/io/ballerina/semtype/subtypedata/BooleanSubtype.java rename to tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestEnv.java index d4b9ede5829e..3a1afd44326f 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/BooleanSubtype.java +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestEnv.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -15,15 +15,16 @@ * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype.subtypedata; -import io.ballerina.semtype.ProperSubtypeData; +package io.ballerina.semtype.port.test; -/** - * Represent BooleanSubtype. - * - * @since 2.0.0 - */ -public class BooleanSubtype implements ProperSubtypeData { +import java.util.Map; + +public interface TypeTestEnv { + + Map getTypeNameSemTypeMap(); + + void addTypeDef(String value, SemType semtype); + Object getInnerEnv(); } diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/README.md b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/README.md new file mode 100644 index 000000000000..1a832baa8cd3 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/README.md @@ -0,0 +1,6 @@ +Note: +===== +`-e.bal` files in this test folder does not mean what it means in nBallerina repo. In this folder it means the +tests should run even though jBallerina compiler frontend emit errors. +This was necessary as jBallerina emit errors for some valid type constructs such as empty tuple. +We need to make sure that type negation operator (!) is not used in these `-e.bal` tests. \ No newline at end of file diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/anydata.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/anydata.bal new file mode 100644 index 000000000000..d80f571250bb --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/anydata.bal @@ -0,0 +1,8 @@ +import ballerina/lang.regexp; + +// DataType<:Anydata +// Anydata<:DataType + +type Anydata anydata; + +type DataType ()|boolean|int|float|decimal|string|xml|regexp:RegExp|DataType[]|map|table>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/basic.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/basic.bal new file mode 100644 index 000000000000..147ee10e2db9 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/basic.bal @@ -0,0 +1,19 @@ +// Boolean<:Any +// Decimal<:Any +// Float<:Any +// Handle<:Any +// Int<:Any +// Nil<:Any +// String<:Any +type Nil (); +type String string; +type Boolean boolean; +type Int int; +type Float float; +type Decimal decimal; +type Error error; +type Handle handle; +type Any any; + + + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/bdddiff1.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/bdddiff1.bal new file mode 100644 index 000000000000..48ee7543c899 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/bdddiff1.bal @@ -0,0 +1,9 @@ +// IA<:INA +// IA<:U + +// INA<:U +// U<:INA + +type INA int?[]; +type IA int[]; +type U IA|INA; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/boolean-subtype.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/boolean-subtype.bal new file mode 100644 index 000000000000..438c2f94a61b --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/boolean-subtype.bal @@ -0,0 +1,10 @@ +// B<:TF +// F<:B +// F<:TF +// T<:B +// T<:TF +// TF<:B +type T true; +type F false; +type TF true|false; +type B boolean; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/contextual.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/contextual.bal new file mode 100644 index 000000000000..7782b2311a75 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/contextual.bal @@ -0,0 +1,14 @@ +// F<:F_TY +// F_TY<:F +// I<:I_TY +// I<:ZERO_OR_ONE +// I_TY<:I +// I_TY<:ZERO_OR_ONE + +const int I_TY = 1; +const float F_TY = 1; + +const I = 1; +const F = 1f; + +type ZERO_OR_ONE 0|1; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/decimal-singleton.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/decimal-singleton.bal new file mode 100644 index 000000000000..9ca8da408cbe --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/decimal-singleton.bal @@ -0,0 +1,18 @@ +// Zero<:DECIMAL +// One<:DECIMAL +// Minus<:DECIMAL +// ZO<:DECIMAL +// ZOM<:DECIMAL +// ZO<:ZOM +// Zero<:ZO +// One<:ZO +// Zero<:ZOM +// One<:ZOM +// Minus<:ZOM + +type Zero 0d; +type One 1d; +type Minus -1d; +type ZO Zero|One; +type ZOM Zero|One|Minus; +type DECIMAL decimal; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/decimal-singleton2.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/decimal-singleton2.bal new file mode 100644 index 000000000000..a80c04032552 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/decimal-singleton2.bal @@ -0,0 +1,14 @@ +// Zero<:DECIMAL +// MinusZero<:DECIMAL +// One<:DECIMAL +// FpOne<:DECIMAL +// Zero<:MinusZero +// MinusZero<:Zero +// One<:FpOne +// FpOne<:One + +type Zero 0d; +type MinusZero -0d; +type One 1d; +type FpOne 1.0d; +type DECIMAL decimal; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/distinct-error.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/distinct-error.bal new file mode 100644 index 000000000000..aa40d551a92f --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/distinct-error.bal @@ -0,0 +1,21 @@ +// E1<:E +// E2<:E +// E2<:E4 +// E3<:E +// E3<:E1 +// E3<:E2 +// E3<:E4 +// E4<:E +// E4<:E2 + +type E error; + +type E1 distinct error; + +type E2 error; + +type E3 E1 & E2; + +type E4 error; + +type R1 record {}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/error1.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/error1.bal new file mode 100644 index 000000000000..5ae9ffb96d91 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/error1.bal @@ -0,0 +1,9 @@ +// EL<:E +// ER1<:E +// ER1<:ER2 +// ER2<:E +// ER2<:ER1 +type EL error; +type ER1 error; +type ER2 error; +type E error; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/error2.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/error2.bal new file mode 100644 index 000000000000..3cd76b901d0c --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/error2.bal @@ -0,0 +1,14 @@ +// E<:Cloneable +// E<:EM +// E<:ER +// EM<:Cloneable +// EM<:E +// EM<:ER +// ER<:Cloneable +// ER<:E +// ER<:EM +type E error; +type ER error>; +type Cloneable readonly|xml|Cloneable[]|map; +type EM error>; + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-2-e.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-2-e.bal new file mode 100644 index 000000000000..54d71b2f1e78 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-2-e.bal @@ -0,0 +1,15 @@ +// EMPTY<:ALL +// EMPTY<:IntArray +// IS1<:ALL +// IS2<:ALL +// IS3<:ALL +// IntArray<:ALL + +type IntArray int[]; +type IS int|string; +type EMPTY []; +type IS1 IS[1]; +type IS2 IS[2]; +type IS3 IS[3]; + +type ALL EMPTY|IS1|IS2|IS3|[IS, IS, IS, IS, IS...]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-3-e.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-3-e.bal new file mode 100644 index 000000000000..3a468cf411e8 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-3-e.bal @@ -0,0 +1,42 @@ +// EMPTY<:ISArray +// EMPTY<:U +// EMPTY<:V +// EMPTY<:W +// EMPTY<:X +// EMPTY<:Y +// EMPTY<:Z +// ISArray<:U +// ISArray<:V +// ISArray<:W +// ISArray<:X +// ISArray<:Y +// ISArray<:Z +// X<:Y +// X<:Z +// Y<:X +// Y<:Z +// Z<:X +// Z<:Y + + +type IS int|string; +type EMPTY []; +type ISArray IS[]; + +// @type ISArray < U +type U EMPTY|[IS|float, IS...]; + +// @type ISArray < V +type V EMPTY|[IS]|[IS, IS|float, IS...]; + +// @type ISArray < W +type W EMPTY|[IS]|[IS, IS|float]|[IS, IS, IS|float, IS...]; + +// @type ISArray < X +type X EMPTY|[IS]|[IS, IS|float]|[IS, IS, IS|float]|[IS, IS, IS, IS|float, IS...]; + +// @type ISArray < Y +type Y EMPTY|[IS, IS, IS, IS|float, IS...]|[IS, IS, IS|float]|[IS, IS|float]|[IS]; + +// @type ISArray < Z +type Z [IS, IS, IS, IS|float, IS...]|[IS, IS, IS|float]|[IS, IS|float]|[IS]|EMPTY; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-4-e.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-4-e.bal new file mode 100644 index 000000000000..02c4649d8958 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-4-e.bal @@ -0,0 +1,44 @@ +// EMPTY<:IntArray +// EMPTY<:P +// EMPTY<:Q +// EMPTY<:R +// EMPTY<:S +// EMPTY<:T +// EMPTY<:T1 +// IntArray<:P +// IntArray<:Q +// IntArray<:R +// IntArray<:S +// IntArray<:T +// IntArray<:T1 +// P<:Q +// P<:T +// P<:T1 +// R<:Q +// S<:Q +// S<:R +// S<:T1 +// T1<:Q +// T<:Q + +type IntArray int[]; +type IS int|string; +type EMPTY []; + +// @type IntArray < P +type P EMPTY|[int]|[IS, int]|[IS, IS, IS, IS...]; + +// @type IntArray < Q +type Q EMPTY|[int]|[IS, IS]|[IS, IS, IS|float, IS...]; + +// @type IntArray < R +type R EMPTY|[int]|[IS, IS]|[int, int, IS|float, IS...]; + +// @type IntArray < S +type S EMPTY|[int]|[IS, IS]|[int, int, int, IS...]; + +// @type IntArray < T +type T EMPTY|[int]|[IS, int]|[IS, IS, IS|float, IS...]; + +// @type IntArray < T1 +type T1 EMPTY|[int]|[IS, IS, string...]|[IS, IS, IS, IS...]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2.bal new file mode 100644 index 000000000000..8cf15c770a8f --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2.bal @@ -0,0 +1,17 @@ +// LargeArray<:IntArray +// LargeArray2<:IntArray + +public const int MAX_VALUE = 2147483637; +public const int MAX_VALUE_M_1 = 2147483636; + +type IntArray int[]; + +// @type LargeArray < IntArray +type LargeArray int[MAX_VALUE]; + +// @type LargeArray2 < IntArray +// @type LargeArray <> LargeArray2 +type LargeArray2 int[MAX_VALUE_M_1]; + +// @type Int5Intersection = Int5 +//type Int5Intersection int[5] & !LargeArray; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-tuple.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-tuple.bal new file mode 100644 index 000000000000..df4bd385f125 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-tuple.bal @@ -0,0 +1,23 @@ +// Int1<:IntT +// Int2<:IntIntT +// Int2R<:Int2 +// Int2R<:IntIntRT +// Int2R<:IntIntT +// IntIntRT<:Int2 +// IntIntRT<:Int2R +// IntIntRT<:IntIntT +// IntIntT<:Int2 +// IntT<:Int1 + +type Int1 int[1]; +type Int2 int[2]; + +type IntT [int]; + +type IntIntT [int, int]; + +type IntIntRT readonly & [int, int]; + +type Int2R readonly & int[2]; + +type Int int; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array.bal new file mode 100644 index 000000000000..a8cecd384acc --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array.bal @@ -0,0 +1,93 @@ +// Array2OfInt5<:ArrayOfInt5 +// Array2OfInt5<:ArrayOfIntArray +// Array2OfInt5<:ArrayOfIntFive +// Array5OfInt5<:Array5OfIntArray +// Array5OfInt5<:ArrayFiveOfIntFive +// Array5OfInt5<:ArrayOfInt5 +// Array5OfInt5<:ArrayOfIntArray +// Array5OfInt5<:ArrayOfIntFive +// Array5OfIntArray<:ArrayOfIntArray +// Array7OfArray2OfInt5<:Array7x2x5 +// Array7x2x5<:Array7OfArray2OfInt5 +// ArrayFiveOfIntFive<:Array5OfInt5 +// ArrayFiveOfIntFive<:Array5OfIntArray +// ArrayFiveOfIntFive<:ArrayOfInt5 +// ArrayFiveOfIntFive<:ArrayOfIntArray +// ArrayFiveOfIntFive<:ArrayOfIntFive +// ArrayOfInt5<:ArrayOfIntArray +// ArrayOfInt5<:ArrayOfIntFive +// ArrayOfIntFive<:ArrayOfInt5 +// ArrayOfIntFive<:ArrayOfIntArray +// EmptyIntArray<:ArrayOfInt5 +// EmptyIntArray<:ArrayOfIntArray +// EmptyIntArray<:ArrayOfIntFive +// EmptyIntArray<:IntArray +// FIVE<:INT +// Int5<:IntArray +// N<:Array2OfInt5 +// N<:Array5OfInt5 +// N<:Array5OfIntArray +// N<:Array7OfArray2OfInt5 +// N<:Array7x2x5 +// N<:ArrayFiveOfIntFive +// N<:ArrayOfInt5 +// N<:ArrayOfIntArray +// N<:ArrayOfIntFive +// N<:EmptyIntArray +// N<:FIVE +// N<:INT +// N<:Int5 +// N<:IntArray +// N<:ROArrayFiveOfIntFive +// N<:ROInt5 +// N<:ROIntArray +// N<:TwoArraysOfInt5 +// ROArrayFiveOfIntFive<:Array5OfInt5 +// ROArrayFiveOfIntFive<:Array5OfIntArray +// ROArrayFiveOfIntFive<:ArrayFiveOfIntFive +// ROArrayFiveOfIntFive<:ArrayOfInt5 +// ROArrayFiveOfIntFive<:ArrayOfIntArray +// ROArrayFiveOfIntFive<:ArrayOfIntFive +// ROInt5<:Int5 +// ROInt5<:IntArray +// ROInt5<:ROIntArray +// ROIntArray<:IntArray +// EmptyIntArray<:ROIntArray + +type IntArray int[]; + +type Int5 int[5]; + +type ArrayOfIntArray int[][]; + +type ArrayOfInt5 int[][5]; + +type Array5OfInt5 int[5][5]; + +type INT int; + +type Array5OfIntArray int[5][]; + +type ROIntArray readonly & IntArray; + +type ROInt5 readonly & int[5]; + +const FIVE = 5; + +type ArrayOfIntFive int[][FIVE]; + +type ArrayFiveOfIntFive int[FIVE][FIVE]; + +type ROArrayFiveOfIntFive ArrayFiveOfIntFive & readonly; + +type N never; + +type TwoArraysOfInt5 int[2][][5]; + +type EmptyIntArray int[0]; + +type Array2OfInt5 Int5[2]; + +type Array7OfArray2OfInt5 Array2OfInt5[7]; + +type Array7x2x5 int[7][2][5]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/float-singleton.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/float-singleton.bal new file mode 100644 index 000000000000..7a28045e105e --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/float-singleton.bal @@ -0,0 +1,25 @@ +// Z<:F +// Z<:ZO +// Z<:ZOT +// Z<:ZT +// ZO<:F +// ZO<:ZOT +// ZOT<:F +// ZT<:F +// ZT<:ZOT +// O<:F +// O<:ZO +// O<:ZOT +// T<:F +// T<:ZOT +// T<:ZT + +type Z 0.0; +type O 1.0; +type T -2.0; + +type ZOT Z|O|T; +type ZO Z|O; +type ZT Z|T; + +type F float; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/float-singleton2.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/float-singleton2.bal new file mode 100644 index 000000000000..3f404c273992 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/float-singleton2.bal @@ -0,0 +1,20 @@ +// ALSO_NAN<:Float +// ALSO_NAN<:NAN +// INFINITY<:Float +// NAN<:ALSO_NAN +// NAN<:Float +// NEGATIVE_INFINITY<:Float +// NegativeZero<:Float +// NegativeZero<:Zero +// Zero<:Float +// Zero<:NegativeZero + +const INFINITY = 1.0/0.0; +const NEGATIVE_INFINITY = -1.0/0.0; +const NAN = 0f/0f; +const ALSO_NAN = -NAN; + +type Zero 0.0; +type NegativeZero -0.0; + +type Float float; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/function.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/function.bal new file mode 100644 index 000000000000..c1657b0ca2cc --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/function.bal @@ -0,0 +1,10 @@ +// F<:A +// S<:T + +type F function() returns F; +type A function() returns any|error; + +type S function(int?) returns string; +type T function(int) returns string?; + + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/future-subtype.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/future-subtype.bal new file mode 100644 index 000000000000..20f552629616 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/future-subtype.bal @@ -0,0 +1,10 @@ +// I<:U1 +// I<:U2 +// S<:U1 +// S<:U2 +// U1<:U2 + +type I future; +type S future; +type U1 I|S; +type U2 future; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/hard.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/hard.bal new file mode 100644 index 000000000000..d64f6c812c45 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/hard.bal @@ -0,0 +1,11 @@ +// J1<:J2 +// J1<:J3 +// J2<:J1 +// J2<:J3 +// J3<:J1 +// J3<:J2 +type J1 [J2,J1]|record {| J1 x; J1 y; |}|()|int|string; +type J2 ()|int|string|[J1,J2]|record {| J2 x; J1 y; |}; +type J3 ()|int|string|[J3,J3]|record {| J3 x; J3 y; |}; + + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-singleton.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-singleton.bal new file mode 100644 index 000000000000..12e49cac6304 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-singleton.bal @@ -0,0 +1,13 @@ +// INT_MIN<:Int +// ONE<:Int +// ONE<:ZERO_OR_ONE +// ZERO<:Int +// ZERO<:ZERO_OR_ONE +// ZERO_OR_ONE<:Int +const ONE = 1; +const ZERO = 0; +const INT_MIN = -9223372036854775807 - 1; + +type ZERO_OR_ONE ZERO|ONE; +type Int int; + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-singleton2.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-singleton2.bal new file mode 100644 index 000000000000..c5d713c07693 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-singleton2.bal @@ -0,0 +1,7 @@ +// MinusOne<:Sign +// One<:Byte +// One<:Sign +type One 1; +type MinusOne -1; +type Sign One|MinusOne; +type Byte byte; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-subtype.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-subtype.bal new file mode 100644 index 000000000000..bc5fa06625d9 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-subtype.bal @@ -0,0 +1,26 @@ +// Byte<:Sint16 +// Byte<:Sint32 +// Byte<:Uint16 +// Byte<:Uint32 +// Byte<:Uint8 +// N33000<:Sint32 +// N33000<:Uint16 +// N33000<:Uint32 +// Sint16<:Sint32 +// Sint8<:Sint16 +// Sint8<:Sint32 +// Uint16<:Sint32 +// Uint16<:Uint32 +// Uint8<:Byte +// Uint8<:Sint16 +// Uint8<:Sint32 +// Uint8<:Uint16 +// Uint8<:Uint32 +type Byte byte; +type Uint8 int:Unsigned8; +type Uint16 int:Unsigned16; +type Uint32 int:Unsigned32; +type Sint8 int:Signed8; +type Sint16 int:Signed16; +type Sint32 int:Signed32; +const N33000 = 33000; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/list-fixed.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/list-fixed.bal new file mode 100644 index 000000000000..eb030abfabfc --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/list-fixed.bal @@ -0,0 +1,8 @@ +// IF<:I +// IF<:IFF +// IFF<:I +// IFF<:IF + +type I int[]; +type IF int[12224]; +type IFF int[12224]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mapping1.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mapping1.bal new file mode 100644 index 000000000000..e07ea23f6ec2 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mapping1.bal @@ -0,0 +1,19 @@ +// M1<:M +// M1<:M2 +// M1<:N +// M2<:M +// M2<:M1 +// M2<:N +// M<:M1 +// M<:M2 +// M<:N +// N<:M +// N<:M1 +// N<:M2 +type M map; + +type N map; + +type M1 map|record {}; + +type M2 map|record {}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mapping2.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mapping2.bal new file mode 100644 index 000000000000..53dec4dc4440 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mapping2.bal @@ -0,0 +1,19 @@ +// M1<:M +// M1<:M2 +// M1<:N +// M2<:M +// M2<:M1 +// M2<:N +// M<:M1 +// M<:M2 +// M<:N +// N<:M +// N<:M1 +// N<:M2 +type M map<(1|2|3)>; + +type N map<(1|2|3)>; + +type M1 M|map<(1|2)>; + +type M2 N|map<(1|2)>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mutable-record1.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mutable-record1.bal new file mode 100644 index 000000000000..3cc12b9c5b3d --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mutable-record1.bal @@ -0,0 +1,36 @@ +// NN<:U +// NN<:UU +// NS<:U +// NS<:UU +// SN<:U +// SN<:UU +// SS<:U +// SS<:UU +// U<:UU + +type NN record {| + int x; + int y; +|}; + +type SS record {| + string x; + string y; +|}; + +type NS record {| + int x; + string y; +|}; + +type SN record {| + string x; + int y; +|}; + +type UU record {| + int|string x; + int|string y; +|}; + +type U NN|SS|NS|SN; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/never.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/never.bal new file mode 100644 index 000000000000..2516326d50c9 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/never.bal @@ -0,0 +1,21 @@ +// Fun<:FunOrNever +// FunOrNever<:Fun +// Int<:IntOrNever +// IntOrNever<:Int +// Key<:KeyOrNever +// KeyOrNever<:Key +// Never<:Fun +// Never<:FunOrNever +// Never<:Int +// Never<:IntOrNever +// Never<:Key +// Never<:KeyOrNever +type Never never; +type Int int; +type Fun function(int); +type FunOrNever Fun|never; +type IntOrNever Int|never; +type Key "key"; +type KeyOrNever Key|never; + + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/readonly1.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/readonly1.bal new file mode 100644 index 000000000000..0f841c7862de --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/readonly1.bal @@ -0,0 +1,19 @@ +// Boolean<:RO +// Decimal<:RO +// Error<:RO +// Float<:RO +// Handle<:RO +// Int<:RO +// Nil<:RO +// String<:RO +type RO readonly; +type Nil (); +type String string; +type Boolean boolean; +type Int int; +type Float float; +type Decimal decimal; +type Error error; +type Handle handle; +type List (any|error)[]; +type Mapping map; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/readonly2.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/readonly2.bal new file mode 100644 index 000000000000..39142d87a52b --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/readonly2.bal @@ -0,0 +1,26 @@ +// E<:ER +// ER<:E +// IR<:Int +// Int<:IR +// LR1<:LR +// LR1<:LR2 +// LR2<:LR +// LR2<:LR1 +// MR1<:MR +// MR1<:MR2 +// MR2<:MR +// MR2<:MR1 +type E error; +type ER error>; +type LR readonly[]; +type MR map; +// These two should be equivalent +type MR1 readonly & map; +type MR2 readonly & map; +// As should these +type LR1 readonly & (any|error)[]; +type LR2 readonly & readonly[]; +// As should these +type IR int & readonly; +type Int int; + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-recursive.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-recursive.bal new file mode 100644 index 000000000000..bb595595ecaa --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-recursive.bal @@ -0,0 +1,8 @@ +// SR<:S +type SR stream; + +type S stream; + +// S1<:SR +// S1<:S +type S1 stream<()>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-subtype.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-subtype.bal new file mode 100644 index 000000000000..372455449317 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-subtype.bal @@ -0,0 +1,10 @@ +// I<:U1 +// I<:U2 +// S<:U1 +// S<:U2 +// U1<:U2 + +type I stream; +type S stream; +type U1 I|S; +type U2 stream; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-subtype2.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-subtype2.bal new file mode 100644 index 000000000000..a170dd69ffe3 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-subtype2.bal @@ -0,0 +1,20 @@ +// I<:J +// I<:J1 +// I<:J2 +type I stream; + +// S<:J +// S<:J1 +// S<:J2 +type S stream; + + +// J<:J1 +// J1<:J +type J stream; +type J1 stream; + +// J<:J2 +// J1<:J2 +type J2 stream; +type J3 stream; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-all-subtypes.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-all-subtypes.bal new file mode 100644 index 000000000000..33ca5ed91c29 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-all-subtypes.bal @@ -0,0 +1,29 @@ +// A<:Bar +// A<:Baz +// A<:C +// A<:S +// A<:SC +// Bar<:Baz +// Bar<:S +// Bar<:SC +// Baz<:S +// Baz<:SC +// C<:Baz +// C<:S +// C<:SC +// Foo<:Bar +// Foo<:Baz +// Foo<:S +// Foo<:SC +// S<:Baz +// S<:SC +// SC<:Baz +// SC<:S + +type S string; +type C string:Char; +type A "A"; +type Foo "Foo"; +type Bar A|Foo; +type Baz Bar|S|C; +type SC S|C; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-char.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-char.bal new file mode 100644 index 000000000000..b487d618849f --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-char.bal @@ -0,0 +1,13 @@ +// C1<:C +// C1<:D +// C1<:S +// C<:C1 +// C<:D +// C<:S +// D<:C +// D<:C1 +// D<:S +type C string:Char; +type C1 string:Char; +type D C|C1; +type S string; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-singleton-same-shape.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-singleton-same-shape.bal new file mode 100644 index 000000000000..681bc54ca07a --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-singleton-same-shape.bal @@ -0,0 +1,27 @@ +// WHY<:S +// WHY<:Y +// WHY<:Y1 +// X1<:S +// X1<:X +// X1<:XES +// X2<:S +// X2<:XES +// X<:S +// X<:X1 +// X<:XES +// XES<:S +// Y1<:S +// Y1<:WHY +// Y1<:Y +// Y<:S +// Y<:WHY +// Y<:Y1 + +type X "X"; +type X1 "X"; +type X2 "x"; +type Y "Why"; +type Y1 "Why"; +type XES X|X1|X2; +type WHY Y|Y1; +type S string; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-singleton.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-singleton.bal new file mode 100644 index 000000000000..b23a064fca77 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-singleton.bal @@ -0,0 +1,24 @@ +// X<:S +// X<:XY +// X<:XYZ +// X<:XZ +// XY<:S +// XY<:XYZ +// XYZ<:S +// XZ<:S +// XZ<:XYZ +// Y<:S +// Y<:XY +// Y<:XYZ +// Z<:S +// Z<:XYZ +// Z<:XZ +type X "x"; +type Y "y-y"; +type Z "z-z"; + +type XYZ X|Y|Z; +type XY X|Y; +type XZ X|Z; + +type S string; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/table-key.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/table-key.bal new file mode 100644 index 000000000000..108808ce389b --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/table-key.bal @@ -0,0 +1,30 @@ +type R record {| + readonly [string, string] name; + readonly string department; + readonly string city; + int salary; +|}; + +type KS1 table key(name); + +type KS2 table key(city); + +type KS3 table key(department); + +type KS4 table key(city, department); + +type KS5 table key(department, city); + +type KS6 table key(name, department, city); + +// KS2<:KC1 +// KS3<:KC1 +type KC1 table key; + +// KS1<:KC2 +// KS4<:KC2 +// KS5<:KC2 +type KC2 table key<[string, string]>; + +// KS6<:KC3 +type KC3 table key<[[string, string], string, string]>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/tuple1.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/tuple1.bal new file mode 100644 index 000000000000..3b43eec0ab60 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/tuple1.bal @@ -0,0 +1,6 @@ +// T1<:T2 +// T4<:T3 +type T1 [int]; +type T2 [int?]; +type T3 [int, int?]; +type T4 [int, int] | [int, ()]; \ No newline at end of file diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/tuple4.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/tuple4.bal new file mode 100644 index 000000000000..ecb6410c704e --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/tuple4.bal @@ -0,0 +1,11 @@ +// T2<:T1 +// T2<:T4 +// T3<:T1 +// T3<:T2 +// T3<:T4 + +type T1 [int...]; +type T2 [int,int...]; + +type T4 [int?,int?...]; +type T3 [int, int, int, int...]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/typedesc-subtype.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/typedesc-subtype.bal new file mode 100644 index 000000000000..64b30d899d46 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/typedesc-subtype.bal @@ -0,0 +1,10 @@ +// I<:U1 +// I<:U2 +// S<:U1 +// S<:U2 +// U1<:U2 + +type I typedesc; +type S typedesc; +type U1 I|S; +type U2 typedesc; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-never.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-never.bal new file mode 100644 index 000000000000..d8054d1fcecd --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-never.bal @@ -0,0 +1,34 @@ +// C<:CS +// E<:ENS +// E<:ES +// ENS<:ES +// ES<:ENS +// N<:CS +// N<:ENS +// N<:ES +// N<:NS +// N<:PS +// N<:T +// N<:TS +// NS<:CS +// NS<:ENS +// NS<:ES +// NS<:N +// NS<:PS +// NS<:T +// NS<:TS +// P<:PS +// T<:TS +// TS<:T + +type N xml; +type NS xml; +type E xml:Element; +type T xml:Text; +type C xml:Comment; +type P xml:ProcessingInstruction; +type ES xml; +type CS xml; +type TS xml; +type PS xml; +type ENS xml; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-sequence.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-sequence.bal new file mode 100644 index 000000000000..ccb734bfffd4 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-sequence.bal @@ -0,0 +1,90 @@ +// E<:P +// E<:Q +// E<:U +// E<:V +// E<:X +// E<:XE +// E<:XEU +// E<:Y +// N<:P +// N<:Q +// N<:T +// N<:U +// N<:V +// N<:X +// N<:XE +// N<:XEU +// N<:XT +// N<:Y +// P<:Q +// P<:X +// P<:XE +// P<:XEU +// P<:Y +// Q<:P +// Q<:X +// Q<:XE +// Q<:XEU +// Q<:Y +// T<:P +// T<:Q +// T<:V +// T<:X +// T<:XE +// T<:XEU +// T<:XT +// T<:Y +// U<:P +// U<:Q +// U<:V +// U<:X +// U<:XE +// U<:XEU +// U<:Y +// V<:P +// V<:Q +// V<:X +// V<:XE +// V<:XEU +// V<:Y +// X<:P +// X<:Q +// X<:XE +// X<:XEU +// X<:Y +// XE<:P +// XE<:Q +// XE<:X +// XE<:XEU +// XE<:Y +// XEU<:P +// XEU<:Q +// XEU<:X +// XEU<:XE +// XEU<:Y +// XT<:P +// XT<:Q +// XT<:T +// XT<:V +// XT<:X +// XT<:XE +// XT<:XEU +// XT<:Y +// Y<:P +// Y<:Q +// Y<:X +// Y<:XE +// Y<:XEU + +type X xml; +type Y xml; +type P xml; +type Q xml

; +type U xml; +type V xml; +type N xml; +type E xml:Element; +type XE xml; +type XEU xml|E; +type T xml:Text; +type XT xml; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-singleton.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-singleton.bal new file mode 100644 index 000000000000..ca48dee0cd10 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-singleton.bal @@ -0,0 +1,13 @@ +// C<:CTE +// C<:X +// CTE<:X +// E<:CTE +// E<:X +// T<:CTE +// T<:X + +type C xml:Comment; +type E xml:Element; +type CTE C|T|E; +type T xml:Text; +type X xml; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml.bal new file mode 100644 index 000000000000..5b347b8ccea5 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml.bal @@ -0,0 +1,13 @@ +// C<:EC +// C<:X +// E<:EC +// E<:ET +// E<:X +// EC<:X +// ET<:X + +type E xml; +type C xml; +type EC xml; +type ET xml; +type X xml; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/fixed-length-array-too-large-te.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/fixed-length-array-too-large-te.bal new file mode 100644 index 000000000000..6b25f6b2e889 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/fixed-length-array-too-large-te.bal @@ -0,0 +1,2 @@ +public const int MAX_VALUE = 9223372036854775807; +type LargeArray int[MAX_VALUE]; // @error diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/localVar/func.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/localVar/func.bal new file mode 100644 index 000000000000..a4511d0bca26 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/localVar/func.bal @@ -0,0 +1,11 @@ +// x = R +// y = S + +type N int; +type S string; +type R N|S; + +function foo() { + R x = 1; + S y = "str"; +} diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/float-altered.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/float-altered.bal new file mode 100644 index 000000000000..985013bd1e1f --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/float-altered.bal @@ -0,0 +1,9 @@ +// NegativeZero<:Float +// NegativeZero<:Zero +// Zero<:Float +// Zero<:NegativeZero + +type Zero 0.0; +type NegativeZero -0.0; + +type Float float; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/function-altered.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/function-altered.bal new file mode 100644 index 000000000000..016125fd386a --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/function-altered.bal @@ -0,0 +1,8 @@ +// F<:A +// S<:T + +type F function() returns S; +type A function() returns any; + +type S function(int?) returns string; +type T function(int) returns string?; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/int-singleton-altered.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/int-singleton-altered.bal new file mode 100644 index 000000000000..75faa34ff975 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/int-singleton-altered.bal @@ -0,0 +1,13 @@ +// INT_MIN<:Int +// ONE<:Int +// ONE<:ZERO_OR_ONE +// ZERO<:Int +// ZERO<:ZERO_OR_ONE +// ZERO_OR_ONE<:Int + +const ONE = 1; +const ZERO = 0; +const int INT_MIN = -9223372036854775807 - 1; + +type ZERO_OR_ONE ZERO|ONE; +type Int int; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/list-type-test.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/list-type-test.bal new file mode 100644 index 000000000000..62d962464787 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/list-type-test.bal @@ -0,0 +1,19 @@ +// IIT<:IRIT +// IIT<:L +// IIT<:L1 +// IIT<:RIT +// IRIT<:L +// IRIT<:L1 +// IRIT<:RIT +// L1<:L +// L1<:RIT +// L<:L1 +// L<:RIT +// RIT<:L +// RIT<:L1 + +type L int[]; +type L1 int[]; +type RIT [int...]; +type IRIT [int, int...]; +type IIT [int, int]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/map-type-test.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/map-type-test.bal new file mode 100644 index 000000000000..f0de0af00861 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/map-type-test.bal @@ -0,0 +1,11 @@ +// MB<:MI +// MB<:RIC +// MI<:RIC +// RC<:MI +// RC<:RIC +// RIC<:MI + +type MI map; +type MB map; +type RIC record {| int...; |}; +type RC record {| int i; |}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/type-test.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/type-test.bal new file mode 100644 index 000000000000..dadcd0eaaca4 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/type-test.bal @@ -0,0 +1,11 @@ +// N<:R +// N<:T +// R<:N +// R<:T +// T<:N +// T<:R + +type T int; +type T1 string; +type N int; +type R N; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel-wrong.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel-wrong.bal new file mode 100644 index 000000000000..0dfe7e74972a --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel-wrong.bal @@ -0,0 +1,4 @@ +type X int; + +// Y <: X +type Y byte; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/anydata-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/anydata-tv.bal new file mode 100644 index 000000000000..f0eb6b30bc68 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/anydata-tv.bal @@ -0,0 +1,19 @@ +type A anydata; + +// @type T < A +type T table>; + +// @type TB < A +type TB table>; + +// @type TI < A +type TI table>; + +// @type TARR < A +type TARR table>; + +// @type TANY <> A +type TANY table>; + +// @type TERR <> A +type TERR table>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/basic-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/basic-tv.bal new file mode 100644 index 000000000000..fabd47e8f069 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/basic-tv.bal @@ -0,0 +1,17 @@ +// @type T1 < T2 +// @type T1 <> T3 +type T1 1|1.0|"foo"; +type T2 int|float|string; +type T3 int|string; + +// @type T4 = OneFoo +type T4 T3 & T1; +type OneFoo 1|"foo"; + +// @type T5 = One +// @type DoubleOne = One +// @type AnotherDoubleOne = One +type T5 1|1; +type One 1; +type DoubleOne One|One; +type AnotherDoubleOne One|1; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/bdddiff1-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/bdddiff1-tv.bal new file mode 100644 index 000000000000..3012aea1fdae --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/bdddiff1-tv.bal @@ -0,0 +1,4 @@ +type INA int?[]; +type IA int[]; +// @type U = INA +type U IA|INA; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/cyclic-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/cyclic-tv.bal new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/decimal-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/decimal-tv.bal new file mode 100644 index 000000000000..257448a987f1 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/decimal-tv.bal @@ -0,0 +1,13 @@ +// @type PosZero < Decimal +// @type NegZero < Decimal +// @type OtherZero < Decimal +// @type PosZero = NegZero +// @type PosZero = OtherZero +type PosZero 0.0d; +type NegZero -0.0d; +type OtherZero 0d; + +type Decimal decimal; + +// @type IntZero <> OtherZero +type IntZero 0; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/dependently-typed-fn-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/dependently-typed-fn-tv.bal new file mode 100644 index 000000000000..b0b9d98eeae3 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/dependently-typed-fn-tv.bal @@ -0,0 +1,9 @@ +// @type F1 < F2 +type F1 function (typedesc td) returns td; + +type F2 function (typedesc td) returns anydata; + +// @type Fu1 < Fu2 +type Fu1 function (typedesc td) returns td|error; + +type Fu2 function (typedesc td) returns anydata|error; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/error1-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/error1-tv.bal new file mode 100644 index 000000000000..8a188010c68f --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/error1-tv.bal @@ -0,0 +1,9 @@ +// @type EL < E +// @type ER1 < E +// @type ER1 = ER2 +// @type EL <> ER1 +// @type ER2 < E +type EL error; +type ER1 error; +type ER2 error; +type E error; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-large-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-large-t.bal new file mode 100644 index 000000000000..52bcf3cdb1f8 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-large-t.bal @@ -0,0 +1,21 @@ +type IntArray int[]; +type Int5 int[5]; +type ISTArray (1|2|3)[]; + +public const int MAX_VALUE = 2147483637; +public const int MAX_VALUE_M_1 = MAX_VALUE - 1; + +// @type LargeArray < IntArray +type LargeArray int[MAX_VALUE]; + +// @type LargeArray2 < IntArray +// @type LargeArray <> LargeArray2 +type LargeArray2 int[MAX_VALUE_M_1]; + +// -@type Int5Intersection = Int5 +type Int5Intersection int[5] & !LargeArray; + +type Int10000 int[100000]; + +// -@type ISTArray < I10000A +type I10000A Int10000|(!Int10000 & IntArray); diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-t.bal new file mode 100644 index 000000000000..66421f63cf41 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-t.bal @@ -0,0 +1,68 @@ +type IntArray int[]; + +// @type Int5 < IntArray +type Int5 int[5]; + +// @type Int5 = Int5AndIntArray +// @type Int5AndIntArray < IntArray +type Int5AndIntArray Int5 & IntArray; + +// @type IntArray <> ArrayOfIntArray +type ArrayOfIntArray int[][]; + +// @type ArrayOfInt5 < ArrayOfIntArray +// @type Int5 <> ArrayOfInt5 +// @type Int5 = ArrayOfInt5[0] +// @type Int5 = ArrayOfInt5[5] +// @type Int5 = ArrayOfInt5[6] +type ArrayOfInt5 int[][5]; + +// @type Array5OfInt5 < ArrayOfInt5 +// @type Array5OfInt5 < ArrayOfIntArray +type Array5OfInt5 int[5][5]; + +type INT int; + +// @type Array5OfInt5 < Array5OfIntArray +// @type Array5OfIntArray < ArrayOfIntArray +// @type IntArray = Array5OfIntArray[0] +// @type IntArray = Array5OfIntArray[4] +type Array5OfIntArray int[5][]; + +type ROIntArray readonly & IntArray; + +// @type ROInt5 < Int5 +// @type ROInt5 < ROIntArray +type ROInt5 readonly & int[5]; + +// -@type ArrayExcept5 <> Int5; +// -@type ArrayExcept5 < IntArray; +type ArrayExcept5 IntArray & !Int5; + +const FIVE = 5; + +// @type ArrayOfInt5 = ArrayOfIntFive +type ArrayOfIntFive int[][FIVE]; + +// @type Array5OfInt5 = ArrayFiveOfIntFive +type ArrayFiveOfIntFive int[FIVE][FIVE]; + +// @type ROArrayFiveOfIntFive < ArrayFiveOfIntFive +// @type ROArrayFiveOfIntFive < Array5OfInt5 +type ROArrayFiveOfIntFive ArrayFiveOfIntFive & readonly; + +type N never; +// @type ArrayOfInt5 = TwoArraysOfInt5[0] +// @type ArrayOfInt5 = TwoArraysOfInt5[1] +// @type N = TwoArraysOfInt5[2] +type TwoArraysOfInt5 int[2][][5]; + +// @type EmptyIntArray < IntArray +type EmptyIntArray int[0]; + +type Array2OfInt5 Int5[2]; + +type Array7OfArray2OfInt5 Array2OfInt5[7]; + +// @type Array7x2x5 = Array7OfArray2OfInt5 +type Array7x2x5 int[7][2][5]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-tuple-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-tuple-t.bal new file mode 100644 index 000000000000..91cab8527bef --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-tuple-t.bal @@ -0,0 +1,27 @@ +type Int1 int[1]; +type Int2 int[2]; + +// @type IntT = Int1 +type IntT [int]; + +// @type IntIntT = Int2 +type IntIntT [int, int]; + +// @type IntIntRT < IntIntT +// @type IntIntRT < Int2 +type IntIntRT readonly & [int, int]; + +// @type Int2R = IntIntRT +type Int2R readonly & int[2]; + +// @type Int = IntIntT[0] +type Int int; + +// @type Int = Int2Intersection[0] +// @type Int = Int2Intersection[1] +type Int2Intersection IntIntT & int[2]; + +// @type Int2Intersection = Int2AnyArrayIntersection +// @type Int = Int2AnyArrayIntersection[0] +// @type Int = Int2AnyArrayIntersection[1] +type Int2AnyArrayIntersection IntIntT & any[]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array2-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array2-t.bal new file mode 100644 index 000000000000..a66701665330 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array2-t.bal @@ -0,0 +1,66 @@ +type IntArray int[]; +type ISArray (int|string)[]; +type ISTArray (1|2|3)[]; + +type Int4 int[4]; +type Int1 int[1]; +type Int14 Int4|Int1; +type NegInt14 (!Int14 & IntArray); + +// -@type I4A = IntArray +// -@type I4A < ISArray +type I4A Int4|(!Int4 & IntArray); + +type Int10000 int[100000]; + +// -@type ISTArray < I10000A +type I10000A Int10000|(!Int10000 & IntArray); + +// -@type IA = IntArray +// -@type IA < ISArray +type IA Int14|NegInt14; + +type IS int|string; +type EMPTY []; +type IS1 IS[1]; +type IS2 IS[2]; +type IS3 IS[3]; + +// @type ALL = ISArray +type ALL EMPTY|IS1|IS2|IS3|[IS, IS, IS, IS, IS...]; + +// @type ISArray < U +type U EMPTY|[IS|float, IS...]; + +// @type ISArray < V +type V EMPTY|[IS]|[IS, IS|float, IS...]; + +// @type ISArray < W +type W EMPTY|[IS]|[IS, IS|float]|[IS, IS, IS|float, IS...]; + +// @type ISArray < X +type X EMPTY|[IS]|[IS, IS|float]|[IS, IS, IS|float]|[IS, IS, IS, IS|float, IS...]; + +// @type ISArray < Y +type Y EMPTY|[IS, IS, IS, IS|float, IS...]|[IS, IS, IS|float]|[IS, IS|float]|[IS]; + +// @type ISArray < Z +type Z [IS, IS, IS, IS|float, IS...]|[IS, IS, IS|float]|[IS, IS|float]|[IS]|EMPTY; + +// @type IntArray < P +type P EMPTY|[int]|[IS, int]|[IS, IS, IS, IS...]; + +// @type IntArray < Q +type Q EMPTY|[int]|[IS, IS]|[IS, IS, IS|float, IS...]; + +// @type IntArray < R +type R EMPTY|[int]|[IS, IS]|[int, int, IS|float, IS...]; + +// @type IntArray < S +type S EMPTY|[int]|[IS, IS]|[int, int, int, IS...]; + +// @type IntArray < T +type T EMPTY|[int]|[IS, int]|[IS, IS, IS|float, IS...]; + +// @type IntArray < T1 +type T1 EMPTY|[int]|[IS, IS, string...]|[IS, IS, IS, IS...]; \ No newline at end of file diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/float-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/float-tv.bal new file mode 100644 index 000000000000..e11c0d03170c --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/float-tv.bal @@ -0,0 +1,22 @@ +// @type NegativeZero < Float +// @type Zero < Float +// @type NegativeZero = Zero + +type Zero 0.0; + +type NegativeZero -0.0; + +type Float float; + +// @type D1 <> Float +// @type D1 <> Zero +type D1 0.0d; + +// @type F1 < Float +// @type F1 = Zero +// @type F1 = NegativeZero +// @type F2 = Zero +// @type F2 = NegativeZero +type F1 Zero|NegativeZero; + +type F2 F1 & Zero; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/func-quals-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/func-quals-tv.bal new file mode 100644 index 000000000000..b1d05f965f6a --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/func-quals-tv.bal @@ -0,0 +1,43 @@ +// @type IsolatedFuncTop < FuncTop +type IsolatedFuncTop isolated function; + +type FuncTop function; + +// @type FI1 < F1 +// @type FI1 < IsolatedFuncTop +// @type FI1 < FuncTop +type FI1 isolated function (); + +type F1 function (); + +type FI2 isolated function (int); + +type FI3 isolated function (int, int); + +// @type FI1 < FIX +// @type FI2 < FIX +// @type FI3 < FIX +// @type FIX < IsolatedFuncTop +// @type FIX < FuncTop +type FIX FI1|FI2|FI3; + +type F2 function (int); + +type F3 function (int, int); + +// @type FIX < FX +type FX F1|F2|F3; + +// @type F1 < FT1 +type FT1 transactional function (); + +type FT2 transactional function (int); + +type FT3 transactional function (int, int); + +// @type FT1 < FTX +// @type FT2 < FTX +// @type FT3 < FTX +// @type FX < FTX +// @type FIX < FTX +type FTX FT1|FT2|FT3; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-intersection-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-intersection-tv.bal new file mode 100644 index 000000000000..51d2c21b43b3 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-intersection-tv.bal @@ -0,0 +1,6 @@ +type F1 function(1|2|3); +type F2 function(2|3|4); + +// @type F < F1 +// @type F < F2 +type F F1&F2; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-param-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-param-tv.bal new file mode 100644 index 000000000000..92e13f181a44 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-param-tv.bal @@ -0,0 +1,7 @@ +// @type F12 < F1 +type F12 function(1|2); +type F1 function(1); + +// @type F_ret1 < F_ret12 +type F_ret12 function() returns 1|2; +type F_ret1 function() returns 1; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-rec-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-rec-tv.bal new file mode 100644 index 000000000000..203a184e923d --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-rec-tv.bal @@ -0,0 +1,7 @@ +// @type F < Fx +type F function() returns F; +type Fx function() returns function; + +// @type Gx < G +type G function(G); +type Gx function(function); diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-rest-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-rest-tv.bal new file mode 100644 index 000000000000..9699a0cec5a6 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-rest-tv.bal @@ -0,0 +1,8 @@ +// @type FInt < F1 +// @type FInt < F2 +// @type FInt < F3 +// @type F3 < F2 +type FInt function(int...); +type F1 function(int); +type F2 function(int, int); +type F3 function(int, int, int...); diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-tv.bal new file mode 100644 index 000000000000..a1d805b70a34 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-tv.bal @@ -0,0 +1,16 @@ +type F function; + +// @type F1 < F +// @type F1_bar < F +type F1 function(int); +type F1_bar function(int a); + +// @type F2 < F +// @type F2_bar < F +type F2 function(int) returns boolean; +type F2_bar function(int a) returns boolean; + +// @type F3 < F +// @type F3_bar < F +type F3 function(int...) returns boolean; +type F3_bar function(int... a) returns boolean; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-union-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-union-tv.bal new file mode 100644 index 000000000000..6858050e36ff --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-union-tv.bal @@ -0,0 +1,8 @@ +type F1 function(1|2); +type F2 function(2|3); + +// @type F1 < F +// @type F2 < F +// @type F < Fx +type F F1|F2; +type Fx function(2); diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function2-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function2-tv.bal new file mode 100644 index 000000000000..1a6563cd54f9 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function2-tv.bal @@ -0,0 +1,13 @@ +type ANY any; + +// @type F1 < ANY +// @type F_INT < ANY +// @type F_INT < F1 +type F1 function(1); +type F_INT function(int); + +// @type F1_ret < ANY +// @type F_INT_ret < ANY +// @type F1_ret < F_INT_ret +type F1_ret function() returns 1; +type F_INT_ret function() returns int; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/future-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/future-tv.bal new file mode 100644 index 000000000000..0a9e4c396769 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/future-tv.bal @@ -0,0 +1,7 @@ +// @type FInt < FUTURE +// @type FByte < FInt +type FUTURE future; + +type FInt future; + +type FByte future; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/list1-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/list1-tv.bal new file mode 100644 index 000000000000..d80b50d5df6e --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/list1-tv.bal @@ -0,0 +1,3 @@ +// @type A <> B +type A int[2][][5]; +type B int[][]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-basic-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-basic-tv.bal new file mode 100644 index 000000000000..a17a0d5da75d --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-basic-tv.bal @@ -0,0 +1,3 @@ +// @type M_INT < M_ANY +type M_ANY map; +type M_INT map; \ No newline at end of file diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-record-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-record-tv.bal new file mode 100644 index 000000000000..8b0262b000ab --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-record-tv.bal @@ -0,0 +1,32 @@ +// @type R1 < M1 +type R1 record {| int x; int...; |}; +type M1 map; + +// @type R3 = R1 +type R3 record {| int x; int ...; |}; + +// @type R4 < M2 +// @type R1 < M2 +// @type M1 < M2 +type R4 record {| int|string x; int|string ...; |}; +type M2 map; + +// @type R5 < M1 +// @type R5 < R1 +type R5 record {| int x; |}; + +// @type R6 < R1 +// @type R6 < M1 +type R6 record {| int x; int y; int ...; |}; + +// @type R7 <> R6 +type R7 record {| int x; int y; string ...; |}; + +// @type R6 < R8 +type R8 record {| int x; int y; int|string ...; |}; + +// @type R9 <> R8 +type R9 record {| int j; string k; |}; + +// @type R10 < R8 +type R10 record {| int x; int y; string j; |}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-t.bal new file mode 100644 index 000000000000..7ea9c2f212b9 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-t.bal @@ -0,0 +1,25 @@ +// @type T < S +public type T R1|map<"A">; + +public type R1 record {| + byte A; + float...; +|}; + +public type S R2|map; + +public type R2 record {| + int A; + float...; +|}; + +// @type T2504 < T2525 +public type T2504 [map<[int]>, map<1>]; + +public type T2525 (map|map)[]; + + +// @type MISI < MIS +type MISI map|map; + +type MIS map; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping1-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping1-t.bal new file mode 100644 index 000000000000..71bf0b0446ca --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping1-t.bal @@ -0,0 +1,15 @@ +// the order of type defns are intentional + +type M map; + +// @type M = N +type N map; + +// @type M1 = N +// @type M1 = M +type M1 M|record {}; + +// @type M1 = M2 +// @type M2 = M +// @type M2 = N +type M2 N|record {}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping2-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping2-t.bal new file mode 100644 index 000000000000..b12f20a25f38 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping2-t.bal @@ -0,0 +1,15 @@ +// the order of type defns are intentional + +type M map<(1|2|3)>; + +// @type M = N +type N map<(1|2|3)>; + +// @type M1 = N +// @type M1 = M +type M1 M|map<(1|2)>; + +// @type M1 = M2 +// @type M2 = M +// @type M2 = N +type M2 N|map<(1|2)>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping3-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping3-t.bal new file mode 100644 index 000000000000..81a50d3cb104 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping3-t.bal @@ -0,0 +1,13 @@ +type M1 map|record {}; + +// @type M1 = M2 +type M2 mapM|record {}; + +type M3 map<(1|2|3)>|map<(1|2)>; + +// @type M3 = M4 +// @type M4 < M1 +// @type M4 < M2 +// @type M3 < M1 +// @type M3 < M2 +type M4 map<(1|2|3)>|map<(1|2)>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mappingIntersect-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mappingIntersect-tv.bal new file mode 100644 index 000000000000..375bc186966c --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mappingIntersect-tv.bal @@ -0,0 +1,11 @@ +type M1 map; +type M2 map; + +// @type T1 = M1M2 +type M1M2 map; +type T1 M1 & M2; + +type M3 map; + +// @type T2 = M1M2 +type T2 M1 & M2 & M3; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mutable-record-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mutable-record-t.bal new file mode 100644 index 000000000000..c9029bf6b586 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mutable-record-t.bal @@ -0,0 +1,82 @@ +type I record {| + int x; +|}; + +type S record {| + string x; +|}; + +type IS record {| + int|string x; +|}; + +// @type IorS < IS +type IorS I|S; + +type NN record {| + int x; + int y; +|}; + +type SS record {| + string x; + string y; +|}; + +type NS record {| + int x; + string y; +|}; + +type SN record {| + string x; + int y; +|}; + +type UU record {| + int|string x; + int|string y; +|}; + +// @type U < UU +type U NN|SS|NS|SN; + +type P record {| + I|S x; +|}; + +// @type P < Q +type Q record {| + IS x; +|}; + +type P2 record {| + I|S x; + boolean y; +|}; + +// @type P2 < Q2 +type Q2 record {| + IS x; + boolean y; +|}; + +type P3 record {| + I|S x; + boolean...; +|}; + +// @type P3 < Q3 +type Q3 record {| + IS x; + boolean...; +|}; + +type P4 record { + I|S x; +}; + +// @type P4 < Q4 +type Q4 record { + IS x; +}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/not1-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/not1-tv.bal new file mode 100644 index 000000000000..ae68b0827b06 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/not1-tv.bal @@ -0,0 +1,5 @@ + +type T1 int; + +// -@type T1 = T2 +type T2 int? & !(); diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-binaryops-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-binaryops-tv.bal new file mode 100644 index 000000000000..fce837339546 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-binaryops-tv.bal @@ -0,0 +1,34 @@ +type TOP object {}; +type O1 object { + int a; +}; + +type O2 object { + float b; +}; + +// @type O12 < TOP +// @type O1 < O12 +// @type O2 < O12 +type O12 O1|O2; + +type O3 object { + int a; + float b; + decimal c; +}; + +type O4 object { + int a; + float b; + string c; +}; + +type O34 O3 & O4; + +// @type OX < TOP +// @type O34 < OX +type OX object { + int a; + float b; +}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-distinct-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-distinct-tv.bal new file mode 100644 index 000000000000..bae6af6458b4 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-distinct-tv.bal @@ -0,0 +1,27 @@ +// @type DistinctObject1 < ObjectTy1 +// @type DistinctObject2 < ObjectTy1 +// @type DistinctObject1 <> DistinctObject2 +// @type DistinctObject3 < DistinctObject1 + +type DistinctObject1 distinct ObjectTy1; + +type DistinctObject2 distinct ObjectTy1; + +type DistinctObject3 distinct DistinctObject1; + +type ObjectTy1 object { + int foo; + function bar() returns int; +}; + +// @type RecursiveDistinctObject1 < RecursiveObject +// @type RecursiveDistinctObject1 <> RecursiveDistinctObject2 +type RecursiveDistinctObject1 distinct object { + RecursiveDistinctObject1? oo; +}; + +type RecursiveDistinctObject2 distinct RecursiveObject; + +type RecursiveObject object { + RecursiveObject? oo; +}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-qulifiers-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-qulifiers-tv.bal new file mode 100644 index 000000000000..d56694168bac --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-qulifiers-tv.bal @@ -0,0 +1,61 @@ +// @type IO1 < O1 +type IO1 isolated object { + int x; + function f() returns int; +}; + +// @type IO1 < IO2 +// @type IO2 <> O1 +type IO2 isolated object { + int x; +}; + +type O1 object { + int x; + function f() returns int; +}; + +// @type SO1 < O1 +type SO1 service object { + int x; + function f() returns int; +}; + +// @type ISO1 < O1 +// @type ISO1 < IO1 +// @type ISO1 < SO1 +type ISO1 isolated service object { + int x; + function f() returns int; +}; + +// @type CO1 < O1 +// @type CO1 <> SO1 +type CO1 client object { + int x; + function f() returns int; +}; + +// @type ICO1 < O1 +// @type ICO1 < IO1 +// @type ICO1 < CO1 +// @type ICO1 <> SO1 +// @type ICO1 <> ISO1 +type ICO1 isolated client object { + int x; + function f() returns int; +}; + +// @type I_TOP < TOP +// @type IO1 < I_TOP +type I_TOP isolated object {}; + +// @type S_TOP < TOP +// @type SO1 < S_TOP +type S_TOP service object {}; + +// @type C_TOP < TOP +// @type CO1 < C_TOP +type C_TOP client object {}; + +type TOP object {}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-rec-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-rec-tv.bal new file mode 100644 index 000000000000..f269f36c136a --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-rec-tv.bal @@ -0,0 +1,18 @@ +// @type O1 = O2 +type O1 object { + O1? other; +}; + +type O2 object { + O2? other; +}; + +// @type O4 < O3 +type O3 object { + function foo(O3 other); +}; + +type O4 object { + function foo(O3 other); + function bar(O4 other); +}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-resource-fn-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-resource-fn-tv.bal new file mode 100644 index 000000000000..0f7843aac4fc --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-resource-fn-tv.bal @@ -0,0 +1,32 @@ +// @type C1 <> C2 +type C1 client object { + resource function get [int](); +}; + +// @type Ci1 <> C1 +type Ci1 client object { + resource function post [int](); +}; + +type C2 client object { + resource function get [string](); +}; + +// @type C1 < C3 +type C3 client object { + resource function get [byte](); +}; + +type Cx1 client object { + resource function get foo/[int](int x); +}; + +// @type Cx1 < Cx2 +type Cx2 client object { + resource function get foo/[byte](byte x); +}; + +// @type Cx3 <> Cx2 +type Cx3 client object { + resource function get bar/[byte]/bar(byte x); +}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-simple-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-simple-tv.bal new file mode 100644 index 000000000000..902997948a66 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-simple-tv.bal @@ -0,0 +1,64 @@ +// @type O1 = O2 +type O1 object { + public int a; +}; + +type O2 object { + public int a; +}; + +// @type O3 < O1 +type O3 object { + public int a; + public string b; +}; + +// @type O4 < O1 +type O4 object { + public byte a; +}; + +// @type OO1 = OO2 +type OO1 object { + public function foo(int a) returns int; +}; + +type OO2 object { + public function foo(int a) returns int; +}; + +// @type OO3 < OO1 +type OO3 object { + public function foo(int a, int... rest) returns int; +}; + +// @type OO4 < OO1 +type OO4 object { + public function foo(int a) returns int; + public int a; +}; + +// @type OO5 <> OO4 +type OO5 object { + public function (int a) returns int foo; + public int a; +}; + +// @type G3 <> O3 +// @type G3 < O1 +type G3 object { + public int a; + string b; +}; + +// @type OO4 <> GG4 +// @type GG4 < O1 +type GG4 object { + function foo(int a) returns int; + public int a; +}; + +// @type I1 < OO1 +type I1 object { + public isolated function foo(int a) returns int; +} diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-with-dependently-typed-object-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-with-dependently-typed-object-tv.bal new file mode 100644 index 000000000000..629eb7bcf547 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-with-dependently-typed-object-tv.bal @@ -0,0 +1,8 @@ +// @type Bar < Baz +type Bar object { + public function get(typedesc td) returns td|error; +}; + +type Baz object { + public function get(typedesc td) returns anydata|error; +}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record1-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record1-t.bal new file mode 100644 index 000000000000..2fa3c00a3606 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record1-t.bal @@ -0,0 +1,52 @@ +type M1 map; + +type R1 record {| int a; |}; + +// @type R2 < M1 +// @type R1 < R2 +type R2 record {| int a?; |}; + +// @type R2 <> R3 +type R3 record {| int? a; |}; + +// @type R4 <> R2 +type R4 record {| int a?; string b; |}; + +type R5 record {| int a; string b; |}; + +// @type R1 < R6 +// @type R2 < R6 +// @type R4 < R6 +// @type R5 < R6 +type R6 record {| int a?; string b?; |}; + +// @type R1 < R7 +// @type R2 <> R7 +// @type R4 <> R7 +// @type R5 < R7 +// @type R7 < R6 +type R7 record {| int a; string b?; |}; + +// @type R2 < R8 +type R8 record {| int|string a?; |}; + +// @type R2 < R9 +// @type R1 < R9 +type R9 record {| int|string a?; string|boolean b?; boolean c?; |}; + +// @type R1 < R10 +// @type R2 < R10 +type R10 record {| int? a?; |}; + +// @type M2 <> R1 +// @type M2 < R2 +// @type M2 <> R3 +// @type M2 <> R4 +// @type M2 <> R5 +// @type M2 < R6 +// @type M2 <> R7 +// @type M2 < R8 +// @type M2 < R9 +// @type M2 < R10 +// @type M2 < M1 +type M2 map; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record2-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record2-t.bal new file mode 100644 index 000000000000..7e17b13ff91d --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record2-t.bal @@ -0,0 +1,52 @@ + +type M1 map; + +type R1 record {| int a; anydata...; |}; + +// @type M1 < R2 +// @type R1 < R2 +type R2 record {| int a?; anydata...; |}; + +// @type R2 <> R3 +type R3 record {| int? a; anydata...; |}; + +// @type R4 < R2 +type R4 record {| int a?; string b; anydata...; |}; + +type R5 record {| int a; string b; anydata...; |}; + +// @type R1 <> R6 +// @type R6 < R2 +// @type R4 < R6 +// @type R5 < R6 +type R6 record {| int a?; string b?; anydata...; |}; + +// @type R7 < R1 +// @type R7 < R2 +// @type R4 <> R7 +// @type R5 < R7 +// @type R7 < R6 +type R7 record {| int a; string b?; anydata...; |}; + +// @type R2 < R8 +type R8 record {| int|string a?; anydata...; |}; + +// @type R9 <> R2 +// @type R1 <> R9 +type R9 record {| int|string a?; string|boolean b?; boolean c?; anydata...; |}; + +// @type R1 < R10 +// @type R2 < R10 +type R10 record {| int? a?; anydata...; |}; + +// @type R1 < M2 +// @type R2 < M2 +// @type R3 < M2 +// @type R4 < M2 +// @type R5 < M2 +// @type R6 < M2 +// @type R7 < M2 +// @type R8 < M2 +// @type R9 < M2 +// @type R10 < M2 +type M2 map; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record3-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record3-t.bal new file mode 100644 index 000000000000..2be19be8b930 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record3-t.bal @@ -0,0 +1,173 @@ +// @type R11 < R12 +type R11 record {| int a; |}; + +type R12 record {| int a; anydata...; |}; + +// @type R11 < R21 +// @type R21 < R22 +type R21 record {| int a?; |}; + +// @type R11 < R22 +// @type R12 < R22 +type R22 record {| int a?; anydata...; |}; + +// @type R11 < R31 +// @type R31 < R32 +type R31 record {| int? a; |}; + +// @type R11 < R32 +// @type R12 < R32 +type R32 record {| int? a; anydata...; |}; + +// @type R41 < R22 +// @type R41 < R42 +type R41 record {| int a?; string b; |}; + +// @type R42 < R22 +type R42 record {| int a?; string b; anydata...; |}; + +// @type R51 < R12 +// @type R51 < R22 +// @type R51 < R32 +// @type R51 < R41 +// @type R51 < R42 +// @type R51 < R52 +type R51 record {| int a; string b; |}; + +// @type R52 < R12 +// @type R52 < R22 +// @type R52 < R32 +// @type R52 < R42 +type R52 record {| int a; string b; anydata...; |}; + +// @type R11 < R61 +// @type R21 < R61 +// @type R61 < R22 +// @type R41 < R61 +// @type R51 < R61 +// @type R61 < R62 +type R61 record {| int a?; string b?; |}; + +// @type R11 < R62 +// @type R21 < R62 +// @type R41 < R62 +// @type R42 < R62 +// @type R51 < R62 +// @type R52 < R62 +// @type R62 < R22 +type R62 record {| int a?; string b?; anydata...; |}; + +// @type R11 < R71 +// @type R71 < R12 +// @type R71 < R22 +// @type R71 < R32 +// @type R71 < R61 +// @type R71 < R62 +// @type R51 < R71 +// @type R71 < R72 +type R71 record {| int a; string b?; |}; + +// @type R11 < R72 +// @type R72 < R12 +// @type R72 < R22 +// @type R72 < R32 +// @type R51 < R72 +// @type R52 < R72 +// @type R72 < R62 +type R72 record {| int a; string b?; anydata...; |}; + +// @type R11 < R81 +// @type R21 < R81 +// @type R81 < R82 +type R81 record {| int|string a?; |}; + +// @type R11 < R82 +// @type R12 < R82 +// @type R21 < R82 +// @type R22 < R82 +// @type R42 < R82 +// @type R41 < R82 +// @type R51 < R82 +// @type R52 < R82 +// @type R61 < R82 +// @type R62 < R82 +// @type R71 < R82 +// @type R72 < R82 +type R82 record {| int|string a?; anydata...; |}; + +// @type R11 < R91 +// @type R21 < R91 +// @type R41 < R91 +// @type R51 < R91 +// @type R61 < R91 +// @type R71 < R91 +// @type R81 < R91 +// @type R91 < R82 +// @type R91 < R92 +type R91 record {| int|string a?; string|boolean b?; boolean c?; |}; + +// @type R11 < R92 +// @type R21 < R92 +// @type R41 < R92 +// @type R51 < R92 +// @type R61 < R92 +// @type R71 < R92 +// @type R81 < R92 +// @type R92 < R82 +type R92 record {| int|string a?; string|boolean b?; boolean c?; anydata...; |}; + +// @type R11 < R101 +// @type R21 < R101 +// @type R31 < R101 +// @type R101 < R102 +type R101 record {| int? a?; |}; + +// @type R11 < R102 +// @type R12 < R102 +// @type R21 < R102 +// @type R22 < R102 +// @type R31 < R102 +// @type R41 < R102 +// @type R42 < R102 +// @type R51 < R102 +// @type R52 < R102 +// @type R61 < R102 +// @type R62 < R102 +// @type R71 < R102 +// @type R72 < R102 +type R102 record {| int? a?; anydata...; |}; + +// @type M1 < R21 +// @type M1 < R22 +// @type M1 < R61 +// @type M1 < R62 +// @type M1 < R81 +// @type M1 < R82 +// @type M1 < R91 +// @type M1 < R92 +// @type M1 < R101 +// @type M1 < R102 +// @type M1 < M2 +type M1 map; + +// @type R11 < M2 +// @type R12 < M2 +// @type R21 < M2 +// @type R22 < M2 +// @type R31 < M2 +// @type R32 < M2 +// @type R41 < M2 +// @type R42 < M2 +// @type R51 < M2 +// @type R52 < M2 +// @type R61 < M2 +// @type R62 < M2 +// @type R71 < M2 +// @type R72 < M2 +// @type R81 < M2 +// @type R82 < M2 +// @type R91 < M2 +// @type R92 < M2 +// @type R101 < M2 +// @type R102 < M2 +type M2 map; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj1-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj1-tv.bal new file mode 100644 index 000000000000..9481bf4bb97b --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj1-tv.bal @@ -0,0 +1,41 @@ +type B boolean; + +// @type TF = B +type TF true|false; + +// @type T < TF +type T true; + +// @type F < B +type F false; + +// @type INTEGER <> B +type INTEGER int; + +type S string; +type I int; +type N 2; +const ONE = 1; + +// @type BL[1] = B +// @type BL[2] = B +// @type BL[I] = B +// @type BL[N] = B +// @type BL[ONE] = B +type BL boolean[]; + +// @type M[S] = B +type M map; + +type f1 "f1"; +type f2 "f2"; +const FOO = "f2"; + +// @type R[f1] = INTEGER +// @type R[f2] = B +// @type R[FOO] = B +type R record {| + int f1; + boolean f2; +|}; + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj10-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj10-tv.bal new file mode 100644 index 000000000000..b23b7c7614b1 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj10-tv.bal @@ -0,0 +1,22 @@ +type NEVER never; +type INT int; +type FLOAT float; + +type FirstFive 0|1|2|3|4|5; +type FFFloat FirstFive|float; + +type T1 [int...]; +type T2 [int, int, int...]; + +// @type T3[0] = NEVER +// @type T3[1] = NEVER +// @type T3[1] = NEVER +type T3 T1 & !T2; + +type T4 [FirstFive|float...]; +type T5 [int, int, FirstFive...]; + +// @type T6[0] = FFFloat +// @type T6[1] = FFFloat +// @type T6[3] = FFFloat +type T6 T4 & !T5; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj2-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj2-tv.bal new file mode 100644 index 000000000000..0dd6026f4e1a --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj2-tv.bal @@ -0,0 +1,6 @@ +type I int; + +// -@type A[I] = I +// -@type A[0] = I +// -@type A[1] = I +type A [int?, int?] & ![(), ()] & ![int, ()] & ![(), int]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj3-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj3-tv.bal new file mode 100644 index 000000000000..ce6d648ef2c7 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj3-tv.bal @@ -0,0 +1,13 @@ +type IB int|boolean; +type SB string|boolean; +type C string:Char; +type NonC string & !C; + +// @type R[C] = IB +// -@type R[NonC] = SB +type R record {| + int a; + int b; + string fieldName; + boolean...; +|}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj4-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj4-tv.bal new file mode 100644 index 000000000000..7fd417cabbfe --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj4-tv.bal @@ -0,0 +1,11 @@ +const ZERO = 0; +const ONE = 1; +type Index ZERO|ONE; +type Int int; +type Str string; +type IntStr Int|Str; + +// @type T[ZERO] = Int +// @type T[ONE] = Str +// @type T[Index] = IntStr +type T [int, string]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj5-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj5-tv.bal new file mode 100644 index 000000000000..3e0de9d8ed3f --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj5-tv.bal @@ -0,0 +1,14 @@ +const THREE = 3; +type F float; +type Int int; +type ISF int|string|float; +type IF int|float; +type SF string|float; +type T02 0|2; +type T12 1|2; + +// @type T[THREE] = F +// @type T[Int] = ISF +// @type T[T02] = IF +// @type T[T12] = SF +type T [int, string, float...]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj6-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj6-tv.bal new file mode 100644 index 000000000000..7db777560fb1 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj6-tv.bal @@ -0,0 +1,19 @@ +const THREE = 3; +const FOUR = 4; +type F float; +type Int int; +type ISF int|string|float; +type IF int|float; +type SF string|float; +type T02 0|2; +type T12 1|2; +type NEVER never; + +type T1 [int, string, float...]; + +// @type T[THREE] = F +// @type T[FOUR] = NEVER +// @type T[Int] = ISF +// @type T[T02] = IF +// @type T[T12] = SF +type T T1 & any[4]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj7-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj7-tv.bal new file mode 100644 index 000000000000..00c78786b66d --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj7-tv.bal @@ -0,0 +1,25 @@ +type NEVER never; +type BOOL boolean; +type BOOLOPT boolean?; +type INT int; +type NIL (); +type INTOPT int?; +type STROPT string?; +type INTSTROPT int|string?; +type INT_FLOAT_BOOL_OPT int|float|boolean?; +type STR_FLOAT_BOOL_OPT string|float|boolean?; +type INT_STR_FLOAT_BOOL_OPT int|string|float|boolean?; + +type C01 0|1; +type C02 0|2; +type C12 1|2; + +type T1 [int?, string?, float|boolean...]; +type T2 [int, (string|float)...]; +// @type T3[0] = INTOPT +// @type T3[1] = STROPT +// @type T3[C01] = INTSTROPT +// @type T3[C02] = INT_FLOAT_BOOL_OPT +// @type T3[C12] = STR_FLOAT_BOOL_OPT +// @type T3[INT] = INT_STR_FLOAT_BOOL_OPT +type T3 T1 & !T2; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj8-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj8-tv.bal new file mode 100644 index 000000000000..080363062f2d --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj8-tv.bal @@ -0,0 +1,20 @@ +type INT int; +type STRING string; +type STROPT string?; +type NIL (); + +type C01 0|1; +type C02 0|2; +type C12 1|2; + +type T1 [int?, string...]; +type T2 [int, (string|float)...]; +// @type T3[0] = NIL +// @type T3[1] = STRING +// @type T3[2] = STRING +// @type T3[100] = STRING +// @type T3[C01] = STROPT +// @type T3[C02] = STROPT +// @type T3[C12] = STRING +// @type T3[INT] = STROPT +type T3 T1 & !T2; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj9-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj9-tv.bal new file mode 100644 index 000000000000..b84619878962 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj9-tv.bal @@ -0,0 +1,30 @@ +type INT int; +type INTFLOAT int|float; +type NEVER never; + +type C01 0|1; +type C02 0|2; +type C12 1|2; + +type NOTC01 !C01 & int; + +type T1 int[100000]; +type T2 [C01, C01, C01, (int|float)...]; + + +// @type T3[0] = NOTC01 +// @type T3[100] = INT +// @type T3[1000] = INT +// @type T3[10000] = INT +// @type T3[99999] = INT +// @type T3[100000] = NEVER +type T3 T1 & !T2; + +// @type T4[0] = C01 +// @type T4[1] = C01 +// @type T4[2] = C01 +// @type T4[3] = INTFLOAT +// @type T4[100] = INTFLOAT +// @type T4[1000] = INTFLOAT +// @type T4[100000] = INTFLOAT +type T4 T2 & !T1; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/record-proj-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/record-proj-tv.bal new file mode 100644 index 000000000000..fc65b365f1eb --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/record-proj-tv.bal @@ -0,0 +1,45 @@ +type A record {| + int x; + string y; +|}; + +// @type B[XorY] = IorS +// @type B[other] = NEVER +type B record {| + string x; + int y; +|}; + +// @type C[other] = BOOLEAN +// @type C[XorY] = IorS +// @type C[XorYorOther] = IorSOrB +type C record {| + string x; + int y; + float z; + boolean...; +|}; + +type IorS int|string; +type IorSOrB IorS|boolean; +type IorSorF IorS|float; + +const x = "x"; +const z = "z"; +const other = "other"; +type XorY "x"|"y"; +type NEVER never; +type BOOLEAN boolean; +type FLOAT float; + +type XorYorOther XorY|other; + +// @type AorB[x] = IorS +// @type AorB[XorY] = IorS +type AorB A|B; + +// @type AorBorC[x] = IorS +// @type AorBorC[z] = FLOAT +// @type AorBorC[other] = BOOLEAN +// @type AorBorC[XorYorOther] = IorSOrB +type AorBorC AorB|C; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/record-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/record-t.bal new file mode 100644 index 000000000000..b6ad2c517c54 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/record-t.bal @@ -0,0 +1,16 @@ +// the order of type defns are intentional + +type T1 record {| + 65 X; +|}; + +type T2 record {| + int:Signed8 X; +|}; + +// @type T1 < T3 +type T3 T4|T2; + +type T4 record {| + int:Signed8 a; +|}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/recursive-record-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/recursive-record-t.bal new file mode 100644 index 000000000000..1a6b0375ae40 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/recursive-record-t.bal @@ -0,0 +1,9 @@ +type Bdd Node|boolean; + +// @type Node < Bdd +type Node readonly & record {| + int atom; + Bdd left; + Bdd middle; + Bdd right; +|}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-recursive-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-recursive-tv.bal new file mode 100644 index 000000000000..5b96f55fc9ca --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-recursive-tv.bal @@ -0,0 +1,8 @@ +// @type SR < S +type SR stream; + +type S stream; + +// @type S1 < SR +// @type S1 < S +type S1 stream<()>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-subtype-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-subtype-tv.bal new file mode 100644 index 000000000000..20237c2bc796 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-subtype-tv.bal @@ -0,0 +1,10 @@ +// @type I < U1 +// @type I < U2 +// @type S < U1 +// @type S < U2 +// @type U1 < U2 + +type I stream; +type S stream; +type U1 I|S; +type U2 stream; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-subtype2-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-subtype2-tv.bal new file mode 100644 index 000000000000..32ccf067c9fe --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-subtype2-tv.bal @@ -0,0 +1,19 @@ +// @type I < J +// @type I < J1 +// @type I < J2 +type I stream; + +// @type S < J +// @type S < J1 +// @type S < J2 +type S stream; + +type T int|string|(); +// @type J = J1 +type J stream; +type J1 stream; + +// @type J < J2 +// @type J1 < J2 +type J2 stream; +type J3 stream; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/string-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/string-tv.bal new file mode 100644 index 000000000000..db4f84e356ac --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/string-tv.bal @@ -0,0 +1,16 @@ +// @type U1 < String +// @type U1 < Char +type U1 "අ"; +// @type U2 < String +// @type U2 < Char +type U2 "🛧"; +// @type C1 < String +// @type C1 < Char +type C1 "a"; + +// @type S1 < String +// @type S1 <> Char +type S1 "abc"; + +type String string; +type Char string:Char; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table-readonly-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table-readonly-t.bal new file mode 100644 index 000000000000..ebebd154b787 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table-readonly-t.bal @@ -0,0 +1,25 @@ +type R record {| + int id; + int f; +|}; + +type R1 record {| + int id; + int f; + float d; +|}; + +type READ readonly; + +// @type W < T +// @type W < READ +// @type T < READ +// @type W < Y1 +// @type Y1 <> T +// -@type Z < Y1 +// -@type Z <> T +type T table & readonly | table & readonly; +type W table & readonly; +type Y1 table; +type Y table; +type Z table & !readonly; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table-t.bal new file mode 100644 index 000000000000..9c52d92c3703 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table-t.bal @@ -0,0 +1,13 @@ +type X1 record {| + int id; + string f; +|}; + +type X2 record {| + int id; + string:Char f; +|}; + +// @type T2 < T1 +type T1 table; +type T2 table; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table2-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table2-t.bal new file mode 100644 index 000000000000..f7e40035b911 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table2-t.bal @@ -0,0 +1,31 @@ +type R1 record {| + int id; + string f; +|}; + +type R2 record {| + int id; + string f; + float d; +|}; + +type R3 record {| + int id; + string:Char f; +|}; + +type READ readonly; + +type T1 table; +type T2 table; +type T3 table; + +// @type TI < T1 +// @type TI = T3 +// @type T1 < TU +// @type T2 < TU +type TI T1 & T3; +type TU T1|T2; + +// -@type T1 <> TC +type TC !T1; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table3-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table3-t.bal new file mode 100644 index 000000000000..0c639556f062 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table3-t.bal @@ -0,0 +1,27 @@ +type X1 record {| + int x; +|}; + +type X2 record {| + string x; +|}; + +type T1 table; +type T2 table; +type T3 T1|T2; +// @type T3 < T4 +type T4 table; + + +type X3 map; +type X4 map; + +type T5 table; +type T6 table; +type T7 T5|T6; +// @type T7 < T8 +type T8 table; + +// @type T8 < T9 +// @type T7 < T9 +type T9 table>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/test_test.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/test_test.bal new file mode 100644 index 000000000000..009d8ac1322c --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/test_test.bal @@ -0,0 +1,17 @@ +type I int; +type F float; +type S string; + +// @type I <> F + +type K decimal; + +type M map; + +// @type M[S] = I + +// @type L[I] = I +type L int[]; + +// @type SL[I] = S +type SL string[]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple1-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple1-tv.bal new file mode 100644 index 000000000000..cfac1e3640a7 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple1-tv.bal @@ -0,0 +1,16 @@ +type R record {| + int f1; + boolean f2; +|}; + +// @type A1 = T1 +type A1 int[]; +type T1 [int...]; + +// @type A2 = T2 +type A2 R[]; +type T2 [R...]; + +// @type A3 = T3 +type A3 T2[]; +type T3 [T2...]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple2-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple2-tv.bal new file mode 100644 index 000000000000..d0f64d6d92ad --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple2-tv.bal @@ -0,0 +1,14 @@ +type IS16 int:Signed16; +type I int; +type I2 int?; + +// @type T1 <> T2 +type T1 [int, string]; +type T2 [string, int]; + +// @type T4 < T3 +// @type T3[0] = I2 +// @type T3[1] = I +// @type T4[1] = IS16 +type T3 [int?, int, any]; +type T4 [int, int:Signed16, int]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple3-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple3-tv.bal new file mode 100644 index 000000000000..9a12b1aed2cc --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple3-tv.bal @@ -0,0 +1,20 @@ +type R record {| + int f1; + boolean f2; +|}; + +type R1 record {| + int f1; + boolean f2; +|}; + +type R2 record {| + int:Signed16 f1; + boolean f2; +|}; + +// @type T1 = T +// @type T2 < T +type T [R...]; +type T1 [R1...]; +type T2 [R2...]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple4-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple4-tv.bal new file mode 100644 index 000000000000..537b8c836d1d --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple4-tv.bal @@ -0,0 +1,7 @@ +// @type T2 < T1 +type T1 [int...]; +type T2 [int,int...]; + +// @type T3 < T1 +type T4 [int?,int?...]; +type T3 [int, int, int, int...]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/typedesc-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/typedesc-tv.bal new file mode 100644 index 000000000000..146649ab6d7e --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/typedesc-tv.bal @@ -0,0 +1,10 @@ +// @type I < U1 +// @type I < U2 +// @type S < U1 +// @type S < U2 +// @type U1 < U2 + +type I typedesc; +type S typedesc; +type U1 I|S; +type U2 typedesc; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-complex-ro-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-complex-ro-tv.bal new file mode 100644 index 000000000000..6716b76bfc66 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-complex-ro-tv.bal @@ -0,0 +1,38 @@ +type X xml; +type RX readonly & X; + +type N xml; +type T xml:Text; +type E readonly & xml:Element; +type P readonly & xml:ProcessingInstruction; +type C readonly & xml:Comment; +type XE xml; +type XP xml

; +type XC xml; + +type ReadOnlyFlat T|E|P|C; + +// -@type NonEmptyRoSingletons < ReadOnlyFlat +// -@type NonEmptyRoSingletons <> T +// -@type NonEmptyRoSingletons <> N +// -@type E < NonEmptyRoSingletons +// -@type P < NonEmptyRoSingletons +// -@type C < NonEmptyRoSingletons +type NonEmptyRoSingletons ReadOnlyFlat & !N; + +// -@type NonEmptyRoSingletons < UX +type UX XE|XP|XC|T; + +// -@type XNonEmptyRoSingletons = RX +// -@type XNonEmptyRoSingletons < X +type XNonEmptyRoSingletons xml; + +// @type XUX = RX +type XUX xml; + +type NEVER never; +type RWX X & !readonly; + +// -@type RX_UNION_RO = X +type RX_UNION_RO RX | RWX; + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-complex-rw-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-complex-rw-tv.bal new file mode 100644 index 000000000000..9c9e293ca8c9 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-complex-rw-tv.bal @@ -0,0 +1,30 @@ +type X xml; + +type N xml; +type T xml:Text; +type E xml:Element; +type P xml:ProcessingInstruction; +type C xml:Comment; +type XE xml; +type XP xml

; +type XC xml; + +type S T|E|P|C; + +// -@type NonEmptyS < S +// -@type NonEmptyS <> T +// -@type NonEmptyS <> N +// -@type E < NonEmptyS +// -@type P < NonEmptyS +// -@type C < NonEmptyS +type NonEmptyS S & !N; + +// -@type NonEmptyS < UX +type UX XE|XP|XC|T; + +// -@type XNonEmptyS = X +type XNonEmptyS xml; + +// @type XUX = X +type XUX xml; + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-never-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-never-tv.bal new file mode 100644 index 000000000000..7a4059117df9 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-never-tv.bal @@ -0,0 +1,31 @@ +type N xml; + +// @type N = NS +type NS xml; + +// @type N <> E +type E xml:Element; + +// @type N < T +type T xml:Text; + +// @type N <> C +type C xml:Comment; + +// @type N <> P +type P xml:ProcessingInstruction; + +// @type N < ES +type ES xml; + +// @type N < CS +type CS xml; + +// @type N < TS +type TS xml; + +// @type N < PS +type PS xml; + +// @type ENS = ES +type ENS xml; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-readonly-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-readonly-tv.bal new file mode 100644 index 000000000000..1256d31d26e7 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-readonly-tv.bal @@ -0,0 +1,33 @@ +type R readonly; +type X xml; + +// @type N < R +type N xml; + +// @type T < R +// @type N < T +type T xml:Text; + +// @type N = RN +type RN readonly & N; + +// @type RO_E < R +type RO_E xml:Element & readonly; + +// @type RO_C < R +type RO_C xml:Comment & readonly; + +// @type RO_P < R +type RO_P xml:ProcessingInstruction & readonly; + +// @type RX < R +// @type RX < X +type RX readonly & X; + +// @type RX = RO_XML +type RO_XML xml; + +// @type RO_XML_INTERSECTION = RO_XML +// @type RO_XML_INTERSECTION = RX +type RO_XML_INTERSECTION RO_XML & readonly; + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-sequence-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-sequence-tv.bal new file mode 100644 index 000000000000..e37193290a6e --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-sequence-tv.bal @@ -0,0 +1,40 @@ + +type X xml; + +// @type X = Y +type Y xml; + +// @type X = P +// @type Y = P +type P xml; + +// @type X = Q +type Q xml

; + +// @type U < X +type U xml; + +// @type V < X +// @type U < V +type V xml; + +// @type N < X +// @type N < V +// @type N < U +type N xml; + +// @type N <> E +// @type E < V +// @type E < X +type E xml:Element; + +// @type XE = X +type XE xml; + +// @type XEU = X +type XEU xml|E; + +type T xml:Text; + +// @type T = XT +type XT xml; \ No newline at end of file diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-te.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-te.bal new file mode 100644 index 000000000000..c923d65298fd --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-te.bal @@ -0,0 +1,7 @@ +type X xml; +type RX readonly & X; +type NEVER never; + +type RWX X & !readonly; + +type RX_MINUS_RO RX & RWX; // @error diff --git a/tests/jballerina-semtype-port-test/src/test/resources/testng.xml b/tests/jballerina-semtype-port-test/src/test/resources/testng.xml new file mode 100644 index 000000000000..e1138781de8e --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/testng.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + diff --git a/tests/jballerina-semtype-test/src/test/java/io/ballerina/test/SemTypeTest.java b/tests/jballerina-semtype-test/src/test/java/io/ballerina/test/SemTypeTest.java index 80a1c769613e..d494a411fcf3 100644 --- a/tests/jballerina-semtype-test/src/test/java/io/ballerina/test/SemTypeTest.java +++ b/tests/jballerina-semtype-test/src/test/java/io/ballerina/test/SemTypeTest.java @@ -55,7 +55,7 @@ */ public class SemTypeTest { - private final Types types = Types.getInstance(new CompilerContext()); + private Types types; @DataProvider(name = "filePathProvider") public Object[] filePathProvider() { @@ -63,7 +63,7 @@ public Object[] filePathProvider() { List files = new ArrayList<>(); for (File file : Objects.requireNonNull(dir.listFiles())) { String fileName = file.getName(); - if (fileName.endsWith(FAILING_FILE) || fileName.endsWith(DISABLED_FILE)) { + if (!fileName.endsWith(".bal") || fileName.endsWith(FAILING_FILE) || fileName.endsWith(DISABLED_FILE)) { continue; } files.add(file.getAbsolutePath()); @@ -83,6 +83,9 @@ public void testSubTypeRelationship(String filePath) throws IOException { private Set actualSubTypeRelations(String filePath) { CompileResult compileResult = BCompileUtil.compile(filePath); BLangPackage bLangPackage = (BLangPackage) compileResult.getAST(); + // Need to use the same semtypeEnv used in the compiler as we keep cache for some BTypes. + types = new Types(new CompilerContext(), bLangPackage.semtypeEnv); + List bTypeDefinitionSymbols = new ArrayList<>(); for (Scope.ScopeEntry value : bLangPackage.symbol.scope.entries.values()) { BSymbol bSymbol = value.symbol; diff --git a/tests/jballerina-semtype-test/src/test/resources/test-src/proj1-f.bal b/tests/jballerina-semtype-test/src/test/resources/test-src/proj1-f.bal deleted file mode 100644 index 980df3145686..000000000000 --- a/tests/jballerina-semtype-test/src/test/resources/test-src/proj1-f.bal +++ /dev/null @@ -1,5 +0,0 @@ -// B<:TF -type B boolean; -type TF true|false; -// B should be a subtype of TF -// BUG #34711 diff --git a/tests/jballerina-semtype-test/src/test/resources/test-src/proj1.bal b/tests/jballerina-semtype-test/src/test/resources/test-src/proj1.bal index 9f50bee77dec..48de5616ac03 100644 --- a/tests/jballerina-semtype-test/src/test/resources/test-src/proj1.bal +++ b/tests/jballerina-semtype-test/src/test/resources/test-src/proj1.bal @@ -1,3 +1,4 @@ // TF<:B +// B<:TF type B boolean; type TF true|false; diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/runtime/api/tests/TypeReference.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/runtime/api/tests/TypeReference.java index d5b51e67fa30..688d474423d0 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/runtime/api/tests/TypeReference.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/runtime/api/tests/TypeReference.java @@ -18,6 +18,7 @@ package org.ballerinalang.nativeimpl.jvm.runtime.api.tests; import io.ballerina.runtime.api.creators.ErrorCreator; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.ObjectType; import io.ballerina.runtime.api.types.Parameter; import io.ballerina.runtime.api.types.ReferenceType; @@ -39,7 +40,6 @@ import io.ballerina.runtime.internal.types.BErrorType; import io.ballerina.runtime.internal.types.BFunctionType; import io.ballerina.runtime.internal.types.BIntersectionType; -import io.ballerina.runtime.internal.types.BMapType; import io.ballerina.runtime.internal.types.BParameterizedType; import io.ballerina.runtime.internal.types.BRecordType; import io.ballerina.runtime.internal.types.BStreamType; @@ -112,7 +112,7 @@ public static Boolean validateIntersectionType(BTypedesc typedesc) { } public static Boolean validateMapType(BTypedesc typedesc) { - BMapType mapType = (BMapType) TypeUtils.getImpliedType(typedesc.getDescribingType()); + MapType mapType = (MapType) TypeUtils.getImpliedType(typedesc.getDescribingType()); if (mapType.getConstrainedType().getTag() != TypeTags.TYPE_REFERENCED_TYPE_TAG) { throw ErrorCreator.createError(StringUtils.fromString("map type API provided a non type reference " + diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/tests/VariableReturnType.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/tests/VariableReturnType.java index 891e1beb46a4..4e7ed88a701d 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/tests/VariableReturnType.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/tests/VariableReturnType.java @@ -198,10 +198,10 @@ public static MapValue getRecord(BTypedesc td) { BRecordType recType = (BRecordType) td.getDescribingType(); MapValueImpl person = new MapValueImpl<>(recType); - if (recType.getName().equals("Person")) { + if (recType.getName().contains("Person")) { person.put(NAME, JOHN_DOE); person.put(AGE, 20); - } else if (recType.getName().equals("Employee")) { + } else if (recType.getName().contains("Employee")) { person.put(NAME, JANE_DOE); person.put(AGE, 25); person.put(DESIGNATION, SOFTWARE_ENGINEER); @@ -226,7 +226,7 @@ public static Object getVariedUnion(long x, BTypedesc td1, BTypedesc td2) { } MapValueImpl rec = new MapValueImpl<>(type2); - if (type2.getName().equals("Person")) { + if (type2.getName().contains("Person")) { rec.put(NAME, JOHN_DOE); rec.put(AGE, 20); } else { @@ -279,10 +279,10 @@ private static Object getValue(Type type) { BRecordType recType = (BRecordType) type; MapValueImpl person = new MapValueImpl<>(recType); - if (recType.getName().equals("Person")) { + if (recType.getName().contains("Person")) { person.put(NAME, JOHN_DOE); person.put(AGE, 20); - } else if (recType.getName().equals("Employee")) { + } else if (recType.getName().contains("Employee")) { person.put(NAME, JANE_DOE); person.put(AGE, 25); person.put(DESIGNATION, SOFTWARE_ENGINEER); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/action/ClientResourceAccessActionNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/action/ClientResourceAccessActionNegativeTest.java index 7ddf03eb3a50..776589197342 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/action/ClientResourceAccessActionNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/action/ClientResourceAccessActionNegativeTest.java @@ -159,14 +159,14 @@ public void testClientResourcePathNegative() { " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 111, 28); validateError(clientResourcePathNegative, index++, "undefined resource method 'put' on target resource in object 'isolated object" + @@ -174,13 +174,13 @@ public void testClientResourcePathNegative() { "returns (int); resource function get path/[int]/foo(string) returns (int); resource" + " function get path/[int]/foo2(string,string) returns (int); resource function get" + " path/foo/bar() returns (); resource function get stringPath/[string]() returns (int);" + - " resource function get intQuotedPath/5() returns (int); resource function get " + + " resource function get intQuotedPath/'5() returns (int); resource function get " + "intPath/[int]() returns (int); resource function get booleanPath/[boolean]() " + "returns (int); resource function get stringRestPath/[string...]() returns (int);" + " resource function get intRestPath/[int...]() returns (int); resource function get " + "booleanRestPath/[boolean...]() returns (int); resource function get x(int) " + "returns (string); resource function get y(int?) returns (string?); resource " + - "function get 5(string) returns (string); resource function get 6(string?) " + + "function get '5(string) returns (string); resource function get '6(string?) " + "returns (string?); }'", 112, 34); validateError(clientResourcePathNegative, index++, @@ -188,28 +188,28 @@ public void testClientResourcePathNegative() { " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 113, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 114, 28); validateError(clientResourcePathNegative, index++, "resource access path segment is not allowed after resource access rest segment", @@ -223,364 +223,364 @@ public void testClientResourcePathNegative() { " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 116, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 117, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 118, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 119, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 120, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 121, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 122, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 123, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 124, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 125, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 126, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 127, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 128, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 129, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 130, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 131, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 132, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 133, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 134, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 135, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 136, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 137, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 138, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 139, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 140, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 141, 28); validateError(clientResourcePathNegative, index++, "unsupported computed resource access path segment type: expected 'int', 'string'," + @@ -599,84 +599,84 @@ public void testClientResourcePathNegative() { " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 145, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 146, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 147, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 148, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 149, 28); validateError(clientResourcePathNegative, index++, "undefined resource path in object 'isolated object { resource function get path()" + " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 150, 28); validateError(clientResourcePathNegative, index++, @@ -684,14 +684,14 @@ public void testClientResourcePathNegative() { " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 151, 28); validateError(clientResourcePathNegative, index++, @@ -699,14 +699,14 @@ public void testClientResourcePathNegative() { " returns (int); resource function get path/[int]() returns (int); resource function get" + " path/[int]/foo(string) returns (int); resource function get path/[int]/foo2(string,string)" + " returns (int); resource function get path/foo/bar() returns (); resource function get" + - " stringPath/[string]() returns (int); resource function get intQuotedPath/5()" + + " stringPath/[string]() returns (int); resource function get intQuotedPath/'5()" + " returns (int); resource function get intPath/[int]() returns (int); resource function" + " get booleanPath/[boolean]() returns (int); resource function get " + "stringRestPath/[string...]() returns (int); resource function get intRestPath/[int...]()" + " returns (int); resource function get booleanRestPath/[boolean...]() returns (int);" + " resource function get x(int) returns (string); resource function get y(int?) " + - "returns (string?); resource function get 5(string) returns (string); resource" + - " function get 6(string?) returns (string?); }'", + "returns (string?); resource function get '5(string) returns (string); resource" + + " function get '6(string?) returns (string?); }'", 152, 28); validateError(clientResourcePathNegative, index++, "unsupported resource access rest segment type: expected array of 'int', 'string', " + diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/action/start/StartActionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/action/start/StartActionTest.java index 08e3b060a65e..3a403d6e41ee 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/action/start/StartActionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/action/start/StartActionTest.java @@ -59,7 +59,6 @@ public void testStartActionNegative() { BAssertUtil.validateError(result, indx++, "'wait' cannot be used with actions", 72, 18); BAssertUtil.validateError(result, indx++, "action invocation as an expression not allowed here", 72, 28); BAssertUtil.validateError(result, indx++, "action invocation as an expression not allowed here", 76, 25); - BAssertUtil.validateError(result, indx++, "incompatible types: expected 'other', found 'int'", 90, 13); BAssertUtil.validateError(result, indx++, "incompatible types: '(int[]|error)' is not an iterable collection" , 90, 22); BAssertUtil.validateError(result, indx++, "'wait' cannot be used with actions", 90, 27); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/bala/functions/DependentlyTypedFunctionsBalaTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/bala/functions/DependentlyTypedFunctionsBalaTest.java index 66ce02a19730..9c7d8b31021a 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/bala/functions/DependentlyTypedFunctionsBalaTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/bala/functions/DependentlyTypedFunctionsBalaTest.java @@ -50,7 +50,7 @@ public void testRuntimeCastError() { @Test(expectedExceptions = BLangTestException.class, expectedExceptionsMessageRegExp = ".*error: \\{ballerina}TypeCastError \\{\"message\":\"incompatible types:" + - " 'Person' cannot be cast to 'int'\"}.*") + " 'PersonDTBT' cannot be cast to 'int'\"}.*") public void testCastingForInvalidValues() { BRunUtil.invoke(result, "testCastingForInvalidValues"); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/EqualAndNotEqualOperationsTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/EqualAndNotEqualOperationsTest.java index acbed8f5c684..0ab237cfc05f 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/EqualAndNotEqualOperationsTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/EqualAndNotEqualOperationsTest.java @@ -127,7 +127,8 @@ public Object[] getMapTestFunctions() { return new String[]{ "checkMapEqualityPositive", "checkMapEqualityNegative", "checkComplexMapEqualityPositive", "checkComplexMapEqualityNegative", "checkUnionConstrainedMapsPositive", - "checkUnionConstrainedMapsNegative", "testEmptyMapAndRecordEquality" + "checkUnionConstrainedMapsNegative", "testEmptyMapAndRecordEquality", + "checkEqualityOfMapsOfIncompatibleConstraintTypes" }; } @@ -200,6 +201,18 @@ public void selfAndCyclicReferencingFunctions(String testFunctionName) { BRunUtil.invoke(result, testFunctionName); } + @Test(dataProvider = "getReadonlyEqualityFunctions") + public void testReadonlyEquality(String testFunctionName) { + BRunUtil.invoke(result, testFunctionName); + } + + @DataProvider(name = "getReadonlyEqualityFunctions") + public Object[] getReadonlyEqualityFunctions() { + return new String[]{ + "readonlyMapEquality", "readonlyListEquality" + }; + } + @Test(description = "Test equal and not equal with errors") public void testEqualAndNotEqualNegativeCases() { int i = 0; @@ -207,50 +220,43 @@ public void testEqualAndNotEqualNegativeCases() { validateError(resultNegative, i++, "operator '!=' not defined for 'int' and 'string'", 20, 24); validateError(resultNegative, i++, "operator '==' not defined for 'int[2]' and 'string[2]'", 26, 21); validateError(resultNegative, i++, "operator '!=' not defined for 'int[2]' and 'string[2]'", 26, 33); - validateError(resultNegative, i++, "operator '==' not defined for 'map' and 'map'", 38, 21); - validateError(resultNegative, i++, "operator '!=' not defined for 'map' and 'map'", 38, 33); - validateError(resultNegative, i++, "operator '==' not defined for 'map<(string|int)>' and 'map'", - 42, 21); - validateError(resultNegative, i++, "operator '!=' not defined for 'map<(string|int)>' and 'map'", - 42, 33); validateError(resultNegative, i++, "operator '==' not defined for '[string,int]' and '[boolean,float]'", - 50, 21); + 38, 21); validateError(resultNegative, i++, "operator '!=' not defined for '[string,int]' and '[boolean,float]'", - 50, 33); + 38, 33); validateError(resultNegative, i++, "operator '==' not defined for '[(float|int),int]' and '[boolean,int]'", - 54, 21); + 42, 21); validateError(resultNegative, i++, "operator '!=' not defined for '[(float|int),int]' and '[boolean,int]'", - 54, 33); - validateError(resultNegative, i++, "operator '==' not defined for 'Employee' and 'Person'", 62, 17); - validateError(resultNegative, i++, "operator '!=' not defined for 'Employee' and 'Person'", 62, 29); - validateError(resultNegative, i++, "operator '==' not defined for 'EmployeeWithOptionalId' and " + - "'PersonWithOptionalId'", 66, 17); - validateError(resultNegative, i++, "operator '!=' not defined for 'EmployeeWithOptionalId' and " + - "'PersonWithOptionalId'", 66, 31); - validateError(resultNegative, i++, "operator '==' not defined for 'map' and 'ClosedDept'", 75, 23); - validateError(resultNegative, i++, "operator '!=' not defined for 'ClosedDept' and 'map'", 75, 35); - validateError(resultNegative, i++, "operator '==' not defined for 'int[]' and '[float,float]'", 82, 23); - validateError(resultNegative, i++, "operator '!=' not defined for 'int[]' and '[float,float]'", 82, 35); - validateError(resultNegative, i++, "operator '==' not defined for 'int[]' and '[int,float]'", 85, 23); - validateError(resultNegative, i++, "operator '!=' not defined for '[int,float]' and 'int[]'", 85, 35); - validateError(resultNegative, i++, "operator '==' not defined for 'Employee' and '()'", 138, 9); - validateError(resultNegative, i++, "operator '==' not defined for 'Foo' and '()'", 144, 9); + 42, 33); + validateError(resultNegative, i++, "operator '==' not defined for 'Employee' and 'Person'", 50, 17); + validateError(resultNegative, i++, "operator '!=' not defined for 'Employee' and 'Person'", 50, 29); + validateError(resultNegative, i++, "operator '==' not defined for 'map' and 'ClosedDept'", 59, 23); + validateError(resultNegative, i++, "operator '!=' not defined for 'ClosedDept' and 'map'", 59, 35); + validateError(resultNegative, i++, "operator '==' not defined for 'int[]' and '[float,float]'", 66, 23); + validateError(resultNegative, i++, "operator '!=' not defined for 'int[]' and '[float,float]'", 66, 35); + validateError(resultNegative, i++, "operator '==' not defined for 'int[]' and '[int,float]'", 69, 23); + validateError(resultNegative, i++, "operator '!=' not defined for '[int,float]' and 'int[]'", 69, 35); + validateError(resultNegative, i++, "operator '==' not defined for 'Employee' and '()'", 117, 9); + validateError(resultNegative, i++, "operator '==' not defined for 'Foo' and '()'", 123, 9); validateError(resultNegative, i++, "operator '==' not defined for 'function () returns (string)' and '()'", - 150, 9); - validateError(resultNegative, i++, "operator '!=' not defined for 'readonly' and 'map'", - 168, 12); - validateError(resultNegative, i++, "operator '==' not defined for '[int,map]' and '[int,float]'", 179, + 129, 9); + validateError(resultNegative, i++, "operator '==' not defined for '[int,map]' and '[int,float]'", 142, 23); - validateError(resultNegative, i++, "operator '!=' not defined for '[int,float]' and '[int,map]'", 179, + validateError(resultNegative, i++, "operator '!=' not defined for '[int,float]' and '[int,map]'", 142, 35); - validateError(resultNegative, i++, "operator '==' not defined for 'MyObject' and '()'", 182, + validateError(resultNegative, i++, "operator '==' not defined for 'MyObject' and '()'", 145, 15); - validateError(resultNegative, i++, "operator '!=' not defined for 'MyObject' and '()'", 182, + validateError(resultNegative, i++, "operator '!=' not defined for 'MyObject' and '()'", 145, 30); - validateError(resultNegative, i++, "operator '==' not defined for 'MyObject' and 'MyObject'", 184, + validateError(resultNegative, i++, "operator '==' not defined for 'MyObject' and 'MyObject'", 147, 15); - validateError(resultNegative, i++, "operator '!=' not defined for 'MyObject' and 'MyObject'", 184, + validateError(resultNegative, i++, "operator '!=' not defined for 'MyObject' and 'MyObject'", 147, 32); + validateError(resultNegative, i++, "operator '!=' not defined for 'FloatOne' and 'FloatTwo'", 161, 18); + validateError(resultNegative, i++, "operator '==' not defined for 'FloatOne' and 'FloatTwo'", 161, 45); + validateError(resultNegative, i++, "operator '==' not defined for 'IntOne' and 'IntTwo'", 162, 19); + validateError(resultNegative, i++, "operator '!=' not defined for 'IntOne' and 'IntTwo'", 162, 44); + validateError(resultNegative, i++, "operator '==' not defined for 'Array' and 'Mapping'", 171, 17); Assert.assertEquals(resultNegative.getErrorCount(), i); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/NegativeTypeTestExprTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/NegativeTypeTestExprTest.java index bb2f5f9bbd11..222aba4fbee2 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/NegativeTypeTestExprTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/NegativeTypeTestExprTest.java @@ -79,8 +79,6 @@ public void testNegativeTypeTestExprNegative() { "expression will always evaluate to 'false'", 131, 17); BAssertUtil.validateHint(negativeResult, i++, "unnecessary condition: expression will always evaluate to 'true'", 131, 32); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'int[]' will not be matched to 'float[]'", - 132, 17); BAssertUtil.validateHint(negativeResult, i++, "expression will always evaluate to 'false'", 133, 17); BAssertUtil.validateHint(negativeResult, i++, @@ -170,11 +168,7 @@ public void testNegativeTypeTestExprNegative() { BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'ClosedRecordWithIntField' will not be matched to " + "'record {| int i; string s; |}'", 297, 17); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'object { }[]' will not be matched to " + - "'anydata'", 330, 10); BAssertUtil.validateWarning(negativeResult, i++, "unused variable 'p'", 331, 9); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'anydata' will not be matched to 'object " + - "{ }[]'", 336, 10); BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'Record' will not be matched to " + "'RecordWithIntFieldAndNeverRestField'", 358, 17); BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'Record' will not be matched to " + diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/RefEqualAndNotEqualOperationsTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/RefEqualAndNotEqualOperationsTest.java index c449c3e2e41f..5c05d0fb49fe 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/RefEqualAndNotEqualOperationsTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/RefEqualAndNotEqualOperationsTest.java @@ -376,16 +376,6 @@ public void testRefEqualNegativeCases() { validateError(resultNegative, i++, "operator '!==' not defined for 'int' and 'string'", 20, 25); validateError(resultNegative, i++, "operator '===' not defined for 'int[2]' and 'string[2]'", 26, 21); validateError(resultNegative, i++, "operator '!==' not defined for 'int[2]' and 'string[2]'", 26, 34); - validateError(resultNegative, i++, "operator '===' not defined for '(float|int)?[]' and '(boolean|xml)?[]'", 30, - 21); - validateError(resultNegative, i++, "operator '!==' not defined for '(float|int)?[]' and '(boolean|xml)?[]'", 30, - 34); - validateError(resultNegative, i++, "operator '===' not defined for 'map' and 'map'", 38, 21); - validateError(resultNegative, i++, "operator '!==' not defined for 'map' and 'map'", 38, 34); - validateError(resultNegative, i++, "operator '===' not defined for 'map<(string|int)>' and 'map'", 42, - 21); - validateError(resultNegative, i++, "operator '!==' not defined for 'map<(string|int)>' and 'map'", 42, - 34); validateError(resultNegative, i++, "operator '===' not defined for '[string,int]' and '[boolean,float]'", 50, 21); validateError(resultNegative, i++, "operator '!==' not defined for '[string,int]' and '[boolean,float]'", 50, @@ -400,8 +390,6 @@ public void testRefEqualNegativeCases() { "xml])' and 'json'", 68, 21); validateError(resultNegative, i++, "operator '!==' not defined for '(record {| xml x; anydata...; |}|[string," + "xml])' and 'json'", 68, 34); - validateError(resultNegative, i++, "operator '===' not defined for 'Abc' and 'Def'", 76, 12); - validateError(resultNegative, i++, "operator '!==' not defined for 'Def' and 'Abc'", 76, 25); Assert.assertEquals(resultNegative.getErrorCount(), i); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/TypeTestExprTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/TypeTestExprTest.java index ec14e679b854..84360a8f5b7d 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/TypeTestExprTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/TypeTestExprTest.java @@ -89,8 +89,6 @@ public void testTypeTestExprNegative() { "unnecessary condition: expression will always evaluate to 'true'", 131, 17); BAssertUtil.validateHint(negativeResult, i++, "unnecessary condition: expression will always evaluate to 'true'", 131, 31); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'int[]' will not be matched to 'float[]'", - 132, 17); BAssertUtil.validateHint(negativeResult, i++, "unnecessary condition: expression will always evaluate to 'true'", 133, 17); BAssertUtil.validateHint(negativeResult, i++, @@ -175,15 +173,12 @@ public void testTypeTestExprNegative() { BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'ClosedRecordWithIntField' will not be matched to " + "'record {| int i; string s; |}'", 297, 17); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'object { }[]' will not be matched to " + - "'anydata'", 330, 8); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'anydata' will not be matched to 'object " + - "{ }[]'", 336, 8); BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'Record' will not be matched to " + "'RecordWithIntFieldAndNeverRestField'", 358, 17); BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'Record' will not be matched to " + "'RecordWithIntFieldAndEffectivelyNeverRestField'", 359, 17); - Assert.assertEquals(negativeResult.getErrorCount(), 35); + Assert.assertEquals(negativeResult.getErrorCount(), 32); + Assert.assertEquals(negativeResult.getDiagnostics().length, i); } @Test diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/conversion/NativeConversionNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/conversion/NativeConversionNegativeTest.java index 63bc74d68d79..56ca7a0f5992 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/conversion/NativeConversionNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/conversion/NativeConversionNegativeTest.java @@ -142,10 +142,6 @@ public void testConvertRecordToMapWithCyclicValueReferences() { Object results = BRunUtil.invoke(negativeResult, "testConvertRecordToMapWithCyclicValueReferences"); Object error = results; Assert.assertEquals(getType(error).getClass(), BErrorType.class); - Assert.assertEquals( - ((BMap) ((BError) results).getDetails()).get(StringUtils.fromString("message")) - .toString(), - "'Manager' value has cyclic reference"); } @Test(description = "Test converting record to json having cyclic reference.") @@ -153,10 +149,6 @@ public void testConvertRecordToJsonWithCyclicValueReferences() { Object results = BRunUtil.invoke(negativeResult, "testConvertRecordToJsonWithCyclicValueReferences"); Object error = results; Assert.assertEquals(getType(error).getClass(), BErrorType.class); - Assert.assertEquals( - ((BMap) ((BError) results).getDetails()).get(StringUtils.fromString("message")) - .toString(), - "'Manager' value has cyclic reference"); } @Test(dataProvider = "testConversionFunctionList") diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/lambda/IterableOperationsTests.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/lambda/IterableOperationsTests.java index 7a34bd628fd6..c1d84b08c851 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/lambda/IterableOperationsTests.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/lambda/IterableOperationsTests.java @@ -55,7 +55,6 @@ public void setup() { @Test() public void testNegative() { - Assert.assertEquals(negative.getErrorCount(), 33); int index = 0; BAssertUtil.validateError(negative, index++, "undefined function 'forEach' in type 'int'", 6, 7); BAssertUtil.validateError(negative, index++, "undefined function 'map' in type 'string'", 8, 7); @@ -68,11 +67,9 @@ public void testNegative() { BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string', found 'any'", 35, 27); BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string', found 'any'", 38, 22); BAssertUtil.validateError(negative, index++, "incompatible types: expected 'int', found '()'", 46, 9); - BAssertUtil.validateError(negative, index++, "incompatible types: expected '[other,other]', found 'string[]'", - 48, 18); BAssertUtil.validateError(negative, index++, - "invalid list binding pattern: attempted to infer a list type, but found 'other'", - 48, 18); + "invalid list binding pattern; member variable count mismatch with member type count", + 48, 5); BAssertUtil.validateError(negative, index++, "invalid operation: type 'string' does not support field access", 49, 35); BAssertUtil.validateError(negative, index++, "too many arguments in call to 'length()'", 55, 9); @@ -96,9 +93,6 @@ public void testNegative() { "'function (ballerina/lang.array:0.0.0:Type) returns (boolean)', " + "found 'function (other) returns ()'", 67, 14); BAssertUtil.validateError(negative, index++, "unknown type 'person'", 67, 24); - BAssertUtil.validateError(negative, index++, "incompatible types: expected " + - "'function (ballerina/lang.array:0.0.0:Type) " + - "returns (boolean)', found 'function (string) returns (other)'", 68, 18); BAssertUtil.validateError(negative, index++, "unknown type 'person'", 68, 48); BAssertUtil.validateError(negative, index++, "incompatible types: expected 'int[]', found 'any[]'", 73, 15); BAssertUtil.validateError(negative, index++, "incompatible types: expected 'int[]', found 'string[]'", 80, 15); @@ -107,7 +101,8 @@ public void testNegative() { BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string', found 'map'", 103, 16); BAssertUtil.validateError(negative, index++, "incompatible types: expected 'boolean', found 'int'", 111, 20); BAssertUtil.validateError(negative, index++, "incompatible types: expected 'float', found 'int'", 120, 39); - BAssertUtil.validateError(negative, index, "incompatible types: expected 'float', found 'int'", 137, 42); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'float', found 'int'", 137, 42); + Assert.assertEquals(negative.getErrorCount(), index); } @Test diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/mappingconstructor/MappingConstructorExprTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/mappingconstructor/MappingConstructorExprTest.java index 9a2c6b1f7a23..2716d4a63c97 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/mappingconstructor/MappingConstructorExprTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/mappingconstructor/MappingConstructorExprTest.java @@ -318,7 +318,8 @@ public Object[][] spreadOpFieldTests() { { "testSpreadOpInGlobalMap" }, { "testMappingConstrExprAsSpreadExpr" }, { "testSpreadFieldWithRecordTypeHavingNeverField" }, - { "testSpreadFieldWithRecordTypeHavingRestDescriptor" } + { "testSpreadFieldWithRecordTypeHavingRestDescriptor" }, + { "testSpreadFieldWithRecordTypeReference" } }; } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/AnydataStampInbuiltFunctionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/AnydataStampInbuiltFunctionTest.java index 52a2fc6d489b..4ae4e03ea2fe 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/AnydataStampInbuiltFunctionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/AnydataStampInbuiltFunctionTest.java @@ -17,6 +17,7 @@ */ package org.ballerinalang.test.expressions.stamp; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; @@ -89,7 +90,7 @@ public void testStampAnydataToJSONV2() { BMap mapValue0 = (BMap) results; Assert.assertTrue(getType(mapValue0) instanceof BMapType); - Assert.assertTrue(((BMapType) getType(mapValue0)).getConstrainedType() instanceof BJsonType); + Assert.assertTrue(((MapType) getType(mapValue0)).getConstrainedType() instanceof BJsonType); Assert.assertEquals((mapValue0).size(), 5); Assert.assertEquals(((LinkedHashMap) mapValue0).get(StringUtils.fromString("school")).toString(), @@ -190,8 +191,8 @@ public void testStampAnydataToAnydata() { Object results = BRunUtil.invoke(compileResult, "stampAnydataToAnydata"); BMap mapValue = (BMap) results; - Assert.assertTrue(getType(mapValue) instanceof BMapType); - Assert.assertTrue(((BMapType) getType(mapValue)).getConstrainedType() instanceof BAnydataType); + Assert.assertTrue(getType(mapValue) instanceof MapType); + Assert.assertTrue(((MapType) getType(mapValue)).getConstrainedType() instanceof BAnydataType); } @Test @@ -202,8 +203,8 @@ public void testStampAnydataMapToUnion() { Assert.assertEquals(mapValue.size(), 5); - Assert.assertTrue(getType(mapValue) instanceof BMapType); - Assert.assertTrue(((BMapType) getType(mapValue)).getConstrainedType() instanceof BJsonType); + Assert.assertTrue(getType(mapValue) instanceof MapType); + Assert.assertTrue(((MapType) getType(mapValue)).getConstrainedType() instanceof BJsonType); Assert.assertEquals(mapValue.get(StringUtils.fromString("name")).toString(), "Raja"); Assert.assertTrue(getType(mapValue.get(StringUtils.fromString("name"))) instanceof BStringType); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/ArrayStampInbuiltFunctionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/ArrayStampInbuiltFunctionTest.java index 8bdd33f86ec3..80440910dfa0 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/ArrayStampInbuiltFunctionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/ArrayStampInbuiltFunctionTest.java @@ -17,6 +17,7 @@ */ package org.ballerinalang.test.expressions.stamp; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; @@ -66,10 +67,10 @@ public void testStampRecordToAnydataArray() { Assert.assertEquals(results.size(), 2); - Assert.assertEquals(getType((mapValue0)).getClass(), BMapType.class); - Assert.assertEquals(((BMapType) mapValue0.getType()).getConstrainedType().getClass(), BAnydataType.class); - Assert.assertEquals(getType((mapValue1)).getClass(), BMapType.class); - Assert.assertEquals(((BMapType) mapValue1.getType()).getConstrainedType().getClass(), BAnydataType.class); + Assert.assertTrue(getType(mapValue0) instanceof MapType); + Assert.assertEquals(((MapType) mapValue0.getType()).getConstrainedType().getClass(), BAnydataType.class); + Assert.assertTrue(getType(mapValue1) instanceof MapType); + Assert.assertEquals(((MapType) mapValue1.getType()).getConstrainedType().getClass(), BAnydataType.class); } @Test diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/typecast/TypeCastExprTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/typecast/TypeCastExprTest.java index 1a1138915746..f8f157e2f45d 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/typecast/TypeCastExprTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/typecast/TypeCastExprTest.java @@ -274,7 +274,7 @@ public void testNullJsonToBoolean() { @Test(description = "Test casting nil to a record", expectedExceptions = {BLangTestException.class}, - expectedExceptionsMessageRegExp = ".*incompatible types: '\\(\\)' cannot be cast to 'Student'.*") + expectedExceptionsMessageRegExp = ".*incompatible types: '\\(\\)' cannot be cast to 'StudentTC'.*") public void testNullStructToStruct() { BRunUtil.invoke(result, "testNullStructToStruct"); } @@ -317,7 +317,7 @@ public void testAnyMapToJson() { @Test(description = "Test casting a struct as any type to json", expectedExceptions = {BLangTestException.class}, - expectedExceptionsMessageRegExp = ".*incompatible types: 'Address' cannot be cast to 'json'.*") + expectedExceptionsMessageRegExp = ".*incompatible types: 'AddressTC' cannot be cast to 'json'.*") public void testAnyStructToJson() { BRunUtil.invoke(result, "testAnyStructToJson"); } @@ -368,14 +368,14 @@ public void testStructAsAnyToStruct() { @Test(description = "Test casting any to struct", expectedExceptions = {BLangTestException.class}, - expectedExceptionsMessageRegExp = ".*incompatible types: 'map' cannot be cast to 'Person'.*") + expectedExceptionsMessageRegExp = ".*incompatible types: 'map' cannot be cast to 'PersonTC'.*") public void testAnyToStruct() { BRunUtil.invoke(result, "testAnyToStruct"); } @Test(description = "Test casting a null stored as any to struct", expectedExceptions = {BLangTestException.class}, - expectedExceptionsMessageRegExp = ".*incompatible types: '\\(\\)' cannot be cast to 'Person'.*") + expectedExceptionsMessageRegExp = ".*incompatible types: '\\(\\)' cannot be cast to 'PersonTC'.*") public void testAnyNullToStruct() { Object returns = BRunUtil.invoke(result, "testAnyNullToStruct"); Assert.assertNull(returns); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/typecast/TypeCastExpressionsTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/typecast/TypeCastExpressionsTest.java index 874bea5794fc..36fc99cebefd 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/typecast/TypeCastExpressionsTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/typecast/TypeCastExpressionsTest.java @@ -18,6 +18,7 @@ import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; +import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BMap; import org.ballerinalang.test.BCompileUtil; import org.ballerinalang.test.BRunUtil; @@ -41,11 +42,12 @@ */ public class TypeCastExpressionsTest { - private CompileResult result; + private CompileResult result, result2; @BeforeClass public void setup() { result = BCompileUtil.compile("test-src/expressions/typecast/type_cast_expr.bal"); + result2 = BCompileUtil.compile("test-src/expressions/typecast/type_cast_expr_runtime_errors.bal"); } @Test(dataProvider = "positiveTests") @@ -119,12 +121,6 @@ public void testCastNegatives() { , 13); validateError(resultNegative, errIndex++, "incompatible types: '(json|error)' cannot be cast to 'string'", 69, 13); - validateError(resultNegative, errIndex++, "incompatible types: '(string[]|int)' cannot be cast to 'byte[]'", - 78, 32); - validateError(resultNegative, errIndex++, "incompatible mapping constructor expression for type '(record {| " + - "byte[] a; anydata...; |}|record {| string a; anydata...; |})'", 79, 47); - validateError(resultNegative, errIndex++, "incompatible types: '(string[]|int)' cannot be cast to 'byte[]'", - 79, 51); validateError(resultNegative, errIndex++, "incompatible mapping constructor expression for type '(record {| " + "string[] a; anydata...; |}|record {| string a; anydata...; |})'", 82, 49); validateError(resultNegative, errIndex++, "incompatible types: expected 'Obj', found 'object { int i; }'", 96, @@ -276,8 +272,28 @@ public Object[] typeCastWithConstructorTests() { }; } + @Test(dataProvider = "typeCastRuntimeErrorTests") + public void testTypeCastRuntimeErrors(String testFuncName) { + Object returns = BRunUtil.invoke(result2, testFuncName); + BError error = (BError) returns; + Assert.assertEquals(error.getErrorMessage().getValue(), "{ballerina}TypeCastError"); + } + + @DataProvider + public Object[] typeCastRuntimeErrorTests() { + return new Object[]{ + "testTupleToJSONCastRuntimeError", + "testCastingWithEmptyKeyedKeylessTbl", + "testMapCastingRuntimeError", + "testListCastingRuntimeError", + "testCastingObjects", + "testCastingObjects2", + }; + } + @AfterClass public void tearDown() { result = null; + result2 = null; } } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/DependentlyTypedFunctionsTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/DependentlyTypedFunctionsTest.java index b17b3e374633..61e6acff92a6 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/DependentlyTypedFunctionsTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/DependentlyTypedFunctionsTest.java @@ -58,7 +58,6 @@ public void testNegatives() { validateError(errors, indx++, "incompatible types: expected 'typedesc<(int|float|decimal|string|boolean)>', " + "found 'typedesc'", 41, 23); validateError(errors, indx++, "unknown type 'aTypeVar'", 44, 60); - validateError(errors, indx++, "incompatible types: expected 'map', found 'map'", 51, 18); validateError(errors, indx++, "incompatible types: expected 'int', found 'customType'", 61, 13); validateError(errors, indx++, "incompatible types: expected 'float', found 'customType'", 62, 15); validateError(errors, indx++, "unknown type 'td'", 65, 73); @@ -82,20 +81,21 @@ public void testNegatives() { validateError(errors, indx++, "mismatched function signatures: expected 'public function get" + "(typedesc) returns (td|error)', found 'public function get(typedesc) returns" + " (other|error)'", 140, 5); - validateError(errors, indx++, "a function with a non-'external' function body cannot be a dependently-typed " + - "function", 140, 64); + validateError(errors, indx++, + "a function with a non-'external' function body cannot be a dependently-typed function", 140, 64); validateError(errors, indx++, "mismatched function signatures: expected 'public function get" + "(typedesc) returns (td|error)', found 'public function get(typedesc) returns" + " (other|error)'", 144, 5); - validateError(errors, indx++, "a function with a non-'external' function body cannot be a dependently-typed " + - "function", 144, 64); + validateError(errors, indx++, + "a function with a non-'external' function body cannot be a dependently-typed function", 144, 64); validateError(errors, indx++, "incompatible types: expected 'Bar', found 'Baz'", 176, 15); validateError(errors, indx++, "incompatible types: expected 'Quux', found 'Qux'", 180, 17); validateError(errors, indx++, "incompatible types: expected 'Qux', found 'Quux'", 181, 15); validateError(errors, indx++, "incompatible types: expected 'Baz', found 'Quux'", 182, 16); validateError(errors, indx++, "incompatible types: expected 'Quuz', found 'Qux'", 183, 17); - validateError(errors, indx++, "incompatible types: expected 'Corge', found 'Grault'", 185, 19); - validateError(errors, indx++, "incompatible types: expected 'Grault', found 'Corge'", 186, 21); + // TODO: 26/8/24 verify +// validateError(errors, indx++, "incompatible types: expected 'Corge', found 'Grault'", 185, 19); +// validateError(errors, indx++, "incompatible types: expected 'Grault', found 'Corge'", 186, 21); validateError(errors, indx++, "incompatible types: expected 'string', found 'int'", 196, 16); validateError(errors, indx++, "incompatible types: expected 'string', found 'int'", 197, 16); validateError(errors, indx++, "incompatible types: expected 'int', found 'string'", 198, 13); @@ -178,6 +178,8 @@ public void testNegatives() { validateError(errors, indx++, "incompatible types: expected 'string', found 'int'", 363, 18); validateError(errors, indx++, "incompatible type for parameter 't' with inferred typedesc value: expected " + "'typedesc<(int|string)>', found 'typedesc'", 369, 17); + validateError(errors, indx++, "a wildcard binding pattern can be used only with a value that belong to type " + + "'any'", 371, 5); validateError(errors, indx++, "incompatible types: expected 'TargetType', found 'typedesc'", 371, 64); validateError(errors, indx++, "incompatible type for parameter 'td' with inferred typedesc value: expected " + "'typedesc', found 'typedesc'", 383, 24); @@ -193,7 +195,7 @@ public void testRuntimeCastError() { @Test(expectedExceptions = BLangTestException.class, expectedExceptionsMessageRegExp = "error: \\{ballerina\\}TypeCastError \\{\"message\":\"incompatible types:" + - " 'Person' cannot be cast to 'int'.*") + " 'PersonDTFT' cannot be cast to 'int'.*") public void testCastingForInvalidValues() { BRunUtil.invoke(result, "testCastingForInvalidValues"); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/InferredDependentlyTypeFunctionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/InferredDependentlyTypeFunctionTest.java index 4dcd51d0d91e..ebc1cd4c9877 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/InferredDependentlyTypeFunctionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/InferredDependentlyTypeFunctionTest.java @@ -203,6 +203,8 @@ public void testDependentlyTypedFunctionWithInferredTypedescValueNegative() { "an argument for the parameter or a contextually-expected type to infer the argument", 185, 44); validateError(negativeResult, index++, "cannot infer the 'typedesc' argument for parameter 'td': expected " + "an argument for the parameter or a contextually-expected type to infer the argument", 186, 52); + validateError(negativeResult, index++, "invalid return type: members of a dependently-typed union type with " + + "an inferred typedesc parameter should have disjoint basic types", 193, 64); validateError(negativeResult, index++, "cannot infer the 'typedesc' argument for parameter 'td': expected " + "an argument for the parameter or a contextually-expected type to infer the argument", 196, 5); validateError(negativeResult, index++, "cannot infer the 'typedesc' argument for parameter 'td2': expected " + diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/basic/NegativeValidationTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/basic/NegativeValidationTest.java index 41aadfc88701..c4fdb3f15920 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/basic/NegativeValidationTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/basic/NegativeValidationTest.java @@ -284,14 +284,7 @@ public void testMethodSignatureNotMatch7() { public void testMethodSignatureNotMatch8() { CompileResult compileResult = BCompileUtil.compile("test-src/javainterop/negative/distinct_error"); compileResult.getDiagnostics(); - Assert.assertEquals(compileResult.getDiagnostics().length, 2); - BAssertUtil.validateError(compileResult, 0, - "{ballerina/jballerina.java}METHOD_SIGNATURE_DOES_NOT_MATCH " + - "'Incompatible ballerina return type for Java method " + - "'returnDistinctErrorUnionWhichThrowsCheckedException' which throws checked exception " + - "found in class 'org.ballerinalang.nativeimpl.jvm.tests.StaticMethods': " + - "expected 'int|error', found '(int|testorg/distinct_error.errors:1.0.0:DistinctError)''", - 21, 1); + Assert.assertEquals(compileResult.getErrorCount(), 0); } @Test @@ -551,15 +544,7 @@ public void testMethodSignatureNotMatch16() { String path = "test-src/javainterop/negative/method_sig_not_match16.bal"; CompileResult compileResult = BCompileUtil.compile(path); compileResult.getDiagnostics(); - Assert.assertEquals(compileResult.getDiagnostics().length, 1); - BAssertUtil.validateError(compileResult, 0, - "{ballerina/jballerina.java}METHOD_SIGNATURE_DOES_NOT_MATCH " + - "'Incompatible ballerina return type for Java method " + - "'acceptIntErrorUnionReturnWhichThrowsUncheckedException' which throws " + - "'java.lang.RuntimeException' found in class " + - "'org.ballerinalang.nativeimpl.jvm.tests.StaticMethods': " + - "expected 'int', found '(int|error)''", - "method_sig_not_match16.bal", 19, 1); + Assert.assertEquals(compileResult.getDiagnostics().length, 0); } @Test @@ -569,12 +554,10 @@ public void testMethodSignatureNotMatch17() { compileResult.getDiagnostics(); Assert.assertEquals(compileResult.getDiagnostics().length, 1); BAssertUtil.validateError(compileResult, 0, - "{ballerina/jballerina.java}METHOD_SIGNATURE_DOES_NOT_MATCH " + - "'Incompatible ballerina return type for Java method " + - "'acceptIntErrorUnionReturnWhichThrowsUncheckedException' which throws " + - "'java.lang.RuntimeException' found in class " + + "{ballerina/jballerina.java}METHOD_SIGNATURE_DOES_NOT_MATCH 'Incompatible return type for method " + + "'acceptIntErrorUnionReturnWhichThrowsUncheckedException' in class " + "'org.ballerinalang.nativeimpl.jvm.tests.StaticMethods': " + - "no return type expected but found 'error''", + "Java type 'long' will not be matched to ballerina type 'error''", "method_sig_not_match17.bal", 19, 1); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/jvm/ObjectSubtypingTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/jvm/ObjectSubtypingTest.java index da7b9cde168a..c8ebdbe98049 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/jvm/ObjectSubtypingTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/jvm/ObjectSubtypingTest.java @@ -29,7 +29,6 @@ import static org.ballerinalang.test.BAssertUtil.validateError; import static org.ballerinalang.test.BAssertUtil.validateWarning; import static org.testng.Assert.assertEquals; -import static java.lang.String.format; /** * Test cases for testing the object subtyping rules. @@ -94,12 +93,6 @@ public void testNegatives() { int i = 0; validateError(result, i++, "private qualifier in object member descriptor", 21, 5); validateError(result, i++, "private qualifier in object member descriptor", 28, 5); - validateError(result, i++, format(msgFormat, "ObjWithPvtField", "AnotherObjWithAPvtField"), 45, 26); - validateError(result, i++, format(msgFormat, "ObjWithPvtMethod", "AnotherObjWithPvtMethod"), 46, 27); - validateError(result, i++, format(msgFormat, "testorg/subtyping:1.0.0:ModuleLevelSubtypableObj", "Subtype1"), - 65, 45); - validateError(result, i++, format(msgFormat, "testorg/subtyping:1.0.0:ModuleLevelSubtypableObj2", "Subtype2"), - 66, 46); assertEquals(result.getErrorCount(), i); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/klass/ServiceClassTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/klass/ServiceClassTest.java index 0d020f637b5c..4ad19a162ed3 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/klass/ServiceClassTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/klass/ServiceClassTest.java @@ -180,9 +180,6 @@ public void testResourceFunctionWithInvalidPathParam() { CompileResult result = BCompileUtil.compile("test-src/klass/resource_function_with_invalid_path_param_type_negative.bal"); int index = 0; - validateError(result, index++, "only 'int', 'string', 'float', 'boolean', 'decimal' types " + - "are supported as path params, found 'other'", - 24, 29); validateError(result, index++, "undefined module 'module1'", 24, 29); validateError(result, index++, "unknown type 'RequestMessage'", 24, 29); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/object/ObjectEquivalencyNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/object/ObjectEquivalencyNegativeTest.java index da5afea56a50..7bc9cfde21cc 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/object/ObjectEquivalencyNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/object/ObjectEquivalencyNegativeTest.java @@ -31,32 +31,27 @@ public class ObjectEquivalencyNegativeTest { @Test(description = "Test equivalence of objects that are in the same package") public void testEquivalenceOfObjectsInSamePackage() { CompileResult compileResult = BCompileUtil.compile("test-src/object/object-equivalency-01-negative.bal"); - Assert.assertEquals(compileResult.getErrorCount(), 12); - BAssertUtil.validateError(compileResult, 0, - "incompatible types: 'employee01' cannot be cast to 'person01'", 24, 18); - BAssertUtil.validateError(compileResult, 1, + int i = 0; + BAssertUtil.validateError(compileResult, i++, "incompatible types: 'employee02' cannot be cast to 'person02'", 51, 18); - BAssertUtil.validateError(compileResult, 2, + BAssertUtil.validateError(compileResult, i++, "incompatible types: 'employee04' cannot be cast to 'person04'", 108, 18); - BAssertUtil.validateError(compileResult, 3, + BAssertUtil.validateError(compileResult, i++, "incompatible types: 'employee05' cannot be cast to 'person05'", 145, 18); - BAssertUtil.validateError(compileResult, 4, + BAssertUtil.validateError(compileResult, i++, "incompatible types: 'employee06' cannot be cast to 'person06'", 175, 18); - BAssertUtil.validateError(compileResult, 5, - "incompatible types: 'employee08' cannot be cast to 'person08'", 284, 18); - BAssertUtil.validateError(compileResult, 6, - "incompatible types: 'employee09' cannot be cast to 'person09'", 341, 18); - BAssertUtil.validateError(compileResult, 7, + BAssertUtil.validateError(compileResult, i++, "incompatible types: expected 'ObjWithRemoteMethod', found 'NonClientObj'", 460, 29); - BAssertUtil.validateError(compileResult, 8, + BAssertUtil.validateError(compileResult, i++, "incompatible types: expected 'ObjWithRemoteMethod', found 'ClientObjWithoutRemoteMethod'", 465, 29); - BAssertUtil.validateError(compileResult, 9, + BAssertUtil.validateError(compileResult, i++, "incompatible types: expected 'ObjWithOnlyRemoteMethod', " + "found 'ClientObjWithoutRemoteMethod'", 470, 33); - BAssertUtil.validateError(compileResult, 10, + BAssertUtil.validateError(compileResult, i++, "incompatible types: expected 'ObjWithOnlyRemoteMethod', found 'NonClientObj'", 475, 33); - BAssertUtil.validateError(compileResult, 11, + BAssertUtil.validateError(compileResult, i++, "incompatible types: expected 'NonClientObj', found 'ObjWithRemoteMethod'", 480, 22); + Assert.assertEquals(compileResult.getErrorCount(), i); } @Test(description = "Test equivalence of objects that are in the same package from a third package") diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/GroupByClauseTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/GroupByClauseTest.java index bebc32dcd459..59b02cec27e5 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/GroupByClauseTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/GroupByClauseTest.java @@ -229,8 +229,6 @@ public void testNegativeCases() { "constructor or function invocation", 32, 28); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element list " + "constructor or function invocation", 35, 25); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'seq int', found 'seq int'", - 36, 20); BAssertUtil.validateError(negativeResult, i++, "operator '+' not defined for 'seq int' and 'int'", 39, 20); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element list " + "constructor or function invocation", 39, 20); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/JoinClauseTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/JoinClauseTest.java index 9d482b6a9ef9..b70824a062d5 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/JoinClauseTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/JoinClauseTest.java @@ -127,10 +127,10 @@ public void testJoinClauseWithLargeList() { @Test(description = "Test negative scenarios for query expr with join clause") public void testNegativeScenarios() { - Assert.assertEquals(negativeResult.getErrorCount(), 40); int i = 0; validateError(negativeResult, i++, "incompatible types: expected 'Department', found 'Person'", 46, 13); validateError(negativeResult, i++, "undeclared field 'name' in record 'Person'", 51, 19); + validateError(negativeResult, i++, "incompatible types: expected 'Department', found 'other'", 69, 13); validateError(negativeResult, i++, "unknown type 'XYZ'", 69, 13); validateError(negativeResult, i++, "incompatible types: expected 'int', found 'other'", 70, 28); validateError(negativeResult, i++, "undefined symbol 'deptId'", 93, 11); @@ -167,9 +167,8 @@ public void testNegativeScenarios() { validateError(negativeResult, i++, "incompatible types: expected 'int', found 'other'", 389, 59); validateError(negativeResult, i++, "invalid operation: type 'Person?' does not support field access", 389, 59); validateError(negativeResult, i++, "invalid operation: type 'Person?' does not support field access", 395, 22); - validateError(negativeResult, i++, "order by not supported for complex type fields, order key should belong" + - " to a basic type", 395, 22); validateError(negativeResult, i++, "invalid operation: type 'Person?' does not support field access", 397, 36); + Assert.assertEquals(negativeResult.getErrorCount(), i); } @AfterClass diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/OrderByClauseTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/OrderByClauseTest.java index 448332adbbf7..791844e1dcf1 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/OrderByClauseTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/OrderByClauseTest.java @@ -234,12 +234,7 @@ public void testQueryExprWithOrderByClauseReturnXML() { @Test(description = "Test negative scenarios for query expr with order by clause") public void testNegativeScenarios() { int index = 0; - - validateError(negativeResult, index++, "order by not supported for complex type fields, " + - "order key should belong to a basic type", - 35, 18); - validateError(negativeResult, index++, "undefined symbol 'address'", - 35, 18); + validateError(negativeResult, index++, "undefined symbol 'address'", 35, 18); validateError(negativeResult, index++, "order by not supported for complex type fields, order key should belong to a basic type", 47, 18); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java index fe8ed238035a..e9dcf503eb5b 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java @@ -107,7 +107,6 @@ public void testQueryActionOrExprSemanticsNegative() { validateError(negativeResult, i++, "incompatible types: '()' is not an iterable collection", 42, 24); validateError(negativeResult, i++, "incompatible types: '()' is not an iterable collection", 51, 27); validateError(negativeResult, i++, "incompatible types: '()' is not an iterable collection", 61, 27); - validateError(negativeResult, i++, "incompatible types: expected 'other', found 'int'", 71, 18); validateError(negativeResult, i++, "incompatible types: '()' is not an iterable collection", 71, 27); validateError(negativeResult, i++, "incompatible types: '()' is not an iterable collection", 82, 27); validateError(negativeResult, i++, "incompatible types: '()' is not an iterable collection", 93, 27); @@ -117,10 +116,7 @@ public void testQueryActionOrExprSemanticsNegative() { validateError(negativeResult, i++, "receive action not supported wth 'var' type", 168, 27); validateError(negativeResult, i++, "action invocation as an expression not allowed here", 279, 15); validateError(negativeResult, i++, "action invocation as an expression not allowed here", 291, 18); - validateError(negativeResult, i++, "order by not supported for complex type fields, order key should " + - "belong to a basic type", 291, 18); validateError(negativeResult, i++, "action invocation as an expression not allowed here", 303, 15); - validateError(negativeResult, i++, "incompatible types: expected 'int', found 'other'", 303, 15); validateError(negativeResult, i++, "action invocation as an expression not allowed here", 315, 23); validateError(negativeResult, i++, "incompatible types: expected 'int', found 'other'", 316, 21); validateError(negativeResult, i++, "action invocation as an expression not allowed here", 320, 50); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryExprWithQueryConstructTypeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryExprWithQueryConstructTypeTest.java index f279c44272da..e62ae3d3a9df 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryExprWithQueryConstructTypeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryExprWithQueryConstructTypeTest.java @@ -36,14 +36,11 @@ */ public class QueryExprWithQueryConstructTypeTest { - private CompileResult result, negativeResult, semanticsNegativeResult; + private CompileResult result; @BeforeClass public void setup() { result = BCompileUtil.compile("test-src/query/query-expr-with-query-construct-type.bal"); - negativeResult = BCompileUtil.compile("test-src/query/query-expr-query-construct-type-negative.bal"); - semanticsNegativeResult = BCompileUtil.compile("test-src/query/query-expr-query-construct-type-semantics" + - "-negative.bal"); } @Test(description = "Test query expr returning a stream ") @@ -161,10 +158,12 @@ public void testMapConstructQueryWithConflictingKeys() { BRunUtil.invoke(result, "testMapConstructQueryWithConflictingKeys"); } - @Test(description = "Test negative scenarios for query expr with query construct type") + @Test(enabled = false, description = "Test negative scenarios for query expr with query construct type") public void testNegativeScenarios() { - int index = 0; + CompileResult negativeResult = + BCompileUtil.compile("test-src/query/query-expr-query-construct-type-negative.bal"); + int index = 0; validateError(negativeResult, index++, "incompatible types: expected 'Person[]', found 'stream'", 54, 35); validateError(negativeResult, index++, "incompatible types: expected 'Customer[]', " + @@ -335,6 +334,9 @@ public void testNegativeScenarios() { @Test(description = "Test semantic negative scenarios for query expr with query construct type") public void testSemanticNegativeScenarios() { + CompileResult semanticsNegativeResult = + BCompileUtil.compile("test-src/query/query-expr-query-construct-type-semantics-negative.bal"); + int index = 0; validateError(semanticsNegativeResult, index++, "on conflict can only be used with queries which produce " + "maps or tables with key specifiers", 39, 13); @@ -360,7 +362,7 @@ public void testSemanticNegativeScenarios() { @Test(expectedExceptions = BLangTestException.class, expectedExceptionsMessageRegExp = "error: \\{ballerina/lang.map\\}InherentTypeViolation " + "\\{\"message\":\"cannot update 'readonly' field 'id' in record of type 'record " + - "\\{\\| readonly int id; readonly string name; User user; \\|\\}'\".*") + "\\{\\| readonly int id; readonly string name; UserX user; \\|\\}'\".*") public void testQueryConstructingTableUpdateKeyPanic1() { BRunUtil.invoke(result, "testQueryConstructingTableUpdateKeyPanic1"); } @@ -368,7 +370,7 @@ public void testQueryConstructingTableUpdateKeyPanic1() { @Test(expectedExceptions = BLangTestException.class, expectedExceptionsMessageRegExp = "error: \\{ballerina/lang.map\\}InherentTypeViolation " + "\\{\"message\":\"cannot update 'readonly' field 'id' in record of type 'record " + - "\\{\\| readonly int id; readonly string name; User user; \\|\\}'\".*") + "\\{\\| readonly int id; readonly string name; UserX user; \\|\\}'\".*") public void testQueryConstructingTableUpdateKeyPanic2() { BRunUtil.invoke(result, "testQueryConstructingTableUpdateKeyPanic2"); } @@ -534,7 +536,5 @@ public void testInnerQueryConstructedWithCEP() { @AfterClass public void tearDown() { result = null; - negativeResult = null; - semanticsNegativeResult = null; } } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryNegativeTests.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryNegativeTests.java index 29c4fcdc886b..3e7f756b767d 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryNegativeTests.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryNegativeTests.java @@ -42,6 +42,7 @@ public void testFromClauseWithInvalidType() { 64, 18); validateError(compileResult, index++, "undeclared field 'lastName' in record 'Teacher'", 67, 30); validateError(compileResult, index++, "undeclared field 'age' in record 'Teacher'", 68, 25); + validateError(compileResult, index++, "incompatible types: expected 'Person', found 'other'", 83, 18); validateError(compileResult, index++, "unknown type 'XYZ'", 83, 18); validateError(compileResult, index++, "undefined field 'lastName' in record 'Teacher'", 103, 20); validateError(compileResult, index++, "incompatible types: 'int' is not an iterable collection", 116, 32); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/reachability/ReachabilityAnalysisTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/reachability/ReachabilityAnalysisTest.java index 96644ffad04f..1dda40d7ff6c 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/reachability/ReachabilityAnalysisTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/reachability/ReachabilityAnalysisTest.java @@ -302,7 +302,6 @@ public void testUnreachability() { validateError(result, i++, ERROR_TYPE_NEVER_EXPRESSION_NOT_ALLOWED, 818, 19); validateError(result, i++, ERROR_UNREACHABLE_CODE, 829, 9); validateError(result, i++, ERROR_TYPE_NEVER_EXPRESSION_NOT_ALLOWED, 829, 19); - validateError(result, i++, ERROR_TYPE_NEVER_EXPRESSION_NOT_ALLOWED, 839, 15); validateError(result, i++, ERROR_UNREACHABLE_CODE, 840, 9); validateError(result, i++, ERROR_TYPE_NEVER_EXPRESSION_NOT_ALLOWED, 840, 19); validateHint(result, i++, HINT_UNNECESSARY_CONDITION, 850, 8); @@ -556,7 +555,7 @@ public void testUnreachability2() { validateError(result, i++, ERROR_UNREACHABLE_CODE, 401, 9); validateError(result, i++, ERROR_UNREACHABLE_CODE, 408, 9); validateError(result, i++, ERROR_UNREACHABLE_CODE, 419, 9); - validateError(result, i++, ERROR_UNREACHABLE_CODE, 428, 9); + validateError(result, i++, ERROR_UNREACHABLE_CODE, 430, 9); validateError(result, i++, ERROR_UNREACHABLE_CODE, 432, 9); validateError(result, i++, ERROR_UNREACHABLE_CODE, 438, 9); validateError(result, i++, ERROR_UNREACHABLE_CODE, 446, 9); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/record/RecordTypeInclusionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/record/RecordTypeInclusionTest.java index 4736ac5a3bc3..3351ce1f7a9a 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/record/RecordTypeInclusionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/record/RecordTypeInclusionTest.java @@ -54,14 +54,12 @@ public void testCyclicInclusionViaFieldNegative() { "test-src/record/test_record_cyclic_inclusion_via_field_negative.bal"); int index = 0; validateError(compileResult, index++, "missing non-defaultable required record field 'x'", 26, 13); - validateError(compileResult, index++, "incompatible types: expected 'Bar', found 'int'", 27, 11); + validateError(compileResult, index++, "cannot update 'readonly' value of type 'Foo'", 27, 5); validateError(compileResult, index++, "incompatible types: expected 'Bar', found 'int'", 29, 17); validateError(compileResult, index++, "incompatible types: expected 'Bar', found 'int'", 31, 21); validateError(compileResult, index++, "missing non-defaultable required record field 'innerError'", 42, 21); validateError(compileResult, index++, "incompatible types: expected 'record {| string code?; ... innerError; " + "anydata...; |}', found 'int'", 43, 20); - validateError(compileResult, index++, "incompatible types: expected 'int', found 'record {| string code?; ..." + - " innerError; anydata...; |}'", 46, 13); Assert.assertEquals(compileResult.getErrorCount(), index); } } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/arrays/ArrayFillTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/arrays/ArrayFillTest.java index f7fc07fb2dd8..8112cc9ee997 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/arrays/ArrayFillTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/arrays/ArrayFillTest.java @@ -303,7 +303,7 @@ public void testFiniteTypeArrayFill1() { assertEquals(unionArr.size(), index + 1); for (int i = 0; i < index; i++) { - assertEquals(unionArr.get(i), 0L); + assertEquals(unionArr.get(i), 0); } assertEquals(unionArr.get(index), 5L); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/arrays/arraysofarrays/SealedArraysOfArraysTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/arrays/arraysofarrays/SealedArraysOfArraysTest.java index 8c27e3bed470..a41a788529ad 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/arrays/arraysofarrays/SealedArraysOfArraysTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/arrays/arraysofarrays/SealedArraysOfArraysTest.java @@ -114,7 +114,6 @@ public void testStringSealedArraysOfArrays() { @Test() public void testNegativeSealedArraysOfArrays() { - Assert.assertEquals(resultNegative.getErrorCount(), 34); int i = 0; BAssertUtil.validateError( resultNegative, i++, "size mismatch in closed array. expected '2', but found '3'", 19, 23); @@ -153,10 +152,7 @@ public void testNegativeSealedArraysOfArrays() { BAssertUtil.validateError( resultNegative, i++, "size mismatch in closed array. expected '3', but found '4'", 72, 66); BAssertUtil.validateError( - resultNegative, i++, "invalid usage of closed type: array not initialized", 73, 5); - BAssertUtil.validateError( - resultNegative, i++, "incompatible types: expected '((float[*][]|string) & readonly)', " + - "found '(float[2][2] & readonly)'", 76, 40); + resultNegative, i++, "invalid usage of closed type: array not initialized", 73, 5);; BAssertUtil.validateError( resultNegative, i++, "list index out of range: index: '4'", 83, 11); BAssertUtil.validateError( @@ -183,8 +179,9 @@ public void testNegativeSealedArraysOfArrays() { resultNegative, i++, "incompatible types: expected 'map', found 'map<(float|int[1][1])>'", 118, 19); BAssertUtil.validateError( - resultNegative, i, "incompatible types: expected '[(int[*][] & readonly),float]', " + + resultNegative, i++, "incompatible types: expected '[(int[*][] & readonly),float]', " + "found '[(string|int[1][]),float]'", 121, 34); + Assert.assertEquals(resultNegative.getErrorCount(), i); } @Test diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/ifelse/TypeGuardTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/ifelse/TypeGuardTest.java index cdfc29dd5868..7d624653296d 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/ifelse/TypeGuardTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/ifelse/TypeGuardTest.java @@ -63,6 +63,8 @@ public void testTypeGuardNegative() { "incompatible types: '(int|boolean)' will not be matched to 'float'", 90, 63); BAssertUtil.validateError(negativeResult, i++, "incompatible types: '(boolean|float)' will not be matched to 'int'", 99, 30); + BAssertUtil.validateError(negativeResult, i++, + "incompatible types: '(int|string)' will not be matched to 'boolean'", 99, 44); BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'string' will not be matched to 'int'", 108, 25); BAssertUtil.validateError(negativeResult, i++, @@ -93,14 +95,10 @@ public void testTypeGuardNegative() { BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'map<(int|string)>' will not be matched to 'record {| int i; float f; |}'", 214, 8); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'map<(int|string)>' will not be matched " + - "to 'map'", 221, 8); BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'CyclicComplexUnion' will not" + " be matched to 'float'", 232, 8); BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'CyclicComplexUnion' will not" + " be matched to 'floatUnion'", 239, 8); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'CyclicComplexUnion' will not" + - " be matched to 'float[]'", 245, 8); Assert.assertEquals(negativeResult.getDiagnostics().length, i); } @@ -134,12 +132,12 @@ public void testTypeGuardSemanticsNegative() { BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(int|boolean)'", 181, 17); // TODO : Fix me : #21609 -// BAssertUtil.validateError(negativeResult, i++, -// "incompatible types: expected 'string', found '(float|string|int|boolean)'", 183, -// 20); + BAssertUtil.validateError(negativeResult, i++, + "incompatible types: expected 'string', found '(float|string)'", 183, + 20); // TODO : Fix me : #21609 -// BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(boolean|float)'", -// 190, 17); + BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(int|string|float)'", + 190, 17); // BAssertUtil.validateError(negativeResult, i++, // "incompatible types: expected 'string', found '(boolean|int|string)'", 192, 20); BAssertUtil.validateError(negativeResult, i++, @@ -149,16 +147,18 @@ public void testTypeGuardSemanticsNegative() { BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'string', found '(float|string)'", 201, 20); // TODO : Fix me : #21609 -// BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(string|boolean)'", -// 208, 17); -// BAssertUtil.validateError(negativeResult, i++, -// "incompatible types: expected 'string', found '(int|float|string)'", 210, 20); + BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(string|boolean)'", + 208, 17); + // TODO : Fix me : #21609 + BAssertUtil.validateError(negativeResult, i++, + "incompatible types: expected 'string', found '(int|float)'", 210, 20); BAssertUtil.validateError(negativeResult, i++, "unknown type 'T'", 216, 30); // TODO : Fix me : #21609 -// BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(string|boolean)'", -// 217, 17); -// BAssertUtil.validateError(negativeResult, i++, -// "incompatible types: expected 'string', found '(int|float|string)'", 219, 20); + BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(string|boolean)'", + 217, 17); + // TODO : Fix me : #21609 + BAssertUtil.validateError(negativeResult, i++, + "incompatible types: expected 'string', found '(int|float)'", 219, 20); // BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(Person|boolean)'", // 238, 17); BAssertUtil.validateError(negativeResult, i++, @@ -1080,8 +1080,6 @@ public void testTypeGuardTypeNarrowing3() { "incompatible types: expected 'string', found '(string|int)'", 243, 20); BAssertUtil.validateError(result, index++, "incompatible types: expected 'string', found '(string|int)'", 252, 20); - BAssertUtil.validateError(result, index++, - "incompatible types: expected 'int', found 'other'", 263, 17); // issue #34965 BAssertUtil.validateError(result, index++, "incompatible types: expected 'string', found '(string|int)'", 265, 20); // issue #34965 BAssertUtil.validateError(result, index++, @@ -1124,14 +1122,7 @@ public void testTypeGuardTypeNarrowing3() { @Test public void testTypeGuardTypeNarrowing4() { CompileResult result = BCompileUtil.compile("test-src/statements/ifelse/test_type_guard_type_narrow_4.bal"); - int index = 0; - BAssertUtil.validateError(result, index++, - "expression of type 'never' or equivalent to type 'never' not allowed here", 21, 19); // issue #34965 - BAssertUtil.validateError(result, index++, - "expression of type 'never' or equivalent to type 'never' not allowed here", 27, 19); // issue #34965 - BAssertUtil.validateError(result, index++, - "expression of type 'never' or equivalent to type 'never' not allowed here", 33, 19); // issue #34965 - Assert.assertEquals(result.getDiagnostics().length, index); + Assert.assertEquals(result.getDiagnostics().length, 0); } @Test(description = "Test type guard type narrowing with no errors") diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/matchstmt/MatchGuardAnalysisTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/matchstmt/MatchGuardAnalysisTest.java index 3b994cba5e9d..68007630242d 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/matchstmt/MatchGuardAnalysisTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/matchstmt/MatchGuardAnalysisTest.java @@ -64,14 +64,6 @@ public void testMatchGuardCodeAnalysisNegative() { BAssertUtil.validateWarning(result, i++, "pattern will not be matched", 42, 9); BAssertUtil.validateError(result, i++, "incompatible types: 'Foo' will not be " + "matched to 'record {| string x; int i; anydata...; |}'", 42, 30); - BAssertUtil.validateError(result, i++, "incompatible types: 'function () returns ()' will not be " + - "matched to 'isolated function'", 51, 19); - BAssertUtil.validateError(result, i++, "incompatible types: 'function (int,string) returns ()' " + - "will not be matched to 'isolated function'", 60, 19); - BAssertUtil.validateError(result, i++, "incompatible types: 'function (int,string) returns (int)' " + - "will not be matched to 'isolated function'", 69, 19); - BAssertUtil.validateError(result, i++, "incompatible types: 'function () returns (int)' will not be " + - "matched to 'isolated function'", 78, 19); BAssertUtil.validateError(result, i++, "incompatible types: '\"P2\"' will not be matched to '\"P3\"'", 88, 17); Assert.assertEquals(result.getWarnCount(), 3); Assert.assertEquals(result.getErrorCount(), i - 3); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/ConstantExpressionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/ConstantExpressionTest.java index 14931b3531f7..e5040c89c631 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/ConstantExpressionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/ConstantExpressionTest.java @@ -58,11 +58,9 @@ public void constExpressionNegative() { BAssertUtil.validateError(compileResult1, i++, "operator '~' not defined for 'false'", 37, 22); BAssertUtil.validateError(compileResult1, i++, "illegal cyclic reference '[A, B, C]'", 39, 1); BAssertUtil.validateError(compileResult1, i++, "illegal cyclic reference '[E, F]'", 44, 1); - BAssertUtil.validateError(compileResult1, i++, "'-9.223372036854776E18' is out of range for 'int'", 47, 20); BAssertUtil.validateError(compileResult1, i++, "'9223372036854775808' is out of range for 'int'", 47, 21); BAssertUtil.validateError(compileResult1, i++, "illegal cyclic reference '[CONST5]'", 49, 1); - BAssertUtil.validateError(compileResult1, i++, "cannot declare a constant with type 'T', " + - "expected a subtype of 'anydata' that is not 'never'", 49, 7); + BAssertUtil.validateError(compileResult1, i++, "constant declaration not yet supported for type 'T'", 49, 7); Assert.assertEquals(compileResult1.getErrorCount(), i); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/ListConstantNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/ListConstantNegativeTest.java index 912ce6d9130a..76a9968d7167 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/ListConstantNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/ListConstantNegativeTest.java @@ -46,7 +46,6 @@ public void testListConstructorExprAsConstantExprNegative() { " does not have a filler value", 30, 41); validateError(compileResult, i++, "invalid usage of list constructor: type '1|2' does not have a filler value", 31, 26); - validateError(compileResult, i++, "ambiguous type '(int[2]|[int,int])'", 32, 38); validateError(compileResult, i++, "incompatible types: expected '1', found '3'", 33, 27); validateError(compileResult, i++, "incompatible types: expected 'byte', found '300'", 34, 24); validateError(compileResult, i++, "size mismatch in closed array. expected '2', but found '3'", 35, 23); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/SimpleConstantNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/SimpleConstantNegativeTest.java index 2a80fdcac758..ba441c2c5125 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/SimpleConstantNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/SimpleConstantNegativeTest.java @@ -50,9 +50,8 @@ public void testNegative() { 9, 14); BAssertUtil.validateError(compileResult, index++, "'_' is a keyword, and may not be used as an identifier", 10, 7); - BAssertUtil.validateError(compileResult, index++, "cannot declare a constant with type 'invalidType', " + - "expected a subtype of 'anydata' that is not 'never'", - 12, 7); + BAssertUtil.validateError(compileResult, index++, "constant declaration not yet supported for type " + + "'invalidType'", 12, 7); BAssertUtil.validateError(compileResult, index++, "unknown type 'invalidType'", 12, 7); BAssertUtil.validateError(compileResult, index++, "cannot update constant value", 26, 5); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finite/FiniteTypeNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finite/FiniteTypeNegativeTest.java index 9048996d7897..d5b142770971 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finite/FiniteTypeNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finite/FiniteTypeNegativeTest.java @@ -163,7 +163,7 @@ public void testInvalidLiteralAssignment() { 183, 12); validateError(resultNegativeTwo, i++, "incompatible types: expected '2d', found 'string'", 187, 12); - validateError(resultNegativeTwo, i++, "incompatible types: 'int' cannot be cast to '2f'", + validateError(resultNegativeTwo, i++, "incompatible types: expected '2d', found '2f'", 191, 12); validateError(resultNegativeTwo, i++, "incompatible types: expected '3d', found '4f'", 192, 12); @@ -233,14 +233,14 @@ public void testInvalidLiteralAssignment() { "found 'float'", 276, 34); validateError(resultNegativeTwo, i++, "incompatible types: expected '1.7976931348623157E309d', " + "found 'decimal'", 277, 34); - validateError(resultNegativeTwo, i++, "'-9.223372036854776E18' is out of range for 'int'", - 280, 20); + validateError(resultNegativeTwo, i++, "'9223372036854775808' is out of range for 'int'", + 280, 21); validateError(resultNegativeTwo, i++, "unknown type 'testType'", 282, 5); validateError(resultNegativeTwo, i++, "'9223372036854775808' is out of range for 'int'", 285, 30); validateError(resultNegativeTwo, i++, "unknown type 'testType'", 286, 5); - validateError(resultNegativeTwo, i++, "'-9.223372036854776E18' is out of range for 'int'", - 290, 19); + validateError(resultNegativeTwo, i++, "'9223372036854775808' is out of range for 'int'", + 290, 20); validateError(resultNegativeTwo, i++, "unknown type 'InvalidTest1'", 292, 5); validateError(resultNegativeTwo, i++, "incompatible types: expected '1f', found 'float'", 296, 12); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/map/ConstrainedMapTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/map/ConstrainedMapTest.java index b216a7ee5b0e..74b48bff3127 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/map/ConstrainedMapTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/map/ConstrainedMapTest.java @@ -50,7 +50,7 @@ public void setup() { @Test(description = "Test Map constrained with type negative semantic validations.") public void testConstrainedMapNegative() { - Assert.assertEquals(negativeResult.getErrorCount(), 7); + Assert.assertEquals(negativeResult.getErrorCount(), 6); int i = 0; BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'map', found 'map'", 3, 12); BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found 'string'", 7, 44); @@ -61,8 +61,6 @@ public void testConstrainedMapNegative() { "incompatible types: expected 'map', " + "found 'map'", 35, 31); BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'map', found 'map'", 45, 31); - BAssertUtil.validateError(negativeResult, i, "incompatible types: 'map' cannot be cast to" + - " 'map'", 77, 29); } @Test(description = "Test Map constrained with value type value retrieval positive case.") diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/never/NeverTypeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/never/NeverTypeTest.java index e894bd8f93ac..b0d049346c0c 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/never/NeverTypeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/never/NeverTypeTest.java @@ -338,8 +338,6 @@ public Object[] dataToTestNeverRuntime() { "testNeverRuntime7", "testNeverRuntime8", "testNeverRuntime9", - "testNeverRuntime10", - "testNeverRuntime11", "testNeverRuntime12", "testNeverWithAnyAndAnydataRuntime", "testNeverFieldTypeCheck", @@ -402,6 +400,25 @@ public void testNeverTypeCodeAnalysisNegative() { Assert.assertEquals(compileResult.getWarnCount(), 1); } + @Test + public void testNeverTypeIsExprNegative() { + CompileResult res = BCompileUtil.compile("test-src/types/never/never_type_is_expr_negative.bal"); + int i = 0; + BAssertUtil.validateError(res, i++, "incompatible types: 'int' will not be matched to 'never'", 19, 17); + BAssertUtil.validateError(res, i++, "incompatible types: 'Record' will not be matched to 'never'", 29, 17); + BAssertUtil.validateError(res, i++, "incompatible types: 'record {| int x; anydata...; |}' " + + "will not be matched to 'record {| never x?; anydata...; |}'", 34, 17); + BAssertUtil.validateError(res, i++, "incompatible types: 'record {| never? x; anydata...; |}' " + + "will not be matched to 'record {| never x?; anydata...; |}'", 37, 17); + BAssertUtil.validateError(res, i++, "incompatible types: 'record {| int? x; anydata...; |}' " + + "will not be matched to 'record {| never x?; anydata...; |}'", 40, 17); + BAssertUtil.validateError(res, i++, "incompatible types: '(record {| int x; anydata...; |} & readonly)' " + + "will not be matched to 'record {| never x?; anydata...; |}'", 43, 17); + BAssertUtil.validateError(res, i++, "incompatible types: '(record {| never? x; anydata...; |} & readonly)' " + + "will not be matched to 'record {| never x?; anydata...; |}'", 46, 17); + Assert.assertEquals(res.getErrorCount(), i); + } + @AfterClass public void tearDown() { neverTypeTestResult = null; diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/readonly/InherentlyImmutableTypeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/readonly/InherentlyImmutableTypeTest.java index 58533c393875..f7f4aad79930 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/readonly/InherentlyImmutableTypeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/readonly/InherentlyImmutableTypeTest.java @@ -54,16 +54,14 @@ public void testReadonlyTypeNegative() { BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'any', found 'readonly'", 19, 14); BAssertUtil.validateError(negativeResult, i++, - "operator '==' not defined for 'readonly' and '[int,int,int]'", 24, 14); + "incompatible types: expected 'error?', found 'readonly'", 24, 26); BAssertUtil.validateError(negativeResult, i++, - "incompatible types: expected 'error?', found 'readonly'", 29, 26); + "incompatible types: expected '(int|any)', found 'readonly'", 25, 27); BAssertUtil.validateError(negativeResult, i++, - "incompatible types: expected '(int|any)', found 'readonly'", 30, 27); + "incompatible types: expected '(string|readonly)', found '(readonly|any)'", 27, 43); BAssertUtil.validateError(negativeResult, i++, - "incompatible types: expected '(string|readonly)', found '(readonly|any)'", 32, 43); - BAssertUtil.validateError(negativeResult, i++, - "incompatible types: expected 'any', found '(readonly|string)'", 34, 17); - Assert.assertEquals(negativeResult.getErrorCount(), 6); + "incompatible types: expected 'any', found '(readonly|string)'", 29, 17); + Assert.assertEquals(negativeResult.getErrorCount(), i); } @AfterClass diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/table/TableCastTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/table/TableCastTest.java index e9c5fe210614..d478380285a7 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/table/TableCastTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/table/TableCastTest.java @@ -72,15 +72,14 @@ public void testCastingWithEmptyKeyedKeylessTbl() { @Test public void testNegativeCases() { - Assert.assertEquals(negativeResult.getErrorCount(), 5); - BAssertUtil.validateError(negativeResult, 0, "incompatible types: 'PersonTable1' " + + int index = 0; + BAssertUtil.validateError(negativeResult, index++, "incompatible types: 'PersonTable1' " + "cannot be cast to 'table key'", 49, 34); - BAssertUtil.validateError(negativeResult, 1, "incompatible types: expected 'int', found 'string'", 50, 16); - BAssertUtil.validateError(negativeResult, 2, "incompatible types: expected 'string', found 'int'", 60, 16); - BAssertUtil.validateError(negativeResult, 3, "incompatible types: 'CustomerTable' cannot be" + - " cast to 'CustomerEmptyKeyedTbl'", 77, 34); - BAssertUtil.validateError(negativeResult, 4, "incompatible types: 'CustomerTable' cannot be cast to " + - "'table'", 83, 20); + BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'int', found 'string'", 50, + 16); + BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'string', found 'int'", 60, + 16); + Assert.assertEquals(negativeResult.getErrorCount(), index); } @AfterClass diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/table/TableNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/table/TableNegativeTest.java index 90b5e63918c8..83eceba270a1 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/table/TableNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/table/TableNegativeTest.java @@ -184,6 +184,9 @@ public void testTableNegativeCases() { validateError(compileResult, index++, "incompatible types: expected " + "'table key', " + "found 'table'", 539, 9); + validateError(compileResult, index++, "incompatible types: expected " + + "'table key', " + + "found 'table key'", 551, 9); validateError(compileResult, index++, "incompatible types: expected 'never', found 'int'", 551, 29); validateError(compileResult, index++, "incompatible types: expected " + "'table key', " + diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/tuples/BasicTupleTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/tuples/BasicTupleTest.java index 629de1bd6008..c4d4b31a3ddb 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/tuples/BasicTupleTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/tuples/BasicTupleTest.java @@ -193,7 +193,7 @@ public void testTupleWithUnion() { @Test(description = "Test negative scenarios of assigning tuple literals") public void testNegativeTupleLiteralAssignments() { - Assert.assertEquals(resultNegative.getErrorCount(), 55); + Assert.assertEquals(resultNegative.getErrorCount(), 54); int i = 0; BAssertUtil.validateError( resultNegative, i++, @@ -310,21 +310,19 @@ public void testTupleToJSONAssignmentNegative() { int i = 36; BAssertUtil.validateError(resultNegative, i++, "incompatible types: expected 'json', " + "found '[string,int,xml...]'", 199, 21); - BAssertUtil.validateError(resultNegative, i++, "incompatible types: '[string,(int|xml),string...]' " + - "cannot be cast to 'json[]'", 202, 16); BAssertUtil.validateError(resultNegative, i, "incompatible types: expected 'json', " + "found '[string,(int|xml),string...]'", 203, 16); } @Test(description = "Test ambiguous tuple assignment to unions of tuple types") public void testAmbiguousTupleTupeNegative() { - int i = 39; + int i = 38; BAssertUtil.validateError(resultNegative, i, "ambiguous type '([1,\"hello\"]|[1])'", 208, 10); } @Test(description = "Test the tuple argument when the variable is already declared") public void testTupleParamWithExistingArg() { - int i = 40; + int i = 39; BAssertUtil.validateError(resultNegative, i++, "redeclared symbol 'i'", 215, 34); BAssertUtil.validateError(resultNegative, i++, "redeclared symbol 'i'", 222, 41); BAssertUtil.validateError(resultNegative, i++, "operator '+' not defined for 'int' and 'string'", 230, 21); @@ -338,7 +336,7 @@ public void testTupleAsTupleFirstMember() { @Test(description = "Test the tuple annotations") public void testTupleAnnotations1() { - int i = 44; + int i = 43; BAssertUtil.validateError(resultNegative, i++, "annotation 'ballerina/lang.annotations:0.0.0:typeParam' is not allowed on field", 239, 7); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/xml/XMLIterationTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/xml/XMLIterationTest.java index 2e5b28fcdbbb..fc7276975860 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/xml/XMLIterationTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/xml/XMLIterationTest.java @@ -58,27 +58,18 @@ public void testNegative() { BAssertUtil.validateError(negative, index++, "incompatible types: " + "expected 'function (ballerina/lang.xml:0.0.0:ItemType) returns ()', " + "found 'function ([int,xml,string]) returns ()'", 18, 19); - BAssertUtil.validateError(negative, index++, - "incompatible types: expected 'other', found 'xml:Element'", - 29, 13); BAssertUtil.validateError(negative, index++, "incompatible types: 'xml:Element' is not an iterable collection", 29, 34); BAssertUtil.validateError(negative, index++, "incompatible types: expected 'record {| xml:Comment value; |}?', found " + "'record {| xml:Element value; |}?'", 33, 54); - BAssertUtil.validateError(negative, index++, - "incompatible types: expected 'other', found 'xml:Comment'", - 40, 13); BAssertUtil.validateError(negative, index++, "incompatible types: 'xml:Comment' is not an iterable collection", 40, 34); BAssertUtil.validateError(negative, index++, "incompatible types: expected 'record {| xml:Element value; |}?', found " + "'record {| xml:Comment value; |}?'", 44, 54); - BAssertUtil.validateError(negative, index++, - "incompatible types: expected 'other', found 'xml:ProcessingInstruction'", - 51, 13); BAssertUtil.validateError(negative, index++, "incompatible types: 'xml:ProcessingInstruction' is not an iterable collection", 51, 48); @@ -91,15 +82,9 @@ public void testNegative() { BAssertUtil.validateError(negative, index++, "incompatible types: expected '(xml|xml)', found 'xml'", 60, 44); - BAssertUtil.validateError(negative, index++, - "incompatible types: expected 'other', found '(xml:Element|xml:Text)'", - 63, 13); BAssertUtil.validateError(negative, index++, "incompatible types: '(xml:Element|xml:Text)' is not an iterable collection", 63, 44); - BAssertUtil.validateError(negative, index++, - "incompatible types: expected 'other', found '(xml:Element|xml:Text)'", - 68, 13); BAssertUtil.validateError(negative, index++, "incompatible types: '(xml|xml)' is not an iterable collection", 68, 44); diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation.bal index ae0be91693b8..bffcab3d6b6f 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation.bal @@ -153,20 +153,17 @@ function checkAnyDataEquality() { } type IntOne 1; -type FloatOne 1.0; -type IntTwo 2; -type FloatTwo 2f; function checkFiniteTypeEquality() { IntOne intOne_1 = 1; IntOne intOne_2 = 1; - IntTwo intTwo = 2; - FloatOne floatOne = 1f; - FloatTwo floatTwo = 2.0; + byte byteOne = 1; + byte byteTwo = 2; test:assertTrue((intOne_1 == intOne_2) && !(intOne_1 != intOne_2)); - test:assertTrue((floatOne != floatTwo) && !(floatOne == floatTwo)); - test:assertFalse((intOne_1 == intTwo) && !(intOne_1 != intTwo)); + test:assertTrue((intOne_1 == byteOne) && !(intOne_1 != byteOne)); + test:assertTrue(!(intOne_1 == byteTwo) && (intOne_1 != byteTwo)); + test:assertTrue(!(byteOne == byteTwo) && (byteOne != byteTwo)); } type ErrorDetail record { @@ -231,6 +228,16 @@ function testOpenRecordWithOptionalFieldsEqualityNegative() { test:assertFalse((e1 == e2) || !(e1 != e2) || (e3 == e4) || !(e3 != e4)); } +type EmployeeWithOptionalId record {| + string name; + float id?; +|}; + +type PersonWithOptionalId record {| + string name; + string id?; +|}; + function testClosedRecordWithOptionalFieldsEqualityPositive() { ClosedRecordWithOptionalFieldOne e1 = {name: "Em", one: 4000}; ClosedRecordWithOptionalFieldOne e2 = e1; @@ -239,6 +246,10 @@ function testClosedRecordWithOptionalFieldsEqualityPositive() { ClosedRecordWithOptionalFieldTwo e4 = {name: "Em"}; test:assertTrue((e1 == e2) && !(e1 != e2) && isEqual(e3, e4)); + + EmployeeWithOptionalId e5 = { name: "Maryam" }; + PersonWithOptionalId p1 = { name: "Maryam" }; + test:assertTrue(e5 == p1 && !(e5 != p1)); } function testClosedRecordWithOptionalFieldsEqualityNegative() { @@ -626,10 +637,6 @@ function checkTupleEqualityNegative() { [string, ClosedEmployee] t6 = ["hi", {name: "Em"}]; test:assertFalse(t1 == t2 || !(t1 != t2) || t3 == t4 || !(t3 != t4) || t5 == t6 || !(t5 != t6)); - - Array a = ["array", 1]; - Mapping b = ["mapping", 2]; - test:assertFalse(a == b); } function checkUnionConstrainedMapsPositive() { @@ -700,6 +707,18 @@ function checkUnionConstrainedMapsNegative() { test:assertFalse('equals || m3 == m4 || !(m3 != m4)); } +function checkEqualityOfMapsOfIncompatibleConstraintTypes() { + map a = {}; + map b = {}; + boolean bool1 = a == b && !(a != b); + + map c = {}; + map d = {}; + boolean bool2 = c == d && !(c != d); + + test:assertTrue(bool1 && bool2); +} + function checkUnionArrayPositive() { (string|int)?[] a1 = []; int[] a2 = []; @@ -1820,3 +1839,25 @@ function testEqualityWithCyclicReferences() { test:assertTrue(t3 == t4); test:assertTrue(t3 == t5); } + +function readonlyMapEquality() { + map & readonly immutableMarks = { + math: 80, + physics: 85, + chemistry: 75 + }; + readonly readonlyMarks = immutableMarks; + + map marks = { + math: 80, + physics: 85, + chemistry: 75 + }; + + test:assertTrue(readonlyMarks == marks); +} + +function readonlyListEquality() { + readonly arr = [1, 2 , 3]; + test:assertTrue(arr == [1, 2 , 3]); +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation_negative.bal index d6b45203a8c1..c9fbed15a20c 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation_negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation_negative.bal @@ -32,18 +32,6 @@ function checkEqualityOfArraysOfDifferentTypes() returns boolean { return bool1 && bool2; } -function checkEqualityOfMapsOfIncompatibleConstraintTypes() returns boolean { - map a = {}; - map b = {}; - boolean bool1 = a == b && !(a != b); - - map c = {}; - map d = {}; - boolean bool2 = c == d && !(c != d); - - return bool1 && bool2; -} - function checkEqualityOfTuplesOfDifferentTypes() returns boolean { [string, int] a = ["", 0]; [boolean, float] b = [false, 0.0]; @@ -60,10 +48,6 @@ function checkEqualityOfRecordsOfIncompatibleTypes() returns boolean { Employee e = { name: "Maryam" }; Person p = { name: "Maryam" }; boolean b = e == p && !(e != p); - - EmployeeWithOptionalId e1 = { name: "Maryam" }; - PersonWithOptionalId p1 = { name: "Maryam" }; - return b && e1 == p1 && !(e1 != p1); } function checkEqualityWithJsonRecordMapForIncompatibleType() returns boolean { @@ -123,11 +107,6 @@ type EmployeeWithOptionalId record {| float id?; |}; -type PersonWithOptionalId record {| - string name; - string id?; -|}; - class Foo { string s = ""; } @@ -152,22 +131,6 @@ function refAndNilEqualityCheck() { } } -function readonlyEquality() returns boolean { - map & readonly immutableMarks = { - math: 80, - physics: 85, - chemistry: 75 - }; - readonly readonlyMarks = immutableMarks; - - map marks = { - math: 80, - physics: 85, - chemistry: 75 - }; - return readonlyMarks != marks; -} - type MyObject object { int i; }; @@ -183,3 +146,27 @@ function testEqualityWithNonAnydataType() returns boolean { MyObject obj2 = object {int i = 10;}; 'equals = obj == obj2 && !(obj != obj2); } + +type IntOne 1; +type FloatOne 1.0; +type IntTwo 2; +type FloatTwo 2f; + +function checkFiniteTypeEqualityNegative() { + IntOne intOne_1 = 1; + IntTwo intTwo = 2; + FloatOne floatOne = 1f; + FloatTwo floatTwo = 2.0; + + boolean _ = (floatOne != floatTwo) && !(floatOne == floatTwo); + boolean _ = ((intOne_1 == intTwo) && !(intOne_1 != intTwo)); +} + +type Array ["array", 1]; +type Mapping ["mapping", 2]; + +function checkTupleEqualityNegative() { + Array a = ["array", 1]; + Mapping b = ["mapping", 2]; + boolean _ = a == b; +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/negative-type-test-expr.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/negative-type-test-expr.bal index b06341511c3e..9565ccb4161c 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/negative-type-test-expr.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/negative-type-test-expr.bal @@ -92,19 +92,19 @@ function testTypeCheckInTernary() returns string { // ========================== Records ========================== -type A1 record { +type A1NTT record { int x = 0; }; -type B1 record { +type B1NTT record { int x = 0; string y = ""; }; function testSimpleRecordTypes_1() returns string { - A1 a1 = {}; + A1NTT a1 = {}; any a = a1; - if (a !is A1) { + if (a !is A1NTT) { return "n/a"; } else { return "a is A1"; @@ -112,49 +112,49 @@ function testSimpleRecordTypes_1() returns string { } function testSimpleRecordTypes_2() returns [boolean, boolean] { - B1 b = {}; + B1NTT b = {}; any a = b; - return [a !is A1, a !is B1]; + return [a !is A1NTT, a !is B1NTT]; } -type A2 record { +type A2NTT record { int x = 0; }; -type B2 record { +type B2NTT record { int x = 0; }; function testSimpleRecordTypes_3() returns [boolean, boolean] { - B2 b = {}; + B2NTT b = {}; any a = b; - return [a !is A2, a !is B2]; + return [a !is A2NTT, a !is B2NTT]; } -type Human record { +type HumanNTT record { string name; (function (int, string) returns string) | () foo = (); }; -type Man record { +type ManNTT record { string name; (function (int, string) returns string) | () foo = (); int age = 0; }; function testRecordsWithFunctionType_1() returns [string, string] { - Human m = {name:"Piyal"}; + HumanNTT m = {name:"Piyal"}; any a = m; string s1; string s2; - if (a !is Man) { + if (a !is ManNTT) { s1 = "a is not a man"; } else { s1 = "Man: " + m.name; } - if (a !is Human) { + if (a !is HumanNTT) { s2 = "a is not a human"; } else { s2 = "Human: " + m.name; @@ -164,18 +164,18 @@ function testRecordsWithFunctionType_1() returns [string, string] { } function testRecordsWithFunctionType_2() returns [string, string] { - Man m = {name:"Piyal"}; + ManNTT m = {name:"Piyal"}; any a = m; string s1; string s2; - if (a !is Man) { + if (a !is ManNTT) { s1 = "a is not a man"; } else { s1 = "Man: " + m.name; } - if (a !is Human) { + if (a !is HumanNTT) { s2 = "a is not a human"; } else { s2 = "Human: " + m.name; @@ -184,36 +184,36 @@ function testRecordsWithFunctionType_2() returns [string, string] { return [s1, s2]; } -type X record { +type XTN record { int p = 0; string q = ""; - A1 r = {}; + A1NTT r = {}; }; -type Y record { +type YTN record { int p = 0; string q = ""; - B1 r = {}; // Assignable to A1. Hence Y is assignable to X. + B1NTT r = {}; // Assignable to A1. Hence Y is assignable to X. }; function testNestedRecordTypes() returns [boolean, boolean] { - Y y = {}; + YTN y = {}; any x = y; - return [x is X, x is Y]; + return [x is XTN, x is YTN]; } -type A3 record { +type A3NTT record { int x = 0; }; -type B3 record {| +type B3NTT record {| int x = 0; |}; function testSealedRecordTypes() returns [boolean, boolean] { - A3 a3 = {}; + A3NTT a3 = {}; any a = a3; - return [a !is A3, a !is B3]; + return [a !is A3NTT, a !is B3NTT]; } // ========================== Objects ========================== @@ -375,18 +375,18 @@ function testObjectWithUnorderedFields() returns [string, string, string, string return [s1, s2, s3, s4]; } -public type A4 object { +public type A4NTT object { public int p; public string q; }; -public type B4 object { +public type B4NTT object { public float r; - *A4; + *A4NTT; }; public class C4 { - *B4; + *B4NTT; public boolean s; public function init(int p, string q, float r, boolean s) { @@ -403,11 +403,11 @@ function testPublicObjectEquivalency() returns [string, string, string] { string s2 = "n/a"; string s3 = "n/a"; - if !(x !is A4) { + if !(x !is A4NTT) { s1 = "values: " + x.p.toString() + ", " + x.q; } - if !(x !is B4) { + if !(x !is B4NTT) { s2 = "values: " + x.p.toString() + ", " + x.q + ", " + x.r.toString(); } @@ -418,18 +418,18 @@ function testPublicObjectEquivalency() returns [string, string, string] { return [s1, s2, s3]; } -type A5 object { +type A5NTT object { int p; string q; }; -type B5 object { +type B5NTT object { float r; - *A5; + *A5NTT; }; class C5 { - *B5; + *B5NTT; boolean s; public function init(int p, string q, float r, boolean s) { @@ -446,11 +446,11 @@ function testPrivateObjectEquivalency() returns [string, string, string] { string s2 = "n/a"; string s3 = "n/a"; - if !(x !is A5) { + if !(x !is A5NTT) { s1 = "values: " + x.p.toString() + ", " + x.q; } - if !(x !is B5) { + if !(x !is B5NTT) { s2 = "values: " + x.p.toString() + ", " + x.q + ", " + x.r.toString(); } @@ -467,7 +467,7 @@ function testAnonymousObjectEquivalency() returns [string, string, string] { string s2 = "n/a"; string s3 = "n/a"; - if !(x !is object { public float r; *A4; }) { + if !(x !is object { public float r; *A4NTT; }) { s1 = "values: " + x.p.toString() + ", " + x.q + ", " + x.r.toString(); } @@ -482,50 +482,50 @@ function testAnonymousObjectEquivalency() returns [string, string, string] { return [s1, s2, s3]; } -class Qux { - Qux? fn; +class QuxNeg { + QuxNeg? fn; - public function init(Qux? fn = ()) { + public function init(QuxNeg? fn = ()) { self.fn = fn; } } -class Quux { - Quux? fn = (); +class QuuxNeg { + QuuxNeg? fn = (); } -class Quuz { - Quuz? fn = (); +class QuuzNeg { + QuuzNeg? fn = (); int i = 1; } -class ABC { - Qux f; +class ABCNeg { + QuxNeg f; string s; - function init(Qux f, string s) { + function init(QuxNeg f, string s) { self.f = f; self.s = s; } } function testObjectIsCheckWithCycles() { - Qux f1 = new; - Qux f2 = new (f1); + QuxNeg f1 = new; + QuxNeg f2 = new (f1); - any a1 = f1; - assertFalse(a1 !is Quux); - assertTrue(a1 !is Quuz); + any a1 = f1; + assertFalse(a1 !is QuuxNeg); + assertTrue(a1 !is QuuzNeg); - any a2 = f2; - assertFalse(a2 !is Quux); - assertTrue(a2 !is Quuz); + any a2 = f2; + assertFalse(a2 !is QuuxNeg); + assertTrue(a2 !is QuuzNeg); - ABC ob = new (f2, "ballerina"); + ABCNeg ob = new (f2, "ballerina"); any a3 = ob; - assertFalse(a3 !is object { Qux f; }); - assertTrue(a3 !is object { Quuz f; }); + assertFalse(a3 !is object {QuxNeg f;}); + assertTrue(a3 !is object {QuuzNeg f;}); } // ========================== Arrays ========================== @@ -539,11 +539,11 @@ function testSimpleArrays() returns [boolean, boolean, boolean, boolean, boolean } function testRecordArrays() returns [boolean, boolean, boolean, boolean] { - X[] a = [{}, {}]; - X[][] b = [[{}, {}], [{}, {}]]; + XTN[] a = [{}, {}]; + XTN[][] b = [[{}, {}], [{}, {}]]; any c = a; any d = b; - return [c !is X[], d !is X[][], c !is Y[], d !is Y[][]]; + return [c !is XTN[], d !is XTN[][], c !is YTN[], d !is YTN[][]]; } public function testUnionType() { @@ -641,20 +641,20 @@ function testSimpleTuples() returns [boolean, boolean, boolean, boolean, boolean } function testTupleWithAssignableTypes_1() returns [boolean, boolean, boolean, boolean] { - [X, Y] p = [{}, {}]; + [XTN, YTN] p = [{}, {}]; any q = p; - boolean b0 = q !is [X, X]; - boolean b1 = q !is [X, Y]; - boolean b2 = q !is [Y, X]; - boolean b3 = q !is [Y, Y]; + boolean b0 = q !is [XTN, XTN]; + boolean b1 = q !is [XTN, YTN]; + boolean b2 = q !is [YTN, XTN]; + boolean b3 = q !is [YTN, YTN]; return [b0, b1, b2, b3]; } function testTupleWithAssignableTypes_2() returns boolean { - [Y, Y] p = [{}, {}]; - [X, Y] q = p; - boolean b1 = q !is [Y, Y]; - return q !is [Y, Y]; + [YTN, YTN] p = [{}, {}]; + [XTN, YTN] q = p; + boolean b1 = q !is [YTN, YTN]; + return q !is [YTN, YTN]; } public function testRestType() { @@ -1079,12 +1079,12 @@ public function testXMLNeverType() { xml e = xml ``; assertEquality( e !is byte, true); - assertEquality( e !is xml<'xml:Element>, true); + assertEquality( e is xml<'xml:Element>, true); assertEquality( e !is xml<'xml:Text>, false); assertEquality( e !is xml, false); assertEquality( e !is 'xml:Text, false); assertEquality( e !is 'xml:Element, true); - assertEquality( e !is xml<'xml:Element|'xml:Comment>, true); + assertEquality( e is xml<'xml:Element|'xml:Comment>, true); } function testXMLTextType(){ diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/type-test-expr.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/type-test-expr.bal index b6c8c183f2ba..3a8c6533ec95 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/type-test-expr.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/type-test-expr.bal @@ -100,21 +100,21 @@ function testTypeCheckInTernary() returns string { // ========================== Records ========================== -type A1 record { +type A1TN record { int x = 0; }; -type B1 record { +type B1TN record { int x = 0; string y = ""; }; function testSimpleRecordTypes_1() returns string { - A1 a1 = {}; + A1TN a1 = {}; any a = a1; - if (a is A1) { + if (a is A1TN) { return "a is A1"; - } else if (a is B1) { + } else if (a is B1TN) { return "a is B1"; } @@ -122,49 +122,49 @@ function testSimpleRecordTypes_1() returns string { } function testSimpleRecordTypes_2() returns [boolean, boolean] { - B1 b = {}; + B1TN b = {}; any a = b; - return [a is A1, a is B1]; + return [a is A1TN, a is B1TN]; } -type A2 record { +type A2TN record { int x = 0; }; -type B2 record { +type B2TN record { int x = 0; }; function testSimpleRecordTypes_3() returns [boolean, boolean] { - B2 b = {}; + B2TN b = {}; any a = b; - return [a is A2, a is B2]; + return [a is A2TN, a is B2TN]; } -type Human record { +type HumanTN record { string name; (function (int, string) returns string) | () foo = (); }; -type Man record { +type ManTN record { string name; (function (int, string) returns string) | () foo = (); int age = 0; }; function testRecordsWithFunctionType_1() returns [string, string] { - Human m = {name:"Piyal"}; + HumanTN m = {name:"Piyal"}; any a = m; string s1; string s2; - if (a is Man) { + if (a is ManTN) { s1 = "Man: " + m.name; } else { s1 = "a is not a man"; } - if (a is Human) { + if (a is HumanTN) { s2 = "Human: " + m.name; } else { s2 = "a is not a human"; @@ -174,18 +174,18 @@ function testRecordsWithFunctionType_1() returns [string, string] { } function testRecordsWithFunctionType_2() returns [string, string] { - Man m = {name:"Piyal"}; + ManTN m = {name:"Piyal"}; any a = m; string s1; string s2; - if (a is Man) { + if (a is ManTN) { s1 = "Man: " + m.name; } else { s1 = "a is not a man"; } - if (a is Human) { + if (a is HumanTN) { s2 = "Human: " + m.name; } else { s2 = "a is not a human"; @@ -194,39 +194,39 @@ function testRecordsWithFunctionType_2() returns [string, string] { return [s1, s2]; } -type X record { +type XTTE record { int p = 0; string q = ""; - A1 r = {}; + A1TN r = {}; }; -type Y record { +type YTTE record { int p = 0; string q = ""; - B1 r = {}; // Assignable to A1. Hence Y is assignable to X. + B1TN r = {}; // Assignable to A1. Hence Y is assignable to X. }; function testNestedRecordTypes() returns [boolean, boolean] { - Y y = {}; + YTTE y = {}; any x = y; - return [x is X, x is Y]; + return [x is XTTE, x is YTTE]; } -type A3 record { +type A3TN record { int x = 0; }; -type B3 record {| +type B3TN record {| int x = 0; |}; function testSealedRecordTypes() returns [boolean, boolean] { - A3 a3 = {}; + A3TN a3 = {}; any a = a3; - return [a is A3, a is B3]; + return [a is A3TN, a is B3TN]; } -type Country record {| +type CountryTN record {| readonly string code?; string name?; record {| @@ -236,7 +236,7 @@ type Country record {| |} continent?; |}; -type MyCountry record {| +type MyCountryTN record {| readonly string code?; record {| string code?; @@ -245,9 +245,9 @@ type MyCountry record {| |}; function testRecordsWithOptionalFields() { - MyCountry x = {}; - Country y = x; - test:assertTrue(x is Country); + MyCountryTN x = {}; + CountryTN y = x; + test:assertTrue(x is CountryTN); } // ========================== Objects ========================== @@ -409,18 +409,18 @@ function testObjectWithUnorderedFields() returns [string, string, string, string return [s1, s2, s3, s4]; } -public type A4 object { +public type A4TN object { public int p; public string q; }; -public type B4 object { +public type B4TN object { public float r; - *A4; + *A4TN; }; -public class C4 { - *B4; +public class C4TN { + *B4TN; public boolean s; public function init(int p, string q, float r, boolean s) { @@ -432,16 +432,16 @@ public class C4 { } function testPublicObjectEquivalency() returns [string, string, string] { - any x = new C4(5, "foo", 6.7, true); + any x = new C4TN(5, "foo", 6.7, true); string s1 = "n/a"; string s2 = "n/a"; string s3 = "n/a"; - if(x is A4) { + if(x is A4TN) { s1 = "values: " + x.p.toString() + ", " + x.q; } - if (x is B4) { + if (x is B4TN) { s2 = "values: " + x.p.toString() + ", " + x.q + ", " + x.r.toString(); } @@ -452,18 +452,18 @@ function testPublicObjectEquivalency() returns [string, string, string] { return [s1, s2, s3]; } -type A5 object { +type A5TN object { int p; string q; }; -type B5 object { +type B5TN object { float r; - *A5; + *A5TN; }; class C5 { - *B5; + *B5TN; boolean s; public function init(int p, string q, float r, boolean s) { @@ -480,11 +480,11 @@ function testPrivateObjectEquivalency() returns [string, string, string] { string s2 = "n/a"; string s3 = "n/a"; - if(x is A5) { + if(x is A5TN) { s1 = "values: " + x.p.toString() + ", " + x.q; } - if (x is B5) { + if (x is B5TN) { s2 = "values: " + x.p.toString() + ", " + x.q + ", " + x.r.toString(); } @@ -496,12 +496,12 @@ function testPrivateObjectEquivalency() returns [string, string, string] { } function testAnonymousObjectEquivalency() returns [string, string, string] { - any x = new C4(5, "foo", 6.7, true); + any x = new C4TN(5, "foo", 6.7, true); string s1 = "n/a"; string s2 = "n/a"; string s3 = "n/a"; - if(x is object { public float r; *A4; }) { + if(x is object { public float r; *A4TN; }) { s1 = "values: " + x.p.toString() + ", " + x.q + ", " + x.r.toString(); } @@ -516,50 +516,50 @@ function testAnonymousObjectEquivalency() returns [string, string, string] { return [s1, s2, s3]; } -class Qux { - Qux? fn; +class QuxFoo { + QuxFoo? fn; - public function init(Qux? fn = ()) { + public function init(QuxFoo? fn = ()) { self.fn = fn; } } -class Quux { - Quux? fn = (); +class QuuxFoo { + QuuxFoo? fn = (); } -class Quuz { - Quuz? fn = (); +class QuuzFoo { + QuuzFoo? fn = (); int i = 1; } -class ABC { - Qux f; +class ABCFoo { + QuxFoo f; string s; - function init(Qux f, string s) { + function init(QuxFoo f, string s) { self.f = f; self.s = s; } } function testObjectIsCheckWithCycles() { - Qux f1 = new; - Qux f2 = new (f1); + QuxFoo f1 = new; + QuxFoo f2 = new (f1); - any a1 = f1; - test:assertTrue(a1 is Quux); - test:assertFalse(a1 is Quuz); + any a1 = f1; + test:assertTrue(a1 is QuuxFoo); + test:assertFalse(a1 is QuuzFoo); - any a2 = f2; - test:assertTrue(a2 is Quux); - test:assertFalse(a2 is Quuz); + any a2 = f2; + test:assertTrue(a2 is QuuxFoo); + test:assertFalse(a2 is QuuzFoo); - ABC ob = new (f2, "ballerina"); + ABCFoo ob = new (f2, "ballerina"); any a3 = ob; - test:assertTrue(a3 is object { Qux f; }); - test:assertFalse(a3 is object { Quuz f; }); + test:assertTrue(a3 is object {QuxFoo f;}); + test:assertFalse(a3 is object {QuuzFoo f;}); } service class ServiceClassA { @@ -625,11 +625,11 @@ function testSimpleArrays() returns [boolean, boolean, boolean, boolean, boolean } function testRecordArrays() returns [boolean, boolean, boolean, boolean] { - X[] a = [{}, {}]; - X[][] b = [[{}, {}], [{}, {}]]; + XTTE[] a = [{}, {}]; + XTTE[][] b = [[{}, {}], [{}, {}]]; any c = a; any d = b; - return [c is X[], d is X[][], c is Y[], d is Y[][]]; + return [c is XTTE[], d is XTTE[][], c is YTTE[], d is YTTE[][]]; } public function testUnionType() { @@ -742,20 +742,20 @@ function testSimpleTuples() returns [boolean, boolean, boolean, boolean, boolean } function testTupleWithAssignableTypes_1() returns [boolean, boolean, boolean, boolean] { - [X, Y] p = [{}, {}]; + [XTTE, YTTE] p = [{}, {}]; any q = p; - boolean b0 = q is [X, X]; - boolean b1 = q is [X, Y]; - boolean b2 = q is [Y, X]; - boolean b3 = q is [Y, Y]; + boolean b0 = q is [XTTE, XTTE]; + boolean b1 = q is [XTTE, YTTE]; + boolean b2 = q is [YTTE, XTTE]; + boolean b3 = q is [YTTE, YTTE]; return [b0, b1, b2, b3]; } function testTupleWithAssignableTypes_2() returns boolean { - [Y, Y] p = [{}, {}]; - [X, Y] q = p; - boolean b1 = q is [Y, Y]; - return q is [Y, Y]; + [YTTE, YTTE] p = [{}, {}]; + [XTTE, YTTE] q = p; + boolean b1 = q is [YTTE, YTTE]; + return q is [YTTE, YTTE]; } public function testRestType() { @@ -852,35 +852,35 @@ function testJsonArrays() returns [boolean, boolean, boolean] { // ========================== Finite type ========================== -type State "on"|"off"; +type StateTN "on"|"off"; function testFiniteType() returns [boolean, boolean, boolean] { - State a = "on"; + StateTN a = "on"; any b = a; any c = "off"; any d = "hello"; - return [b is State, c is State, d is State]; + return [b is StateTN, c is StateTN, d is StateTN]; } function testFiniteTypeInTuple() returns [boolean, boolean, boolean, boolean] { - [State, string] x = ["on", "off"]; + [StateTN, string] x = ["on", "off"]; any y = x; - boolean b0 = y is [State, State]; - boolean b1 = y is [State, string]; - boolean b2 = y is [string, State]; + boolean b0 = y is [StateTN, StateTN]; + boolean b1 = y is [StateTN, string]; + boolean b2 = y is [string, StateTN]; boolean b3 = y is [string, string]; return [b0, b1, b2, b3]; } -function testFiniteTypeInTuplePoisoning() returns [State, State] { - [State, string] x = ["on", "off"]; +function testFiniteTypeInTuplePoisoning() returns [StateTN, StateTN] { + [StateTN, string] x = ["on", "off"]; any y = x; - [State, State] z = ["on", "on"]; + [StateTN, StateTN] z = ["on", "on"]; - if (y is [State, State]) { + if (y is [StateTN, StateTN]) { z = y; } @@ -892,11 +892,11 @@ public const APPLE = "apple"; public const ORANGE = "orange"; public const GRAPE = "grape"; -type Fruit APPLE | ORANGE | GRAPE; +type FruitTN APPLE | ORANGE | GRAPE; function testFiniteType_1() returns string { any a = APPLE; - if (a is Fruit) { + if (a is FruitTN) { return "a is a fruit"; } @@ -1001,13 +1001,13 @@ function testIntersectingUnionFalse() returns [boolean, boolean] { function testValueTypeAsFiniteTypeTrue() returns [boolean, boolean] { string s = "orange"; float f = 2.0; - return [s is Fruit, f is IntTwo]; + return [s is FruitTN, f is IntTwo]; } function testValueTypeAsFiniteTypeFalse() returns [boolean, boolean] { string s = "mango"; float f = 12.0; - return [s is Fruit, f is IntTwo]; + return [s is FruitTN, f is IntTwo]; } const ERR_REASON = "error reason"; @@ -1213,12 +1213,12 @@ public function testXMLNeverType() { xml e = xml ``; test:assertEquals( e is byte, false); - test:assertEquals( e is xml<'xml:Element>, false); + test:assertEquals( e is xml<'xml:Element>, true); test:assertEquals( e is xml<'xml:Text>, true); test:assertEquals( e is xml, true); test:assertEquals( e is 'xml:Text, true); test:assertEquals( e is 'xml:Element, false); - test:assertEquals( e is xml<'xml:Element|'xml:Comment>, false); + test:assertEquals( e is xml<'xml:Element|'xml:Comment>, true); } function testXMLTextType(){ @@ -1428,7 +1428,7 @@ type MyClientObjectType client object { }; function testResourceMethodTyping() { - client object {} objectVar = client object { + object {} objectVar = client object { resource function post .() { } }; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/listconstructor/list_constructor_infer_type.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/listconstructor/list_constructor_infer_type.bal index 8a4be1b310b8..2001b8768bc0 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/listconstructor/list_constructor_infer_type.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/listconstructor/list_constructor_infer_type.bal @@ -14,12 +14,12 @@ // specific language governing permissions and limitations // under the License. -type Foo record { +type FooListInfer record { string s; int i = 1; }; -class Bar { +class BarListInfer { int i; public function init(int i) { @@ -27,8 +27,8 @@ class Bar { } } -Foo f = {s: "hello"}; -Bar b = new (1); +FooListInfer f = {s: "hello"}; +BarListInfer b = new (1); json j = 1; function inferSimpleTuple() { @@ -40,14 +40,14 @@ function inferSimpleTuple() { function inferStructuredTuple() { var x = [f, b, xml `text`, j]; typedesc ta = typeof x; - assertEquality("typedesc [Foo,Bar,lang.xml:Text,json]", ta.toString()); + assertEquality("typedesc [FooListInfer,BarListInfer,lang.xml:Text,json]", ta.toString()); } function inferNestedTuple() { int[2] arr = [1, 2]; var x = [1, 2.0d, [3, f, [b, b]], arr, j]; typedesc ta = typeof x; - assertEquality("typedesc [int,decimal,[int,Foo,[Bar,Bar]],int[2],json]", ta.toString()); + assertEquality("typedesc [int,decimal,[int,FooListInfer,[BarListInfer,BarListInfer]],int[2],json]", ta.toString()); } function testInferSameRecordsInTuple() { @@ -102,15 +102,15 @@ function testInferringForReadOnly() { error err = res; assertEquality("modification not allowed on readonly value", err.detail()["message"]); - Foo & readonly foo = { + FooListInfer & readonly foo = { s: "May", i: 20 }; readonly rd2 = [1, [b, false], foo, foo]; - assertEquality(true, rd2 is [int, [boolean, boolean], Foo, Foo & readonly] & readonly); - assertEquality(false, rd2 is [int, [boolean, boolean], object {} & readonly, Foo & readonly] & readonly); - [int, [boolean, boolean], Foo, Foo] arr2 = <[int, [boolean, boolean], Foo, Foo] & readonly> checkpanic rd2; + assertEquality(true, rd2 is [int, [boolean, boolean], FooListInfer, FooListInfer & readonly] & readonly); + assertEquality(false, rd2 is [int, [boolean, boolean], object {} & readonly, FooListInfer & readonly] & readonly); + [int, [boolean, boolean], FooListInfer, FooListInfer] arr2 = <[int, [boolean, boolean], FooListInfer, FooListInfer] & readonly> checkpanic rd2; fn = function() { arr2[0] = 2; @@ -123,17 +123,17 @@ function testInferringForReadOnly() { } function testInferringForReadOnlyInUnion() { - Foo & readonly foo = { + FooListInfer & readonly foo = { s: "May", i: 20 }; boolean b = true; - readonly|(Foo|int)[] rd = [1, [b, false], foo, foo]; + readonly|(FooListInfer|int)[] rd = [1, [b, false], foo, foo]; - assertEquality(true, rd is [int, [boolean, boolean], Foo, Foo & readonly] & readonly); - assertEquality(false, rd is [int, [boolean, boolean], object {} & readonly, Foo & readonly] & readonly); - [int, [boolean, boolean], Foo, Foo] arr = <[int, [boolean, boolean], Foo, Foo] & readonly> checkpanic rd; + assertEquality(true, rd is [int, [boolean, boolean], FooListInfer, FooListInfer & readonly] & readonly); + assertEquality(false, rd is [int, [boolean, boolean], object {} & readonly, FooListInfer & readonly] & readonly); + [int, [boolean, boolean], FooListInfer, FooListInfer] arr = <[int, [boolean, boolean], FooListInfer, FooListInfer] & readonly> checkpanic rd; var fn = function() { arr[0] = 2; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/mappingconstructor/spread_op_field.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/mappingconstructor/spread_op_field.bal index ad1e89a00c3c..e36531597120 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/mappingconstructor/spread_op_field.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/mappingconstructor/spread_op_field.bal @@ -298,6 +298,32 @@ function testSpreadFieldWithRecordTypeHavingRestDescriptor() { assertEquality("s", r4.s); } +type RetryConfig record {| + int count; + decimal interval; + float backOffFactor; +|}; + +public type RetryConfigClone record {| + int count = 0; + decimal interval = 0; + float backOffFactor = 0.0; + decimal maxWaitInterval = 0; + int[] statusCodes = []; +|}; + +type RetryTyperef RetryConfigClone; + +function testSpreadFieldWithRecordTypeReference() { + RetryConfig rc = {count: 3, interval: 0.5, backOffFactor: 0.5}; + RetryTyperef re = {...rc}; + assertEquality(3, re.count); + assertEquality(0.5d, re.interval); + assertEquality(0.5, re.backOffFactor); + assertEquality(0d, re.maxWaitInterval); + assertEquality(0, re.statusCodes.length()); +} + function assertEquality(any|error expected, any|error actual) { if expected is anydata && actual is anydata && expected == actual { return; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/typecast/type-casting.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/typecast/type-casting.bal index cb1f503e661f..eb260a6c4ec9 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/typecast/type-casting.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/typecast/type-casting.bal @@ -481,27 +481,27 @@ function testStringToJson(string s) returns (json) { return s; } -type Person record { +type PersonTC record { string name; int age; map address = {}; int[] marks = []; - Person | () parent = (); + PersonTC | () parent = (); json info = {}; anydata a = 0; float score = 0.0; boolean alive = true; }; -type Student record { +type StudentTC record { string name; int age; map address = {}; int[] marks = []; }; -function testStructToStruct() returns (Student) { - Person p = { name:"Supun", +function testStructToStruct() returns (StudentTC) { + PersonTC p = { name:"Supun", age:25, parent:{name:"Parent", age:50}, address:{"city":"Kandy", "country":"SriLanka"}, @@ -512,13 +512,13 @@ function testStructToStruct() returns (Student) { return p2; } -function testNullStructToStruct() returns Student { - Person? p = (); - return p; +function testNullStructToStruct() returns StudentTC { + PersonTC? p = (); + return p; } -function testStructAsAnyToStruct() returns Person|error { - Person p1 = { name:"Supun", +function testStructAsAnyToStruct() returns PersonTC|error { + PersonTC p1 = { name:"Supun", age:25, parent:{name:"Parent", age:50}, address:{"city":"Kandy", "country":"SriLanka"}, @@ -526,11 +526,11 @@ function testStructAsAnyToStruct() returns Person|error { marks:[24, 81] }; any a = p1; - var p2 = check trap a; + var p2 = check trap a; return p2; } -function testAnyToStruct() returns Person { +function testAnyToStruct() returns PersonTC { json address = {"city":"Kandy", "country":"SriLanka"}; map parent = {name:"Parent", age:50}; map info = {status:"single"}; @@ -543,18 +543,18 @@ function testAnyToStruct() returns Person { marks:marks }; any b = a; - var p2 = b; + var p2 = b; return p2; } -function testAnyNullToStruct() returns Person { +function testAnyNullToStruct() returns PersonTC { any a = (); - var p = a; + var p = a; return p; } function testRecordToAny() returns (any) { - Person p = { name:"Supun", + PersonTC p = { name:"Supun", age:25, parent:{name:"Parent", age:50}, address:{"city":"Kandy", "country":"SriLanka"}, @@ -601,7 +601,7 @@ function testBooleanInJsonToInt() { } } -type Address record { +type AddressTC record { string city; string country = ""; }; @@ -671,7 +671,7 @@ function testAnyMapToJson() returns json { } function testAnyStructToJson() returns json { - Address adrs = {city:"CA"}; + AddressTC adrs = {city:"CA"}; any a = adrs; json value; value = a; @@ -917,18 +917,18 @@ function testJSONValueCasting() returns [string|error, int|error, float|error, b } function testAnyToTable() { - table tb = table [ + table tb = table [ {id:1, name:"Jane"}, {id:2, name:"Anne"} ]; any anyValue = tb; - var casted = > anyValue; - table castedValue = casted; + var casted = > anyValue; + table castedValue = casted; assertEquality("[{\"id\":1,\"name\":\"Jane\"},{\"id\":2,\"name\":\"Anne\"}]", castedValue.toString()); } -type Employee record { +type EmployeeTC record { int id; string name; }; @@ -998,32 +998,31 @@ function testCastOfReadonlyUnionArrayToByteArray() { assertEquality("[1,2,3]", f.toString()); } -type Foo record {| +type FooTC record {| string s; int[] arr; |}; -type Bar record {| +type BarTC record {| string s; byte[] arr; |}; function testCastOfReadonlyRecord() { - (Foo & readonly) f = {s: "a", arr: [1,2,3]}; + (FooTC & readonly) f = {s: "a", arr: [1,2,3]}; any a = f; - Bar b = a; + BarTC b = a; assertEquality(true, b === a); assertEquality("{\"s\":\"a\",\"arr\":[1,2,3]}", b.toString()); } function testCastOfReadonlyRecordNegative() { - (Foo & readonly) f = {s: "a", arr: [1,2,300]}; + (FooTC & readonly) f = {s: "a", arr: [1,2,300]}; any a = f; - Bar|error b = trap a; + BarTC|error b = trap a; assertEquality(true, b is error); error err = b; - string errMsg = "incompatible types: '(Foo & readonly)' cannot be cast to 'Bar': " + - "\n\t\tfield 'arr' in record 'Bar' should be of type 'byte[]', found '[1,2,300]'"; + string errMsg = "incompatible types: '(FooTC & readonly)' cannot be cast to 'BarTC'"; assertEquality("{ballerina}TypeCastError", err.message()); assertEquality(errMsg, checkpanic err.detail()["message"]); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/typecast/type_cast_expr_runtime_errors.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/typecast/type_cast_expr_runtime_errors.bal new file mode 100644 index 000000000000..677ca81a319f --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/typecast/type_cast_expr_runtime_errors.bal @@ -0,0 +1,245 @@ + // Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). + // + // WSO2 LLC. licenses this file to you under the Apache License, + // Version 2.0 (the "License"); you may not use this file except + // in compliance with the License. + // You may obtain a copy of the License at + // + // http://www.apache.org/licenses/LICENSE-2.0 + // + // Unless required by applicable law or agreed to in writing, + // software distributed under the License is distributed on an + // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + // KIND, either express or implied. See the License for the + // specific language governing permissions and limitations + // under the License. + +function f1() { + xml A = xml `xml string`; + [string, int|xml, string...] C = ["text1", 1, A.toString()]; + json jsonTest = C; +} + +function testTupleToJSONCastRuntimeError() returns error? { + var e = trap f1(); + return e is error ? e : (); +} + +type Teacher record { + readonly string name; + readonly int age; + string school; +}; + +type Customer record { + readonly int id; + readonly string name; + string lname; +}; + +type CustomerTable table; + +CustomerTable tab3 = table key(name) [ + {id: 13, name: "Foo", lname: "QWER"}, + {id: 13, name: "Bar", lname: "UYOR"} +]; + +type Customer2 record {| + int id; + string name; + string lname; +|}; + +type CustomerEmptyKeyedTbl table key(); + +function f2() { + CustomerEmptyKeyedTbl tbl1 = tab3; +} + +function testCastingWithEmptyKeyedKeylessTbl() returns error? { + var e = trap f2(); + return e is error ? e : (); +} + +type Student record { + int index; + int age; +}; + +type Person record { + string name; + int age; + string address; +}; + +function f3() returns map { + map testPMap = {}; + map testSMap = >testPMap; + return testSMap; +} + +function testMapCastingRuntimeError() returns error? { + var e = trap f3(); + return e is error ? e : (); +} + +function f4() { + string[]|int val1 = []; + byte[] a = val1; +} + +function testListCastingRuntimeError() returns error? { + var e = trap f4(); + return e is error ? e : (); +} + +public class person01 { + + public int age = 0; + public string name = ""; + public string address = ""; + +} + +public class employee01 { + + public int age = 0; + public string name = ""; + public string zipcode = "95134"; + + function init (int age, string name) { + self.age = age; + self.name = name; + } +} + +function f5() returns string { + employee01 e = new (14, "rat"); + person01 p = e; + return p.name; +} + +function testCastingObjects() returns error? { + var e = trap f5(); + return e is error ? e : (); +} + +public class employee08 { + + public int age = 0; + public string name = ""; + public string address = ""; + public string zipcode = "95134"; + public string ssn = ""; + + + function init (int age, string name) { + self.age = age; + self.name = name; + } + + public function getName() returns string { + return self.name; + } + + public function getAge() returns int { + return self.age; + } + + public function getSSN() returns string { + return self.ssn; + } +} + +public class person08 { + + public int age = 0; + public string name = ""; + public string address = ""; + public string zipcode = "95134"; + public string ssn = ""; + + + public function getAge() returns int { + return self.age; + } + + public function getName() returns string { + return self.name; + } + + public function setSSN(string s) { + self.ssn = s; + } +} + +function f6() returns string { + employee08 e = new (14, "rat"); + person08 p = e; + return p.name; +} + +function testCastingObjects2() returns error? { + var e = trap f6(); + return e is error ? e : (); +} + +public class person09 { + + public int age = 0; + public string name = ""; + public string address = ""; + public string zipcode = "95134"; + public string ssn = ""; + + + public function getAge() returns int { + return self.age; + } + + public function getName() returns string { + return self.name; + } + + public function setSSN(string s) { + self.ssn = s; + } +} + +public class employee09 { + + public int age = 0; + public string name = ""; + public string address = ""; + public string zipcode = "95134"; + public string ssn = ""; + + + function init (int age, string name) { + self.age = age; + self.name = name; + } + + public function getName() returns string { + return self.name; + } + + public function getAge(int i) returns int { + return self.age; + } + + public function getSSN() returns string { + return self.ssn; + } +} + +function f7() returns string { + employee09 e = new (14, "rat"); + person09 p = e; + return p.name; +} + +function testCastingObjects3() returns error? { + var e = trap f7(); + return e is error ? e : (); +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/dependently_typed_functions_bir_test.bal b/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/dependently_typed_functions_bir_test.bal index 7c9f597e4e39..28bd92404743 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/dependently_typed_functions_bir_test.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/dependently_typed_functions_bir_test.bal @@ -16,18 +16,17 @@ import testorg/foo.dependently_typed as rt; -type Person record { +type PersonDTBT record { readonly string name; int age; }; -type Employee record { - *Person; +type EmployeeDTBT record { + *PersonDTBT; string designation; }; -Person expPerson = {name: "John Doe", age: 20}; - +PersonDTBT expPerson = {name: "John Doe", age: 20}; // Test functions @@ -49,11 +48,11 @@ function testSimpleTypes() { } function testRecordVarRef() { - Person p = rt:getRecord(); + PersonDTBT p = rt:getRecord(); assert(expPerson, p); - Employee e = rt:getRecord(td = Employee); - assert({name: "Jane Doe", age: 25, designation: "Software Engineer"}, e); + EmployeeDTBT e = rt:getRecord(td = EmployeeDTBT); + assert({name: "Jane Doe", age: 25, designation: "Software Engineer"}, e); } function testVarRefInMapConstraint() { @@ -69,12 +68,12 @@ function testRuntimeCastError() { } function testVarRefUseInMultiplePlaces() { - [int, Person, float] tup1 = rt:getTuple(int, Person); - assert(<[int, Person, float]>[150, expPerson, 12.34], tup1); + [int, PersonDTBT, float] tup1 = rt:getTuple(int, PersonDTBT); + assert(<[int, PersonDTBT, float]>[150, expPerson, 12.34], tup1); } function testUnionTypes() { - int|Person u = rt:getVariedUnion(1, int, Person); + int|PersonDTBT u = rt:getVariedUnion(1, int, PersonDTBT); assert(expPerson, u); } @@ -84,7 +83,7 @@ function testArrayTypes() { } function testCastingForInvalidValues() { - int _ = rt:getInvalidValue(int, Person); + int _ = rt:getInvalidValue(int, PersonDTBT); } type XmlElement xml:Element; @@ -101,19 +100,21 @@ function testStream() { stream newSt = rt:getStream(st, string); string s = ""; - newSt.forEach(function (string x) { s += x; }); + newSt.forEach(function(string x) { + s += x; + }); assert("helloworldfromballerina", s); } function testTable() { - table key(name) tab = table [ - { name: "Chiran", age: 33, designation: "SE" }, - { name: "Mohan", age: 37, designation: "SE" }, - { name: "Gima", age: 38, designation: "SE" }, - { name: "Granier", age: 34, designation: "SE" } + table key(name) tab = table [ + {name: "Chiran", age: 33, designation: "SE"}, + {name: "Mohan", age: 37, designation: "SE"}, + {name: "Gima", age: 38, designation: "SE"}, + {name: "Granier", age: 34, designation: "SE"} ]; - table newTab = rt:getTable(tab, Person); + table newTab = rt:getTable(tab, PersonDTBT); assert(tab, newTab); } @@ -125,12 +126,12 @@ function testFunctionPointers() { } function testTypedesc() { - typedesc tP = rt:getTypedesc(Person); - assert(Person.toString(), tP.toString()); + typedesc tP = rt:getTypedesc(PersonDTBT); + assert(PersonDTBT.toString(), tP.toString()); } function testFuture() { - var fn = function (string name) returns string => "Hello " + name; + var fn = function(string name) returns string => "Hello " + name; future f = start fn("Pubudu"); future fNew = rt:getFuture(f, string); string res = checkpanic wait fNew; @@ -150,9 +151,12 @@ class PersonObj { function name() returns string => self.fname + " " + self.lname; } -type PersonTable table; +type PersonTable table; + type IntStream stream; + type IntArray int[]; + type XmlType xml; function testComplexTypes() { @@ -165,7 +169,7 @@ function testComplexTypes() { int[] ar = rt:echo([20, 30, 40], IntArray); assert([20, 30, 40], ar); - PersonObj pObj = new("John", "Doe"); + PersonObj pObj = new ("John", "Doe"); PersonObj nObj = rt:echo(pObj, PersonObj); assertSame(pObj, nObj); @@ -174,17 +178,19 @@ function testComplexTypes() { stream newSt = rt:echo(st, IntStream); int tot = 0; - newSt.forEach(function (int x1) { tot+= x1; }); + newSt.forEach(function(int x1) { + tot += x1; + }); assert(150, tot); - table key(name) tab = table [ - { name: "Chiran", age: 33}, - { name: "Mohan", age: 37}, - { name: "Gima", age: 38}, - { name: "Granier", age: 34} + table key(name) tab = table [ + {name: "Chiran", age: 33}, + {name: "Mohan", age: 37}, + {name: "Gima", age: 38}, + {name: "Granier", age: 34} ]; - table newTab = rt:echo(tab, PersonTable); + table newTab = rt:echo(tab, PersonTable); assert(tab, newTab); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/dependently_typed_functions_test.bal b/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/dependently_typed_functions_test.bal index 49817c076771..bb681bdbc8c4 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/dependently_typed_functions_test.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/dependently_typed_functions_test.bal @@ -17,23 +17,24 @@ import ballerina/jballerina.java; type ItemType xml:Element|xml:Comment|xml:ProcessingInstruction|xml:Text; + type XmlElement xml:Element; -type Person record { +type PersonDTFT record { readonly string name; int age; }; -type Employee record { - *Person; +type EmployeeDTFT record { + *PersonDTFT; string designation; }; -Person expPerson = {name: "John Doe", age: 20}; +PersonDTFT expPerson = {name: "John Doe", age: 20}; -type Foo int|float; +type FooDTFT int|float; -type Foo2 Foo; +type Foo2DTFT FooDTFT; type AnnotationRecord record {| int minValue; @@ -52,7 +53,7 @@ type Foo3 int|float; minValue: 12, maxValue: 24 } -type Foo4 Foo; +type Foo4 FooDTFT; // Test functions @@ -74,31 +75,31 @@ function testSimpleTypes() { } function testReferredTypes() { - map annotationMapValue = getAnnotationValue(Foo); - AnnotationRecord? 'annotation = annotationMapValue["BarAnnotation"]; + map annotationMapValue = getAnnotationValue(FooDTFT); + AnnotationRecord? 'annotation = annotationMapValue["BarAnnotation"]; assert('annotation, ()); - map annotationMapValue2 = getAnnotationValue(Foo2); - AnnotationRecord? annotation2 = annotationMapValue2["BarAnnotation"]; + map annotationMapValue2 = getAnnotationValue(Foo2DTFT); + AnnotationRecord? annotation2 = annotationMapValue2["BarAnnotation"]; assert(annotation2, ()); map annotationMapValue3 = getAnnotationValue(Foo3); - AnnotationRecord annotation3 = annotationMapValue3["BarAnnotation"]; + AnnotationRecord annotation3 = annotationMapValue3["BarAnnotation"]; assert(annotation3, {minValue: 18, maxValue: 36}); assertTrue(getAnnotationValue2(1, Foo3, "BarAnnotation", 18, 36) is anydata); map annotationMapValue4 = getAnnotationValue(Foo4); - AnnotationRecord annotation4 = annotationMapValue4["BarAnnotation"]; - assert(annotation4, {minValue: 12, maxValue: 24}); + AnnotationRecord annotation4 = annotationMapValue4["BarAnnotation"]; + assert(annotation4, {minValue: 12, maxValue: 24}); assertTrue(getAnnotationValue2(1, Foo4, "BarAnnotation", 12, 24) is anydata); } function testRecordVarRef() { - Person p = getRecord(); + PersonDTFT p = getRecord(); assert(expPerson, p); - Employee e = getRecord(td = Employee); - assert({name: "Jane Doe", age: 25, designation: "Software Engineer"}, e); + EmployeeDTFT e = getRecord(td = EmployeeDTFT); + assert({name: "Jane Doe", age: 25, designation: "Software Engineer"}, e); } function testVarRefInMapConstraint() { @@ -114,12 +115,12 @@ function testRuntimeCastError() { } function testVarRefUseInMultiplePlaces() { - [int, Person, float] tup1 = getTuple(int, Person); - assert(<[int, Person, float]>[150, expPerson, 12.34], tup1); + [int, PersonDTFT, float] tup1 = getTuple(int, PersonDTFT); + assert(<[int, PersonDTFT, float]>[150, expPerson, 12.34], tup1); } function testUnionTypes() { - int|Person u = getVariedUnion(1, int, Person); + int|PersonDTFT u = getVariedUnion(1, int, PersonDTFT); assert(expPerson, u); } @@ -129,7 +130,7 @@ function testArrayTypes() { } function testCastingForInvalidValues() { - int x = getInvalidValue(int, Person); + int x = getInvalidValue(int, PersonDTFT); } function testXML() { @@ -144,19 +145,21 @@ function testStream() { stream newSt = getStream(st, string); string s = ""; - error? err = newSt.forEach(function (string x) { s += x; }); + error? err = newSt.forEach(function(string x) { + s += x; + }); assert("helloworldfromballerina", s); } function testTable() { - table key(name) tab = table [ - { name: "Chiran", age: 33, designation: "SE" }, - { name: "Mohan", age: 37, designation: "SE" }, - { name: "Gima", age: 38, designation: "SE" }, - { name: "Granier", age: 34, designation: "SE" } + table key(name) tab = table [ + {name: "Chiran", age: 33, designation: "SE"}, + {name: "Mohan", age: 37, designation: "SE"}, + {name: "Gima", age: 38, designation: "SE"}, + {name: "Granier", age: 34, designation: "SE"} ]; - table newTab = getTable(tab, Person); + table newTab = getTable(tab, PersonDTFT); assert(tab, newTab); } @@ -168,12 +171,12 @@ function testFunctionPointers() { } function testTypedesc() { - typedesc tP = getTypedesc(Person); - assert(Person.toString(), tP.toString()); + typedesc tP = getTypedesc(PersonDTFT); + assert(PersonDTFT.toString(), tP.toString()); } function testFuture() { - var fn = function (string name) returns string => "Hello " + name; + var fn = function(string name) returns string => "Hello " + name; future f = start fn("Pubudu"); future fNew = getFuture(f, string); string res = checkpanic wait fNew; @@ -211,7 +214,7 @@ class PersonObj { type IntStream stream; -type PersonTable table; +type PersonTable table; type IntArray int[]; @@ -227,7 +230,7 @@ function testComplexTypes() { int[] ar = echo([20, 30, 40], IntArray); assert([20, 30, 40], ar); - PersonObj pObj = new("John", "Doe"); + PersonObj pObj = new ("John", "Doe"); PersonObj nObj = echo(pObj, PersonObj); assertSame(pObj, nObj); @@ -236,22 +239,24 @@ function testComplexTypes() { stream newSt = echo(st, IntStream); int tot = 0; - error? err = newSt.forEach(function (int x1) { tot+= x1; }); + error? err = newSt.forEach(function(int x1) { + tot += x1; + }); assert(150, tot); - table key(name) tab = table [ - { name: "Chiran", age: 33}, - { name: "Mohan", age: 37}, - { name: "Gima", age: 38}, - { name: "Granier", age: 34} + table key(name) tab = table [ + {name: "Chiran", age: 33}, + {name: "Mohan", age: 37}, + {name: "Gima", age: 38}, + {name: "Granier", age: 34} ]; - table newTab = echo(tab, PersonTable); + table newTab = echo(tab, PersonTable); assert(tab, newTab); } function testObjectExternFunctions() { - PersonObj pObj = new("John", "Doe"); + PersonObj pObj = new ("John", "Doe"); string s = pObj.getObjectValue(string); assert("John Doe", s); s = pObj.getObjectValueWithTypeDescParam(string); @@ -275,7 +280,6 @@ function testFunctionAssignment() { x = fn(string); } - // Interop functions function getValue(typedesc td) returns td = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", @@ -283,20 +287,20 @@ function getValue(typedesc td) returns td = @j paramTypes: ["io.ballerina.runtime.api.values.BTypedesc"] } external; -function getAnnotationValue(typedesc y) returns map = +function getAnnotationValue(typedesc y) returns map = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "getAnnotationValue" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "getAnnotationValue" +} external; function getAnnotationValue2(anydata value, typedesc td = <>, string annotationName = "", - int min = 0, int max = 0) returns td|error = + int min = 0, int max = 0) returns td|error = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "getAnnotationValue2" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "getAnnotationValue2" +} external; -function getRecord(typedesc td = Person) returns td = @java:Method { +function getRecord(typedesc td = PersonDTFT) returns td = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", name: "getRecord", paramTypes: ["io.ballerina.runtime.api.values.BTypedesc"] @@ -314,7 +318,7 @@ function getTuple(typedesc td1, typedesc td2, typedesc td1, typedesc td2) returns (td1|td2) = @java:Method { +function getVariedUnion(int x, typedesc td1, typedesc td2) returns (td1|td2) = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", name: "getVariedUnion", paramTypes: ["long", "io.ballerina.runtime.api.values.BTypedesc", "io.ballerina.runtime.api.values.BTypedesc"] @@ -332,7 +336,7 @@ function getArrayInferred(typedesc td = <>) returns td[] = @java:Method paramTypes: ["io.ballerina.runtime.api.values.BTypedesc"] } external; -function getInvalidValue(typedesc td1, typedesc td2) returns td1 = @java:Method { +function getInvalidValue(typedesc td1, typedesc td2) returns td1 = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", name: "getInvalidValue", paramTypes: ["io.ballerina.runtime.api.values.BTypedesc", "io.ballerina.runtime.api.values.BTypedesc"] @@ -360,8 +364,11 @@ function getFunction(function (string|int) returns anydata fn, typedesc returns function (param) returns ret = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", name: "getFunction", - paramTypes: ["io.ballerina.runtime.api.values.BFunctionPointer", "io.ballerina.runtime.api.values.BTypedesc", - "io.ballerina.runtime.api.values.BTypedesc"] + paramTypes: [ + "io.ballerina.runtime.api.values.BFunctionPointer", + "io.ballerina.runtime.api.values.BTypedesc", + "io.ballerina.runtime.api.values.BTypedesc" + ] } external; function getTypedesc(typedesc td) returns typedesc = @java:Method { @@ -425,13 +432,13 @@ var outParameterObject = object OutParameter { function testDependentlyTypedMethodsWithObjectTypeInclusion() { OutParameterClass c1 = new; int|error v1 = c1.get(int); - assert(1234, checkpanic v1); + assert(1234, checkpanic v1); assertSame(c1.c, c1.get(float)); - assert("hello world", checkpanic c1.get(string)); + assert("hello world", checkpanic c1.get(string)); - assert(321, checkpanic outParameterObject.get(int)); + assert(321, checkpanic outParameterObject.get(int)); decimal|error v2 = outParameterObject.get(decimal); - assert(23.45d, checkpanic v2); + assert(23.45d, checkpanic v2); } public class Bar { @@ -509,49 +516,49 @@ public function testSubtypingWithDependentlyTypedMethods() { Baz baz = new; Qux qux = new; - assert(1, checkpanic bar.get(int)); - assert(2, checkpanic baz.get(int)); - assert(3, checkpanic qux.get(int)); + assert(1, checkpanic bar.get(int)); + assert(2, checkpanic baz.get(int)); + assert(3, checkpanic qux.get(int)); decimal|error v2 = bar.get(decimal); - assert(23.45d, checkpanic v2); + assert(23.45d, checkpanic v2); anydata|error v3 = baz.get(decimal); - assert(23.45d, checkpanic v3); + assert(23.45d, checkpanic v3); v2 = qux.get(decimal); - assert(23.45d, checkpanic v2); + assert(23.45d, checkpanic v2); Baz baz1 = bar; Bar bar1 = qux; - assert(1, checkpanic baz1.get(int)); - assert(3, checkpanic bar1.get(int)); + assert(1, checkpanic baz1.get(int)); + assert(3, checkpanic bar1.get(int)); - assert(true, bar is Baz); - assert(true, qux is Bar); - assert(true, bar is Qux); - assert(false, baz is Bar); - assert(false, new Quux() is Qux); - assert(false, qux is Quux); + assert(true, bar is Baz); + assert(true, qux is Bar); + assert(true, bar is Qux); + assert(false, baz is Bar); + assert(false, new Quux() is Qux); + assert(false, qux is Quux); Corge corge = new Grault(); - assert(200, checkpanic corge.get(int, string)); - assert("Hello World!", checkpanic corge.get(string, int)); + assert(200, checkpanic corge.get(int, string)); + assert("Hello World!", checkpanic corge.get(string, int)); Grault grault = new Corge(); - assert(100, checkpanic grault.get(int, string)); - assert("Hello World!", checkpanic grault.get(string, float)); + assert(100, checkpanic grault.get(int, string)); + assert("Hello World!", checkpanic grault.get(string, float)); - assert(true, new Corge() is Grault); - assert(true, new Grault() is Corge); - assert(false, new Corge() is Garply); - assert(false, new Garply() is Corge); - assert(false, new Grault() is Garply); - assert(false, new Garply() is Grault); + assert(true, new Corge() is Grault); + assert(true, new Grault() is Corge); + assert(true, new Corge() is Garply); + assert(true, new Garply() is Corge); + assert(true, new Grault() is Garply); + assert(true, new Garply() is Grault); } function getWithDefaultableParams(int|string x, int|string y = 1, typedesc z = int) returns z = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "getWithDefaultableParams" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "getWithDefaultableParams" +} external; function testDependentlyTypedFunctionWithDefaultableParams() { int a = getWithDefaultableParams(1); @@ -584,12 +591,12 @@ type IntOrString int|string; public function testStartActionWithDependentlyTypedFunctions() { Client cl = new; - var assert1 = function (future f) { + var assert1 = function(future f) { int|string|error r = wait f; assert(true, r is error); - error e = r; + error e = r; assert("Error!", e.message()); - assert("Union typedesc", checkpanic e.detail()["message"]); + assert("Union typedesc", checkpanic e.detail()["message"]); }; future a = start getWithUnion("", IntOrString); assert1(a); @@ -598,7 +605,7 @@ public function testStartActionWithDependentlyTypedFunctions() { future c = start cl->remoteGet("", IntOrString); assert1(c); - var assert2 = function (future f, int expected) { + var assert2 = function(future f, int expected) { int|error r = wait f; assert(true, r is int); assert(expected, checkpanic r); @@ -612,7 +619,7 @@ public function testStartActionWithDependentlyTypedFunctions() { future g = start cl->remoteGet("hi", int); assert2(g, 2); - var assert3 = function (future f, string expected) { + var assert3 = function(future f, string expected) { string|error r = wait f; assert(true, r is string); assert(expected, checkpanic r); @@ -627,9 +634,9 @@ public function testStartActionWithDependentlyTypedFunctions() { function getWithUnion(int|string x, typedesc y) returns y|error = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "getWithUnion" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "getWithUnion" +} external; client class Client { function get(int|string x, typedesc y = int) returns y|error = @java:Method { @@ -644,12 +651,12 @@ client class Client { } function getWithRestParam(int i, typedesc j, int... k) returns j|boolean = - @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType" - } external; + @java:Method { + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType" +} external; function getWithMultipleTypedescs(int i, typedesc j, typedesc k, typedesc... l) - returns j|k|boolean = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType" } external; + returns j|k|boolean = @java:Method {'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType"} external; function testArgsForDependentlyTypedFunctionViaTupleRestArg() { [typedesc] a = [string]; @@ -728,7 +735,7 @@ type IJK record {| |}; function testArgsForDependentlyTypedFunctionViaRecordRestArg() { - record {| typedesc y; |} a = {y: string}; + record {|typedesc y;|} a = {y: string}; string|error b = getWithUnion(123, ...a); assert("123", checkpanic b); @@ -740,7 +747,7 @@ function testArgsForDependentlyTypedFunctionViaRecordRestArg() { string|boolean f = getWithRestParam(...e); assert(true, f); - record {| typedesc j = string; |} g = {}; + record {|typedesc j = string;|} g = {}; string|boolean h = getWithRestParam(1, ...g); assert(false, h); @@ -748,11 +755,11 @@ function testArgsForDependentlyTypedFunctionViaRecordRestArg() { int|string|boolean n = getWithMultipleTypedescs(...m); assert(true, n); - record {| typedesc j = string; typedesc k; |} o = {k: int}; + record {|typedesc j = string; typedesc k;|} o = {k: int}; int|string|boolean p = getWithMultipleTypedescs(1, ...o); assert(true, p); - record {| int i; typedesc j = byte; typedesc k; |} q = {i: 1, k: byte}; + record {|int i; typedesc j = byte; typedesc k;|} q = {i: 1, k: byte}; byte|boolean r = getWithMultipleTypedescs(...q); assert(true, r); } @@ -767,29 +774,29 @@ public type TargetType typedesc; public client class ClientWithMethodWithIncludedRecordParamAndDefaultableParams { remote function post(TargetType targetType = int, *ClientActionOptions options) returns @tainted targetType = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "clientPost" - } external; - + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "clientPost" + } external; + function calculate(int i, TargetType targetType = int, *ClientActionOptions options) returns @tainted targetType = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "calculate" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "calculate" + } external; } public client class ClientWithMethodWithIncludedRecordParamAndRequiredParams { remote function post(TargetType targetType, *ClientActionOptions options) returns @tainted targetType = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "clientPost" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "clientPost" + } external; function calculate(int i, TargetType targetType, *ClientActionOptions options) returns @tainted targetType|error = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "calculate" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "calculate" + } external; } function testDependentlyTypedFunctionWithIncludedRecordParam() { @@ -885,57 +892,58 @@ function testDependentlyTypedFunctionWithIncludedRecordParam() { client class ClientObjImpl { *ClientObject; - remote isolated function query(stream strm, typedesc rowType = <>) returns stream = + + remote isolated function query(stream strm, typedesc rowType = <>) returns stream = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "getStreamOfRecords", - paramTypes: ["io.ballerina.runtime.api.values.BStream", "io.ballerina.runtime.api.values.BTypedesc"] - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "getStreamOfRecords", + paramTypes: ["io.ballerina.runtime.api.values.BStream", "io.ballerina.runtime.api.values.BTypedesc"] + } external; } public type ClientObject client object { - remote isolated function query(stream strm, typedesc rowType = <>) returns stream ; + remote isolated function query(stream strm, typedesc rowType = <>) returns stream; }; function testDependentlyTypedMethodCallOnObjectType() { ClientObject cl = new ClientObjImpl(); - Person p1 = getRecord(); - Person p2 = getRecord(); - Person[] personList = [p1, p2]; - stream personStream = personList.toStream(); - stream y = cl->query(personStream, Person); + PersonDTFT p1 = getRecord(); + PersonDTFT p2 = getRecord(); + PersonDTFT[] personList = [p1, p2]; + stream personStream = personList.toStream(); + stream y = cl->query(personStream, PersonDTFT); var rec = y.next(); - if (rec is record {| Person value; |}) { - Person person = rec.value; + if (rec is record {|PersonDTFT value;|}) { + PersonDTFT person = rec.value; assert(20, person.age); assert("John Doe", person.name); } rec = y.next(); - assert(true, rec is record {| Person value; |}); + assert(true, rec is record {|PersonDTFT value;|}); } function testDependentlyTypedMethodCallOnObjectTypeWithInferredArgument() { ClientObject cl = new ClientObjImpl(); - Person p1 = getRecord(); - Person p2 = getRecord(); - Person[] personList = [p1, p2]; - stream personStream = personList.toStream(); - stream y = cl->query(personStream); + PersonDTFT p1 = getRecord(); + PersonDTFT p2 = getRecord(); + PersonDTFT[] personList = [p1, p2]; + stream personStream = personList.toStream(); + stream y = cl->query(personStream); var rec = y.next(); - if (rec is record {| Person value; |}) { - Person person = rec.value; + if (rec is record {|PersonDTFT value;|}) { + PersonDTFT person = rec.value; assert(20, person.age); assert("John Doe", person.name); } rec = y.next(); - assert(true, rec is record {| Person value; |}); + assert(true, rec is record {|PersonDTFT value;|}); } function functionWithInferredArgForParamOfTypeReferenceType(TargetType t = <>) returns t = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "functionWithInferredArgForParamOfTypeReferenceType" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "functionWithInferredArgForParamOfTypeReferenceType" +} external; function testDependentlyTypedFunctionWithInferredArgForParamOfTypeReferenceType() { int a = functionWithInferredArgForParamOfTypeReferenceType(); @@ -963,18 +971,18 @@ function testDependentlyTypedResourceMethods() { ClientWithExternalResourceBody cl = new ClientWithExternalResourceBody(); string|error a = cl->/games/carrom(targetType = string); assert("[\"games\",\"carrom\"]", checkpanic a); - + var cl2 = client object { resource function get [string... path](TargetType targetType = <>) returns targetType|error = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "getResource" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "getResource" + } external; }; string|error b = cl2->/games/football(targetType = string); assert("[\"games\",\"football\"]", checkpanic b); - + int|error c = cl2->/games/football(); assert(0, checkpanic c); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/inferred_dependently_typed_func_signature.bal b/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/inferred_dependently_typed_func_signature.bal index d1e9afa45b5e..06beac3920e1 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/inferred_dependently_typed_func_signature.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/inferred_dependently_typed_func_signature.bal @@ -16,18 +16,17 @@ import ballerina/jballerina.java; -type Person record { +type PersonIDTF record { readonly string name; int age; }; -type Employee record { - *Person; +type EmployeeIDTF record { + *PersonIDTF; string designation; }; -Person expPerson = {name: "John Doe", age: 20}; - +PersonIDTF expPerson = {name: "John Doe", age: 20}; // Test functions @@ -49,11 +48,11 @@ function testSimpleTypes() { } function testRecordVarRef() { - Person p = getRecord(); + PersonIDTF p = getRecord(); assert(expPerson, p); - Employee e = getRecord(td = Employee); - assert({name: "Jane Doe", age: 25, designation: "Software Engineer"}, e); + EmployeeIDTF e = getRecord(td = EmployeeIDTF); + assert({name: "Jane Doe", age: 25, designation: "Software Engineer"}, e); } function testVarRefInMapConstraint() { @@ -69,15 +68,15 @@ function testRuntimeCastError() { error err = m1; assert("{ballerina}TypeCastError", err.message()); - assert("incompatible types: 'map' cannot be cast to 'map'", checkpanic err.detail()["message"]); + assert("incompatible types: 'map' cannot be cast to 'map'", checkpanic err.detail()["message"]); } function testTupleTypes() { - [int, Person, float] tup1 = getTuple(int, Person); - assert(<[int, Person, float]>[150, expPerson, 12.34], tup1); + [int, PersonIDTF, float] tup1 = getTuple(int, PersonIDTF); + assert(<[int, PersonIDTF, float]>[150, expPerson, 12.34], tup1); - [int, Person, boolean...] tup2 = getTupleWithRestDesc(int, Person); - assert(<[int, Person, boolean...]>[150, expPerson, true, true], tup2); + [int, PersonIDTF, boolean...] tup2 = getTupleWithRestDesc(int, PersonIDTF); + assert(<[int, PersonIDTF, boolean...]>[150, expPerson, true, true], tup2); tup2[4] = false; assert(5, tup2.length()); } @@ -88,11 +87,12 @@ function testArrayTypes() { } type XmlComment xml:Comment; + type XmlElement xml:Element; function testXml() { xml:Comment x1 = xml ``; - xml x2 = > x1.concat(xml ``); + xml x2 = >x1.concat(xml ``); xml a = getXml(val = x2); assert(x2, a); assert(2, a.length()); @@ -128,14 +128,14 @@ function testXml() { function testCastingForInvalidValues() { var fn = function() { - int x = getInvalidValue(td2 = Person); + int x = getInvalidValue(td2 = PersonIDTF); }; error? y = trap fn(); assert(true, y is error); - error err = y; + error err = y; assert("{ballerina}TypeCastError", err.message()); - assert("incompatible types: 'Person' cannot be cast to 'int'", checkpanic err.detail()["message"]); + assert("incompatible types: 'PersonIDTF' cannot be cast to 'int'", checkpanic err.detail()["message"]); } function testStream() { @@ -144,19 +144,21 @@ function testStream() { stream newSt = getStream(st); string s = ""; - error? err = newSt.forEach(function (string x) { s += x; }); + error? err = newSt.forEach(function(string x) { + s += x; + }); assert("helloworldfromballerina", s); } function testTable() { - table key(name) tab = table [ - { name: "Chiran", age: 33, designation: "SE" }, - { name: "Mohan", age: 37, designation: "SE" }, - { name: "Gima", age: 38, designation: "SE" }, - { name: "Granier", age: 34, designation: "SE" } + table key(name) tab = table [ + {name: "Chiran", age: 33, designation: "SE"}, + {name: "Mohan", age: 37, designation: "SE"}, + {name: "Gima", age: 38, designation: "SE"}, + {name: "Granier", age: 34, designation: "SE"} ]; - table newTab = getTable(tab); + table newTab = getTable(tab); assert(tab, newTab); } @@ -168,12 +170,12 @@ function testFunctionPointers() { } function testTypedesc() { - typedesc tP = getTypedesc(); - assert(Person.toString(), tP.toString()); + typedesc tP = getTypedesc(); + assert(PersonIDTF.toString(), tP.toString()); } function testFuture() { - var fn = function (string name) returns string => "Hello " + name; + var fn = function(string name) returns string => "Hello " + name; future f = start fn("Pubudu"); future fNew = getFuture(f, string); string res = checkpanic wait fNew; @@ -195,7 +197,7 @@ class PersonObj { type IntStream stream; -type PersonTable table; +type PersonTable table; type IntArray int[]; @@ -209,7 +211,7 @@ function testComplexTypes() { int[] ar = echo([20, 30, 40]); assert([20, 30, 40], ar); - PersonObj pObj = new("John", "Doe"); + PersonObj pObj = new ("John", "Doe"); PersonObj nObj = echo(pObj); assertSame(pObj, nObj); @@ -218,17 +220,19 @@ function testComplexTypes() { stream newSt = echo(st); int tot = 0; - error? err = newSt.forEach(function (int x1) { tot+= x1; }); + error? err = newSt.forEach(function(int x1) { + tot += x1; + }); assert(150, tot); - table key(name) tab = table [ - { name: "Chiran", age: 33}, - { name: "Mohan", age: 37}, - { name: "Gima", age: 38}, - { name: "Granier", age: 34} + table key(name) tab = table [ + {name: "Chiran", age: 33}, + {name: "Mohan", age: 37}, + {name: "Gima", age: 38}, + {name: "Granier", age: 34} ]; - table newTab = echo(tab); + table newTab = echo(tab); assert(tab, newTab); } @@ -244,7 +248,7 @@ function testFunctionAssignment() { error err = v; assert("{ballerina}TypeCastError", err.message()); - assert("incompatible types: 'string' cannot be cast to 'int'", checkpanic err.detail()["message"]); + assert("incompatible types: 'string' cannot be cast to 'int'", checkpanic err.detail()["message"]); } function testUnionTypes() { @@ -261,10 +265,10 @@ function testUnionTypes() { assert("hello", d); int[]|map? e = getComplexUnion(); - assert( [1, 2], e); + assert([1, 2], e); map<[int, string][]>|()|[int, string][] f = getComplexUnion(); - assert(> {entry: [[100, "Hello World"]]}, f); + assert(>{entry: [[100, "Hello World"]]}, f); int|boolean? g = getSimpleUnion(1, int); assert(1, g); @@ -279,47 +283,47 @@ function testUnionTypes() { assert("hello", j); int[]|map? k = getComplexUnion(int); - assert( [1, 2], k); + assert([1, 2], k); map<[int, string][]>|()|[int, string][] l = getComplexUnion(td = [int, string]); - assert(> {entry: [[100, "Hello World"]]}, l); + assert(>{entry: [[100, "Hello World"]]}, l); } function testArgCombinations() { int[] a = funcWithMultipleArgs(1, int, ["hello", "world"]); - assert( [2, 1], a); + assert([2, 1], a); string[] b = funcWithMultipleArgs(td = string, arr = ["hello", "world"], i = 3); - assert( ["hello", "world", "3"], b); + assert(["hello", "world", "3"], b); - record {| string[] arr = ["hello", "world", "Ballerina"]; int i = 123; typedesc td; |} rec1 = {td: int}; + record {|string[] arr = ["hello", "world", "Ballerina"]; int i = 123; typedesc td;|} rec1 = {td: int}; int[] c = funcWithMultipleArgs(...rec1); - assert( [3, 123], c); + assert([3, 123], c); - record {| string[] arr = ["hello", "world"]; |} rec2 = {}; + record {|string[] arr = ["hello", "world"];|} rec2 = {}; int[] d = funcWithMultipleArgs(1234, int, ...rec2); - assert( [2, 1234], d); + assert([2, 1234], d); [int, typedesc, string[]] tup1 = [21, string, ["hello"]]; string[] e = funcWithMultipleArgs(...tup1); - assert( ["hello", "21"], e); + assert(["hello", "21"], e); [string[]] tup2 = [["hello"]]; string[] f = funcWithMultipleArgs(34, string, ...tup2); - assert( ["hello", "34"], f); + assert(["hello", "34"], f); int[] g = funcWithMultipleArgs(1); - assert( [0, 1], g); + assert([0, 1], g); string[] h = funcWithMultipleArgs(101, arr = ["hello", "world"]); - assert( ["hello", "world", "101"], h); + assert(["hello", "world", "101"], h); int[] i = funcWithMultipleArgs(arr = ["hello", "world"], i = 202); - assert( [2, 202], i); + assert([2, 202], i); } function testBuiltInRefType() { - stream strm = ( [1, 2, 3]).toStream(); + stream strm = ([1, 2, 3]).toStream(); readonly|handle|stream a = funcReturningUnionWithBuiltInRefType(strm); assertSame(strm, a); @@ -331,65 +335,65 @@ function testBuiltInRefType() { assertSame(strm, d); stream|readonly e = funcReturningUnionWithBuiltInRefType(strm); assert(true, e is handle); - string? str = java:toString( checkpanic e); + string? str = java:toString(checkpanic e); assert("hello world", str); } function testParameterizedTypeInUnionWithNonParameterizedTypes() { - record {| stream x; |} rec = {x: ( [1, 2, 3]).toStream()}; - object {}|record {| stream x; |}|int|error a = getValueWithUnionReturnType(rec); - assert(101, checkpanic a); + record {|stream x;|} rec = {x: ([1, 2, 3]).toStream()}; + object {}|record {|stream x;|}|int|error a = getValueWithUnionReturnType(rec); + assert(101, checkpanic a); PersonObj pObj = new ("John", "Doe"); - object {}|record {| stream x; |}|string[]|error b = getValueWithUnionReturnType(pObj); + object {}|record {|stream x;|}|string[]|error b = getValueWithUnionReturnType(pObj); assertSame(pObj, b); - error|object {}|record {| stream x; |}|boolean c = getValueWithUnionReturnType(true, boolean); - assert(false, checkpanic c); + error|object {}|record {|stream x;|}|boolean c = getValueWithUnionReturnType(true, boolean); + assert(false, checkpanic c); - error|object {}|record {| stream x; |}|boolean d = getValueWithUnionReturnType(td = boolean, val = false); - assert(true, checkpanic d); + error|object {}|record {|stream x;|}|boolean d = getValueWithUnionReturnType(td = boolean, val = false); + assert(true, checkpanic d); } function testUsageWithVarWithUserSpecifiedArg() { - stream strm = ( [1, 2, 3]).toStream(); + stream strm = ([1, 2, 3]).toStream(); var x = funcReturningUnionWithBuiltInRefType(strm, IntStream); assertSame(strm, x); } - function testFunctionWithAnyFunctionParamType() { - var fn = function (function x, int y) { +function testFunctionWithAnyFunctionParamType() { + var fn = function(function x, int y) { }; function (function, int) z = getFunctionWithAnyFunctionParamType(fn); assertSame(fn, z); - } +} function testUsageWithCasts() { - int a = getValue(); + int a = getValue(); assert(150, a); - var b = getValue(); + var b = getValue(); assert(12.34, b); - any c = getValue(); - assert(23.45d, c); + any c = getValue(); + assert(23.45d, c); - string|xml|float d = getValue(); + string|xml|float d = getValue(); assert("Hello World!", d); - anydata e = getValue(); + anydata e = getValue(); assert(true, e); - anydata f = <[int, Person, boolean...]> getTupleWithRestDesc(int, Person); - assert(<[int, Person, boolean...]>[150, expPerson, true, true], f); + anydata f = <[int, PersonIDTF, boolean...]>getTupleWithRestDesc(int, PersonIDTF); + assert(<[int, PersonIDTF, boolean...]>[150, expPerson, true, true], f); - record {| stream x; |} g = {x: ( [1, 2, 3]).toStream()}; - var h = x; |}|int> checkpanic getValueWithUnionReturnType(g); - assert(101, h); + record {|stream x;|} g = {x: ([1, 2, 3]).toStream()}; + var h = x;|}|int>checkpanic getValueWithUnionReturnType(g); + assert(101, h); PersonObj i = new ("John", "Doe"); - any|error j = x; |}|string[]|error> getValueWithUnionReturnType(i); + any|error j = x;|}|string[]|error>getValueWithUnionReturnType(i); assertSame(i, j); } @@ -420,7 +424,7 @@ function getTuple(typedesc td1, typedesc td2, typedesc td1, typedesc td2, typedesc td3 = <>) returns [td1, td2, td3...] = - @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType" } external; + @java:Method {'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType"} external; function getArray(typedesc td = <>) returns td[] = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", @@ -430,10 +434,10 @@ function getArray(typedesc td = <>) returns td[] = @java:Method { function getXml(typedesc td = <>, xml val = xml ``) returns xml = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType" +} external; -function getInvalidValue(typedesc td1 = <>, typedesc td2 = Person) returns td1 = +function getInvalidValue(typedesc td1 = <>, typedesc td2 = PersonIDTF) returns td1 = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", name: "getInvalidValue", diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/jvm/objects_subtyping.bal b/tests/jballerina-unit-test/src/test/resources/test-src/jvm/objects_subtyping.bal index b0aa1255ab05..7d9ea39b789a 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/jvm/objects_subtyping.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/jvm/objects_subtyping.bal @@ -161,9 +161,7 @@ public function testObjectAssignabilityBetweenNonClientAndClientObject() { subtyping:ClientObjectWithoutRemoteMethod o2 = new subtyping:ClientObjectWithoutRemoteMethod("ClientObjectWithoutRemoteMethod"); subtyping:NonClientObject obj3 = o2; - subtyping:ClientObjectWithoutRemoteMethod obj4 = obj1; - assertEquality("NonClientObject", obj4.name); assertEquality("ClientObjectWithoutRemoteMethod", obj3.name); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/object/ObjectEquivalencyProject/object-equivalency.bal b/tests/jballerina-unit-test/src/test/resources/test-src/object/ObjectEquivalencyProject/object-equivalency.bal index 0a9988ceb3b9..db8c5066fc87 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/object/ObjectEquivalencyProject/object-equivalency.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/object/ObjectEquivalencyProject/object-equivalency.bal @@ -654,13 +654,9 @@ client class ClientObjectWithoutRemoteMethod { } function testObjectAssignabilityBetweenNonClientAndClientObject() { - NonClientObject obj1 = new("NonClientObject"); - ClientObjectWithoutRemoteMethod obj2 = new("ClientObjectWithoutRemoteMethod"); + ClientObjectWithoutRemoteMethod obj2 = new ("ClientObjectWithoutRemoteMethod"); NonClientObject obj3 = obj2; - ClientObjectWithoutRemoteMethod obj4 = obj1; - - assertEquality("NonClientObject", obj4.name); assertEquality("ClientObjectWithoutRemoteMethod", obj3.name); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/multiple-order-by-clauses.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/multiple-order-by-clauses.bal index 9fd7422c55fc..c6ae94051aa2 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/multiple-order-by-clauses.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/multiple-order-by-clauses.bal @@ -14,59 +14,59 @@ // specific language governing permissions and limitations // under the License. -type Student record {| - readonly int id; - string? fname; - float fee; - decimal impact; - boolean isUndergrad; +type StudentY record {| + readonly int id; + string? fname; + float fee; + decimal impact; + boolean isUndergrad; |}; -type Person record {| +type PersonY record {| string firstName; string lastName; int age; string address; |}; -type Customer record {| +type CustomerY record {| readonly int id; readonly string name; int noOfItems; |}; -type CustomerProfile record {| +type CustomerProfileY record {| string name; int age; int noOfItems; string address; |}; -type StudentTable table key(id); +type StudentTableY table key(id); function testQueryExprWithMultipleOrderByClauses() returns boolean { boolean testPassed = true; - Student s1 = {id: 1, fname: "John", fee: 2000.56, impact: 0.4, isUndergrad: true}; - Student s2 = {id: 2, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; - Student s3 = {id: 2, fname: (), fee: 4000.56, impact: 0.4, isUndergrad: true}; - Student s4 = {id: 2, fname: "Kate", fee: 2000.56, impact: 0.4, isUndergrad: true}; - Student s5 = {id: 3, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; + StudentY s1 = {id: 1, fname: "John", fee: 2000.56, impact: 0.4, isUndergrad: true}; + StudentY s2 = {id: 2, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; + StudentY s3 = {id: 2, fname: (), fee: 4000.56, impact: 0.4, isUndergrad: true}; + StudentY s4 = {id: 2, fname: "Kate", fee: 2000.56, impact: 0.4, isUndergrad: true}; + StudentY s5 = {id: 3, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; - Student[] studentList = [s1, s2, s3, s4, s5]; + StudentY[] studentList = [s1, s2, s3, s4, s5]; - Student[] opStudentList = + StudentY[] opStudentList = from var student in studentList - order by student.fname descending, student.impact - order by student.id descending - select student; + order by student.fname descending, student.impact + order by student.id descending + select student; testPassed = testPassed && opStudentList.length() == 5; - testPassed = testPassed && opStudentList[0] == studentList[4]; - testPassed = testPassed && opStudentList[1] == studentList[3]; - testPassed = testPassed && opStudentList[2] == studentList[1]; - testPassed = testPassed && opStudentList[3] == studentList[2]; - testPassed = testPassed && opStudentList[4] == studentList[0]; + testPassed = testPassed && opStudentList[0] == studentList[4]; + testPassed = testPassed && opStudentList[1] == studentList[3]; + testPassed = testPassed && opStudentList[2] == studentList[1]; + testPassed = testPassed && opStudentList[3] == studentList[2]; + testPassed = testPassed && opStudentList[4] == studentList[0]; return testPassed; } @@ -74,32 +74,32 @@ function testQueryExprWithMultipleOrderByClauses() returns boolean { function testQueryExprWithMultipleFromAndMultipleOrderByClauses() returns boolean { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 7, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; + CustomerY c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerY c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerY c3 = {id: 7, name: "James", noOfItems: 25}; + CustomerY c4 = {id: 0, name: "James", noOfItems: 25}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23, address: "New York"}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30, address: "California"}; + PersonY p1 = {firstName: "Amy", lastName: "Melina", age: 23, address: "New York"}; + PersonY p2 = {firstName: "Frank", lastName: "James", age: 30, address: "California"}; - Customer[] customerList = [c1, c2, c3, c4]; - Person[] personList = [p1, p2]; + CustomerY[] customerList = [c1, c2, c3, c4]; + PersonY[] personList = [p1, p2]; - Customer[] opCustomerList = + CustomerY[] opCustomerList = from var customer in customerList - from var person in personList - let string customerName = "Johns" - where person.lastName == customer.name - order by customer.id descending - order by person.address - select { - id: customer.id, - name: customerName, - noOfItems: customer.noOfItems - }; + from var person in personList + let string customerName = "Johns" + where person.lastName == customer.name + order by customer.id descending + order by person.address + select { + id: customer.id, + name: customerName, + noOfItems: customer.noOfItems + }; testPassed = testPassed && opCustomerList.length() == 4; - Customer cus; + CustomerY cus; cus = opCustomerList[0]; testPassed = testPassed && cus.id == 7 && cus.noOfItems == 25; cus = opCustomerList[1]; @@ -114,32 +114,32 @@ function testQueryExprWithMultipleFromAndMultipleOrderByClauses() returns boolea function testQueryExprWithJoinAndMultipleOrderByClauses() returns boolean { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "James", noOfItems: 25}; - Customer c4 = {id: 4, name: "James", noOfItems: 25}; + CustomerY c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerY c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerY c3 = {id: 3, name: "James", noOfItems: 25}; + CustomerY c4 = {id: 4, name: "James", noOfItems: 25}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23, address: "New York"}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30, address: "California"}; + PersonY p1 = {firstName: "Amy", lastName: "Melina", age: 23, address: "New York"}; + PersonY p2 = {firstName: "Frank", lastName: "James", age: 30, address: "California"}; - Customer[] customerList = [c1, c2, c3, c4]; - Person[] personList = [p1, p2]; + CustomerY[] customerList = [c1, c2, c3, c4]; + PersonY[] personList = [p1, p2]; - CustomerProfile[] customerProfileList = + CustomerProfileY[] customerProfileList = from var customer in customerList - join var person in personList + join var person in personList on customer.name equals person.lastName - order by customer.noOfItems descending - order by person.address - select { - name: person.firstName, - age : person.age, - noOfItems: customer.noOfItems, - address: person.address - }; + order by customer.noOfItems descending + order by person.address + select { + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems, + address: person.address + }; testPassed = testPassed && customerProfileList.length() == 4; - CustomerProfile cp; + CustomerProfileY cp; cp = customerProfileList[0]; testPassed = testPassed && cp.name == "Frank" && cp.age == 30 && cp.noOfItems == 25 && cp.address == "California"; cp = customerProfileList[1]; @@ -154,36 +154,40 @@ function testQueryExprWithJoinAndMultipleOrderByClauses() returns boolean { function testQueryExprWithInnerQueriesAndMultipleOrderByClauses() returns boolean { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 62}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 9, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; - Customer c5 = {id: 2, name: "James", noOfItems: 30}; + CustomerY c1 = {id: 1, name: "Melina", noOfItems: 62}; + CustomerY c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerY c3 = {id: 9, name: "James", noOfItems: 25}; + CustomerY c4 = {id: 0, name: "James", noOfItems: 25}; + CustomerY c5 = {id: 2, name: "James", noOfItems: 30}; - Person p1 = {firstName: "Jennifer", lastName: "Melina", age: 23, address: "California"}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30, address: "New York"}; - Person p3 = {firstName: "Zeth", lastName: "James", age: 50, address: "Texas"}; + PersonY p1 = {firstName: "Jennifer", lastName: "Melina", age: 23, address: "California"}; + PersonY p2 = {firstName: "Frank", lastName: "James", age: 30, address: "New York"}; + PersonY p3 = {firstName: "Zeth", lastName: "James", age: 50, address: "Texas"}; - Customer[] customerList = [c1, c2, c3, c4, c5]; - Person[] personList = [p1, p2, p3]; + CustomerY[] customerList = [c1, c2, c3, c4, c5]; + PersonY[] personList = [p1, p2, p3]; - CustomerProfile[] customerProfileList = + CustomerProfileY[] customerProfileList = from var customer in (stream from var c in customerList - order by c.id descending limit 4 select c) - join var person in (from var p in personList order by p.firstName descending select p) - on customer.name equals person.lastName - order by customer.noOfItems descending - order by person.address + order by c.id descending limit 4 - select { - name: person.firstName, - age : person.age, - noOfItems: customer.noOfItems, - address: person.address - }; + select c) + join var person in (from var p in personList + order by p.firstName descending + select p) + on customer.name equals person.lastName + order by customer.noOfItems descending + order by person.address + limit 4 + select { + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems, + address: person.address + }; testPassed = testPassed && customerProfileList.length() == 4; - CustomerProfile cp; + CustomerProfileY cp; cp = customerProfileList[0]; testPassed = testPassed && cp.name == "Jennifer" && cp.age == 23 && cp.noOfItems == 62 && cp.address == "California"; @@ -199,37 +203,40 @@ function testQueryExprWithInnerQueriesAndMultipleOrderByClauses() returns boolea function testQueryExprWithMultipleOrderByAndMultipleLimitClauses() returns boolean { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 62}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 9, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; - Customer c5 = {id: 2, name: "James", noOfItems: 30}; + CustomerY c1 = {id: 1, name: "Melina", noOfItems: 62}; + CustomerY c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerY c3 = {id: 9, name: "James", noOfItems: 25}; + CustomerY c4 = {id: 0, name: "James", noOfItems: 25}; + CustomerY c5 = {id: 2, name: "James", noOfItems: 30}; - Person p1 = {firstName: "Jennifer", lastName: "Melina", age: 23, address: "California"}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30, address: "New York"}; - Person p3 = {firstName: "Zeth", lastName: "James", age: 50, address: "Texas"}; + PersonY p1 = {firstName: "Jennifer", lastName: "Melina", age: 23, address: "California"}; + PersonY p2 = {firstName: "Frank", lastName: "James", age: 30, address: "New York"}; + PersonY p3 = {firstName: "Zeth", lastName: "James", age: 50, address: "Texas"}; - Customer[] customerList = [c1, c2, c3, c4, c5]; - Person[] personList = [p1, p2, p3]; + CustomerY[] customerList = [c1, c2, c3, c4, c5]; + PersonY[] personList = [p1, p2, p3]; - CustomerProfile[] customerProfileList = + CustomerProfileY[] customerProfileList = from var customer in (stream from var c in customerList - order by c.id descending select c) - join var person in (from var p in personList order by p.firstName descending select p) + order by c.id descending + select c) + join var person in (from var p in personList + order by p.firstName descending + select p) on customer.name equals person.lastName - order by customer.noOfItems descending - limit 5 - order by person.address - limit 2 - select { - name: person.firstName, - age : person.age, - noOfItems: customer.noOfItems, - address: person.address - }; + order by customer.noOfItems descending + limit 5 + order by person.address + limit 2 + select { + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems, + address: person.address + }; testPassed = testPassed && customerProfileList.length() == 2; - CustomerProfile cp; + CustomerProfileY cp; cp = customerProfileList[0]; testPassed = testPassed && cp.name == "Jennifer" && cp.age == 23 && cp.noOfItems == 62 && cp.address == "California"; @@ -240,37 +247,41 @@ function testQueryExprWithMultipleOrderByAndMultipleLimitClauses() returns boole function testQueryActionWithMultipleOrderByClauses() returns boolean { boolean testPassed = true; - CustomerProfile[] customerProfileList = []; + CustomerProfileY[] customerProfileList = []; - Customer c1 = {id: 1, name: "Melina", noOfItems: 62}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 9, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; - Customer c5 = {id: 2, name: "James", noOfItems: 30}; + CustomerY c1 = {id: 1, name: "Melina", noOfItems: 62}; + CustomerY c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerY c3 = {id: 9, name: "James", noOfItems: 25}; + CustomerY c4 = {id: 0, name: "James", noOfItems: 25}; + CustomerY c5 = {id: 2, name: "James", noOfItems: 30}; - Person p1 = {firstName: "Jennifer", lastName: "Melina", age: 23, address: "California"}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30, address: "New York"}; - Person p3 = {firstName: "Zeth", lastName: "James", age: 50, address: "Texas"}; + PersonY p1 = {firstName: "Jennifer", lastName: "Melina", age: 23, address: "California"}; + PersonY p2 = {firstName: "Frank", lastName: "James", age: 30, address: "New York"}; + PersonY p3 = {firstName: "Zeth", lastName: "James", age: 50, address: "Texas"}; - Customer[] customerList = [c1, c2, c3, c4, c5]; - Person[] personList = [p1, p2, p3]; + CustomerY[] customerList = [c1, c2, c3, c4, c5]; + PersonY[] personList = [p1, p2, p3]; error? op = from var customer in customerList - join var person in personList + join var person in personList on customer.name equals person.lastName - order by customer.noOfItems descending - limit 5 - order by person.address - limit 2 - do { - CustomerProfile cp = {name: person.firstName, age : person.age, noOfItems: customer.noOfItems, - address: person.address}; - customerProfileList[customerProfileList.length()] = cp; + order by customer.noOfItems descending + limit 5 + order by person.address + limit 2 + do { + CustomerProfileY cp = { + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems, + address: person.address }; + customerProfileList[customerProfileList.length()] = cp; + }; testPassed = testPassed && customerProfileList.length() == 2; - CustomerProfile cp; + CustomerProfileY cp; cp = customerProfileList[0]; testPassed = testPassed && cp.name == "Jennifer" && cp.age == 23 && cp.noOfItems == 62 && cp.address == "California"; @@ -282,27 +293,27 @@ function testQueryActionWithMultipleOrderByClauses() returns boolean { function testQueryExprWithMultipleOrderByClausesReturnTable() returns boolean { boolean testPassed = true; - Student s1 = {id: 1, fname: "John", fee: 2000.56, impact: 0.4, isUndergrad: true}; - Student s2 = {id: 2, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; - Student s3 = {id: 9, fname: (), fee: 4000.56, impact: 0.4, isUndergrad: true}; - Student s4 = {id: 4, fname: "Kate", fee: 2000.56, impact: 0.4, isUndergrad: true}; - Student s5 = {id: 10, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; + StudentY s1 = {id: 1, fname: "John", fee: 2000.56, impact: 0.4, isUndergrad: true}; + StudentY s2 = {id: 2, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; + StudentY s3 = {id: 9, fname: (), fee: 4000.56, impact: 0.4, isUndergrad: true}; + StudentY s4 = {id: 4, fname: "Kate", fee: 2000.56, impact: 0.4, isUndergrad: true}; + StudentY s5 = {id: 10, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; - Student[] studentList = [s1, s2, s3, s4, s5]; + StudentY[] studentList = [s1, s2, s3, s4, s5]; - StudentTable|error opStudentTable = + StudentTableY|error opStudentTable = table key(id) from var student in studentList - order by student.fname descending, student.impact - order by student.id descending - select student; - - if (opStudentTable is StudentTable) { - Student[] opStudentList = opStudentTable.toArray(); - testPassed = testPassed && opStudentList[0] == studentList[4]; - testPassed = testPassed && opStudentList[1] == studentList[2]; - testPassed = testPassed && opStudentList[2] == studentList[3]; - testPassed = testPassed && opStudentList[3] == studentList[1]; - testPassed = testPassed && opStudentList[4] == studentList[0]; + order by student.fname descending, student.impact + order by student.id descending + select student; + + if (opStudentTable is StudentTableY) { + StudentY[] opStudentList = opStudentTable.toArray(); + testPassed = testPassed && opStudentList[0] == studentList[4]; + testPassed = testPassed && opStudentList[1] == studentList[2]; + testPassed = testPassed && opStudentList[2] == studentList[3]; + testPassed = testPassed && opStudentList[3] == studentList[1]; + testPassed = testPassed && opStudentList[4] == studentList[0]; } return testPassed; } @@ -310,23 +321,23 @@ function testQueryExprWithMultipleOrderByClausesReturnTable() returns boolean { function testQueryExprWithMultipleOrderByClausesReturnStream() returns boolean { boolean testPassed = true; - Student s1 = {id: 1, fname: "John", fee: 2000.56, impact: 0.4, isUndergrad: true}; - Student s2 = {id: 2, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; - Student s3 = {id: 9, fname: (), fee: 4000.56, impact: 0.4, isUndergrad: true}; - Student s4 = {id: 4, fname: "Kate", fee: 2000.56, impact: 0.4, isUndergrad: true}; - Student s5 = {id: 10, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; + StudentY s1 = {id: 1, fname: "John", fee: 2000.56, impact: 0.4, isUndergrad: true}; + StudentY s2 = {id: 2, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; + StudentY s3 = {id: 9, fname: (), fee: 4000.56, impact: 0.4, isUndergrad: true}; + StudentY s4 = {id: 4, fname: "Kate", fee: 2000.56, impact: 0.4, isUndergrad: true}; + StudentY s5 = {id: 10, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; - Student[] studentList = [s1, s2, s3, s4, s5]; + StudentY[] studentList = [s1, s2, s3, s4, s5]; - stream opStudentStream = + stream opStudentStream = stream from var student in studentList - order by student.fname descending, student.impact - order by student.id descending - select student; + order by student.fname descending, student.impact + order by student.id descending + select student; - Student[] opStudentList = []; - record {| Student value; |}|error? v = opStudentStream.next(); - while (v is record {| Student value; |}) { + StudentY[] opStudentList = []; + record {|StudentY value;|}|error? v = opStudentStream.next(); + while (v is record {|StudentY value;|}) { opStudentList.push(v.value); v = opStudentStream.next(); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/order-by-clause.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/order-by-clause.bal index f1edb6added6..8bda7094c615 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/order-by-clause.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/order-by-clause.bal @@ -22,13 +22,13 @@ type Student record {| boolean isUndergrad; |}; -type Person record {| +type PersonPos record {| string firstName; string lastName; int age; |}; -type Customer record {| +type CustomerPos record {| readonly int id; readonly string name; int noOfItems; @@ -57,25 +57,25 @@ type PaymentInfo record {| string modeOfPayment; |}; -type CustomerTable table key(id, name); +type CustomerTable table key(id, name); type CustomerValue record {| - Customer value; + CustomerPos value; |}; type PersonValue record {| - Person value; + PersonPos value; |}; -function getCustomer(record {| Customer value; |}? returnedVal) returns Customer? { +function getCustomer(record {|CustomerPos value;|}? returnedVal) returns CustomerPos? { if (returnedVal is CustomerValue) { - return returnedVal.value; + return returnedVal.value; } else { - return (); + return (); } } -function getPersonValue((record {| Person value; |}|error?)|(record {| Person value; |}?) returnedVal) +function getPersonValue((record {|PersonPos value;|}|error?)|(record {|PersonPos value;|}?) returnedVal) returns PersonValue? { var result = returnedVal; if (result is PersonValue) { @@ -96,13 +96,13 @@ function testQueryExprWithOrderByClause() returns boolean { Student[] studentList = [s1, s2, s3, s4]; Student[] opStudentList = from var student in studentList - order by student.fname descending, student.impact - select student; + order by student.fname descending, student.impact + select student; - testPassed = testPassed && opStudentList[0] == studentList[3]; - testPassed = testPassed && opStudentList[1] == studentList[0]; - testPassed = testPassed && opStudentList[2] == studentList[1]; - testPassed = testPassed && opStudentList[3] == studentList[2]; + testPassed = testPassed && opStudentList[0] == studentList[3]; + testPassed = testPassed && opStudentList[1] == studentList[0]; + testPassed = testPassed && opStudentList[2] == studentList[1]; + testPassed = testPassed && opStudentList[3] == studentList[2]; return testPassed; } @@ -118,71 +118,71 @@ function testQueryExprWithOrderByClause2() returns boolean { Student[] studentList = [s1, s2, s3, s4]; Student[] opStudentList = from var student in studentList - order by student.isUndergrad ascending, student.fee - select student; + order by student.isUndergrad ascending, student.fee + select student; - testPassed = testPassed && opStudentList[0] == studentList[1]; - testPassed = testPassed && opStudentList[1] == studentList[2]; - testPassed = testPassed && opStudentList[2] == studentList[0]; - testPassed = testPassed && opStudentList[3] == studentList[3]; + testPassed = testPassed && opStudentList[0] == studentList[1]; + testPassed = testPassed && opStudentList[1] == studentList[2]; + testPassed = testPassed && opStudentList[2] == studentList[0]; + testPassed = testPassed && opStudentList[3] == studentList[3]; return testPassed; } -function testQueryExprWithOrderByClause3() returns Customer[] { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 7, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; - - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; - - Customer[] customerList = [c1, c2, c3, c4]; - Person[] personList = [p1, p2]; - - Customer[] opCustomerList = from var customer in customerList - from var person in personList - let string customerName = "Johns" - where person.lastName == "James" - order by customer.id descending - select { - id: customer.id, - name: customerName, - noOfItems: customer.noOfItems - }; - - return opCustomerList; +function testQueryExprWithOrderByClause3() returns CustomerPos[] { + CustomerPos c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerPos c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerPos c3 = {id: 7, name: "James", noOfItems: 25}; + CustomerPos c4 = {id: 0, name: "James", noOfItems: 25}; + + PersonPos p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonPos p2 = {firstName: "Frank", lastName: "James", age: 30}; + + CustomerPos[] customerList = [c1, c2, c3, c4]; + PersonPos[] personList = [p1, p2]; + + CustomerPos[] opCustomerList = from var customer in customerList + from var person in personList + let string customerName = "Johns" + where person.lastName == "James" + order by customer.id descending + select { + id: customer.id, + name: customerName, + noOfItems: customer.noOfItems + }; + + return opCustomerList; } function testQueryExprWithOrderByClauseReturnTable() returns boolean { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "James", noOfItems: 25}; - Customer c4 = {id: 4, name: "James", noOfItems: 25}; + CustomerPos c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerPos c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerPos c3 = {id: 3, name: "James", noOfItems: 25}; + CustomerPos c4 = {id: 4, name: "James", noOfItems: 25}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonPos p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonPos p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList = [c1, c2, c3, c4]; - Person[] personList = [p1, p2]; + CustomerPos[] customerList = [c1, c2, c3, c4]; + PersonPos[] personList = [p1, p2]; CustomerTable|error customerTable = table key(id, name) from var customer in customerList - from var person in personList - where person.firstName == "Frank" - order by customer.noOfItems descending, customer.id - limit 3 - select { - id: customer.id, - name: customer.name, - noOfItems: customer.noOfItems - }; + from var person in personList + where person.firstName == "Frank" + order by customer.noOfItems descending, customer.id + limit 3 + select { + id: customer.id, + name: customer.name, + noOfItems: customer.noOfItems + }; if (customerTable is CustomerTable) { var itr = customerTable.iterator(); - Customer? customer = getCustomer(itr.next()); + CustomerPos? customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[2]; customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[3]; @@ -198,19 +198,19 @@ function testQueryExprWithOrderByClauseReturnTable() returns boolean { function testQueryExprWithOrderByClauseReturnStream() returns boolean { boolean testPassed = true; - Person p1 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p2 = {firstName: "John", lastName: "David", age: 33}; - Person p3 = {firstName: "John", lastName: "Fonseka", age: 28}; - Person p4 = {firstName: "John", lastName: "Fonseka", age: 30}; - Person p5 = {firstName: "John", lastName: "Fonseka", age: 20}; + PersonPos p1 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonPos p2 = {firstName: "John", lastName: "David", age: 33}; + PersonPos p3 = {firstName: "John", lastName: "Fonseka", age: 28}; + PersonPos p4 = {firstName: "John", lastName: "Fonseka", age: 30}; + PersonPos p5 = {firstName: "John", lastName: "Fonseka", age: 20}; - Customer c1 = {id: 1, name: "John", noOfItems: 25}; - Customer c2 = {id: 2, name: "Frank", noOfItems: 20}; + CustomerPos c1 = {id: 1, name: "John", noOfItems: 25}; + CustomerPos c2 = {id: 2, name: "Frank", noOfItems: 20}; - Person[] personList = [p1, p2, p3, p4, p5]; - Customer[] customerList = [c1, c2]; + PersonPos[] personList = [p1, p2, p3, p4, p5]; + CustomerPos[] customerList = [c1, c2]; - stream outputPersonStream = stream from var person in personList.toStream() + stream outputPersonStream = stream from var person in personList.toStream() from var customer in customerList let string newLastName = "Turin" let string newFirstName = "Johnas" @@ -223,7 +223,7 @@ function testQueryExprWithOrderByClauseReturnStream() returns boolean { age: person.age }; - record {| Person value; |}? person = getPersonValue(outputPersonStream.next()); + record {|PersonPos value;|}? person = getPersonValue(outputPersonStream.next()); testPassed = testPassed && person?.value?.firstName == "Johnas" && person?.value?.lastName == "Turin" && person?.value?.age == 30; @@ -246,26 +246,26 @@ function testQueryExprWithOrderByClauseReturnStream() returns boolean { } function testQueryExprWithOrderByClauseAndJoin() returns CustomerProfile[] { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "James", noOfItems: 25}; - Customer c4 = {id: 4, name: "James", noOfItems: 25}; + CustomerPos c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerPos c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerPos c3 = {id: 3, name: "James", noOfItems: 25}; + CustomerPos c4 = {id: 4, name: "James", noOfItems: 25}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonPos p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonPos p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList = [c1, c2, c3, c4]; - Person[] personList = [p1, p2]; + CustomerPos[] customerList = [c1, c2, c3, c4]; + PersonPos[] personList = [p1, p2]; CustomerProfile[] customerProfileList = from var customer in customerList - join var person in personList - on customer.name equals person.lastName - order by customer.noOfItems - select { - name: person.firstName, - age : person.age, - noOfItems: customer.noOfItems - }; + join var person in personList + on customer.name equals person.lastName + order by customer.noOfItems + select { + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems + }; return customerProfileList; } @@ -273,14 +273,30 @@ function testQueryExprWithOrderByClauseAndJoin() returns CustomerProfile[] { function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction() returns boolean { boolean testPassed = true; - Employee e1 = {name: "Frank", address: {unitNo: 111, street: "Main Street"}, tokens: {one:1, two:2, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e2 = {name: "James", address: {unitNo: 222, street: "Main Street"}, tokens: {one:1, two:2, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e3 = {name: "James", address: {unitNo: 222, street: "Cross Street"}, tokens: {one:1, two:2, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e4 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:1, two:2, three:3}, - noOfShifts: [1, 2, 3]}; + Employee e1 = { + name: "Frank", + address: {unitNo: 111, street: "Main Street"}, + tokens: {one: 1, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e2 = { + name: "James", + address: {unitNo: 222, street: "Main Street"}, + tokens: {one: 1, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e3 = { + name: "James", + address: {unitNo: 222, street: "Cross Street"}, + tokens: {one: 1, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e4 = { + name: "Frank", + address: {unitNo: 111, street: "Cross Street"}, + tokens: {one: 1, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; Employee[] empList = [e1, e2, e3, e4]; @@ -288,10 +304,10 @@ function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction() retur order by emp.address.unitNo descending, emp.address.street.toLowerAscii() select emp; - testPassed = testPassed && opEmpList[0] == empList[2]; - testPassed = testPassed && opEmpList[1] == empList[1]; - testPassed = testPassed && opEmpList[2] == empList[3]; - testPassed = testPassed && opEmpList[3] == empList[0]; + testPassed = testPassed && opEmpList[0] == empList[2]; + testPassed = testPassed && opEmpList[1] == empList[1]; + testPassed = testPassed && opEmpList[2] == empList[3]; + testPassed = testPassed && opEmpList[3] == empList[0]; return testPassed; } @@ -299,16 +315,36 @@ function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction() retur function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction2() returns boolean { boolean testPassed = true; - Employee e1 = {name: "Frank", address: {unitNo: 111, street: "Main Street"}, tokens: {one:1, two:2, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e2 = {name: "James", address: {unitNo: 222, street: "Main Street"}, tokens: {one:11, two:2, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e3 = {name: "James", address: {unitNo: 222, street: "Cross Street"}, tokens: {one:111, two:2, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e4 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:1111, two:2, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e5 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:1111, two:2, three:3}, - noOfShifts: [3, 2, 3]}; + Employee e1 = { + name: "Frank", + address: {unitNo: 111, street: "Main Street"}, + tokens: {one: 1, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e2 = { + name: "James", + address: {unitNo: 222, street: "Main Street"}, + tokens: {one: 11, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e3 = { + name: "James", + address: {unitNo: 222, street: "Cross Street"}, + tokens: {one: 111, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e4 = { + name: "Frank", + address: {unitNo: 111, street: "Cross Street"}, + tokens: {one: 1111, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e5 = { + name: "Frank", + address: {unitNo: 111, street: "Cross Street"}, + tokens: {one: 1111, two: 2, three: 3}, + noOfShifts: [3, 2, 3] + }; Employee[] empList = [e1, e2, e3, e4, e5]; @@ -316,36 +352,36 @@ function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction2() retu order by emp.name, emp.tokens["one"] descending, emp.noOfShifts[0] descending select emp; - testPassed = testPassed && opEmpList[0] == empList[4]; - testPassed = testPassed && opEmpList[1] == empList[3]; - testPassed = testPassed && opEmpList[2] == empList[0]; - testPassed = testPassed && opEmpList[3] == empList[2]; - testPassed = testPassed && opEmpList[4] == empList[1]; + testPassed = testPassed && opEmpList[0] == empList[4]; + testPassed = testPassed && opEmpList[1] == empList[3]; + testPassed = testPassed && opEmpList[2] == empList[0]; + testPassed = testPassed && opEmpList[3] == empList[2]; + testPassed = testPassed && opEmpList[4] == empList[1]; return testPassed; } function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction3() returns CustomerProfile[] { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "James", noOfItems: 25}; - Customer c4 = {id: 4, name: "James", noOfItems: 25}; + CustomerPos c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerPos c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerPos c3 = {id: 3, name: "James", noOfItems: 25}; + CustomerPos c4 = {id: 4, name: "James", noOfItems: 25}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonPos p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonPos p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList = [c1, c2, c3, c4]; - Person[] personList = [p1, p2]; + CustomerPos[] customerList = [c1, c2, c3, c4]; + PersonPos[] personList = [p1, p2]; CustomerProfile[] customerProfileList = from var customer in customerList - join var person in personList - on customer.name equals person.lastName - order by incrementCount(0), customer.noOfItems descending - select { - name: person.firstName, - age : person.age, - noOfItems: customer.noOfItems - }; + join var person in personList + on customer.name equals person.lastName + order by incrementCount(0), customer.noOfItems descending + select { + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems + }; return customerProfileList; } @@ -353,20 +389,56 @@ function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction3() retu function testQueryExprWithOrderByClauseHavingNaNNilValues() returns boolean { boolean testPassed = true; - Employee e1 = {name: "Frank", address: {unitNo: 111, street: "Main Street"}, tokens: {one:1, two:2, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e2 = {name: "James", address: {unitNo: 222, street: "Main Street"}, tokens: {one:11, two:(), three:3}, - noOfShifts: [1, 2, 3]}; - Employee e3 = {name: "James", address: {unitNo: 222, street: "Cross Street"}, tokens: {one:11, two:(0.0/0.0), - three:3}, noOfShifts: [1, 2, 3]}; - Employee e4 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:11, two:4, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e5 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:11, two:4, three:()}, - noOfShifts: [1, 2, 3]}; - Employee e6 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:11, two:4, - three:(0.0/0.0)}, noOfShifts: [1, 2, 3]}; - Employee e7 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:11, two:4, three:55}, - noOfShifts: [1, 2, 3]}; + Employee e1 = { + name: "Frank", + address: {unitNo: 111, street: "Main Street"}, + tokens: {one: 1, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e2 = { + name: "James", + address: {unitNo: 222, street: "Main Street"}, + tokens: {one: 11, two: (), three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e3 = { + name: "James", + address: {unitNo: 222, street: "Cross Street"}, + tokens: { + one: 11, + two: (0.0 / 0.0), + three: 3 + }, + noOfShifts: [1, 2, 3] + }; + Employee e4 = { + name: "Frank", + address: {unitNo: 111, street: "Cross Street"}, + tokens: {one: 11, two: 4, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e5 = { + name: "Frank", + address: {unitNo: 111, street: "Cross Street"}, + tokens: {one: 11, two: 4, three: ()}, + noOfShifts: [1, 2, 3] + }; + Employee e6 = { + name: "Frank", + address: {unitNo: 111, street: "Cross Street"}, + tokens: { + one: 11, + two: 4, + three: (0.0 / 0.0) + }, + noOfShifts: [1, 2, 3] + }; + Employee e7 = { + name: "Frank", + address: {unitNo: 111, street: "Cross Street"}, + tokens: {one: 11, two: 4, three: 55}, + noOfShifts: [1, 2, 3] + }; Employee[] empList = [e1, e2, e3, e4, e5, e6, e7]; @@ -374,30 +446,30 @@ function testQueryExprWithOrderByClauseHavingNaNNilValues() returns boolean { order by emp.tokens["two"] descending, emp.tokens["three"] ascending select emp; - testPassed = testPassed && opEmpList[0] == empList[3]; - testPassed = testPassed && opEmpList[1] == empList[6]; - testPassed = testPassed && opEmpList[2] == empList[5]; - testPassed = testPassed && opEmpList[3] == empList[4]; - testPassed = testPassed && opEmpList[4] == empList[0]; - testPassed = testPassed && opEmpList[5] == empList[2]; - testPassed = testPassed && opEmpList[6] == empList[1]; + testPassed = testPassed && opEmpList[0] == empList[3]; + testPassed = testPassed && opEmpList[1] == empList[6]; + testPassed = testPassed && opEmpList[2] == empList[5]; + testPassed = testPassed && opEmpList[3] == empList[4]; + testPassed = testPassed && opEmpList[4] == empList[0]; + testPassed = testPassed && opEmpList[5] == empList[2]; + testPassed = testPassed && opEmpList[6] == empList[1]; return testPassed; } function testQueryExprWithOrderByClauseReturnString() returns string { - Person p1 = {firstName: "Amy", lastName: "Melina", age: 34}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; - Person p3 = {firstName: "Melina", lastName: "Kodel", age: 72}; - Person p4 = {firstName: "Terrence", lastName: "Lewis", age: 19}; - Person p5 = {firstName: "Meghan", lastName: "Markle", age: 55}; + PersonPos p1 = {firstName: "Amy", lastName: "Melina", age: 34}; + PersonPos p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonPos p3 = {firstName: "Melina", lastName: "Kodel", age: 72}; + PersonPos p4 = {firstName: "Terrence", lastName: "Lewis", age: 19}; + PersonPos p5 = {firstName: "Meghan", lastName: "Markle", age: 55}; - Person[] personList = [p1, p2, p3, p4, p5]; + PersonPos[] personList = [p1, p2, p3, p4, p5]; string outputNameString = from var person in personList - order by person.age descending - limit 3 - select person.firstName+" "+person.lastName+","; + order by person.age descending + limit 3 + select person.firstName + " " + person.lastName + ","; return outputNameString; } @@ -419,53 +491,58 @@ function testQueryExprWithOrderByClauseReturnXML() returns xml { `; xml authors = from var book in bookStore// - order by book.toString() - limit 2 - select book; + order by book.toString() + limit 2 + select book; - return authors; + return authors; } function testQueryExprWithOrderByClauseAndInnerQueries() returns CustomerProfile[] { - Customer c1 = {id: 1, name: "Melina", noOfItems: 62}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 9, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; - Customer c5 = {id: 2, name: "James", noOfItems: 30}; + CustomerPos c1 = {id: 1, name: "Melina", noOfItems: 62}; + CustomerPos c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerPos c3 = {id: 9, name: "James", noOfItems: 25}; + CustomerPos c4 = {id: 0, name: "James", noOfItems: 25}; + CustomerPos c5 = {id: 2, name: "James", noOfItems: 30}; - Person p1 = {firstName: "Jennifer", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; - Person p3 = {firstName: "Zeth", lastName: "James", age: 50}; + PersonPos p1 = {firstName: "Jennifer", lastName: "Melina", age: 23}; + PersonPos p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonPos p3 = {firstName: "Zeth", lastName: "James", age: 50}; - Customer[] customerList = [c1, c2, c3, c4, c5]; - Person[] personList = [p1, p2, p3]; + CustomerPos[] customerList = [c1, c2, c3, c4, c5]; + PersonPos[] personList = [p1, p2, p3]; CustomerProfile[] customerProfileList = from var customer in (stream from var c in customerList - order by c.id descending limit 4 select c) - join var person in (from var p in personList order by p.firstName descending limit 2 select p) - on customer.name equals person.lastName - order by incrementCount(0), customer.noOfItems descending - limit 3 - select { - name: person.firstName, - age : person.age, - noOfItems: customer.noOfItems - }; + order by c.id descending + limit 4 + select c) + join var person in (from var p in personList + order by p.firstName descending + limit 2 + select p) + on customer.name equals person.lastName + order by incrementCount(0), customer.noOfItems descending + limit 3 + select { + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems + }; return customerProfileList; } function testQueryExprWithOrderByClauseAndInnerQueries2() returns CustomerProfile[] { - Customer c1 = {id: 1, name: "Melina", noOfItems: 62}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 9, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; - Customer c5 = {id: 2, name: "James", noOfItems: 30}; - Customer c6 = {id: 3, name: "Melina", noOfItems: 20}; + CustomerPos c1 = {id: 1, name: "Melina", noOfItems: 62}; + CustomerPos c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerPos c3 = {id: 9, name: "James", noOfItems: 25}; + CustomerPos c4 = {id: 0, name: "James", noOfItems: 25}; + CustomerPos c5 = {id: 2, name: "James", noOfItems: 30}; + CustomerPos c6 = {id: 3, name: "Melina", noOfItems: 20}; - Person p1 = {firstName: "Jennifer", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; - Person p3 = {firstName: "Zeth", lastName: "James", age: 50}; + PersonPos p1 = {firstName: "Jennifer", lastName: "Melina", age: 23}; + PersonPos p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonPos p3 = {firstName: "Zeth", lastName: "James", age: 50}; PaymentInfo i1 = {custId: 1, modeOfPayment: "cash"}; PaymentInfo i2 = {custId: 9, modeOfPayment: "debit card"}; @@ -476,8 +553,8 @@ function testQueryExprWithOrderByClauseAndInnerQueries2() returns CustomerProfil PaymentInfo i7 = {custId: 2, modeOfPayment: "cash"}; PaymentInfo i8 = {custId: 3, modeOfPayment: "cash"}; - Customer[] customerList = [c1, c2, c3, c4, c5, c6]; - Person[] personList = [p1, p2, p3]; + CustomerPos[] customerList = [c1, c2, c3, c4, c5, c6]; + PersonPos[] personList = [p1, p2, p3]; PaymentInfo[] paymentList = [i1, i2, i3, i4, i5, i6, i7, i8]; CustomerProfile[] customerProfileList = from var customer in (stream from var c in customerList diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/query-expr-with-query-construct-type.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/query-expr-with-query-construct-type.bal index 3cf3bc40fafe..0145356cd7ca 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/query-expr-with-query-construct-type.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/query-expr-with-query-construct-type.bal @@ -14,23 +14,23 @@ // specific language governing permissions and limitations // under the License. -type Person record {| +type PersonX record {| string firstName; string lastName; int age; |}; -type Employee record {| +type EmployeeX record {| string firstName; string lastName; string dept; |}; -type Department record { +type DepartmentX record { string dept; }; -type EmpProfile record {| +type EmpProfileX record {| string firstName; string lastName; int age; @@ -38,67 +38,67 @@ type EmpProfile record {| string status; |}; -type PersonValue record {| - Person value; +type PersonValueX record {| + PersonX value; |}; -type EmployeeValue record {| - Employee value; +type EmployeeValueX record {| + EmployeeX value; |}; -type EmpProfileValue record {| - EmpProfile value; +type EmpProfileValueX record {| + EmpProfileX value; |}; -type Customer record {| +type CustomerX record {| readonly int id; readonly string name; int noOfItems; |}; -type CustomerTable table key(id, name); +type CustomerTableX table key(id, name); -type CustomerKeyLessTable table; +type CustomerKeyLessTableX table; -type CustomerValue record {| - Customer value; +type CustomerValueX record {| + CustomerX value; |}; -function getPersonValue((record {| Person value; |}|error?)|(record {| Person value; |}?) returnedVal) -returns PersonValue? { +function getPersonValue((record {|PersonX value;|}|error?)|(record {|PersonX value;|}?) returnedVal) +returns PersonValueX? { var result = returnedVal; - if (result is PersonValue) { + if (result is PersonValueX) { return result; } else { return (); } } -function getEmployeeValue((record {| Employee value; |}|error?)|(record {| Employee value; |}?) returnedVal) -returns EmployeeValue? { +function getEmployeeValue((record {|EmployeeX value;|}|error?)|(record {|EmployeeX value;|}?) returnedVal) +returns EmployeeValueX? { var result = returnedVal; - if (result is EmployeeValue) { + if (result is EmployeeValueX) { return result; } else { return (); } } -function getEmpProfileValue((record {| EmpProfile value; |}|error?)|(record {| EmpProfile value; |}?) returnedVal) -returns EmpProfileValue? { +function getEmpProfileValue((record {|EmpProfileX value;|}|error?)|(record {|EmpProfileX value;|}?) returnedVal) +returns EmpProfileValueX? { var result = returnedVal; - if (result is EmpProfileValue) { + if (result is EmpProfileValueX) { return result; } else { return (); } } -function getCustomer(record {| Customer value; |}? returnedVal) returns Customer? { - if (returnedVal is CustomerValue) { - return returnedVal.value; +function getCustomer(record {|CustomerX value;|}? returnedVal) returns CustomerX? { + if (returnedVal is CustomerValueX) { + return returnedVal.value; } else { - return (); + return (); } } @@ -107,13 +107,13 @@ function getCustomer(record {| Customer value; |}? returnedVal) returns Customer function testSimpleQueryReturnStream() returns boolean { boolean testPassed = true; - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonX p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonX p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonX p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonX[] personList = [p1, p2, p3]; - stream outputPersonStream = stream from var person in personList + stream outputPersonStream = stream from var person in personList where person.firstName == "John" let int newAge = 34 select { @@ -122,7 +122,7 @@ function testSimpleQueryReturnStream() returns boolean { age: newAge }; - record {| Person value; |}? person = getPersonValue(outputPersonStream.next()); + record {|PersonX value;|}? person = getPersonValue(outputPersonStream.next()); testPassed = testPassed && person?.value?.firstName == "John" && person?.value?.lastName == "David" && person?.value?.age == 34; @@ -135,11 +135,11 @@ function testSimpleQueryReturnStream() returns boolean { function testSimpleQueryReturnStream2() { boolean testPassed = true; - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonX p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonX p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonX p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonX[] personList = [p1, p2, p3]; var outputPersonStream = stream from var person in personList where person.firstName == "John" @@ -150,10 +150,10 @@ function testSimpleQueryReturnStream2() { age: newAge }; - assertTrue(outputPersonStream is stream); - stream _ = outputPersonStream; + assertTrue(outputPersonStream is stream); + stream _ = outputPersonStream; - record {| Person value; |}? person = getPersonValue(outputPersonStream.next()); + record {|PersonX value;|}? person = getPersonValue(outputPersonStream.next()); testPassed = testPassed && person?.value?.firstName == "John" && person?.value?.lastName == "David" && person?.value?.age == 34; @@ -163,37 +163,39 @@ function testSimpleQueryReturnStream2() { assertTrue(testPassed); } -type ValueRecord record {| +type ValueRecordX record {| string value; |}; -type TestStream stream; +type TestStreamX stream; -class TestGenerator { - public isolated function next() returns ValueRecord|error? { +class TestGeneratorX { + public isolated function next() returns ValueRecordX|error? { return {value: "Ballerina"}; } } function testSimpleQueryReturnStream3() { - TestGenerator generator = new (); - TestStream testStream = new (generator); + TestGeneratorX generator = new (); + TestStreamX testStream = new (generator); - var outputIntPersonStream = stream from var _ in testStream select 1; + var outputIntPersonStream = stream from var _ in testStream + select 1; assertTrue(outputIntPersonStream is stream); stream _ = outputIntPersonStream; - (record {| int value; |}|error)? x1 = outputIntPersonStream.next(); - if (x1 is record {| int value; |}) { + (record {|int value;|}|error)? x1 = outputIntPersonStream.next(); + if (x1 is record {|int value;|}) { assertEqual(x1.value, 1); } else { assertTrue(false); } - var outputStringPersonStream = stream from var _ in testStream select "ABCD"; + var outputStringPersonStream = stream from var _ in testStream + select "ABCD"; assertTrue(outputStringPersonStream is stream); stream _ = outputStringPersonStream; - (record {| string value; |}|error)? x2 = outputStringPersonStream.next(); - if (x2 is record {| string value; |}) { + (record {|string value;|}|error)? x2 = outputStringPersonStream.next(); + if (x2 is record {|string value;|}) { assertEqual(x2.value, "ABCD"); } else { assertTrue(false); @@ -203,30 +205,30 @@ function testSimpleQueryReturnStream3() { function testStreamInFromClauseWithReturnStream() returns boolean { boolean testPassed = true; - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; - - Person[] personList = [p1, p2, p3]; - - stream outputEmployeeStream = stream from var {firstName, lastName, dept} in - >personList.toStream().filter(function (Person person) returns boolean { - return person.firstName == "John"; - }).'map(function (Person person) returns Employee { - Employee employee = { - firstName: person.firstName, - lastName: person.lastName, - dept: "Engineering" - }; - return employee; - }) - select { - firstName: firstName, - lastName: lastName, - dept: dept - }; - - record {| Employee value; |}? employee = getEmployeeValue(outputEmployeeStream.next()); + PersonX p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonX p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonX p3 = {firstName: "John", lastName: "David", age: 33}; + + PersonX[] personList = [p1, p2, p3]; + + stream outputEmployeeStream = stream from var {firstName, lastName, dept} in + >personList.toStream().filter(function(PersonX person) returns boolean { + return person.firstName == "John"; + }).'map(function(PersonX person) returns EmployeeX { + EmployeeX employee = { + firstName: person.firstName, + lastName: person.lastName, + dept: "Engineering" + }; + return employee; + }) + select { + firstName: firstName, + lastName: lastName, + dept: dept + }; + + record {|EmployeeX value;|}? employee = getEmployeeValue(outputEmployeeStream.next()); testPassed = testPassed && employee?.value?.firstName == "John" && employee?.value?.lastName == "David" && employee?.value?.dept == "Engineering"; @@ -239,33 +241,33 @@ function testStreamInFromClauseWithReturnStream() returns boolean { function testStreamInFromClauseWithReturnStream2() { boolean testPassed = true; - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonX p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonX p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonX p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonX[] personList = [p1, p2, p3]; var outputEmployeeStream = stream from var {firstName, lastName, dept} in - >personList.toStream().filter(function (Person person) returns boolean { - return person.firstName == "John"; - }).'map(function (Person person) returns Employee { - Employee employee = { - firstName: person.firstName, - lastName: person.lastName, - dept: "Engineering" - }; - return employee; - }) - select { - firstName: firstName, - lastName: lastName, - dept: dept - }; - - assertTrue(outputEmployeeStream is stream); - stream _ = outputEmployeeStream; - - record {| Employee value; |}? employee = getEmployeeValue(outputEmployeeStream.next()); + >personList.toStream().filter(function(PersonX person) returns boolean { + return person.firstName == "John"; + }).'map(function(PersonX person) returns EmployeeX { + EmployeeX employee = { + firstName: person.firstName, + lastName: person.lastName, + dept: "Engineering" + }; + return employee; + }) + select { + firstName: firstName, + lastName: lastName, + dept: dept + }; + + assertTrue(outputEmployeeStream is stream); + stream _ = outputEmployeeStream; + + record {|EmployeeX value;|}? employee = getEmployeeValue(outputEmployeeStream.next()); testPassed = testPassed && employee?.value?.firstName == "John" && employee?.value?.lastName == "David" && employee?.value?.dept == "Engineering"; @@ -277,28 +279,28 @@ function testStreamInFromClauseWithReturnStream2() { function testMultipleFromWhereAndLetReturnStream() returns boolean { boolean testPassed = true; - Employee e1 = {firstName: "John", lastName: "Fonseka", dept: "Engineering"}; - Employee e2 = {firstName: "John", lastName: "David", dept: "HR"}; + EmployeeX e1 = {firstName: "John", lastName: "Fonseka", dept: "Engineering"}; + EmployeeX e2 = {firstName: "John", lastName: "David", dept: "HR"}; - Department d1 = {dept: "Support"}; - Department d2 = {dept: "Dev"}; + DepartmentX d1 = {dept: "Support"}; + DepartmentX d2 = {dept: "Dev"}; - Employee[] employeeList = [e1, e2]; - Department[] departmentList = [d1, d2]; + EmployeeX[] employeeList = [e1, e2]; + DepartmentX[] departmentList = [d1, d2]; - stream outputEmployeeStream = stream from var emp in employeeList - from var department in departmentList - where emp.firstName == "John" - where emp.dept == "Engineering" - let string fname = "Johns" - let string deptName = "Research" - select { - firstName: fname, - lastName: emp.lastName, - dept: deptName - }; + stream outputEmployeeStream = stream from var emp in employeeList + from var department in departmentList + where emp.firstName == "John" + where emp.dept == "Engineering" + let string fname = "Johns" + let string deptName = "Research" + select { + firstName: fname, + lastName: emp.lastName, + dept: deptName + }; - record {| Employee value; |}? employee = getEmployeeValue(outputEmployeeStream.next()); + record {|EmployeeX value;|}? employee = getEmployeeValue(outputEmployeeStream.next()); testPassed = testPassed && employee?.value?.firstName == "Johns" && employee?.value?.lastName == "Fonseka" && employee?.value?.dept == "Research"; @@ -315,31 +317,31 @@ function testMultipleFromWhereAndLetReturnStream() returns boolean { function testMultipleFromWhereAndLetReturnStream2() { boolean testPassed = true; - Employee e1 = {firstName: "John", lastName: "Fonseka", dept: "Engineering"}; - Employee e2 = {firstName: "John", lastName: "David", dept: "HR"}; + EmployeeX e1 = {firstName: "John", lastName: "Fonseka", dept: "Engineering"}; + EmployeeX e2 = {firstName: "John", lastName: "David", dept: "HR"}; - Department d1 = {dept: "Support"}; - Department d2 = {dept: "Dev"}; + DepartmentX d1 = {dept: "Support"}; + DepartmentX d2 = {dept: "Dev"}; - Employee[] employeeList = [e1, e2]; - Department[] departmentList = [d1, d2]; + EmployeeX[] employeeList = [e1, e2]; + DepartmentX[] departmentList = [d1, d2]; var outputEmployeeStream = stream from var emp in employeeList - from var department in departmentList - where emp.firstName == "John" - where emp.dept == "Engineering" - let string fname = "Johns" - let string deptName = "Research" - select { - firstName: fname, - lastName: emp.lastName, - dept: deptName - }; - - assertTrue(outputEmployeeStream is stream); - stream _ = outputEmployeeStream; - - record {| Employee value; |}? employee = getEmployeeValue(outputEmployeeStream.next()); + from var department in departmentList + where emp.firstName == "John" + where emp.dept == "Engineering" + let string fname = "Johns" + let string deptName = "Research" + select { + firstName: fname, + lastName: emp.lastName, + dept: deptName + }; + + assertTrue(outputEmployeeStream is stream); + stream _ = outputEmployeeStream; + + record {|EmployeeX value;|}? employee = getEmployeeValue(outputEmployeeStream.next()); testPassed = testPassed && employee?.value?.firstName == "Johns" && employee?.value?.lastName == "Fonseka" && employee?.value?.dept == "Research"; @@ -352,85 +354,90 @@ function testMultipleFromWhereAndLetReturnStream2() { assertTrue(testPassed); } -type Employee2 record { +type Employee2X record { readonly string name; int salary; }; -type Tbl table key(name); +type TblX table key(name); function testConstructTablesWithRecords() { - table key(name) t = table [ - { name: "John", salary: 100 }, - { name: "Jane", salary: 200 } + table key(name) t = table [ + {name: "John", salary: 100}, + {name: "Jane", salary: 200} ]; - var ct = from Employee2 e in t select e; - assertTrue(ct is table); - table a = ct; + var ct = from Employee2X e in t + select e; + assertTrue(ct is table); + table a = ct; assertEqual(a.toString(), "[{\"name\":\"John\",\"salary\":100},{\"name\":\"Jane\",\"salary\":200}]"); - table key(name) t2 = table [ - { name: "John", salary: 100 }, - { name: "Jane", salary: 200 } - ]; + table key(name) t2 = table [ + {name: "John", salary: 100}, + {name: "Jane", salary: 200} + ]; - var ct2 = from record { readonly string name; int salary; } e in t2 select e; - assertTrue(ct2 is table); - table a2 = ct2; + var ct2 = from record {readonly string name; int salary;} e in t2 + select e; + assertTrue(ct2 is table); + table a2 = ct2; assertEqual(a2.toString(), "[{\"name\":\"John\",\"salary\":100},{\"name\":\"Jane\",\"salary\":200}]"); - var ct3 = from Employee2 e in t select {name: e.name}; + var ct3 = from Employee2X e in t + select {name: e.name}; assertTrue(ct3 is table); table a3 = ct3; assertEqual(a3.toString(), "[{\"name\":\"John\"},{\"name\":\"Jane\"}]"); - Tbl t3 = table [ - { name: "John", salary: 100 }, - { name: "Jane", salary: 200 } + TblX t3 = table [ + {name: "John", salary: 100}, + {name: "Jane", salary: 200} ]; - var ct4 = from Employee2 e in t3 select e; - assertTrue(ct4 is table); - table a4 = ct4; + var ct4 = from Employee2X e in t3 + select e; + assertTrue(ct4 is table); + table a4 = ct4; assertEqual(a4.toString(), "[{\"name\":\"John\",\"salary\":100},{\"name\":\"Jane\",\"salary\":200}]"); } function testConstructMapsWithTuples() { map a = {"a": 1, "b": 2}; - var cm = map from var i in a select ["A",1]; - assertTrue(cm is map); - map cm2 = cm; - assertEqual(cm2, {"A": 1}); + var cm = map from var i in a + select ["A", 1]; + assertTrue(cm is map); + map cm2 = cm; + assertEqual(cm2, {"A": 1}); } function testInnerJoinAndLimitReturnStream() returns boolean { boolean testPassed = true; - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonX p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonX p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Employee e1 = {firstName: "Alex", lastName: "George", dept: "Engineering"}; - Employee e2 = {firstName: "John", lastName: "David", dept: "HR"}; - Employee e3 = {firstName: "Ranjan", lastName: "Fonseka", dept: "Operations"}; + EmployeeX e1 = {firstName: "Alex", lastName: "George", dept: "Engineering"}; + EmployeeX e2 = {firstName: "John", lastName: "David", dept: "HR"}; + EmployeeX e3 = {firstName: "Ranjan", lastName: "Fonseka", dept: "Operations"}; - Person[] personList = [p1, p2]; - Employee[] employeeList = [e1, e2, e3]; + PersonX[] personList = [p1, p2]; + EmployeeX[] employeeList = [e1, e2, e3]; - stream outputEmpProfileStream = stream from var person in personList.toStream() - join Employee employee in employeeList.toStream() + stream outputEmpProfileStream = stream from var person in personList.toStream() + join EmployeeX employee in employeeList.toStream() on person.firstName equals employee.firstName - limit 1 - select { - firstName: employee.firstName, - lastName: employee.lastName, - age: person.age, - dept: employee.dept, - status: "Permanent" - }; + limit 1 + select { + firstName: employee.firstName, + lastName: employee.lastName, + age: person.age, + dept: employee.dept, + status: "Permanent" + }; - record {| EmpProfile value; |}? empProfile = getEmpProfileValue(outputEmpProfileStream.next()); + record {|EmpProfileX value;|}? empProfile = getEmpProfileValue(outputEmpProfileStream.next()); testPassed = testPassed && empProfile?.value?.firstName == "Alex" && empProfile?.value?.lastName == "George" && empProfile?.value?.age == 23 && empProfile?.value?.dept == "Engineering" && empProfile?.value?.status == "Permanent"; @@ -444,32 +451,32 @@ function testInnerJoinAndLimitReturnStream() returns boolean { function testInnerJoinAndLimitReturnStream2() { boolean testPassed = true; - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonX p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonX p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Employee e1 = {firstName: "Alex", lastName: "George", dept: "Engineering"}; - Employee e2 = {firstName: "John", lastName: "David", dept: "HR"}; - Employee e3 = {firstName: "Ranjan", lastName: "Fonseka", dept: "Operations"}; + EmployeeX e1 = {firstName: "Alex", lastName: "George", dept: "Engineering"}; + EmployeeX e2 = {firstName: "John", lastName: "David", dept: "HR"}; + EmployeeX e3 = {firstName: "Ranjan", lastName: "Fonseka", dept: "Operations"}; - Person[] personList = [p1, p2]; - Employee[] employeeList = [e1, e2, e3]; + PersonX[] personList = [p1, p2]; + EmployeeX[] employeeList = [e1, e2, e3]; var outputEmpProfileStream = stream from var person in personList.toStream() - join Employee employee in employeeList.toStream() + join EmployeeX employee in employeeList.toStream() on person.firstName equals employee.firstName - limit 1 - select { - firstName: employee.firstName, - lastName: employee.lastName, - age: person.age, - dept: employee.dept, - status: "Permanent" - }; + limit 1 + select { + firstName: employee.firstName, + lastName: employee.lastName, + age: person.age, + dept: employee.dept, + status: "Permanent" + }; - assertTrue(outputEmpProfileStream is stream); - stream _ = outputEmpProfileStream; + assertTrue(outputEmpProfileStream is stream); + stream _ = outputEmpProfileStream; - record {| EmpProfile value; |}? empProfile = getEmpProfileValue(outputEmpProfileStream.next()); + record {|EmpProfileX value;|}? empProfile = getEmpProfileValue(outputEmpProfileStream.next()); testPassed = testPassed && empProfile?.value?.firstName == "Alex" && empProfile?.value?.lastName == "George" && empProfile?.value?.age == 23 && empProfile?.value?.dept == "Engineering" && empProfile?.value?.status == "Permanent"; @@ -484,22 +491,22 @@ function testInnerJoinAndLimitReturnStream2() { function testSimpleQueryExprReturnTable() returns boolean { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3]; + CustomerX[] customerList = [c1, c2, c3]; - CustomerTable customerTable = table key(id, name) from var customer in customerList + CustomerTableX customerTable = table key(id, name) from var customer in customerList select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems }; - if (customerTable is CustomerTable) { + if (customerTable is CustomerTableX) { var itr = customerTable.iterator(); - Customer? customer = getCustomer(itr.next()); + CustomerX? customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[0]; customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[1]; @@ -515,11 +522,11 @@ function testSimpleQueryExprReturnTable() returns boolean { function testSimpleQueryExprReturnTable2() { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3]; + CustomerX[] customerList = [c1, c2, c3]; var customerTable = table key(id, name) from var customer in customerList select { @@ -528,12 +535,12 @@ function testSimpleQueryExprReturnTable2() { noOfItems: customer.noOfItems }; - assertTrue(customerTable is CustomerTable); - CustomerTable _ = customerTable; + assertTrue(customerTable is CustomerTableX); + CustomerTableX _ = customerTable; - if (customerTable is CustomerTable) { + if (customerTable is CustomerTableX) { var itr = customerTable.iterator(); - Customer? customer = getCustomer(itr.next()); + CustomerX? customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[0]; customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[1]; @@ -547,26 +554,29 @@ function testSimpleQueryExprReturnTable2() { } function testTableWithDuplicateKeys() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; - Customer[] customerList = [c1, c2, c1]; + CustomerX[] customerList = [c1, c2, c1]; - CustomerTable customerTable = table key(id, name) from var customer in customerList + CustomerTableX customerTable = table key(id, name) from var customer in customerList select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems }; - assertEqual(customerTable, table key(id,name) [{"id":1,"name":"Melina","noOfItems":12},{"id":2,"name":"James","noOfItems":5}]); + assertEqual(customerTable, table key(id, name) [ + {"id": 1, "name": "Melina", "noOfItems": 12}, + {"id": 2, "name": "James", "noOfItems": 5} + ]); } function testTableWithDuplicateKeys2() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; - Customer[] customerList = [c1, c2, c1]; + CustomerX[] customerList = [c1, c2, c1]; var customerTable = table key(id, name) from var customer in customerList select { @@ -575,23 +585,26 @@ function testTableWithDuplicateKeys2() { noOfItems: customer.noOfItems }; - assertTrue(customerTable is CustomerTable); - CustomerTable _ = customerTable; + assertTrue(customerTable is CustomerTableX); + CustomerTableX _ = customerTable; - assertEqual(customerTable, table key(id,name) [{"id":1,"name":"Melina","noOfItems":12},{"id":2,"name":"James","noOfItems":5}]); + assertEqual(customerTable, table key(id, name) [ + {"id": 1, "name": "Melina", "noOfItems": 12}, + {"id": 2, "name": "James", "noOfItems": 5} + ]); } function testTableNoDuplicatesAndOnConflictReturnTable() returns boolean { boolean testPassed = true; error onConflictError = error("Key Conflict", message = "cannot insert."); - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3]; + CustomerX[] customerList = [c1, c2, c3]; - CustomerTable|error customerTable = table key(id, name) from var customer in customerList + CustomerTableX|error customerTable = table key(id, name) from var customer in customerList select { id: customer.id, name: customer.name, @@ -599,9 +612,9 @@ function testTableNoDuplicatesAndOnConflictReturnTable() returns boolean { } on conflict onConflictError; - if (customerTable is CustomerTable) { + if (customerTable is CustomerTableX) { var itr = customerTable.iterator(); - Customer? customer = getCustomer(itr.next()); + CustomerX? customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[0]; customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[1]; @@ -615,113 +628,113 @@ function testTableNoDuplicatesAndOnConflictReturnTable() returns boolean { } function testTableWithDuplicatesAndOnConflictReturnTable() { - error onConflictError = error("Key Conflict", message = "cannot insert."); + error onConflictError = error("Key Conflict", message = "cannot insert."); - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; - Customer[] customerList = [c1, c2, c1]; + CustomerX[] customerList = [c1, c2, c1]; - CustomerTable|error customerTable = table key(id, name) from var customer in customerList - select { - id: customer.id, - name: customer.name, - noOfItems: customer.noOfItems - } - on conflict onConflictError; + CustomerTableX|error customerTable = table key(id, name) from var customer in customerList + select { + id: customer.id, + name: customer.name, + noOfItems: customer.noOfItems + } + on conflict onConflictError; - validateKeyConflictError(customerTable); + validateKeyConflictError(customerTable); } function testQueryExprWithOtherClausesReturnTable() { - error onConflictError = error("Key Conflict", message = "cannot insert."); + error onConflictError = error("Key Conflict", message = "cannot insert."); - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonX p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonX p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList = [c1, c2, c1]; - Person[] personList = [p1, p2]; + CustomerX[] customerList = [c1, c2, c1]; + PersonX[] personList = [p1, p2]; - CustomerTable|error customerTable = table key(id, name) from var customer in customerList - from var person in personList - let int items = 25 - let string customerName = "Bini" - where customer.id == 1 - where person.firstName == "Amy" - select { - id: customer.id, - name: customerName, - noOfItems: items - } - on conflict onConflictError; + CustomerTableX|error customerTable = table key(id, name) from var customer in customerList + from var person in personList + let int items = 25 + let string customerName = "Bini" + where customer.id == 1 + where person.firstName == "Amy" + select { + id: customer.id, + name: customerName, + noOfItems: items + } + on conflict onConflictError; - validateKeyConflictError(customerTable); + validateKeyConflictError(customerTable); } function validateKeyConflictError(any|error value) { - if (value is error) { - any|error detailMessage = value.detail()["message"]; - if (value.message() == "Key Conflict" + if (value is error) { + any|error detailMessage = value.detail()["message"]; + if (value.message() == "Key Conflict" && detailMessage is string && detailMessage == "cannot insert.") { - return; - } - panic error("Assertion error"); - } - panic error("Expected error, found: " + (typeof value).toString()); + return; + } + panic error("Assertion error"); + } + panic error("Expected error, found: " + (typeof value).toString()); } function testQueryExprWithJoinClauseReturnTable() { - error onConflictError = error("Key Conflict", message = "cannot insert."); + error onConflictError = error("Key Conflict", message = "cannot insert."); - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonX p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonX p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList = [c1, c2, c1]; - Person[] personList = [p1, p2]; + CustomerX[] customerList = [c1, c2, c1]; + PersonX[] personList = [p1, p2]; - CustomerTable|error customerTable = table key(id, name) from var customer in customerList - join var person in personList - on customer.name equals person.lastName - select { - id: customer.id, - name: person.firstName, - noOfItems: customer.noOfItems - } - on conflict onConflictError; + CustomerTableX|error customerTable = table key(id, name) from var customer in customerList + join var person in personList + on customer.name equals person.lastName + select { + id: customer.id, + name: person.firstName, + noOfItems: customer.noOfItems + } + on conflict onConflictError; - validateKeyConflictError(customerTable); + validateKeyConflictError(customerTable); } function testQueryExprWithLimitClauseReturnTable() returns boolean { - boolean testPassed = true; - error onConflictError = error("Key Conflict", message = "cannot insert."); - - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Melina", noOfItems: 25}; - - Customer[] customerList = [c1, c2, c3]; - - CustomerTable|error customerTable = table key(id, name) from var customer in customerList.toStream() - where customer.name == "Melina" - limit 1 - select { - id: customer.id, - name: customer.name, - noOfItems: customer.noOfItems - } - on conflict onConflictError; - - if (customerTable is CustomerTable) { + boolean testPassed = true; + error onConflictError = error("Key Conflict", message = "cannot insert."); + + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Melina", noOfItems: 25}; + + CustomerX[] customerList = [c1, c2, c3]; + + CustomerTableX|error customerTable = table key(id, name) from var customer in customerList.toStream() + where customer.name == "Melina" + limit 1 + select { + id: customer.id, + name: customer.name, + noOfItems: customer.noOfItems + } + on conflict onConflictError; + + if (customerTable is CustomerTableX) { var itr = customerTable.iterator(); - Customer? customer = getCustomer(itr.next()); + CustomerX? customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[0]; customer = getCustomer(itr.next()); testPassed = testPassed && customer == (); @@ -733,22 +746,22 @@ function testQueryExprWithLimitClauseReturnTable() returns boolean { function testKeyLessTableWithReturnTable() returns boolean { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3]; + CustomerX[] customerList = [c1, c2, c3]; - CustomerKeyLessTable customerTable = table key(id, name) from var customer in customerList + CustomerKeyLessTableX customerTable = table key(id, name) from var customer in customerList select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems }; - if (customerTable is CustomerKeyLessTable) { + if (customerTable is CustomerKeyLessTableX) { var itr = customerTable.iterator(); - Customer? customer = getCustomer(itr.next()); + CustomerX? customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[0]; customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[1]; @@ -761,7 +774,7 @@ function testKeyLessTableWithReturnTable() returns boolean { return testPassed; } -type User record { +type UserX record { readonly int id; string firstName; string lastName; @@ -769,67 +782,67 @@ type User record { }; function testQueryConstructingTableUpdateKeyPanic1() returns error? { - table key(id) users = table [ + table key(id) users = table [ {id: 1, firstName: "John", lastName: "Doe", age: 25} ]; var result = table key(id, name) from var user in users - where user.age > 21 && user.age < 60 - select {id: user.id, name: user.firstName, user}; + where user.age > 21 && user.age < 60 + select {id: user.id, name: user.firstName, user}; var r2 = result[1, "John"]; + UserX user; + }>result[1, "John"]; r2.id = 1; } -type NewUser record {| +type NewUserX record {| readonly int id; readonly string name; - User user; + UserX user; |}; function testQueryConstructingTableUpdateKeyPanic2() returns error? { - table key(id) users = table [ + table key(id) users = table [ {id: 1, firstName: "John", lastName: "Doe", age: 25} ]; - table key(id, name) result = - table key(id, name) from var user in users - where user.age > 21 && user.age < 60 - select {id: user.id, name: user.firstName, user}; + table key(id, name) result = + table key(id, name) from var user in users + where user.age > 21 && user.age < 60 + select {id: user.id, name: user.firstName, user}; var r2 = result[1, "John"]; + UserX user; + }>result[1, "John"]; r2.id = 2; } -type CustomErrorDetail record {| +type CustomErrorDetailX record {| string message; int code; |}; -type CustomError error; +type CustomErrorX error; function testTableOnConflict() { error? onConflictError1 = error("Key Conflict", message = "cannot insert."); error|null onConflictError2 = (); error|null onConflictError3 = null; - CustomError? onConflictError4 = error ("error msg 1", message = "error 1", code = 500); - CustomError? onConflictError5 = error ("error msg 2", message = "error 2", code = 500); + CustomErrorX? onConflictError4 = error("error msg 1", message = "error 1", code = 500); + CustomErrorX? onConflictError5 = error("error msg 2", message = "error 2", code = 500); - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 1, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 1, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3]; + CustomerX[] customerList = [c1, c2, c3]; var customerTable1 = table key(id) from var customer in customerList select { @@ -849,7 +862,10 @@ function testTableOnConflict() { } on conflict onConflictError2; - assertEqual(customerTable2, table key(id) [{"id":1,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}]); + assertEqual(customerTable2, table key(id) [ + {"id": 1, "name": "James", "noOfItems": 5}, + {"id": 3, "name": "Anne", "noOfItems": 20} + ]); var customerTable3 = table key(id) from var customer in customerList select { @@ -859,7 +875,10 @@ function testTableOnConflict() { } on conflict onConflictError3; - assertEqual(customerTable3, table key(id) [{"id":1,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}]); + assertEqual(customerTable3, table key(id) [ + {"id": 1, "name": "James", "noOfItems": 5}, + {"id": 3, "name": "Anne", "noOfItems": 20} + ]); var customerTable4 = table key(id) from var customer in customerList select { @@ -889,7 +908,10 @@ function testTableOnConflict() { } on conflict null; - assertEqual(customerTable6, table key(id) [{"id":1,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}]); + assertEqual(customerTable6, table key(id) [ + {"id": 1, "name": "James", "noOfItems": 5}, + {"id": 3, "name": "Anne", "noOfItems": 20} + ]); var customerTable7 = table key(id) from var customer in customerList select { @@ -899,7 +921,10 @@ function testTableOnConflict() { } on conflict (); - assertEqual(customerTable7, table key(id) [{"id":1,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}]); + assertEqual(customerTable7, table key(id) [ + {"id": 1, "name": "James", "noOfItems": 5}, + {"id": 3, "name": "Anne", "noOfItems": 20} + ]); } type Token record {| @@ -911,7 +936,8 @@ type TokenTable table key(idx); function testQueryConstructingTableWithOnConflictClauseHavingNonTableQueryInLetClause() { TokenTable|error tbl1 = table key(idx) from int i in 1 ... 3 - let int[] arr = from var j in 1 ... 3 select j + let int[] arr = from var j in 1 ... 3 + select j select { idx: arr[i - 1], value: "A" + i.toString() @@ -919,10 +945,10 @@ function testQueryConstructingTableWithOnConflictClauseHavingNonTableQueryInLetC on conflict error("Duplicate Key"); TokenTable expectedTbl = table [ - {"idx": 1, "value": "A1"}, - {"idx": 2, "value": "A2"}, - {"idx": 3, "value": "A3"} - ]; + {"idx": 1, "value": "A1"}, + {"idx": 2, "value": "A2"}, + {"idx": 3, "value": "A3"} + ]; assertEqual(true, tbl1 is TokenTable); if tbl1 is TokenTable { @@ -930,7 +956,8 @@ function testQueryConstructingTableWithOnConflictClauseHavingNonTableQueryInLetC } TokenTable|error tbl2 = table key(idx) from int i in [1, 2, 1] - let int[] arr = from var j in 1 ... 3 select j + let int[] arr = from var j in 1 ... 3 + select j select { idx: arr[i], value: "A" + i.toString() @@ -946,7 +973,8 @@ function testQueryConstructingTableWithOnConflictClauseHavingNonTableQueryInLetC function testQueryConstructingTableWithOnConflictClauseHavingNonTableQueryInWhereClause() { TokenTable|error tbl1 = table key(idx) from int i in 1 ... 3 let int[] arr = [1, 2, 3] - where arr == from int j in 1...3 select j + where arr == from int j in 1 ... 3 + select j select { idx: i, value: "A" + i.toString() @@ -954,10 +982,10 @@ function testQueryConstructingTableWithOnConflictClauseHavingNonTableQueryInWher on conflict error("Duplicate Key"); TokenTable expectedTbl = table [ - {"idx": 1, "value": "A1"}, - {"idx": 2, "value": "A2"}, - {"idx": 3, "value": "A3"} - ]; + {"idx": 1, "value": "A1"}, + {"idx": 2, "value": "A2"}, + {"idx": 3, "value": "A3"} + ]; assertEqual(true, tbl1 is TokenTable); if tbl1 is TokenTable { @@ -966,7 +994,8 @@ function testQueryConstructingTableWithOnConflictClauseHavingNonTableQueryInWher TokenTable|error tbl2 = table key(idx) from int i in [1, 2, 1] let int[] arr = [1, 2, 3] - where arr == from int j in 1...3 select j + where arr == from int j in 1 ... 3 + select j select { idx: i, value: "A" + i.toString() @@ -980,7 +1009,7 @@ function testQueryConstructingTableWithOnConflictClauseHavingNonTableQueryInWher } function testQueryConstructingTableWithOnConflictsWithVarRef() { - TokenTable|error tbl1 = table key(idx) from int i in [1, 2, 3, 1, 2, 3] + TokenTable|error tbl1 = table key(idx) from int i in [1, 2, 3, 1, 2, 3] let string value = "A" + i.toString() select { idx: i, @@ -1004,13 +1033,13 @@ function testQueryConstructingTableWithOnConflictsWithVarRef() { } function testMapConstructingQueryExpr() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] list1 = [c1, c2, c3]; + CustomerX[] list1 = [c1, c2, c3]; - map map1 = map from var customer in list1 + map map1 = map from var customer in list1 select [customer.id.toString(), customer]; assertEqual(map1, {"1": {id: 1, name: "Melina", noOfItems: 12}, "2": {id: 2, name: "James", noOfItems: 5}, "3": {id: 3, name: "Anne", noOfItems: 20}}); @@ -1045,37 +1074,37 @@ function testMapConstructingQueryExpr() { map map4 = map from var item in list4 select [item[0], item[1]]; - map expectedMap = {"a":123,"b":123,"c":error("Error"),"zero":0}; + map expectedMap = {"a": 123, "b": 123, "c": error("Error"), "zero": 0}; - assertEqual(expectedMap.length(), (> map4).length()); + assertEqual(expectedMap.length(), (>map4).length()); foreach var key in expectedMap.keys() { - assertEqual(expectedMap[key], (> map4)[key]); + assertEqual(expectedMap[key], (>map4)[key]); } } function testMapConstructingQueryExpr2() { map map1 = map from var e in map from var e in [1, 2, 10, 3, 5, 20] - order by e descending - select [e.toString(), e] - order by e ascending - select [e.toString(), e]; - assertEqual(map1, {"1":1,"2":2,"3":3,"5":5,"10":10,"20":20}); + order by e descending + select [e.toString(), e] + order by e ascending + select [e.toString(), e]; + assertEqual(map1, {"1": 1, "2": 2, "3": 3, "5": 5, "10": 10, "20": 20}); map map2 = map from var e in (from var e in [1, 2, 5, 4] - let int f = e / 2 - order by f ascending - select f) - order by e descending - select [e.toString(), e]; - assertEqual(map2, {"2":2,"1":1,"0":0}); + let int f = e / 2 + order by f ascending + select f) + order by e descending + select [e.toString(), e]; + assertEqual(map2, {"2": 2, "1": 1, "0": 0}); } function testMapConstructingQueryExprWithDuplicateKeys() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] list1 = [c1, c2, c3, c1, c2, c3]; + CustomerX[] list1 = [c1, c2, c3, c1, c2, c3]; var map1 = map from var customer in list1 select [customer.id.toString(), customer]; @@ -1104,12 +1133,12 @@ function testMapConstructingQueryExprWithDuplicateKeys() { } function testMapConstructingQueryExprWithOnConflict() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; ()|error conflictMsg1 = (); - Customer[] list1 = [c1, c2, c3, c1, c2, c3]; + CustomerX[] list1 = [c1, c2, c3, c1, c2, c3]; var mapWithOnConflict1 = map from var customer in list1 select [customer.id.toString(), customer] @@ -1136,7 +1165,7 @@ function testMapConstructingQueryExprWithOnConflict() { [string:Char, int:Signed16] t5 = ["b", 123]; [string, int] t6 = ["c", -123]; [string, int:Unsigned32] t7 = ["zero", 0]; - CustomError? onConflictError4 = error("error msg 1", message = "Error 2", code = 500); + CustomErrorX? onConflictError4 = error("error msg 1", message = "Error 2", code = 500); [string, int][] list3 = [t4, t5, t4, t6, t6, t7, t7, t4]; map|error map3 = map from var item in list3 @@ -1155,14 +1184,14 @@ function testMapConstructingQueryExprWithOnConflict() { function testMapConstructingQueryExprWithOtherClauses() { error onConflictError = error("Key Conflict", message = "cannot insert."); - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonX p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonX p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList1 = [c1, c2, c2]; - Person[] personList = [p1, p2]; + CustomerX[] customerList1 = [c1, c2, c2]; + PersonX[] personList = [p1, p2]; var selectedCustomers1 = map from var customer in customerList1 from var person in personList @@ -1180,14 +1209,14 @@ function testMapConstructingQueryExprWithOtherClauses() { on conflict onConflictError; assertEqual(selectedCustomers1, {"Amy Melina": {"id": 1, "name": "Amy Melina"}}); - Customer c3 = {id: 1, name: "Melina", noOfItems: 22}; - Customer c4 = {id: 2, name: "James", noOfItems: 15}; - Customer c5 = {id: 1, name: "Melina", noOfItems: 10}; - Customer c6 = {id: 2, name: "James", noOfItems: 11}; + CustomerX c3 = {id: 1, name: "Melina", noOfItems: 22}; + CustomerX c4 = {id: 2, name: "James", noOfItems: 15}; + CustomerX c5 = {id: 1, name: "Melina", noOfItems: 10}; + CustomerX c6 = {id: 2, name: "James", noOfItems: 11}; - Customer[] customerList2 = [c1, c2, c3, c4, c5, c6]; + CustomerX[] customerList2 = [c1, c2, c3, c4, c5, c6]; - map|error selectedCustomers2 = map from var customer in customerList2 + map|error selectedCustomers2 = map from var customer in customerList2 from var person in personList let string fullName = person.firstName + " " + person.lastName where customer.name == person.lastName @@ -1224,195 +1253,218 @@ function testMapConstructingQueryExprWithOtherClauses() { function testMapConstructingQueryExprWithJoinClause() { error onConflictError = error("Key Conflict", message = "cannot insert."); - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonX p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonX p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList1 = [c1, c2, c1]; - Person[] personList = [p1, p2]; + CustomerX[] customerList1 = [c1, c2, c1]; + PersonX[] personList = [p1, p2]; var customerMap1 = map from var customer in customerList1 join var person in personList on customer.name equals person.lastName - select [customer.id.toString(), { - id: customer.id, - name: person.firstName, - age: person.age, - noOfItems: customer.noOfItems - }] + select [ + customer.id.toString(), + { + id: customer.id, + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems + } + ] on conflict onConflictError; assertEqual(customerMap1, onConflictError); - Customer[] customerList2 = [c1, c2]; + CustomerX[] customerList2 = [c1, c2]; var customerMap2 = map from var customer in customerList2 join var person in personList on customer.name equals person.lastName - select [customer.id.toString(), { - id: customer.id, - name: person.firstName, - age: person.age, - noOfItems: customer.noOfItems - }] + select [ + customer.id.toString(), + { + id: customer.id, + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems + } + ] on conflict onConflictError; - assertEqual(customerMap2, {"1":{"id":1,"name":"Amy","age":23,"noOfItems":12},"2":{"id":2,"name":"Frank","age":30,"noOfItems":5}}); + assertEqual(customerMap2, {"1": {"id": 1, "name": "Amy", "age": 23, "noOfItems": 12}, "2": {"id": 2, "name": "Frank", "age": 30, "noOfItems": 5}}); } function testMapConstructingQueryExprWithLimitClause() { error onConflictError = error("Key Conflict", message = "cannot insert."); - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Melina", noOfItems: 25}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Melina", noOfItems: 25}; - Customer[] customerList = [c1, c2, c3]; + CustomerX[] customerList = [c1, c2, c3]; - map|error customerMap1 = map from var customer in customerList.toStream() + map|error customerMap1 = map from var customer in customerList.toStream() where customer.name == "Melina" limit 1 - select [customer.name, { - id: customer.id, - name: customer.name, - noOfItems: customer.noOfItems - }] + select [ + customer.name, + { + id: customer.id, + name: customer.name, + noOfItems: customer.noOfItems + } + ] on conflict onConflictError; - assertEqual(customerMap1, {"Melina":{"id":1,"name":"Melina","noOfItems":12}}); + assertEqual(customerMap1, {"Melina": {"id": 1, "name": "Melina", "noOfItems": 12}}); } function testMapConstructingQueryExprWithOrderByClause() { map sorted1 = map from var e in [1, 2, 10, 3, 5, 20] - order by e ascending - select [e.toString(), e]; - assertEqual(sorted1, {"1":1,"2":2,"3":3,"5":5,"10":10,"20":20}); + order by e ascending + select [e.toString(), e]; + assertEqual(sorted1, {"1": 1, "2": 2, "3": 3, "5": 5, "10": 10, "20": 20}); map sorted2 = map from var e in [1, 2, 10, 3, 5, 20] - order by e descending - select [e.toString(), e]; - assertEqual(sorted2, {"20":20,"10":10,"5":5,"3":3,"2":2,"1":1}); + order by e descending + select [e.toString(), e]; + assertEqual(sorted2, {"20": 20, "10": 10, "5": 5, "3": 3, "2": 2, "1": 1}); var sorted3 = map from var e in ["1", "2", "10", "3", "5", "20"] - order by e ascending - select [e, e] on conflict (); - assertEqual(sorted3, {"1":"1","10":"10","2":"2","20":"20","3":"3","5":"5"}); + order by e ascending + select [e, e] + on conflict (); + assertEqual(sorted3, {"1": "1", "10": "10", "2": "2", "20": "20", "3": "3", "5": "5"}); var sorted4 = map from var e in [1, 2, 5, 4] - let int f = e / 2 - order by f ascending - select [f.toString(), e] on conflict error("Error"); + let int f = e / 2 + order by f ascending + select [f.toString(), e] + on conflict error("Error"); assertEqual(sorted4, error("Error")); } type Error error; + type Json json; + type IntOrString int|string; + type ZeroOrOne 1|0; type MapOfJsonOrError map|Error; + type MapOfIntOrError map|Error; + type ErrorOrMapOfZeroOrOne Error|map; function testMapConstructingQueryExprWithReferenceTypes() { map sorted1 = map from var e in [1, 0, 1, 0, 1, 1] - order by e ascending - select [e.toString(), e]; - assertEqual(sorted1, {"0":0,"1":1}); + order by e ascending + select [e.toString(), e]; + assertEqual(sorted1, {"0": 0, "1": 1}); ErrorOrMapOfZeroOrOne sorted2 = map from var e in [1, 0, 1, 0, 0, 0] - order by e ascending - select [e.toString(), e]; - assertEqual(sorted2, {"0":0,"1":1}); + order by e ascending + select [e.toString(), e]; + assertEqual(sorted2, {"0": 0, "1": 1}); map sorted3 = map from var e in ["1", "2", "10", "3", "5", "20"] - order by e ascending - select [e, e] on conflict (); - assertEqual(sorted3, {"1":"1","10":"10","2":"2","20":"20","3":"3","5":"5"}); + order by e ascending + select [e, e] + on conflict (); + assertEqual(sorted3, {"1": "1", "10": "10", "2": "2", "20": "20", "3": "3", "5": "5"}); MapOfJsonOrError sorted4 = map from var e in ["1", "2", "10", "3", "5", "20"] - order by e ascending - select [e, e] on conflict error("Error"); - assertEqual(sorted4, {"1":"1","10":"10","2":"2","20":"20","3":"3","5":"5"}); + order by e ascending + select [e, e] + on conflict error("Error"); + assertEqual(sorted4, {"1": "1", "10": "10", "2": "2", "20": "20", "3": "3", "5": "5"}); MapOfIntOrError sorted5 = map from var e in [1, 2, 5, 4] - let int f = e / 2 - order by f ascending - select [f.toString(), e] on conflict error("Error"); + let int f = e / 2 + order by f ascending + select [f.toString(), e] + on conflict error("Error"); assertEqual(sorted5, error("Error")); } function testReadonlyTable() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList1 = [c1, c2, c3]; + CustomerX[] customerList1 = [c1, c2, c3]; - CustomerKeyLessTable & readonly customerTable1 = table key(id, name) from var customer in customerList1 + CustomerKeyLessTableX & readonly customerTable1 = table key(id, name) from var customer in customerList1 select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems }; - any _ = customerTable1; - assertEqual((typeof(customerTable1)).toString(), "typedesc [{\"id\":1,\"name\":\"Melina\",\"noOfItems\":12},{\"id\":2,\"name\":\"James\",\"noOfItems\":5},{\"id\":3,\"name\":\"Anne\",\"noOfItems\":20}]"); - assertEqual(customerTable1.toString(), [{"id":1,"name":"Melina","noOfItems":12},{"id":2,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}].toString()); + any _ = customerTable1; + assertEqual((typeof (customerTable1)).toString(), "typedesc [{\"id\":1,\"name\":\"Melina\",\"noOfItems\":12},{\"id\":2,\"name\":\"James\",\"noOfItems\":5},{\"id\":3,\"name\":\"Anne\",\"noOfItems\":20}]"); + assertEqual(customerTable1.toString(), [{"id": 1, "name": "Melina", "noOfItems": 12}, {"id": 2, "name": "James", "noOfItems": 5}, {"id": 3, "name": "Anne", "noOfItems": 20}].toString()); - Customer[] & readonly customerList2 = [c1, c2, c3].cloneReadOnly(); + CustomerX[] & readonly customerList2 = [c1, c2, c3].cloneReadOnly(); - CustomerKeyLessTable & readonly|error customerTable2 = table key(id, name) from var customer in customerList2 + CustomerKeyLessTableX & readonly|error customerTable2 = table key(id, name) from var customer in customerList2 select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems - } on conflict error("Error"); - any _ = (checkpanic customerTable2); - assertEqual((typeof(checkpanic customerTable2)).toString(), "typedesc [{\"id\":1,\"name\":\"Melina\",\"noOfItems\":12},{\"id\":2,\"name\":\"James\",\"noOfItems\":5},{\"id\":3,\"name\":\"Anne\",\"noOfItems\":20}]"); - assertEqual((checkpanic customerTable2).toString(), [{"id":1,"name":"Melina","noOfItems":12},{"id":2,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}].toString()); + } + on conflict error("Error"); + any _ = (checkpanic customerTable2); + assertEqual((typeof (checkpanic customerTable2)).toString(), "typedesc [{\"id\":1,\"name\":\"Melina\",\"noOfItems\":12},{\"id\":2,\"name\":\"James\",\"noOfItems\":5},{\"id\":3,\"name\":\"Anne\",\"noOfItems\":20}]"); + assertEqual((checkpanic customerTable2).toString(), [{"id": 1, "name": "Melina", "noOfItems": 12}, {"id": 2, "name": "James", "noOfItems": 5}, {"id": 3, "name": "Anne", "noOfItems": 20}].toString()); - CustomerKeyLessTable customerTable3 = table key(id, name) from var customer in customerList2 + CustomerKeyLessTableX customerTable3 = table key(id, name) from var customer in customerList2 select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems - } on conflict (); - assertEqual((typeof(customerTable3)).toString(), "typedesc table key(id, name)"); - assertEqual(customerTable3.toString(), [{"id":1,"name":"Melina","noOfItems":12},{"id":2,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}].toString()); + } + on conflict (); + assertEqual((typeof (customerTable3)).toString(), "typedesc table key(id, name)"); + assertEqual(customerTable3.toString(), [{"id": 1, "name": "Melina", "noOfItems": 12}, {"id": 2, "name": "James", "noOfItems": 5}, {"id": 3, "name": "Anne", "noOfItems": 20}].toString()); } function testReadonlyTable2() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList1 = [c1, c2, c3]; + CustomerX[] customerList1 = [c1, c2, c3]; - CustomerTable & readonly customerTable1 = table key(id, name) from var customer in customerList1 + CustomerTableX & readonly customerTable1 = table key(id, name) from var customer in customerList1 select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems }; - any _ = customerTable1; + any _ = customerTable1; assertEqual((typeof customerTable1).toString(), "typedesc [{\"id\":1,\"name\":\"Melina\",\"noOfItems\":12},{\"id\":2,\"name\":\"James\",\"noOfItems\":5},{\"id\":3,\"name\":\"Anne\",\"noOfItems\":20}]"); - assertEqual(customerTable1.toString(), [{"id":1,"name":"Melina","noOfItems":12},{"id":2,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}].toString()); + assertEqual(customerTable1.toString(), [{"id": 1, "name": "Melina", "noOfItems": 12}, {"id": 2, "name": "James", "noOfItems": 5}, {"id": 3, "name": "Anne", "noOfItems": 20}].toString()); - Customer[] customerList2 = [c1, c2, c3]; + CustomerX[] customerList2 = [c1, c2, c3]; - CustomerTable & readonly|error customerTable2 = table key(id, name) from var customer in customerList2 - select customer.cloneReadOnly() on conflict error("Error"); - any _ = (checkpanic customerTable2); - assertEqual((typeof(checkpanic customerTable2)).toString(), "typedesc [{\"id\":1,\"name\":\"Melina\",\"noOfItems\":12},{\"id\":2,\"name\":\"James\",\"noOfItems\":5},{\"id\":3,\"name\":\"Anne\",\"noOfItems\":20}]"); - assertEqual((checkpanic customerTable2).toString(), [{"id":1,"name":"Melina","noOfItems":12},{"id":2,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}].toString()); + CustomerTableX & readonly|error customerTable2 = table key(id, name) from var customer in customerList2 + select customer.cloneReadOnly() + on conflict error("Error"); + any _ = (checkpanic customerTable2); + assertEqual((typeof (checkpanic customerTable2)).toString(), "typedesc [{\"id\":1,\"name\":\"Melina\",\"noOfItems\":12},{\"id\":2,\"name\":\"James\",\"noOfItems\":5},{\"id\":3,\"name\":\"Anne\",\"noOfItems\":20}]"); + assertEqual((checkpanic customerTable2).toString(), [{"id": 1, "name": "Melina", "noOfItems": 12}, {"id": 2, "name": "James", "noOfItems": 5}, {"id": 3, "name": "Anne", "noOfItems": 20}].toString()); - CustomerTable customerTable3 = table key(id, name) from var customer in customerList2 + CustomerTableX customerTable3 = table key(id, name) from var customer in customerList2 select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems - } on conflict (); - assertEqual((typeof(customerTable3)).toString(), "typedesc table key(id, name)"); - assertEqual(customerTable3.toString(), [{"id":1,"name":"Melina","noOfItems":12},{"id":2,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}].toString()); + } + on conflict (); + assertEqual((typeof (customerTable3)).toString(), "typedesc table key(id, name)"); + assertEqual(customerTable3.toString(), [{"id": 1, "name": "Melina", "noOfItems": 12}, {"id": 2, "name": "James", "noOfItems": 5}, {"id": 3, "name": "Anne", "noOfItems": 20}].toString()); } type IdRec record {| @@ -1420,76 +1472,89 @@ type IdRec record {| |}; function testReadonlyTable3() { - table key(id) & readonly|error tbl = table key(id) from var i in [1, 2, 3, 4, 2, 3] - select { - id: i - } on conflict (); - - assertEqual((typeof(tbl)).toString(), "typedesc [{\"id\":1},{\"id\":2},{\"id\":3},{\"id\":4}]"); - assertEqual(tbl, table key(id) [{"id":1},{"id":2},{"id":3},{"id":4}]); + table key(id) & readonly|error tbl = table key(id) from var i in [1, 2, 3, 4, 2, 3] + select { + id: i + } + on conflict (); - if tbl !is error { - IdRec? member1 = tbl[1]; - assertEqual(member1, {"id":1}); - } + assertEqual((typeof (tbl)).toString(), "typedesc [{\"id\":1},{\"id\":2},{\"id\":3},{\"id\":4}]"); + assertEqual(tbl, table key(id) [ + {"id": 1}, + {"id": 2}, + {"id": 3}, + {"id": 4} + ]); + + if tbl !is error { + IdRec? member1 = tbl[1]; + assertEqual(member1, {"id": 1}); + } - table & readonly tbl2 = table key() from var i in [1, 2, 3, 4, 2, 3] - select { - id: i - }; + table & readonly tbl2 = table key() from var i in [1, 2, 3, 4, 2, 3] + select { + id: i + }; - assertEqual((typeof(tbl2)).toString(), "typedesc [{\"id\":1},{\"id\":2},{\"id\":3},{\"id\":4},{\"id\":2},{\"id\":3}]"); - assertEqual(tbl2, table key() [{"id":1},{"id":2},{"id":3},{"id":4},{"id":2},{"id":3}]); + assertEqual((typeof (tbl2)).toString(), "typedesc [{\"id\":1},{\"id\":2},{\"id\":3},{\"id\":4},{\"id\":2},{\"id\":3}]"); + assertEqual(tbl2, table key() [ + {"id": 1}, + {"id": 2}, + {"id": 3}, + {"id": 4}, + {"id": 2}, + {"id": 3} + ]); } function testConstructingListOfTablesUsingQueryWithReadonly() { - table key(id) & readonly users1 = table [ + table key(id) & readonly users1 = table [ {id: 1, firstName: "John", lastName: "Doe", age: 25} ]; - (table key(id))[] uList = [users1]; + (table key(id))[] uList = [users1]; - (table key(id))[] & readonly result = from var user in uList - select user.cloneReadOnly(); - assertEqual((typeof(result)).toString(), "typedesc [[{\"id\":1,\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":25}]]"); - assertEqual(result, [table key(id) [{"id":1,"firstName":"John","lastName":"Doe","age":25}]]); + (table key(id))[] & readonly result = from var user in uList + select user.cloneReadOnly(); + assertEqual((typeof (result)).toString(), "typedesc [[{\"id\":1,\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":25}]]"); + assertEqual(result, [table key(id) [{"id": 1, "firstName": "John", "lastName": "Doe", "age": 25}]]); } function testConstructingListOfRecordsUsingQueryWithReadonly() { - Employee emp1 = {firstName: "A1", lastName: "B1", dept: "C1"}; - Employee emp2 = {firstName: "A2", lastName: "B2", dept: "C2"}; - Employee emp3 = {firstName: "A3", lastName: "B3", dept: "C3"}; + EmployeeX emp1 = {firstName: "A1", lastName: "B1", dept: "C1"}; + EmployeeX emp2 = {firstName: "A2", lastName: "B2", dept: "C2"}; + EmployeeX emp3 = {firstName: "A3", lastName: "B3", dept: "C3"}; - (Employee & readonly)[] & readonly result = from var user in [emp1, emp2, emp3] - select user.cloneReadOnly(); - assertEqual((typeof(result)).toString(), "typedesc [{\"firstName\":\"A1\",\"lastName\":\"B1\",\"dept\":\"C1\"},{\"firstName\":\"A2\",\"lastName\":\"B2\",\"dept\":\"C2\"},{\"firstName\":\"A3\",\"lastName\":\"B3\",\"dept\":\"C3\"}]"); - assertEqual(result, [{"firstName":"A1","lastName":"B1","dept":"C1"},{"firstName":"A2","lastName":"B2","dept":"C2"},{"firstName":"A3","lastName":"B3","dept":"C3"}]); + (EmployeeX & readonly)[] & readonly result = from var user in [emp1, emp2, emp3] + select user.cloneReadOnly(); + assertEqual((typeof (result)).toString(), "typedesc [{\"firstName\":\"A1\",\"lastName\":\"B1\",\"dept\":\"C1\"},{\"firstName\":\"A2\",\"lastName\":\"B2\",\"dept\":\"C2\"},{\"firstName\":\"A3\",\"lastName\":\"B3\",\"dept\":\"C3\"}]"); + assertEqual(result, [{"firstName": "A1", "lastName": "B1", "dept": "C1"}, {"firstName": "A2", "lastName": "B2", "dept": "C2"}, {"firstName": "A3", "lastName": "B3", "dept": "C3"}]); } function testConstructingListOfXMLsUsingQueryWithReadonly() { xml a = xml ` 1 John `; (xml & readonly)[] & readonly result = from var user in a - select user.cloneReadOnly(); - assertEqual((typeof(result)).toString(), "typedesc [` 1 `,` `,` John `]"); - assertEqual(result, [xml` 1 `,xml` `,xml` John `]); + select user.cloneReadOnly(); + assertEqual((typeof (result)).toString(), "typedesc [` 1 `,` `,` John `]"); + assertEqual(result, [xml ` 1 `, xml ` `, xml ` John `]); } type Type1 int[]|string; function testConstructingListOfListsUsingQueryWithReadonly() { Type1[] & readonly result = from var user in [[1, 2], "a", "b", [-1, int:MAX_VALUE]] - select user.cloneReadOnly(); - assertEqual((typeof(result)).toString(), "typedesc [[1,2],\"a\",\"b\",[-1,9223372036854775807]]"); - assertEqual(result, [[1,2],"a","b",[-1,9223372036854775807]]); + select user.cloneReadOnly(); + assertEqual((typeof (result)).toString(), "typedesc [[1,2],\"a\",\"b\",[-1,9223372036854775807]]"); + assertEqual(result, [[1, 2], "a", "b", [-1, 9223372036854775807]]); } function testConstructingListOfMapsUsingQueryWithReadonly() { map[] & readonly result = from var item in [[1, 2], "a", "b", [-1, int:MAX_VALUE]] - select {item: item}.cloneReadOnly(); + select {item: item}.cloneReadOnly(); - assertEqual((typeof(result)).toString(), "typedesc [{\"item\":[1,2]},{\"item\":\"a\"},{\"item\":\"b\"},{\"item\":[-1,9223372036854775807]}]"); - assertEqual(result, [{"item":[1,2]},{"item":"a"},{"item":"b"},{"item":[-1,9223372036854775807]}]); + assertEqual((typeof (result)).toString(), "typedesc [{\"item\":[1,2]},{\"item\":\"a\"},{\"item\":\"b\"},{\"item\":[-1,9223372036854775807]}]"); + assertEqual(result, [{"item": [1, 2]}, {"item": "a"}, {"item": "b"}, {"item": [-1, 9223372036854775807]}]); } type T record { @@ -1497,8 +1562,9 @@ type T record { }; function testConstructingListInRecordsUsingQueryWithReadonly() { - T rec1 = { params: from var s in ["a", "b", "c", "abc"] select s }; - assertEqual(rec1, {"params":["a","b","c","abc"]}); + T rec1 = {params: from var s in ["a", "b", "c", "abc"] + select s}; + assertEqual(rec1, {"params": ["a", "b", "c", "abc"]}); } type DepartmentDetails record { @@ -1513,69 +1579,77 @@ type ErrorOrImmutableMapOfInt ImmutableMapOfInt|error; function testReadonlyMap1() { map & readonly mp1 = map from var item in [["1", 1], ["2", 2], ["3", 3], ["4", 4]] - select item; - any _ = mp1; - assertEqual((typeof(mp1)).toString(), "typedesc {\"1\":1,\"2\":2,\"3\":3,\"4\":4}"); - assertEqual(mp1, {"1":1,"2":2,"3":3,"4":4}); + select item; + any _ = mp1; + assertEqual((typeof (mp1)).toString(), "typedesc {\"1\":1,\"2\":2,\"3\":3,\"4\":4}"); + assertEqual(mp1, {"1": 1, "2": 2, "3": 3, "4": 4}); ImmutableMapOfInt mp2 = map from var item in [["1", 1], ["2", 2], ["3", 3], ["4", 4]] - select item; - any _ = mp2; - assertEqual((typeof(mp2)).toString(), "typedesc {\"1\":1,\"2\":2,\"3\":3,\"4\":4}"); - assertEqual(mp2, {"1":1,"2":2,"3":3,"4":4}); - + select item; + any _ = mp2; + assertEqual((typeof (mp2)).toString(), "typedesc {\"1\":1,\"2\":2,\"3\":3,\"4\":4}"); + assertEqual(mp2, {"1": 1, "2": 2, "3": 3, "4": 4}); ImmutableMapOfDept mp3 = map from var item in ["ABC", "DEF", "XY"] - let DepartmentDetails & readonly dept = {dept: item} - select [item, dept]; - any _ = mp3; - assertEqual((typeof(mp3)).toString(), "typedesc {\"ABC\":{\"dept\":\"ABC\"},\"DEF\":{\"dept\":\"DEF\"},\"XY\":{\"dept\":\"XY\"}}"); - assertEqual(mp3, {"ABC":{"dept":"ABC"},"DEF":{"dept":"DEF"},"XY":{"dept":"XY"}}); + let DepartmentDetails & readonly dept = {dept: item} + select [item, dept]; + any _ = mp3; + assertEqual((typeof (mp3)).toString(), "typedesc {\"ABC\":{\"dept\":\"ABC\"},\"DEF\":{\"dept\":\"DEF\"},\"XY\":{\"dept\":\"XY\"}}"); + assertEqual(mp3, {"ABC": {"dept": "ABC"}, "DEF": {"dept": "DEF"}, "XY": {"dept": "XY"}}); ErrorOrImmutableMapOfInt mp4 = map from var item in [["1", 1], ["2", 2], ["3", 3], ["4", 4]] - where item[1] > 1 - select item; - any _ = (checkpanic mp4); - assertEqual((typeof(mp4)).toString(), "typedesc {\"2\":2,\"3\":3,\"4\":4}"); - assertEqual(mp4, {"2":2,"3":3,"4":4}); + where item[1] > 1 + select item; + any _ = (checkpanic mp4); + assertEqual((typeof (mp4)).toString(), "typedesc {\"2\":2,\"3\":3,\"4\":4}"); + assertEqual(mp4, {"2": 2, "3": 3, "4": 4}); [string:Char, int[]][] & readonly list = [["a", [1, 2]], ["b", [3, 4]], ["c", [4]], ["c", [3]]]; - map|error mp5 = map from var item in list select item; - assertEqual(mp5, {"a":[1,2],"b":[3,4],"c":[3]}); - - map & readonly|error mp6 = map from var item in list select item; - assertEqual(mp6, {"a":[1,2],"b":[3,4],"c":[3]}); - any _ = (checkpanic mp6); - - map & readonly|error mp7 = map from var item in list select item on conflict error("Error"); + map|error mp5 = map from var item in list + select item; + assertEqual(mp5, {"a": [1, 2], "b": [3, 4], "c": [3]}); + + map & readonly|error mp6 = map from var item in list + select item; + assertEqual(mp6, {"a": [1, 2], "b": [3, 4], "c": [3]}); + any _ = (checkpanic mp6); + + map & readonly|error mp7 = map from var item in list + select item + on conflict error("Error"); assertEqual(mp7, error("Error")); } function testReadonlyMap2() { map & readonly mp1 = map from var item in [["1", 1], ["2", 2], ["2", 3], ["4", 4]] - select [item[0], item[1] * 2] on conflict (); - assertEqual(mp1, {"1":2,"2":6,"4":8}); + select [item[0], item[1] * 2] + on conflict (); + assertEqual(mp1, {"1": 2, "2": 6, "4": 8}); ImmutableMapOfInt|error mp2 = map from var item in [["1", 1], ["2", 2], ["2", 3], ["4", 4]] - select item on conflict error("Error 1"); + select item + on conflict error("Error 1"); assertEqual(mp2, error("Error 1")); error? conflictMsg = error("Error 2"); ImmutableMapOfDept|error mp3 = map from var item in ["ABC", "DEF", "XY", "ABC"] - let DepartmentDetails & readonly dept = {dept: item} - select [item, dept] on conflict conflictMsg; + let DepartmentDetails & readonly dept = {dept: item} + select [item, dept] + on conflict conflictMsg; assertEqual(mp3, error("Error 2")); conflictMsg = null; ErrorOrImmutableMapOfInt mp4 = map from var item in [["1", 1], ["2", 2], ["3", 3], ["1", 4]] - where item[1] > 1 - select item on conflict conflictMsg; - assertEqual(mp4, {"2":2,"3":3,"1":4}); + where item[1] > 1 + select item + on conflict conflictMsg; + assertEqual(mp4, {"2": 2, "3": 3, "1": 4}); } class EvenNumberGenerator { int i = 0; - public isolated function next() returns record {| int value; |}|error { + + public isolated function next() returns record {|int value;|}|error { return error("Greater than 20!"); } } @@ -1590,19 +1664,19 @@ type NumberRecord record {| |}; function testQueryConstructingMapsAndTablesWithClausesMayCompleteSEarlyWithError() { - EvenNumberGenerator evenGen = new(); - stream evenNumberStream = new(evenGen); + EvenNumberGenerator evenGen = new (); + stream evenNumberStream = new (evenGen); map|error map1 = map from var item in evenNumberStream - select [item.toBalString(), item]; + select [item.toBalString(), item]; assertEqual(map1, error("Greater than 20!")); table|error table1 = table key() from var item in evenNumberStream - select {value: item}; + select {value: item}; assertEqual(table1, error("Greater than 20!")); table key(id)|error table2 = table key(id) from var item in evenNumberStream - select {id: item, value: item.toBalString()}; + select {id: item, value: item.toBalString()}; assertEqual(table2, error("Greater than 20!")); // Enable following tests after fixing issue - lang/#36746 @@ -1625,16 +1699,18 @@ function testQueryConstructingMapsAndTablesWithClausesMayCompleteSEarlyWithError // assertEqual(table4, error("Greater than 20!")); map|error map3 = map from var firstNo in [1, 4, 4, 10] - select [firstNo.toBalString(), firstNo] on conflict error("Error"); + select [firstNo.toBalString(), firstNo] + on conflict error("Error"); assertEqual(map3, error("Error")); table key(id)|error table6 = table key(id) from var firstNo in [1, 4, 4, 10] - select {id: firstNo, value: firstNo.toBalString()} on conflict error("Error"); + select {id: firstNo, value: firstNo.toBalString()} + on conflict error("Error"); assertEqual(table6, error("Error")); } function testQueryConstructingMapWithOnConflictsWithVarRef() { - map|error mp1 = map from int i in [1, 2, 3, 1, 2, 3] + map|error mp1 = map from int i in [1, 2, 3, 1, 2, 3] let string value = "A" + i.toString() select [i.toString(), value] on conflict error(string `Duplicate Key: ${i} Value: ${value}`); @@ -1652,98 +1728,124 @@ function testQueryConstructingMapWithOnConflictsWithVarRef() { } function testQueryConstructingMapsAndTablesWithClausesMayCompleteSEarlyWithError2() { - EvenNumberGenerator evenGen = new(); - stream evenNumberStream = new(evenGen); + EvenNumberGenerator evenGen = new (); + stream evenNumberStream = new (evenGen); - map|error map1 = map from var item in (stream from var integer in evenNumberStream select integer) - select [item.toBalString(), item]; + map|error map1 = map from var item in (stream from var integer in evenNumberStream + select integer) + select [item.toBalString(), item]; assertEqual(map1, error("Greater than 20!")); - table|error table1 = table key() from var item in (stream from var integer in evenNumberStream select integer) - select {value: item}; + table|error table1 = table key() from var item in (stream from var integer in evenNumberStream + select integer) + select {value: item}; assertEqual(table1, error("Greater than 20!")); - table key(id)|error table2 = table key(id) from var item in (stream from var integer in evenNumberStream select integer) - select {id: item, value: item.toBalString()}; + table key(id)|error table2 = table key(id) from var item in (stream from var integer in evenNumberStream + select integer) + select {id: item, value: item.toBalString()}; assertEqual(table2, error("Greater than 20!")); - map|error map3 = map from var item in (stream from var integer in (stream from var integer in evenNumberStream select integer) select integer) - select [item.toBalString(), item]; + map|error map3 = map from var item in (stream from var integer in (stream from var integer in evenNumberStream + select integer) + select integer) + select [item.toBalString(), item]; assertEqual(map3, error("Greater than 20!")); table|error table4 = table key() from var item in - (stream from var integer in (stream from var integer in evenNumberStream select integer) select integer) - select {value: item}; + (stream from var integer in (stream from var integer in evenNumberStream + select integer) + select integer) + select {value: item}; assertEqual(table4, error("Greater than 20!")); } type FooBar1 ("foo"|"bar"|string)[2]; + type FooBar2 ("foo"|"bar")[2]; + type FooBar3 "foo"|"bar"; + type FooBar4 "foo"|"bar"|string:Char; + type FooBar5 "foo"|"bar"|string; function testMapConstructingQueryExprWithStringSubtypes() { FooBar1[] list1 = [["key1", "foo"], ["key2", "foo"], ["key3", "foo"]]; - map|error mp1 = map from var item in list1 select item; - assertEqual(mp1, {"key1":"foo","key2":"foo","key3":"foo"}); + map|error mp1 = map from var item in list1 + select item; + assertEqual(mp1, {"key1": "foo", "key2": "foo", "key3": "foo"}); FooBar2[] list2 = [["foo", "foo"], ["bar", "foo"], ["foo", "foo"]]; - map|error mp2 = map from var item in list2 select item; - assertEqual(mp2, {"foo":"foo","bar":"foo"}); + map|error mp2 = map from var item in list2 + select item; + assertEqual(mp2, {"foo": "foo", "bar": "foo"}); FooBar3[][2] list3 = [["foo", "bar"], ["bar", "foo"], ["foo", "bar"]]; - map|error mp3 = map from var item in list3 select item; - assertEqual(mp3, {"foo":"bar","bar":"foo"}); + map|error mp3 = map from var item in list3 + select item; + assertEqual(mp3, {"foo": "bar", "bar": "foo"}); FooBar4[][2] list4 = [["foo", "4"], ["bar", "2"], ["foo", "3"]]; - map|error mp4 = map from var item in list4 select item; - assertEqual(mp4, {"foo":"3","bar":"2"}); - map|error mp5 = map from var item in list4 select item on conflict error("Error"); + map|error mp4 = map from var item in list4 + select item; + assertEqual(mp4, {"foo": "3", "bar": "2"}); + map|error mp5 = map from var item in list4 + select item + on conflict error("Error"); assertEqual(mp5, error("Error")); FooBar5[][2] list5 = [["key1", "1.4"], ["key2", "2"], ["key3", "3"]]; - map|error mp6 = map from var item in list5 select item; - assertEqual(mp6, {"key1":"1.4","key2":"2","key3":"3"}); + map|error mp6 = map from var item in list5 + select item; + assertEqual(mp6, {"key1": "1.4", "key2": "2", "key3": "3"}); [FooBar3, int|float][] list6 = [["foo", 1.4], ["bar", 2], ["foo", 3]]; - map|error mp7 = map from var item in list6 select item; - assertEqual(mp7, {"foo":3,"bar":2}); - map|error mp8 = map from var item in list6 select item on conflict error("Error"); + map|error mp7 = map from var item in list6 + select item; + assertEqual(mp7, {"foo": 3, "bar": 2}); + map|error mp8 = map from var item in list6 + select item + on conflict error("Error"); assertEqual(mp8, error("Error")); [FooBar4, int|float][] list7 = [["foo", 1.4], ["bar", 2], ["foo", 3]]; - map|error mp9 = map from var item in list7 select item; - assertEqual(mp9, {"foo":3,"bar":2}); - map|error mp10 = map from var item in list7 select item on conflict error("Error"); + map|error mp9 = map from var item in list7 + select item; + assertEqual(mp9, {"foo": 3, "bar": 2}); + map|error mp10 = map from var item in list7 + select item + on conflict error("Error"); assertEqual(mp10, error("Error")); [FooBar5, int|float][] list8 = [["key1", 1.4], ["key2", 2], ["key3", 3]]; - map|error mp11 = map from var item in list8 select item; - assertEqual(mp11, {"key1":1.4,"key2":2,"key3":3}); + map|error mp11 = map from var item in list8 + select item; + assertEqual(mp11, {"key1": 1.4, "key2": 2, "key3": 3}); } function testDiffQueryConstructsUsedAsFuncArgs() returns error? { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3]; + CustomerX[] customerList = [c1, c2, c3]; int tblLength = getTableLength(table key(id, name) from var customer in customerList - select { - id: customer.id, - name: customer.name, - noOfItems: customer.noOfItems - }); + select { + id: customer.id, + name: customer.name, + noOfItems: customer.noOfItems + }); assertEqual(tblLength, 3); FooBar1[] list1 = [["key1", "foo"], ["key2", "foo"], ["key3", "foo"]]; - int mapLength = getMapLength(map from var item in list1 select item); + int mapLength = getMapLength(map from var item in list1 + select item); assertEqual(mapLength, 3); } -function getTableLength(CustomerTable tbl) returns int { +function getTableLength(CustomerTableX tbl) returns int { return tbl.length(); } @@ -1837,9 +1939,9 @@ function testJoinedQueryExprConstructingMapWithRegExp() { let string:RegExp a = re `AB*(A|B|[ab-fgh]+(?im-x:[cdeg-k]??${v})|)|^|PQ?` select [re1.toString() + "1", re1.toString() + a.toString()]; assertEqual({ - A1: "AAB*(A|B|[ab-fgh]+(?im-x:[cdeg-k]??1)|)|^|PQ?", - B1: "BAB*(A|B|[ab-fgh]+(?im-x:[cdeg-k]??1)|)|^|PQ?" - }, arr3); + A1: "AAB*(A|B|[ab-fgh]+(?im-x:[cdeg-k]??1)|)|^|PQ?", + B1: "BAB*(A|B|[ab-fgh]+(?im-x:[cdeg-k]??1)|)|^|PQ?" + }, arr3); } type ModuleDecls [string, FuncDecl...]; @@ -1866,67 +1968,69 @@ function testInnerQueryConstructedWithCEP() { select [name, sig] ]; - assertEqual([["01",["func1",["foo",["int","string"],"boolean"]]],["02"]], decl); + assertEqual([["01", ["func1", ["foo", ["int", "string"], "boolean"]]], ["02"]], decl); } error onConflictError = error("Key Conflict", message = "cannot insert."); function testTableConstructQueryWithNonConflictingKeys() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3]; - CustomerTable|error customerTable = getQueryResult(onConflictError, customerList); - assertEqual(true, customerTable is CustomerTable); - CustomerTable expectedTableValue = table [{id: 1, name: "Melina", noOfItems: 12}, - {id: 2, name: "James", noOfItems: 5}, - {id: 3, name: "Anne", noOfItems: 20}]; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX[] customerList = [c1, c2, c3]; + CustomerTableX|error customerTable = getQueryResult(onConflictError, customerList); + assertEqual(true, customerTable is CustomerTableX); + CustomerTableX expectedTableValue = table [ + {id: 1, name: "Melina", noOfItems: 12}, + {id: 2, name: "James", noOfItems: 5}, + {id: 3, name: "Anne", noOfItems: 20} + ]; assertEqual(customerTable, expectedTableValue); } function testTableConstructQueryWithConflictingKeys() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3, c1]; - CustomerTable|error customerTable = getQueryResult(onConflictError, customerList); + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX[] customerList = [c1, c2, c3, c1]; + CustomerTableX|error customerTable = getQueryResult(onConflictError, customerList); assertEqual(customerTable is error, true); assertEqual((customerTable).message(), "Key Conflict"); } -function getQueryResult(error onConflictError, Customer[] customerList) returns CustomerTable|error { - return table key(id, name) from var customer in customerList - select { +function getQueryResult(error onConflictError, CustomerX[] customerList) returns CustomerTableX|error { + return table key(id, name) from var customer in customerList + select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems - } - on conflict onConflictError; + } + on conflict onConflictError; } function testMapConstructNestedQueryWithConflictingKeys() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3, c1]; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX[] customerList = [c1, c2, c3, c1]; (anydata|error)[] result = from var i in [1] select table key(id, name) from var customer in customerList - select { - id: customer.id, - name: customer.name, - noOfItems: customer.noOfItems - } - on conflict error(string `Error key: ${customer.id} iteration: ${i}`); + select { + id: customer.id, + name: customer.name, + noOfItems: customer.noOfItems + } + on conflict error(string `Error key: ${customer.id} iteration: ${i}`); assertEqual(result[0] is error, true); assertEqual((result[0]).message(), "Error key: 1 iteration: 1"); } function testMapConstructQueryWithConflictingKeys() { - Customer c1 = {id: 1, name: "Abba", noOfItems: 10}; - Customer c2 = {id: 2, name: "Jim", noOfItems: 20}; - Customer c3 = {id: 3, name: "James", noOfItems: 30}; - Customer c4 = {id: 3, name: "Abba", noOfItems: 40}; - Customer[] customerList = [c1, c2, c3, c4]; + CustomerX c1 = {id: 1, name: "Abba", noOfItems: 10}; + CustomerX c2 = {id: 2, name: "Jim", noOfItems: 20}; + CustomerX c3 = {id: 3, name: "James", noOfItems: 30}; + CustomerX c4 = {id: 3, name: "Abba", noOfItems: 40}; + CustomerX[] customerList = [c1, c2, c3, c4]; anydata|error result = map from var {name, noOfItems} in customerList group by name select [name, [noOfItems]] diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/simple-query-with-defined-type.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/simple-query-with-defined-type.bal index 10b35e903ead..344c163bc83b 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/simple-query-with-defined-type.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/simple-query-with-defined-type.bal @@ -14,7 +14,7 @@ // specific language governing permissions and limitations // under the License. -type Person record {| +type PersonK record {| string firstName; string lastName; int age; @@ -33,35 +33,35 @@ type Employee record {| |}; type Person1 record {| - string firstName; - string lastName; - string deptAccess; - Address address; + string firstName; + string lastName; + string deptAccess; + Address address; |}; -type Address record{| +type Address record {| string city; string country; |}; -type Student record{| +type Student record {| string firstName; string lastName; float score; |}; -type Teacher1 record{ - //record type referencing - *Person1; - Student[] classStudents?; - //anonymous record type - record {| - int duration; - string qualitifications; - |} experience; +type Teacher1 record { + //record type referencing + *Person1; + Student[] classStudents?; + //anonymous record type + record {| + int duration; + string qualitifications; + |} experience; }; -type Subscription record{| +type Subscription record {| string firstName; string lastName; float score; @@ -70,6 +70,7 @@ type Subscription record{| class NumberGenerator { int i = 0; + public isolated function next() returns record {|int value;|}|error? { //closes the stream after 5 events if (self.i == 5) { @@ -82,6 +83,7 @@ class NumberGenerator { class NumberGeneratorWithError { int i = 0; + public isolated function next() returns record {|int value;|}|error? { if (self.i == 2) { return error("Custom error thrown explicitly."); @@ -93,6 +95,7 @@ class NumberGeneratorWithError { class NumberGeneratorWithError2 { int i = 0; + public isolated function next() returns record {|int value;|}|error { if (self.i == 2) { return error("Custom error thrown explicitly."); @@ -106,6 +109,7 @@ type ErrorR1 error>; class NumberGeneratorWithCustomError { int i = 0; + public isolated function next() returns record {|int value;|}|ErrorR1? { if (self.i == 3) { return error ErrorR1("Custom error", x = 1); @@ -116,10 +120,10 @@ class NumberGeneratorWithCustomError { } type ResultValue record {| - Person value; + PersonK value; |}; -function getRecordValue((record {| Person value; |}|error?)|(record {| Person value; |}?) returnedVal) returns Person? { +function getRecordValue((record {|PersonK value;|}|error?)|(record {|PersonK value;|}?) returnedVal) returns PersonK? { if (returnedVal is ResultValue) { return returnedVal.value; } else { @@ -127,97 +131,97 @@ function getRecordValue((record {| Person value; |}|error?)|(record {| Person va } } -function testSimpleSelectQueryWithSimpleVariable() returns Person[] { +function testSimpleSelectQueryWithSimpleVariable() returns PersonK[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonK[] personList = [p1, p2, p3]; - Person[] outputPersonList = + PersonK[] outputPersonList = from var person in personList - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age - }; + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age + }; return outputPersonList; } -function testSimpleSelectQueryWithRecordVariable() returns Person[] { +function testSimpleSelectQueryWithRecordVariable() returns PersonK[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonK[] personList = [p1, p2, p3]; - Person[] outputPersonList = - from var { firstName: nm1, lastName: nm2, age: a } in personList - select { - firstName: nm1, - lastName: nm2, - age: a - }; + PersonK[] outputPersonList = + from var {firstName: nm1, lastName: nm2, age: a} in personList + select { + firstName: nm1, + lastName: nm2, + age: a + }; return outputPersonList; } -function testSimpleSelectQueryWithRecordVariableV2() returns Person[] { +function testSimpleSelectQueryWithRecordVariableV2() returns PersonK[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonK[] personList = [p1, p2, p3]; - Person[] outputPersonList = - from var { firstName, lastName, age } in personList - select { - firstName: firstName, - lastName: lastName, - age: age - }; + PersonK[] outputPersonList = + from var {firstName, lastName, age} in personList + select { + firstName: firstName, + lastName: lastName, + age: age + }; return outputPersonList; } function testSimpleSelectQueryWithRecordVariableV3() returns Teacher[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonK[] personList = [p1, p2, p3]; Teacher[] outputPersonList = - from var { firstName, lastName, age } in personList - select { - firstName, - lastName - }; + from var {firstName, lastName, age} in personList + select { + firstName, + lastName + }; return outputPersonList; } -function testSimpleSelectQueryWithWhereClause() returns Person[] { +function testSimpleSelectQueryWithWhereClause() returns PersonK[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonK[] personList = [p1, p2, p3]; - Person[] outputPersonList = + PersonK[] outputPersonList = from var person in personList - where person.age >= 30 - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age - }; + where person.age >= 30 + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age + }; return outputPersonList; } @@ -227,8 +231,8 @@ function testQueryExpressionForPrimitiveType() returns boolean { int[] outputIntList = from var value in intList - where value > 20 - select value; + where value > 20 + select value; return outputIntList == [21, 25]; } @@ -239,123 +243,123 @@ function testQueryExpressionWithSelectExpression() returns boolean { string[] stringOutput = from var value in intList - select value.toString(); + select value.toString(); - return stringOutput == ["1","2", "3"]; + return stringOutput == ["1", "2", "3"]; } -function testFilteringNullElements() returns Person[] { +function testFilteringNullElements() returns PersonK[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person?[] personList = [p1, (), p2]; + PersonK?[] personList = [p1, (), p2]; - Person[] outputPersonList = - from var person in personList - where (person is Person) - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age - }; + PersonK[] outputPersonList = + from var person in personList + where (person is PersonK) + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age + }; return outputPersonList; } function testMapWithArity() returns boolean { map m = {a: "1A", b: "2B", c: "3C", d: "4D"}; string[] val = from var v in m - where v == "1A" - select v; + where v == "1A" + select v; return val == ["1A"]; } function testJSONArrayWithArity() returns boolean { json[] jdata = [{name: "bob", age: 10}, {name: "tom", age: 16}]; string[] val = from var v in jdata - select checkpanic v.name; + select checkpanic v.name; return val == ["bob", "tom"]; } function testArrayWithTuple() returns boolean { [int, string][] arr = [[1, "A"], [2, "B"], [3, "C"]]; string[] val = from var [i, v] in arr - where i == 3 - select v; + where i == 3 + select v; return val == ["C"]; } -function testFromClauseWithStream() returns Person[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 30}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 40}; - Person p3 = {firstName: "John", lastName: "David", age: 50}; +function testFromClauseWithStream() returns PersonK[] { + PersonK p1 = {firstName: "Alex", lastName: "George", age: 30}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 40}; + PersonK p3 = {firstName: "John", lastName: "David", age: 50}; - Person[] personList = [p1, p2, p3]; - stream streamedPersons = personList.toStream(); + PersonK[] personList = [p1, p2, p3]; + stream streamedPersons = personList.toStream(); - Person[] outputPersonList = + PersonK[] outputPersonList = from var person in streamedPersons - where person.age == 40 - select person; + where person.age == 40 + select person; return outputPersonList; } function testSimpleSelectQueryWithLetClause() returns Employee[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonK[] personList = [p1, p2, p3]; Employee[] outputPersonList = from var person in personList - let string depName = "HR", string companyName = "WSO2" - where person.age >= 30 - select { - firstName: person.firstName, - lastName: person.lastName, - department: depName, - company: companyName - }; + let string depName = "HR", string companyName = "WSO2" + where person.age >= 30 + select { + firstName: person.firstName, + lastName: person.lastName, + department: depName, + company: companyName + }; return outputPersonList; } -function testFunctionCallInVarDeclLetClause() returns Person[] { +function testFunctionCallInVarDeclLetClause() returns PersonK[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person[] personList = [p1, p2]; + PersonK[] personList = [p1, p2]; var outputPersonList = - from Person person in personList - let int twiceAge = mutiplyBy2(person.age) - select { - firstName: person.firstName, - lastName: person.lastName, - age: twiceAge - }; + from PersonK person in personList + let int twiceAge = mutiplyBy2(person.age) + select { + firstName: person.firstName, + lastName: person.lastName, + age: twiceAge + }; return outputPersonList; } -function testUseOfLetInWhereClause() returns Person[] { +function testUseOfLetInWhereClause() returns PersonK[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 18}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 22}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 18}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 22}; - Person[] personList = [p1, p2]; + PersonK[] personList = [p1, p2]; var outputPersonList = from var person in personList - let int twiceAge = mutiplyBy2(person.age) - where twiceAge > 40 - select { - firstName: person.firstName, - lastName: person.lastName, - age: twiceAge - }; + let int twiceAge = mutiplyBy2(person.age) + where twiceAge > 40 + select { + firstName: person.firstName, + lastName: person.lastName, + age: twiceAge + }; return outputPersonList; } @@ -369,8 +373,8 @@ public function testQueryWithStream() returns boolean { var numberStream = new stream(numGen); int[]|error oddNumberList = from int num in numberStream - where (num % 2 == 1) - select num; + where (num % 2 == 1) + select num; if (oddNumberList is error) { return false; } else { @@ -378,14 +382,13 @@ public function testQueryWithStream() returns boolean { } } - public function testQueryStreamWithError() { NumberGeneratorWithError numGen = new; var numberStream = new stream(numGen); int[]|error oddNumberList = from int num in numberStream - where (num % 2 == 1) - select num; + where (num % 2 == 1) + select num; if (oddNumberList is error) { return; } @@ -452,10 +455,10 @@ public function testQueryStreamWithDifferentCompletionTypes() { } } -function testOthersAssociatedWithRecordTypes() returns Teacher1[]{ +function testOthersAssociatedWithRecordTypes() returns Teacher1[] { - Person1 p1 = {firstName: "Alex", lastName: "George", deptAccess: "XYZ", address:{city:"NY", country:"America"}}; - Person1 p2 = {firstName: "Ranjan", lastName: "Fonseka", deptAccess: "XYZ", address:{city:"NY", country:"America"}}; + Person1 p1 = {firstName: "Alex", lastName: "George", deptAccess: "XYZ", address: {city: "NY", country: "America"}}; + Person1 p2 = {firstName: "Ranjan", lastName: "Fonseka", deptAccess: "XYZ", address: {city: "NY", country: "America"}}; Student s1 = {firstName: "Alex", lastName: "George", score: 82.5}; Student s2 = {firstName: "Ranjan", lastName: "Fonseka", score: 90.6}; @@ -464,142 +467,142 @@ function testOthersAssociatedWithRecordTypes() returns Teacher1[]{ Student[] studentList = [s1, s2]; Teacher1[] outputteacherList = - from var person in personList - let int period = 10, string degree = "B.Sc." - select{ - //change order of the record fields - firstName:person.firstName, - address: person.address, - //optional field - classStudents: studentList, - deptAccess: person.deptAccess, - //member access - lastName:person["lastName"], - //values for anonymous record fields - experience: { - duration: period, - qualitifications: degree - } - }; - - return outputteacherList; + from var person in personList + let int period = 10, string degree = "B.Sc." + select { + //change order of the record fields + firstName: person.firstName, + address: person.address, + //optional field + classStudents: studentList, + deptAccess: person.deptAccess, + //member access + lastName: person["lastName"], + //values for anonymous record fields + experience: { + duration: period, + qualitifications: degree + } + }; + + return outputteacherList; } function testQueryExprTupleTypedBinding2() returns boolean { - [int,int][] arr1 = [[1,2],[2,3],[3,4]]; - [int,int] arr2 = [1,2]; + [int, int][] arr1 = [[1, 2], [2, 3], [3, 4]]; + [int, int] arr2 = [1, 2]; int[] ouputList = - from [int,int] [a,b] in arr1 - let [int,int] [d1,d2] = arr2, int x=d1+d2 - where b > x - select a; + from [int, int] [a, b] in arr1 + let [int, int] [d1, d2] = arr2, int x = d1 + d2 + where b > x + select a; - return ouputList == [3]; + return ouputList == [3]; } -function testQueryExprWithTypeConversion() returns Person1[]{ +function testQueryExprWithTypeConversion() returns Person1[] { - Person1 p1 = {firstName: "Alex", lastName: "George", deptAccess: "XYZ", address:{city:"NY", country:"America"}}; - Person1 p2 = {firstName: "Ranjan", lastName: "Fonseka", deptAccess: "XYZ", address:{city:"NY", country:"America"}}; + Person1 p1 = {firstName: "Alex", lastName: "George", deptAccess: "XYZ", address: {city: "NY", country: "America"}}; + Person1 p2 = {firstName: "Ranjan", lastName: "Fonseka", deptAccess: "XYZ", address: {city: "NY", country: "America"}}; - map m = {city:"New York", country:"America"}; + map m = {city: "New York", country: "America"}; - Person1[] personList = [p1, p2]; + Person1[] personList = [p1, p2]; - Person1[] outputPersonList = - from var person in personList - select{ - firstName: person.firstName, - lastName: person.lastName, - deptAccess: person.deptAccess, - address: checkpanic m.cloneWithType(Address) - }; + Person1[] outputPersonList = + from var person in personList + select { + firstName: person.firstName, + lastName: person.lastName, + deptAccess: person.deptAccess, + address: checkpanic m.cloneWithType(Address) + }; - return outputPersonList; + return outputPersonList; } -function testQueryExprWithStreamMapAndFilter() returns Subscription[]{ +function testQueryExprWithStreamMapAndFilter() returns Subscription[] { Student s1 = {firstName: "Alex", lastName: "George", score: 82.5}; Student s2 = {firstName: "Ranjan", lastName: "Fonseka", score: 90.6}; Student[] studentList = [s1, s2]; - Subscription[] outputSubscriptionList = - from var subs in >studentList.toStream().filter(function (Student student) returns boolean { - return student.score > 85.3; - }).'map(function (Student student) returns Subscription { - Subscription subscription = { - firstName: student.firstName, - lastName: student.lastName, - score: student.score, - degree: "Bachelor of Medicine" - }; - return subscription; - }) - select subs; + Subscription[] outputSubscriptionList = + from var subs in >studentList.toStream().filter(function(Student student) returns boolean { + return student.score > 85.3; + }).'map(function(Student student) returns Subscription { + Subscription subscription = { + firstName: student.firstName, + lastName: student.lastName, + score: student.score, + degree: "Bachelor of Medicine" + }; + return subscription; + }) + select subs; - return outputSubscriptionList; + return outputSubscriptionList; } function testSimpleSelectQueryReturnStream() returns boolean { boolean testPassed = true; - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonK[] personList = [p1, p2, p3]; - stream outputPersonStream = stream from var person in personList - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age - }; - Person? returnedVal = getRecordValue(outputPersonStream.next()); - testPassed = testPassed && (returnedVal is Person) && (returnedVal == p1); + stream outputPersonStream = stream from var person in personList + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age + }; + PersonK? returnedVal = getRecordValue(outputPersonStream.next()); + testPassed = testPassed && (returnedVal is PersonK) && (returnedVal == p1); returnedVal = getRecordValue(outputPersonStream.next()); - testPassed = testPassed && (returnedVal is Person) && (returnedVal == p2); + testPassed = testPassed && (returnedVal is PersonK) && (returnedVal == p2); returnedVal = getRecordValue(outputPersonStream.next()); - testPassed = testPassed && (returnedVal is Person) && (returnedVal == p3); + testPassed = testPassed && (returnedVal is PersonK) && (returnedVal == p3); return testPassed; } function testQueryWithRecordVarInLetClause() returns Person1[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p3 = {firstName: "John", lastName: "David", age: 33}; Address address = {city: "Colombo", country: "SL"}; - Person[] personList = [p1, p2, p3]; + PersonK[] personList = [p1, p2, p3]; var outputPersonList = from var person in personList - let Address {city: town, country: state } = address - where person.age >= 30 - select { - firstName: person.firstName, - lastName: person.lastName, - deptAccess: "XYZ", - address: {city: town, country: state} - }; + let Address {city: town, country: state} = address + where person.age >= 30 + select { + firstName: person.firstName, + lastName: person.lastName, + deptAccess: "XYZ", + address: {city: town, country: state} + }; return outputPersonList; } function testForeachStream() returns boolean { - Person p1 = {firstName: "Alex", lastName: "George", age: 30}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 40}; - Person p3 = {firstName: "John", lastName: "David", age: 50}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 30}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 40}; + PersonK p3 = {firstName: "John", lastName: "David", age: 50}; - Person[] personList = [p1, p2, p3]; - stream streamedPersons = personList.toStream(); + PersonK[] personList = [p1, p2, p3]; + stream streamedPersons = personList.toStream(); - Person[] outputPersonList = []; - foreach Person person in streamedPersons { + PersonK[] outputPersonList = []; + foreach PersonK person in streamedPersons { if (person.age == 40) { outputPersonList.push(person); } @@ -614,8 +617,8 @@ function testForeachStream() returns boolean { function testTypeTestInWhereClause() { int?[] v = [1, 2, (), 3]; int[] result = from var i in v - where i is int - select i; + where i is int + select i; assertEquality(3, result.length()); assertEquality(1, result[0]); assertEquality(2, result[1]); @@ -718,16 +721,16 @@ function testJoinedQueryExprWithRegExp() { let string:RegExp a = re `AB*[^abc-efg](?:A|B|[ab-fgh]+(?im-x:[cdeg-k]??${v})|)|^|PQ?` select re1.toString() + a.toString(); assertEquality(true, [ - "AAB*[^abc-efg](?:A|B|[ab-fgh]+(?im-x:[cdeg-k]??1)|)|^|PQ?", - "BAB*[^abc-efg](?:A|B|[ab-fgh]+(?im-x:[cdeg-k]??1)|)|^|PQ?" - ] == arr3); + "AAB*[^abc-efg](?:A|B|[ab-fgh]+(?im-x:[cdeg-k]??1)|)|^|PQ?", + "BAB*[^abc-efg](?:A|B|[ab-fgh]+(?im-x:[cdeg-k]??1)|)|^|PQ?" + ] == arr3); } function testQueryExprWithLangLibCallsWithArrowFunctions() { - Person p1 = {firstName: "Alex", lastName: "George", age: 30}; - Person p2 = {firstName: "Anne", lastName: "Frank", age: 40}; - Person p3 = {firstName: "John", lastName: "David", age: 50}; - Person[] personList = [p1, p2, p3]; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 30}; + PersonK p2 = {firstName: "Anne", lastName: "Frank", age: 40}; + PersonK p3 = {firstName: "John", lastName: "David", age: 50}; + PersonK[] personList = [p1, p2, p3]; int[] ageList = [50, 60]; int[] filteredAges = from int age in ageList @@ -744,7 +747,7 @@ function testQueryExprWithLangLibCallsWithArrowFunctions() { select ["John", "Frank"].filter(names => names == firstName).pop(); assertEquality(true, filteredNames2 == ["John"]); - Person[][] filteredPersons = from int age in [50] + PersonK[][] filteredPersons = from int age in [50] let string name = personList.filter(person => person.age == age).pop().firstName select personList.filter(person => person.firstName == name); assertEquality(true, filteredPersons == [[{"firstName":"John", "lastName":"David", "age":50}]]); diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/simple-query-with-var-type.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/simple-query-with-var-type.bal index 401b7a68af22..48af7090ea13 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/simple-query-with-var-type.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/simple-query-with-var-type.bal @@ -14,13 +14,13 @@ // specific language governing permissions and limitations // under the License. -type Person record {| +type PersonQT record {| string firstName; string lastName; int age; |}; -type Teacher record {| +type TeacherQT record {| string firstName; string lastName; int age; @@ -34,7 +34,7 @@ type EmployeeEntity record { int age; }; -type Employee record {| +type EmployeeQT record {| string fname; string lname; int age; @@ -42,6 +42,7 @@ type Employee record {| class NumberGenerator { int i = 0; + public isolated function next() returns record {|int value;|}|error? { //closes the stream after 5 events if (self.i == 5) { @@ -53,104 +54,104 @@ class NumberGenerator { } type ResultValue record {| - Person value; + PersonQT value; |}; -function getRecordValue((record {| Person value; |}|error?)|(record {| Person value; |}?) returnedVal) returns Person? { - if (returnedVal is ResultValue) { - return returnedVal.value; - } else { - return (); - } +function getRecordValue((record {|PersonQT value;|}|error?)|(record {|PersonQT value;|}?) returnedVal) returns PersonQT? { + if (returnedVal is ResultValue) { + return returnedVal.value; + } else { + return (); + } } -function testSimpleSelectQueryWithSimpleVariable() returns Person[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; +function testSimpleSelectQueryWithSimpleVariable() returns PersonQT[] { + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonQT[] personList = [p1, p2, p3]; - Person[] outputPersonList = + PersonQT[] outputPersonList = from var person in personList - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age - }; + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age + }; return outputPersonList; } -function testSimpleSelectQueryWithRecordVariable() returns Person[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; +function testSimpleSelectQueryWithRecordVariable() returns PersonQT[] { + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonQT[] personList = [p1, p2, p3]; - Person[] outputPersonList = - from var { firstName: nm1, lastName: nm2, age: a } in personList - select { - firstName: nm1, - lastName: nm2, - age: a - }; + PersonQT[] outputPersonList = + from var {firstName: nm1, lastName: nm2, age: a} in personList + select { + firstName: nm1, + lastName: nm2, + age: a + }; return outputPersonList; } -function testSimpleSelectQueryWithRecordVariableV2() returns Person[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; +function testSimpleSelectQueryWithRecordVariableV2() returns PersonQT[] { + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonQT[] personList = [p1, p2, p3]; var outputPersonList = - from var { firstName, lastName, age } in personList - select { - firstName: firstName, - lastName: lastName, - age: age - }; + from var {firstName, lastName, age} in personList + select { + firstName: firstName, + lastName: lastName, + age: age + }; return outputPersonList; } -function testSimpleSelectQueryWithRecordVariableV3() returns Person[] { - Teacher p1 = {firstName: "Alex", lastName: "George", age: 23, teacherId: "XYZ01"}; - Teacher p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30, teacherId: "ABC01"}; - Teacher p3 = {firstName: "John", lastName: "David", age: 33, teacherId: "ABC10"}; +function testSimpleSelectQueryWithRecordVariableV3() returns PersonQT[] { + TeacherQT p1 = {firstName: "Alex", lastName: "George", age: 23, teacherId: "XYZ01"}; + TeacherQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30, teacherId: "ABC01"}; + TeacherQT p3 = {firstName: "John", lastName: "David", age: 33, teacherId: "ABC10"}; - Teacher[] teacherList = [p1, p2, p3]; + TeacherQT[] teacherList = [p1, p2, p3]; var outputPersonList = - from var { firstName, lastName, age, teacherId} in teacherList - select { - firstName: firstName, - lastName: lastName, - age: age - }; + from var {firstName, lastName, age, teacherId} in teacherList + select { + firstName: firstName, + lastName: lastName, + age: age + }; return outputPersonList; } -function testSimpleSelectQueryWithWhereClause() returns Person[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; +function testSimpleSelectQueryWithWhereClause() returns PersonQT[] { + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonQT[] personList = [p1, p2, p3]; var outputPersonList = from var person in personList - where person.age >= 30 - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age - }; + where person.age >= 30 + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age + }; return outputPersonList; } @@ -160,8 +161,8 @@ function testQueryExpressionForPrimitiveType() returns boolean { var outputIntList = from var value in intList - where value > 20 - select value; + where value > 20 + select value; return outputIntList == [21, 25]; } @@ -172,102 +173,102 @@ function testQueryExpressionWithSelectExpression() returns boolean { var stringOutput = from var value in intList - select value.toString(); + select value.toString(); return stringOutput == ["1", "2", "3"]; } -function testFilteringNullElements() returns Person[] { +function testFilteringNullElements() returns PersonQT[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person?[] personList = [p1, (), p2]; + PersonQT?[] personList = [p1, (), p2]; var outputPersonList = - from var person in personList - where (person is Person) - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age - }; + from var person in personList + where (person is PersonQT) + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age + }; return outputPersonList; } function testMapWithArity() returns boolean { map m = {a: "1A", b: "2B", c: "3C", d: "4D"}; var val = map from var v in m - where v == "1A" - select ["a", v]; + where v == "1A" + select ["a", v]; return val == {a: "1A"}; } function testJSONArrayWithArity() returns boolean { json[] jdata = [{name: "bob", age: 10}, {name: "tom", age: 16}]; var val = from var v in jdata - select checkpanic v.name; + select checkpanic v.name; return val == ["bob", "tom"]; } function testArrayWithTuple() returns boolean { [int, string][] arr = [[1, "A"], [2, "B"], [3, "C"]]; var val = from var [i, v] in arr - where i == 3 - select v; + where i == 3 + select v; return val == ["C"]; } -function testQueryExpressionWithVarType() returns Teacher[] { +function testQueryExpressionWithVarType() returns TeacherQT[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonQT[] personList = [p1, p2, p3]; var outputPersonList = from var person in personList - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age, - teacherId: "TER1200" - }; + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age, + teacherId: "TER1200" + }; return outputPersonList; } -function testSimpleSelectQueryWithSpreadOperator() returns Person[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; +function testSimpleSelectQueryWithSpreadOperator() returns PersonQT[] { + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonQT[] personList = [p1, p2, p3]; - Person[] outputPersonList = + PersonQT[] outputPersonList = from var person in personList - select { - ...person - }; + select { + ...person + }; return outputPersonList; } -function testQueryExpressionWithSpreadOperatorV2() returns Teacher[] { +function testQueryExpressionWithSpreadOperatorV2() returns TeacherQT[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonQT[] personList = [p1, p2, p3]; var outputPersonList = from var person in personList - select { - ...person, - teacherId: "TER1200" - }; + select { + ...person, + teacherId: "TER1200" + }; return outputPersonList; } @@ -277,11 +278,11 @@ public function testQueryWithStream() returns boolean { var numberStream = new stream(numGen); var oddNumberList = stream from var num in numberStream - where (num % 2 == 1) - select num; + where (num % 2 == 1) + select num; int[] result = []; - record {| int value; |}|error? v = oddNumberList.next(); - while (v is record {| int value; |}) { + record {|int value;|}|error? v = oddNumberList.next(); + while (v is record {|int value;|}) { result.push(v.value); v = oddNumberList.next(); } @@ -290,26 +291,26 @@ public function testQueryWithStream() returns boolean { function testSimpleSelectQueryReturnStream() returns boolean { boolean testPassed = true; - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonQT[] personList = [p1, p2, p3]; var outputPersonStream = stream from var person in personList - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age - }; - Person? returnedVal = getRecordValue(outputPersonStream.next()); - testPassed = testPassed && (returnedVal is Person) && (returnedVal == p1); + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age + }; + PersonQT? returnedVal = getRecordValue(outputPersonStream.next()); + testPassed = testPassed && (returnedVal is PersonQT) && (returnedVal == p1); returnedVal = getRecordValue(outputPersonStream.next()); - testPassed = testPassed && (returnedVal is Person) && (returnedVal == p2); + testPassed = testPassed && (returnedVal is PersonQT) && (returnedVal == p2); returnedVal = getRecordValue(outputPersonStream.next()); - testPassed = testPassed && (returnedVal is Person) && (returnedVal == p3); + testPassed = testPassed && (returnedVal is PersonQT) && (returnedVal == p3); return testPassed; } @@ -318,14 +319,15 @@ string fname = ""; function testVariableShadowingWithQueryExpressions1() returns boolean { EmployeeEntity[] entities = [ - {id: 1232, fname: "Sameera", lname: "Jayasoma", age: 30}, - {id: 1232, fname: "Asanthi", lname: "Kulasinghe", age: 30}, - {id: 1232, fname: "Khiana", lname: "Jayasoma", age: 2} - ]; + {id: 1232, fname: "Sameera", lname: "Jayasoma", age: 30}, + {id: 1232, fname: "Asanthi", lname: "Kulasinghe", age: 30}, + {id: 1232, fname: "Khiana", lname: "Jayasoma", age: 2} + ]; - Employee[] records = from var {fname, lname, age} in entities select {fname, lname, age}; + EmployeeQT[] records = from var {fname, lname, age} in entities + select {fname, lname, age}; boolean testPassed = true; - Employee e = records[0]; + EmployeeQT e = records[0]; testPassed = testPassed && e.fname == "Sameera" && e.lname == "Jayasoma" && e.age == 30; e = records[1]; testPassed = testPassed && e.fname == "Asanthi" && e.lname == "Kulasinghe" && e.age == 30; @@ -337,15 +339,16 @@ function testVariableShadowingWithQueryExpressions1() returns boolean { function testVariableShadowingWithQueryExpressions2() returns boolean { EmployeeEntity[] entities = [ - {id: 1232, fname: "Sameera", lname: "Jayasoma", age: 30}, - {id: 1232, fname: "Asanthi", lname: "Kulasinghe", age: 30}, - {id: 1232, fname: "Khiana", lname: "Jayasoma", age: 2} - ]; + {id: 1232, fname: "Sameera", lname: "Jayasoma", age: 30}, + {id: 1232, fname: "Asanthi", lname: "Kulasinghe", age: 30}, + {id: 1232, fname: "Khiana", lname: "Jayasoma", age: 2} + ]; - Employee[] records = from var {fname, lname, age} in entities select {fname, lname, age}; + EmployeeQT[] records = from var {fname, lname, age} in entities + select {fname, lname, age}; var lname = 5; boolean testPassed = true; - Employee e = records[0]; + EmployeeQT e = records[0]; testPassed = testPassed && e.fname == "Sameera" && e.lname == "Jayasoma" && e.age == 30; e = records[1]; testPassed = testPassed && e.fname == "Asanthi" && e.lname == "Kulasinghe" && e.age == 30; @@ -371,7 +374,7 @@ function testSimpleSelectQueryWithTable() { assertEquality((table [{"id":1234},{"id":4567}]).toString(), t2.toString()); } -type User record { +type UserQuery record { readonly int id; string firstName; string lastName; @@ -379,10 +382,10 @@ type User record { }; function testQueryConstructingTableWithVar() returns error? { - User u1 = {id: 1, firstName: "John", lastName: "Doe", age: 25}; - User u2 = {id: 2, firstName: "Anne", lastName: "Frank", age: 30}; + UserQuery u1 = {id: 1, firstName: "John", lastName: "Doe", age: 25}; + UserQuery u2 = {id: 2, firstName: "Anne", lastName: "Frank", age: 30}; - table key(id) users = table []; + table key(id) users = table []; users.add(u1); users.add(u2); @@ -390,16 +393,16 @@ function testQueryConstructingTableWithVar() returns error? { where user.age > 21 && user.age < 60 select {user}; - assertEquality(true, result1 is table key(user)); + assertEquality(true, result1 is table key(user)); assertEquality({"user": u1}, result1.get(u1)); - User[] userList = [u1, u2]; + UserQuery[] userList = [u1, u2]; var result2 = check table key(user) from var user in userList where user.age > 21 && user.age < 60 select {user}; - assertEquality(true, result2 is table key(user)); + assertEquality(true, result2 is table key(user)); assertEquality({"user": u1}, result2.get(u1)); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/reachability-analysis/unreachability_test.bal b/tests/jballerina-unit-test/src/test/resources/test-src/reachability-analysis/unreachability_test.bal index 2cc46d143d5b..7dd15e0b444d 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/reachability-analysis/unreachability_test.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/reachability-analysis/unreachability_test.bal @@ -836,7 +836,7 @@ function testUnreachabilityWithIfElseStmts6() { int _ = 10; } else if e == 20 { int _ = 20; - } else if e == 10 { + } else { never _ = e; // unreachable code } } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/reachability-analysis/unreachability_test2.bal b/tests/jballerina-unit-test/src/test/resources/test-src/reachability-analysis/unreachability_test2.bal index a5e704049377..1861beac7c92 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/reachability-analysis/unreachability_test2.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/reachability-analysis/unreachability_test2.bal @@ -422,12 +422,12 @@ function testUnreachabilityWithIfStmtWithEqualityExpr14() { function testUnreachabilityWithIfStmtWithEqualityExpr15() { True t = true; - False f = false; + True f = true; if f == t { - return; // unreachable code + return; } else if t == t { - string _ = "Ballerina"; + string _ = "Ballerina"; // unreachable code } else { string _ = "Ballerina"; // unreachable code } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/record/readonly_record_fields.bal b/tests/jballerina-unit-test/src/test/resources/test-src/record/readonly_record_fields.bal index 2912906e6806..2d97b86a3e54 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/record/readonly_record_fields.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/record/readonly_record_fields.bal @@ -232,40 +232,40 @@ function testReadOnlyFieldWithDefaultValue() { assertEquality("cannot update 'readonly' field 'id' in record of type 'Identifier'", err.detail()["message"]); } -type Foo record {| +type FooH record {| string name; int id; float...; |}; -type Bar record {| +type BarH record {| readonly string name; readonly int id; |}; -type EmptyClosedRecord record {| +type EmptyClosedRecordH record {| |}; function testTypeReadOnlyFlagForAllReadOnlyFields() { - Bar st = { + BarH st = { name: "Maryam", id: 1234 }; - Foo & readonly pr = st; - assertTrue(pr is Bar); - assertTrue(pr is Bar & readonly); + FooH & readonly pr = st; + assertTrue(pr is BarH); + assertTrue(pr is BarH & readonly); assertEquality("Maryam", pr.name); assertEquality(1234, pr.id); readonly rd = st; - assertTrue(rd is Bar); - assertTrue(rd is Bar & readonly); + assertTrue(rd is BarH); + assertTrue(rd is BarH & readonly); - EmptyClosedRecord ecr = {}; + EmptyClosedRecordH ecr = {}; readonly rd2 = ecr; - assertTrue(rd2 is EmptyClosedRecord); - assertTrue(rd2 is EmptyClosedRecord & readonly); + assertTrue(rd2 is EmptyClosedRecordH); + assertTrue(rd2 is EmptyClosedRecordH & readonly); assertTrue(rd2 is record {} & readonly); } @@ -275,19 +275,19 @@ record {| function testTypeReadOnlyFlagForAllReadOnlyFieldsInAnonymousRecord() { readonly rd = modAnonRecord; - assertTrue( rd is record { int x; }); - assertTrue(rd is record { int x; } & readonly); - record { int x; } rec = checkpanic rd; + assertTrue(rd is record {int x;}); + assertTrue(rd is record {int x;} & readonly); + record {int x;} rec = checkpanic rd; assertEquality(2, rec.x); record {| readonly int x = 1; - readonly Bar y; + readonly BarH y; |} localAnonRecord = {y: {name: "Amy", id: 1001}}; readonly rd2 = localAnonRecord; - assertTrue( rd2 is record {| int x; Bar y; |}); - assertTrue(rd2 is record { int x; Bar y; } & readonly); - var rec2 = checkpanic rd2; + assertTrue(rd2 is record {|int x; BarH y;|}); + assertTrue(rd2 is record {int x; BarH y;} & readonly); + var rec2 = checkpanic rd2; assertEquality(1, rec2.x); assertEquality("Amy", rec2.y.name); assertEquality(1001, rec2.y.id); diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/record/record_project_closed_rec_equiv/Ballerina.toml b/tests/jballerina-unit-test/src/test/resources/test-src/record/record_project_closed_rec_equiv/Ballerina.toml index b7ec0186ad78..bbccc659c584 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/record/record_project_closed_rec_equiv/Ballerina.toml +++ b/tests/jballerina-unit-test/src/test/resources/test-src/record/record_project_closed_rec_equiv/Ballerina.toml @@ -1,4 +1,4 @@ [package] org = "testorg" -name = "recordproject" +name = "closedrecordproject" version = "1.0.0" diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/record/record_project_closed_rec_equiv/closed_record_equivalency.bal b/tests/jballerina-unit-test/src/test/resources/test-src/record/record_project_closed_rec_equiv/closed_record_equivalency.bal index 6cb5e6e63325..2619d1a62c10 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/record/record_project_closed_rec_equiv/closed_record_equivalency.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/record/record_project_closed_rec_equiv/closed_record_equivalency.bal @@ -14,10 +14,10 @@ // specific language governing permissions and limitations // under the License. -import recordproject.eq; -import recordproject.eq2; -import recordproject.req; -import recordproject.req2; +import closedrecordproject.eq; +import closedrecordproject.eq2; +import closedrecordproject.req; +import closedrecordproject.req2; public type person1 record {| int age = 0; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/test_type_guard_type_narrow_3.bal b/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/test_type_guard_type_narrow_3.bal index 74bff266e75a..6ed8c273e634 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/test_type_guard_type_narrow_3.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/test_type_guard_type_narrow_3.bal @@ -260,7 +260,7 @@ function test20(int|string x) { } if x is int && !(x !is int) { - int _ = x; // Type not narrowed. issue #34965 + int _ = x; // OK } else { string _ = x; // Type not narrowed. issue #34965 } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/test_type_guard_type_narrow_4.bal b/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/test_type_guard_type_narrow_4.bal index 3e95ff0b3554..a6c5ec78e343 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/test_type_guard_type_narrow_4.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/test_type_guard_type_narrow_4.bal @@ -18,18 +18,18 @@ function test1(2|"foo" x) { if x is int && x == 2 { 2 _ = x; // OK } else { - "foo" _ = x; // Type not narrowed. issue #34965 + "foo" _ = x; // OK } if x is 2 && x == 2 { 2 _ = x; // OK } else { - "foo" _ = x; // Type not narrowed. issue #34965 + "foo" _ = x; // OK } if x == 2 && x == 2 { 2 _ = x; // OK } else { - "foo" _ = x; // Type not narrowed. issue #34965 + "foo" _ = x; // OK } } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/list-match-pattern-with-rest-match-pattern.bal b/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/list-match-pattern-with-rest-match-pattern.bal index 50ee838ae2d9..55982e369681 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/list-match-pattern-with-rest-match-pattern.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/list-match-pattern-with-rest-match-pattern.bal @@ -221,10 +221,11 @@ function testListMatchPatternWithRestPattern12() { assertEquals(false, a5[2]); } -class FooObj { +class FooObjLMP { public string s; public float f; public byte b; + function init(string s, float f, byte b) { self.s = s; self.f = f; @@ -232,9 +233,10 @@ class FooObj { } } -class BarObj { +class BarObjLMP { public boolean b; public int i; + function init(boolean b, int i) { self.b = b; self.i = i; @@ -242,36 +244,51 @@ class BarObj { } function testListMatchPatternWithRestPattern13() { - FooObj fooObj1 = new ("Fooo", 3.7, 23); - BarObj barObj1 = new (true, 56); - FooObj fooObj2 = new ("Foo2", 10.2, 30); - BarObj barObj2 = new (false, 56); - BarObj barObj3 = new (true, 58); + FooObjLMP fooObj1 = new ("Fooo", 3.7, 23); + BarObjLMP barObj1 = new (true, 56); + FooObjLMP fooObj2 = new ("Foo2", 10.2, 30); + BarObjLMP barObj2 = new (false, 56); + BarObjLMP barObj3 = new (true, 58); string matched = "Not Matched"; - [[string, [error, map, int, (FooObj|BarObj)...], Bar, (byte|float)...], string, boolean...] t2 = - [["Ballerina", [error("Error", detail1= 12, detail2= true), - {firstName: "John", lastName: "Damon"}, 12, fooObj1, barObj1], {id: 34, flag: true}, 10.5, 20], - "A", true, false]; + [[string, [error, map, int, (FooObjLMP|BarObjLMP)...], Bar, (byte|float)...], string, boolean...] t2 = + [ + [ + "Ballerina", + [ + error("Error", detail1 = 12, detail2 = true), + {firstName: "John", lastName: "Damon"}, + 12, + fooObj1, + barObj1 + ], + {id: 34, flag: true}, + 10.5, + 20 + ], + "A", + true, + false + ]; string a1; error a2; - [map, int, (FooObj|BarObj)...] a3; + [map, int, (FooObjLMP|BarObjLMP)...] a3; [Bar, (byte|float)...] a4; [string, boolean...] a5; map a6; - [int, (FooObj|BarObj)...] a7; + [int, (FooObjLMP|BarObjLMP)...] a7; string b1; error b2; - [map, int, (FooObj|BarObj)...] b3; + [map, int, (FooObjLMP|BarObjLMP)...] b3; [Bar, (byte|float)...] b4; [string, boolean...] b5; map b6; - [int, (FooObj|BarObj)...] b7; + [int, (FooObjLMP|BarObjLMP)...] b7; match t2 { - [[var g1, [var g2, ... var g3], ...var g4], ...var g5] => { + [[var g1, [var g2, ...var g3], ...var g4], ...var g5] => { matched = "Matched1"; a1 = g1; a2 = g2; @@ -286,9 +303,24 @@ function testListMatchPatternWithRestPattern13() { } } - [[g1, g2, ...g3], [...g5], ...g4] = [["Hello", error("Transaction Error"), [{primary: "Blue", - secondary: "Green"}, 1000, barObj2, fooObj2, barObj3]], [["World", true, false, true, false]], - [{id: 40, flag: true}, 0x5, 0x7, 20.25, 0x8]]; + [[g1, g2, ...g3], [...g5], ...g4] = [ + [ + "Hello", + error("Transaction Error"), + [ + { + primary: "Blue", + secondary: "Green" + }, + 1000, + barObj2, + fooObj2, + barObj3 + ] + ], + [["World", true, false, true, false]], + [{id: 40, flag: true}, 0x5, 0x7, 20.25, 0x8] + ]; b1 = g1; b2 = g2; b3 = g3; @@ -323,16 +355,16 @@ function testListMatchPatternWithRestPattern13() { assertEquals("Blue", b3[0]["primary"]); assertEquals("Green", b3[0]["secondary"]); assertEquals(1000, b3[1]); - assertEquals(true, b3[2] is BarObj); - assertEquals(false, (b3[2]).b); - assertEquals(56, (b3[2]).i); - assertEquals(true, b3[3] is FooObj); - assertEquals("Foo2", (b3[3]).s); - assertEquals(10.2, (b3[3]).f); - assertEquals(30, (b3[3]).b); - assertEquals(true, b3[4] is BarObj); - assertEquals(true, (b3[4]).b); - assertEquals(58, (b3[4]).i); + assertEquals(true, b3[2] is BarObjLMP); + assertEquals(false, (b3[2]).b); + assertEquals(56, (b3[2]).i); + assertEquals(true, b3[3] is FooObjLMP); + assertEquals("Foo2", (b3[3]).s); + assertEquals(10.2, (b3[3]).f); + assertEquals(30, (b3[3]).b); + assertEquals(true, b3[4] is BarObjLMP); + assertEquals(true, (b3[4]).b); + assertEquals(58, (b3[4]).i); assertEquals(5, b5.length()); assertEquals("World", b5[0]); assertEquals(true, b5[1]); diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/list-match-pattern.bal b/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/list-match-pattern.bal index 0395cd3756c2..32bb925e047d 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/list-match-pattern.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/list-match-pattern.bal @@ -504,35 +504,35 @@ function testListMatchPattern18() { assertEquals("Default" ,listMatchPattern18(a5)); } -type FooRec record { +type FooRecLMP record { string s; int i; float f; }; -type BarRec record { +type BarRecLMP record { byte b; - FooRec f; + FooRecLMP f; }; function listMatchPattern19(any a) returns string { match a { - [var i, var s] if i is FooRec && s is BarRec => { + [var i, var s] if i is FooRecLMP && s is BarRecLMP => { return "Matched with FooRec and BarRec : " + i.toString() + " , " + s.toString(); } - [var i, var s] if i is FooRec && s is float => { + [var i, var s] if i is FooRecLMP && s is float => { return "Matched with FooRec and float : " + i.toString() + " , " + s.toString(); } - [var i, var s] if i is BarRec && s is FooRec => { + [var i, var s] if i is BarRecLMP && s is FooRecLMP => { return "Matched with BarRec and FooRec : " + i.toString() + " , " + s.toString(); } - [var i, var s] if i is BarRec && s is int => { + [var i, var s] if i is BarRecLMP && s is int => { return "Matched with BarRec and int : " + i.toString() + " , " + s.toString(); } - [var i, var s] if i is float && s is FooRec => { + [var i, var s] if i is float && s is FooRecLMP => { return "Matched with float and FooRec : " + i.toString() + " , " + s.toString(); } - [var i, var s] if i is int && s is BarRec => { + [var i, var s] if i is int && s is BarRecLMP => { return "Matched with int and BarRec : " + i.toString() + " , " + s.toString(); } } @@ -541,34 +541,34 @@ function listMatchPattern19(any a) returns string { } function testListMatchPattern19() { - FooRec fooRec1 = {s: "S", i: 23, f: 5.6}; - BarRec barRec1 = {b: 12, f: fooRec1}; - - [int|FooRec, float|BarRec] | [float|BarRec, int|FooRec] a1 = [fooRec1, barRec1]; - [int|FooRec, float|BarRec] | [float|BarRec, int|FooRec] a2 = [fooRec1, 4.5]; - [int|FooRec, float|BarRec] | [float|BarRec, int|FooRec] a3 = [barRec1, fooRec1]; - [int|FooRec, float|BarRec] | [float|BarRec, int|FooRec] a4 = [barRec1, 543]; - [int|FooRec, float|BarRec] | [float|BarRec, int|FooRec] a5 = [5.2, fooRec1]; - [int|FooRec, float|BarRec] | [float|BarRec, int|FooRec] a6 = [15, barRec1]; - [int|FooRec, float|BarRec] | [float|BarRec, int|FooRec] a7 = [65, 7.4]; - [int|FooRec, float|BarRec] | [float|BarRec, int|FooRec] a8 = [3.6, 42]; + FooRecLMP fooRec1 = {s: "S", i: 23, f: 5.6}; + BarRecLMP barRec1 = {b: 12, f: fooRec1}; + + [int|FooRecLMP, float|BarRecLMP]|[float|BarRecLMP, int|FooRecLMP] a1 = [fooRec1, barRec1]; + [int|FooRecLMP, float|BarRecLMP]|[float|BarRecLMP, int|FooRecLMP] a2 = [fooRec1, 4.5]; + [int|FooRecLMP, float|BarRecLMP]|[float|BarRecLMP, int|FooRecLMP] a3 = [barRec1, fooRec1]; + [int|FooRecLMP, float|BarRecLMP]|[float|BarRecLMP, int|FooRecLMP] a4 = [barRec1, 543]; + [int|FooRecLMP, float|BarRecLMP]|[float|BarRecLMP, int|FooRecLMP] a5 = [5.2, fooRec1]; + [int|FooRecLMP, float|BarRecLMP]|[float|BarRecLMP, int|FooRecLMP] a6 = [15, barRec1]; + [int|FooRecLMP, float|BarRecLMP]|[float|BarRecLMP, int|FooRecLMP] a7 = [65, 7.4]; + [int|FooRecLMP, float|BarRecLMP]|[float|BarRecLMP, int|FooRecLMP] a8 = [3.6, 42]; assertEquals("Matched with FooRec and BarRec : {\"s\":\"S\",\"i\":23,\"f\":5.6} , " + "{\"b\":12,\"f\":{\"s\":\"S\",\"i\":23,\"f\":5.6}}", listMatchPattern19(a1)); - assertEquals("Matched with FooRec and float : {\"s\":\"S\",\"i\":23,\"f\":5.6} , 4.5" ,listMatchPattern19(a2)); + assertEquals("Matched with FooRec and float : {\"s\":\"S\",\"i\":23,\"f\":5.6} , 4.5", listMatchPattern19(a2)); assertEquals("Matched with BarRec and FooRec : {\"b\":12,\"f\":{\"s\":\"S\",\"i\":23,\"f\":5.6}} , " + - "{\"s\":\"S\",\"i\":23,\"f\":5.6}" ,listMatchPattern19(a3)); - assertEquals("Matched with BarRec and int : {\"b\":12,\"f\":{\"s\":\"S\",\"i\":23,\"f\":5.6}} , 543" , - listMatchPattern19(a4)); - assertEquals("Matched with float and FooRec : 5.2 , {\"s\":\"S\",\"i\":23,\"f\":5.6}" ,listMatchPattern19(a5)); - assertEquals("Matched with int and BarRec : 15 , {\"b\":12,\"f\":{\"s\":\"S\",\"i\":23,\"f\":5.6}}" , - listMatchPattern19(a6)); - assertEquals("Default" ,listMatchPattern19(a7)); - assertEquals("Default" ,listMatchPattern19(a8)); + "{\"s\":\"S\",\"i\":23,\"f\":5.6}", listMatchPattern19(a3)); + assertEquals("Matched with BarRec and int : {\"b\":12,\"f\":{\"s\":\"S\",\"i\":23,\"f\":5.6}} , 543", + listMatchPattern19(a4)); + assertEquals("Matched with float and FooRec : 5.2 , {\"s\":\"S\",\"i\":23,\"f\":5.6}", listMatchPattern19(a5)); + assertEquals("Matched with int and BarRec : 15 , {\"b\":12,\"f\":{\"s\":\"S\",\"i\":23,\"f\":5.6}}", + listMatchPattern19(a6)); + assertEquals("Default", listMatchPattern19(a7)); + assertEquals("Default", listMatchPattern19(a8)); } function listMatchPattern20() returns string { - [boolean, string] | [int, string, decimal] v = [1, "A", 1.1d]; + [boolean, string]|[int, string, decimal] v = [1, "A", 1.1d]; match v { [var i, ...var s] => { return "i: " + i.toString() + " s: " + s.toString(); @@ -699,7 +699,7 @@ function testListMatchPatternWithWildCard() { result = "Matched"; } _ => { - result = "Default"; + result = "Default"; } } assertEquals("Default", result); @@ -711,7 +711,7 @@ function testListMatchPatternWithWildCard() { result = "Matched"; } _ => { - result = "Default"; + result = "Default"; } } assertEquals("Not Matched", result); @@ -719,10 +719,10 @@ function testListMatchPatternWithWildCard() { function testListMatchPatternWithArrayAndAnydataIntersection() { int[] x = [1, 2, 3]; - assertEquals(x, listMatchPattern28( [x])); + assertEquals(x, listMatchPattern28([x])); anydata[] y = [["hello", "world"]]; assertEquals(["hello", "world"], listMatchPattern28(y)); - assertEquals("other", listMatchPattern28( [["hello", "world"], 1, 2])); + assertEquals("other", listMatchPattern28([["hello", "world"], 1, 2])); assertEquals("other", listMatchPattern28("hello")); } @@ -745,12 +745,12 @@ function testListMatchPattern29() { assertEquals((), listMatchPattern29(1)); } -type Rec record {| +type RecLMP record {| int|float a; |}; function testListMatchPattern30() { - [int, Rec|string] a1 = [12, {a: 1}]; + [int, RecLMP|string] a1 = [12, {a: 1}]; string result = ""; match a1 { @@ -781,7 +781,7 @@ function testListMatchPattern30() { } assertEquals("Pattern3", result); - [int, Rec|string...] a2 = [12, {a: 1}]; + [int, RecLMP|string...] a2 = [12, {a: 1}]; result = ""; match a2 { @@ -797,7 +797,7 @@ function testListMatchPattern30() { } assertEquals("Pattern3", result); - [int, string, Rec|string...] a3 = [12, "C", {a: 1.5}]; + [int, string, RecLMP|string...] a3 = [12, "C", {a: 1.5}]; result = ""; match a3 { @@ -813,7 +813,7 @@ function testListMatchPattern30() { } assertEquals("Pattern2", result); - [Rec|string...] a4 = [{a: 1}, {a: 2}, {a: 3}]; + [RecLMP|string...] a4 = [{a: 1}, {a: 2}, {a: 3}]; result = ""; match a4 { @@ -838,8 +838,8 @@ function testListMatchPattern30() { } assertEquals("Pattern2", result); - error err1 = error("Error One", data= [{b: 5}, 12]); - [error, Rec|string...] a5 = [err1, {a: 2}, {a: 3}]; + error err1 = error("Error One", data = [{b: 5}, 12]); + [error, RecLMP|string...] a5 = [err1, {a: 2}, {a: 3}]; result = ""; match a5 { @@ -853,21 +853,22 @@ function testListMatchPattern30() { assertEquals("Pattern2", result); } -type T readonly & S; -type S [INT, int]|[STRING, string]; +type TLMP readonly & SLMP; + +type SLMP [INT, int]|[STRING, string]; const INT = 1; const STRING = 2; function testListMatchPattern31() { - T t1 = [STRING, "hello"]; - T t2 = [INT, 1234]; + TLMP t1 = [STRING, "hello"]; + TLMP t2 = [INT, 1234]; assertEquals(["hello", ()], listMatchPattern31(t1)); assertEquals([(), 1234], listMatchPattern31(t2)); } -function listMatchPattern31(T t) returns [string?, int?] { +function listMatchPattern31(TLMP t) returns [string?, int?] { string? s = (); int? i = (); @@ -884,14 +885,14 @@ function listMatchPattern31(T t) returns [string?, int?] { } function testListMatchPattern32() { - T t1 = [STRING, "hello"]; - T t2 = [INT, 1234]; + TLMP t1 = [STRING, "hello"]; + TLMP t2 = [INT, 1234]; assertEquals("hello", listMatchPattern32(t1)); assertEquals(1234, listMatchPattern32(t2)); } -function listMatchPattern32(T t) returns string|int { +function listMatchPattern32(TLMP t) returns string|int { string|int s; match t { @@ -903,19 +904,19 @@ function listMatchPattern32(T t) returns string|int { return s; } -type T2 readonly & ([1, string]|[2, string]|[3, string]); +type T2LMP readonly & ([1, string]|[2, string]|[3, string]); function testListMatchPattern33() { - T2 t1 = [1, "hello"]; - T2 t2 = [2, "1234"]; - T2 t3 = [3, "abcd"]; + T2LMP t1 = [1, "hello"]; + T2LMP t2 = [2, "1234"]; + T2LMP t3 = [3, "abcd"]; assertEquals("hello", listMatchPattern33(t1)); assertEquals("1234", listMatchPattern33(t2)); assertEquals("abcd", listMatchPattern33(t3)); } -function listMatchPattern33(T2 t) returns string { +function listMatchPattern33(T2LMP t) returns string { string s; match t { @@ -927,18 +928,19 @@ function listMatchPattern33(T2 t) returns string { return s; } -type T3 readonly & S3; -type S3 string[2]|int[2]; +type T3LMP readonly & S3LMP; + +type S3LMP string[2]|int[2]; function testListMatchPattern34() { - T3 t1 = ["1", "hello"]; - T3 t2 = [2, 1234]; + T3LMP t1 = ["1", "hello"]; + T3LMP t2 = [2, 1234]; assertEquals("hello", listMatchPattern34(t1)); assertEquals(1234, listMatchPattern34(t2)); } -function listMatchPattern34(T3 t) returns string|int { +function listMatchPattern34(T3LMP t) returns string|int { string|int s = 10; match t { @@ -950,22 +952,22 @@ function listMatchPattern34(T3 t) returns string|int { return s; } -public type T4 ["list", T4[]]|"int"; +public type T4LMP ["list", T4LMP[]]|"int"; function testListMatchPattern35() { - T4[] t1 = ["int"]; - T4[] t2 = ["int", "int", "int"]; + T4LMP[] t1 = ["int"]; + T4LMP[] t2 = ["int", "int", "int"]; - T4 x1 = ["list", t1]; - T4 x2 = ["list", ["int", "int"]]; - T4 x3 = ["list", t2]; + T4LMP x1 = ["list", t1]; + T4LMP x2 = ["list", ["int", "int"]]; + T4LMP x3 = ["list", t2]; assertEquals(listMatchPattern35(x1, t1), "match 4"); assertEquals(listMatchPattern35("int", ()), "match 1"); assertEquals(listMatchPattern35(x2, ()), "match 2"); assertEquals(listMatchPattern35(x3, t2), "match 4"); } -function listMatchPattern35(T4 x, T4[]? t) returns string? { +function listMatchPattern35(T4LMP x, T4LMP[]? t) returns string? { match x { "int" => { return "match 1"; @@ -987,19 +989,19 @@ function listMatchPattern35(T4 x, T4[]? t) returns string? { } function testListMatchPattern36() { - T4[] t1 = ["int"]; - T4[] t2 = ["int", "int", "int"]; + T4LMP[] t1 = ["int"]; + T4LMP[] t2 = ["int", "int", "int"]; - T4 x1 = ["list", t1]; - T4 x2 = ["list", ["int", "int"]]; - T4 x3 = ["list", t2]; + T4LMP x1 = ["list", t1]; + T4LMP x2 = ["list", ["int", "int"]]; + T4LMP x3 = ["list", t2]; assertEquals(listMatchPattern36(x1, t1), "match 4"); assertEquals(listMatchPattern36("int", ()), "match 1"); assertEquals(listMatchPattern36(x2, ()), "match 2"); assertEquals(listMatchPattern36(x3, t2), "match 4"); } -function listMatchPattern36((T4|anydata)? x, T4[]? t) returns string? { +function listMatchPattern36((T4LMP|anydata)? x, T4LMP[]? t) returns string? { string? a = (); match x { "int" => { @@ -1018,16 +1020,17 @@ function listMatchPattern36((T4|anydata)? x, T4[]? t) returns string? { } } -public type T5 ["array", T6]|["cell", T6, string]; -public type T6 ["|", T6]|"int"; +public type T5LMP ["array", T6LMP]|["cell", T6LMP, string]; + +public type T6LMP ["|", T6LMP]|"int"; function testListMatchPattern37() { - T6 t1 = ["|", ["|", "int"]]; - T6 t2 = "int"; - T5 x1 = ["cell", t1, "inf"]; - T5 x2 = ["array", t1]; - T5 x3 = ["cell", t2, "inf1"]; - T5 x4 = ["array", t2]; + T6LMP t1 = ["|", ["|", "int"]]; + T6LMP t2 = "int"; + T5LMP x1 = ["cell", t1, "inf"]; + T5LMP x2 = ["array", t1]; + T5LMP x3 = ["cell", t2, "inf1"]; + T5LMP x4 = ["array", t2]; assertEquals(listMatchPattern37(x1, t1, "inf"), "match 2"); assertEquals(listMatchPattern37(x2, t1, ()), "match 4"); @@ -1035,7 +1038,7 @@ function testListMatchPattern37() { assertEquals(listMatchPattern37(x4, t2, ()), "match 4"); } -function listMatchPattern37(T5 x, T6? t, string? s) returns string { +function listMatchPattern37(T5LMP x, T6LMP? t, string? s) returns string { match x { ["cell", "int", "inf1"] => { return "match 1"; @@ -1059,12 +1062,12 @@ function listMatchPattern37(T5 x, T6? t, string? s) returns string { } function testListMatchPattern38() { - T6 t1 = ["|", ["|", "int"]]; - T6 t2 = "int"; - T5 x1 = ["cell", t1, "inf"]; - T5 x2 = ["array", t1]; - T5 x3 = ["cell", t2, "inf1"]; - T5 x4 = ["array", t2]; + T6LMP t1 = ["|", ["|", "int"]]; + T6LMP t2 = "int"; + T5LMP x1 = ["cell", t1, "inf"]; + T5LMP x2 = ["array", t1]; + T5LMP x3 = ["cell", t2, "inf1"]; + T5LMP x4 = ["array", t2]; assertEquals(listMatchPattern38(x1, t1, "inf"), "match 2"); assertEquals(listMatchPattern38(x2, t1, ()), "match 4"); @@ -1072,7 +1075,7 @@ function testListMatchPattern38() { assertEquals(listMatchPattern38(x4, t2, ()), "match 4"); } -function listMatchPattern38((anydata|T5)? x, T6? t, string? s) returns string? { +function listMatchPattern38((anydata|T5LMP)? x, T6LMP? t, string? s) returns string? { match x { ["cell", "int", "inf1"] => { return "match 1"; @@ -1092,40 +1095,40 @@ function listMatchPattern38((anydata|T5)? x, T6? t, string? s) returns string? { } } -public type T7 ["array", T6]|["cell", T6]; +public type T7LMP ["array", T6LMP]|["cell", T6LMP]; function testListMatchPattern39() { - T6 y1 = "int"; - T6 y2 = ["|", "int"]; + T6LMP y1 = "int"; + T6LMP y2 = ["|", "int"]; - T7 x1 = ["cell", y1]; - T7 x2 = ["array", y1]; - T7 x3 = ["cell", y2]; + T7LMP x1 = ["cell", y1]; + T7LMP x2 = ["array", y1]; + T7LMP x3 = ["cell", y2]; assertEquals(listMatchPattern39(x1, y1), "match 3"); assertEquals(listMatchPattern39(x2, y1), "match 2"); assertEquals(listMatchPattern39(x3, y2), "match 3"); } -function listMatchPattern39(T7 x, T6 y) returns string { +function listMatchPattern39(T7LMP x, T6LMP y) returns string { match x { ["list", var _] => { - T6 _ = x[1]; - T6 a = x[1]; + T6LMP _ = x[1]; + T6LMP a = x[1]; assertEquals(a, y); assertEquals(x[0], "list"); return "match 1"; } ["array", var _] => { - T6 _ = x[1]; - T6 a = x[1]; + T6LMP _ = x[1]; + T6LMP a = x[1]; assertEquals(a, y); assertEquals(x[0], "array"); return "match 2"; } ["cell", var _] => { - T6 _ = x[1]; - T6 a = x[1]; + T6LMP _ = x[1]; + T6LMP a = x[1]; assertEquals(a, y); assertEquals(x[0], "cell"); return "match 3"; @@ -1136,19 +1139,19 @@ function listMatchPattern39(T7 x, T6 y) returns string { } } -public type T8 ["list", T8, T8[]]|["list", T8[]]|"int"; +public type T8LMP ["list", T8LMP, T8LMP[]]|["list", T8LMP[]]|"int"; function testListMatchPattern40() { - T8 t1 = "int"; - T8[] t2 = ["int", "int", "int"]; - T8[] t3 = [t1]; - T8 t4 = ["list", ["int", "int", "int"]]; + T8LMP t1 = "int"; + T8LMP[] t2 = ["int", "int", "int"]; + T8LMP[] t3 = [t1]; + T8LMP t4 = ["list", ["int", "int", "int"]]; - T8 x1 = ["list", t3]; - T8 x2 = ["list", ["int", "int"]]; - T8 x3 = ["list", t2]; - T8 x4 = ["list", "int", t2]; - T8 x5 = ["list", t4, t3]; + T8LMP x1 = ["list", t3]; + T8LMP x2 = ["list", ["int", "int"]]; + T8LMP x3 = ["list", t2]; + T8LMP x4 = ["list", "int", t2]; + T8LMP x5 = ["list", t4, t3]; assertEquals(listMatchPattern40(x1, (), t3, ()), "match 4"); assertEquals(listMatchPattern40("int", (), (), ()), "match 1"); @@ -1158,7 +1161,7 @@ function testListMatchPattern40() { assertEquals(listMatchPattern40(x5, t4, t3, ()), "match 6"); } -function listMatchPattern40(T8 x, T8? t1, T8[]? t2, T8? t3) returns string? { +function listMatchPattern40(T8LMP x, T8LMP? t1, T8LMP[]? t2, T8LMP? t3) returns string? { match x { "int" => { return "match 1"; @@ -1177,7 +1180,7 @@ function listMatchPattern40(T8 x, T8? t1, T8[]? t2, T8? t3) returns string? { return "match 5"; } ["list", var y, var z] => { - T8 _ = z[0]; + T8LMP _ = z[0]; assertEquals(y, t1); assertEquals(z, t2); return "match 6"; @@ -1188,15 +1191,15 @@ function listMatchPattern40(T8 x, T8? t1, T8[]? t2, T8? t3) returns string? { } } -public type T9 ["array", T9]|["cell", T6]|["array", T6]|[string, int]; +public type T9LMP ["array", T9LMP]|["cell", T6LMP]|["array", T6LMP]|[string, int]; function testListMatchPattern41() { - T9 x1 = ["cell", "int"]; - T9 x2 = ["array", ["|", "int"]]; - T9 x3 = ["cell", ["|", "int"]]; - T9 x4 = ["string 1", 1]; - T9 x5 = ["array", x4]; - T9 x6 = ["string 2", 1]; + T9LMP x1 = ["cell", "int"]; + T9LMP x2 = ["array", ["|", "int"]]; + T9LMP x3 = ["cell", ["|", "int"]]; + T9LMP x4 = ["string 1", 1]; + T9LMP x5 = ["array", x4]; + T9LMP x6 = ["string 2", 1]; assertEquals(listMatchPattern41(x1), "match 1"); assertEquals(listMatchPattern41(x2), "match 4"); @@ -1206,7 +1209,7 @@ function testListMatchPattern41() { assertEquals(listMatchPattern41(x6), "match 6"); } -function listMatchPattern41(T9 x) returns string { +function listMatchPattern41(T9LMP x) returns string { match x { ["cell", "int"] => { return "match 1"; @@ -1229,14 +1232,14 @@ function listMatchPattern41(T9 x) returns string { } } -public type T10 [string, decimal, string]|[string, boolean...]|[int...]|[boolean]; +public type T10LMP [string, decimal, string]|[string, boolean...]|[int...]|[boolean]; function testListMatchPattern42() { - T10 x1 = ["string", 1d, "string"]; - T10 x2 = ["string", true, true, true, true, true, true]; - T10 x3 = [1, 1, 1, 1]; - T10 x4 = [true]; - T10 x5 = ["string", true]; + T10LMP x1 = ["string", 1d, "string"]; + T10LMP x2 = ["string", true, true, true, true, true, true]; + T10LMP x3 = [1, 1, 1, 1]; + T10LMP x4 = [true]; + T10LMP x5 = ["string", true]; assertEquals(listMatchPattern42(x1, ["string", 1d, "string"]), "match 1"); assertEquals(listMatchPattern42(x2, ["string", true, true, true, true, [true, true]]), "match 3"); @@ -1245,7 +1248,7 @@ function testListMatchPattern42() { assertEquals(listMatchPattern42(x5, ["string", true]), "match 5"); } -function listMatchPattern42(T10 t, anydata a) returns string { +function listMatchPattern42(T10LMP t, anydata a) returns string { match t { [var x, var y, var z] => { assertEquals([x, y, z], a); @@ -1270,25 +1273,25 @@ function listMatchPattern42(T10 t, anydata a) returns string { } } -public type T11 [int, T11, T11...]|[T11...]|["int"]; +public type T11LMP [int, T11LMP, T11LMP...]|[T11LMP...]|["int"]; public function testListMatchPattern43() { - T11[] t1 = [["int"], ["int"], ["int"]]; - T11 x1 = [1, ["int"], ["int"]]; - T11 x2 = [1, ["int"], ["int"], ["int"], ["int"], ["int"], ["int"], ["int"]]; - T11 x3 = [["int"], ["int"], ["int"], ["int"]]; - T11 x4 = [["int"]]; - T11 x5 = [t1, ["int"]]; + T11LMP[] t1 = [["int"], ["int"], ["int"]]; + T11LMP x1 = [1, ["int"], ["int"]]; + T11LMP x2 = [1, ["int"], ["int"], ["int"], ["int"], ["int"], ["int"], ["int"]]; + T11LMP x3 = [["int"], ["int"], ["int"], ["int"]]; + T11LMP x4 = [["int"]]; + T11LMP x5 = [t1, ["int"]]; assertEquals(listMatchPattern43(x1, [1, ["int"], ["int"]]), "match 1"); assertEquals(listMatchPattern43(x2, - [1, ["int"], ["int"], ["int"], ["int"], [["int"], ["int"], ["int"]]]), "match 3"); + [1, ["int"], ["int"], ["int"], ["int"], [["int"], ["int"], ["int"]]]), "match 3"); assertEquals(listMatchPattern43(x3, [["int"], ["int"], ["int"], [["int"]]]), "match 4"); assertEquals(listMatchPattern43(x4, [["int"]]), "match 2"); assertEquals(listMatchPattern43(x5, [t1, ["int"]]), "match 5"); } -function listMatchPattern43(T11 t, anydata a) returns string { +function listMatchPattern43(T11LMP t, anydata a) returns string { match t { [var x, var y, var z] => { assertEquals([x, y, z], a); @@ -1313,23 +1316,24 @@ function listMatchPattern43(T11 t, anydata a) returns string { } } -public type T12 [int, T12[], T12...]|[T12[]...]|"int"; -public type T13 [int, T13, T13, T13[]...]|[T13...]|"int"; +public type T12LMP [int, T12LMP[], T12LMP...]|[T12LMP[]...]|"int"; + +public type T13LMP [int, T13LMP, T13LMP, T13LMP[]...]|[T13LMP...]|"int"; public function testListMatchPattern44() { - T12[] t1 = ["int", "int", "int"]; - T12 x1 = [1, t1, "int", "int"]; - T12 x2 = [1, t1, "int", "int", "int", "int", "int", "int", "int"]; - T12 x3 = [t1, t1, t1, t1, t1]; - T12 x4 = [t1]; - T12 x5 = [t1, t1]; - - T13[] t2 = ["int", "int", "int"]; - T13 y1 = [1, "int", "int", t2, t2]; - T13 y2 = [1, "int", "int", t2, t2, t2, t2, t2, t2, t2]; - T13 y3 = [t2, t2, t2, t2, t2, t2]; - T13 y4 = [t2]; - T13 y5 = [t2, t2]; + T12LMP[] t1 = ["int", "int", "int"]; + T12LMP x1 = [1, t1, "int", "int"]; + T12LMP x2 = [1, t1, "int", "int", "int", "int", "int", "int", "int"]; + T12LMP x3 = [t1, t1, t1, t1, t1]; + T12LMP x4 = [t1]; + T12LMP x5 = [t1, t1]; + + T13LMP[] t2 = ["int", "int", "int"]; + T13LMP y1 = [1, "int", "int", t2, t2]; + T13LMP y2 = [1, "int", "int", t2, t2, t2, t2, t2, t2, t2]; + T13LMP y3 = [t2, t2, t2, t2, t2, t2]; + T13LMP y4 = [t2]; + T13LMP y5 = [t2, t2]; assertEquals(listMatchPattern44(x1, [1, t1, "int", "int"]), "match 1"); assertEquals(listMatchPattern44(x2, [1, t1, "int", "int", "int", "int", ["int", "int", "int"]]), "match 3"); @@ -1344,8 +1348,8 @@ public function testListMatchPattern44() { assertEquals(listMatchPattern44(y5, [t2, t2]), "match 5"); } -function listMatchPattern44(T12|T13 t, anydata a) returns string? { - if t is T12 { +function listMatchPattern44(T12LMP|T13LMP t, anydata a) returns string? { + if t is T12LMP { match t { [var p, var x, var y, var z] => { assertEquals([p, x, y, z], a); @@ -1394,18 +1398,19 @@ function listMatchPattern44(T12|T13 t, anydata a) returns string? { } } -public type T14 [string]|[int, string]|[int, int, string]; -public type T15 [int]|[T15, T15]|[T15[], T15[], T15[]]; +public type T14LMP [string]|[int, string]|[int, int, string]; + +public type T15LMP [int]|[T15LMP, T15LMP]|[T15LMP[], T15LMP[], T15LMP[]]; public function testListMatchPattern45() { - T14 x1 = ["string"]; - T14 x2 = [1, "string"]; - T14 x3 = [1, 1, "string"]; + T14LMP x1 = ["string"]; + T14LMP x2 = [1, "string"]; + T14LMP x3 = [1, 1, "string"]; - T15 y1 = [1]; - T15[] t2 = [y1, y1]; - T15 y2 = [y1, y1]; - T15 y3 = [t2, t2, t2]; + T15LMP y1 = [1]; + T15LMP[] t2 = [y1, y1]; + T15LMP y2 = [y1, y1]; + T15LMP y3 = [t2, t2, t2]; assertEquals(listMatchPattern45(x1, x1), "match 3"); assertEquals(listMatchPattern45(x2, x2), "match 2"); @@ -1416,7 +1421,7 @@ public function testListMatchPattern45() { assertEquals(listMatchPattern45(y3, y3), "match 1"); } -function listMatchPattern45(T14|T15 t, anydata a) returns string? { +function listMatchPattern45(T14LMP|T15LMP t, anydata a) returns string? { match t { [var p, var x, var y, ...var z] => { assertEquals([p, x, y], a); @@ -1436,14 +1441,14 @@ function listMatchPattern45(T14|T15 t, anydata a) returns string? { } } -public type T16 [string, int]; +public type T16LMP [string, int]; public function testListMatchPattern46() { assertEquals(listMatchPattern46(), "string"); } public function listMatchPattern46() returns string { - T16 a = ["string", 1]; + T16LMP a = ["string", 1]; string b; match a { [_, var x] => { @@ -1453,17 +1458,20 @@ public function listMatchPattern46() returns string { return b; } -type Data string|Data[]; -type Data2 ["call", string, Data...]; -type Data3 ["branch", string]; -type Data4 Data2|Data3; +type DataLMP string|DataLMP[]; + +type Data2LMP ["call", string, DataLMP...]; + +type Data3LMP ["branch", string]; + +type Data4LMP Data2LMP|Data3LMP; public function testListMatchPattern47() { assertEquals(listMatchPattern47(["branch", "b.target"]), "match 2"); assertEquals(listMatchPattern47(["call", "add", "1", "2"]), "match 1"); } -function listMatchPattern47(Data4 d) returns string { +function listMatchPattern47(Data4LMP d) returns string { match d { ["call", "add", ...var operands] => { return "match 1"; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/match-stmt-type-narrow.bal b/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/match-stmt-type-narrow.bal index fb4d64647048..2c57d02fd94a 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/match-stmt-type-narrow.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/match-stmt-type-narrow.bal @@ -226,19 +226,19 @@ function testMatchClauseWithTypeGuard6() { assertEquals("Int or String", matched); } -type Person record {| +type PersonMtStmt record {| string name; int age; - Address address; + AddressMtStmt address; |}; -type Address record { +type AddressMtStmt record { string street; int houseNo; string city; }; -type AddressWithProvince record { +type AddressWithProvinceMtStmt record { string street; int houseNo; string city; @@ -248,20 +248,20 @@ type AddressWithProvince record { type E 100|200; function testMatchClauseWithTypeGuard7() { - Person person = {name: "John", age: 12, address: {street: "Main Street", houseNo:10, city: "Colombo", + PersonMtStmt person = {name: "John", age: 12, address: {street: "Main Street", houseNo:10, city: "Colombo", "country": "Sri Lanka"}}; string matched = "Not Matched"; int age = 0; string street = ""; - Person newPerson = person; + PersonMtStmt newPerson = person; match person { - {name: var a, ...var b} if a is string && b is record {|int age; AddressWithProvince address;|} => { + {name: var a, ...var b} if a is string && b is record {|int age; AddressWithProvinceMtStmt address;|} => { matched = "Pattern1"; age = b.age; street = b.address.street; } - {name: var a, ...var b} if a is string && b is record {|int age; Address address;|} => { + {name: var a, ...var b} if a is string && b is record {|int age; AddressMtStmt address;|} => { matched = "Pattern2"; age = b.age; street = b.address.street; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/structured_record_match_patterns.bal b/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/structured_record_match_patterns.bal index dcdad44551b9..b37e6d5fabab 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/structured_record_match_patterns.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/structured_record_match_patterns.bal @@ -14,14 +14,14 @@ // specific language governing permissions and limitations // under the License. -type Foo record { +type FooSRMP record { string s; int i; float f; }; function testStructuredMatchPatternsBasic1() returns string { - Foo foo = {s: "S", i: 23, f: 5.6}; + FooSRMP foo = {s: "S", i: 23, f: 5.6}; match foo { var {s, i: integer, f} => { @@ -32,14 +32,14 @@ function testStructuredMatchPatternsBasic1() returns string { return "Default"; } -type Bar record { +type BarSRMP record { byte b; - Foo f; + FooSRMP f; }; function testStructuredMatchPatternsBasic2() returns string { - Foo foo = {s: "S", i: 23, f: 5.6}; - Bar bar = {b: 12, f: foo}; + FooSRMP foo = {s: "S", i: 23, f: 5.6}; + BarSRMP bar = {b: 12, f: foo}; match bar { var {b: byteValue, f: {s, i, f}} => { @@ -52,8 +52,8 @@ function testStructuredMatchPatternsBasic2() returns string { } function testStructuredMatchPatternsBasic3() returns string { - Foo foo = {s: "S", i: 23, f: 5.6}; - Bar bar = {b: 12, f: foo}; + FooSRMP foo = {s: "S", i: 23, f: 5.6}; + BarSRMP bar = {b: 12, f: foo}; match bar { var {b, f} => { @@ -65,8 +65,8 @@ function testStructuredMatchPatternsBasic3() returns string { } function testStructuredMatchPatternsBasic4() returns string { - Foo foo = {s: "S", i: 23, f: 5.6}; - Bar bar = {b: 12, f: foo}; + FooSRMP foo = {s: "S", i: 23, f: 5.6}; + BarSRMP bar = {b: 12, f: foo}; match bar { var {a} => { @@ -105,17 +105,17 @@ function testStructuredMatchPatternsBasics5() returns string[] { ClosedFoo3 foo3 = {var1: "Hello", var2: 150, var3: true}; ClosedFoo4 foo4 = {var1: "Hello"}; - ClosedFoo1 | ClosedFoo2 | ClosedFoo3 | ClosedFoo4 a1 = foo1; - ClosedFoo1 | ClosedFoo2 | ClosedFoo3 | ClosedFoo4 a2 = foo2; - ClosedFoo1 | ClosedFoo2 | ClosedFoo3 | ClosedFoo4 a3 = foo3; - ClosedFoo1 | ClosedFoo2 | ClosedFoo3 | ClosedFoo4 a4 = foo4; + ClosedFoo1|ClosedFoo2|ClosedFoo3|ClosedFoo4 a1 = foo1; + ClosedFoo1|ClosedFoo2|ClosedFoo3|ClosedFoo4 a2 = foo2; + ClosedFoo1|ClosedFoo2|ClosedFoo3|ClosedFoo4 a3 = foo3; + ClosedFoo1|ClosedFoo2|ClosedFoo3|ClosedFoo4 a4 = foo4; string[] result = [basicMatch(a1), basicMatch(a2), basicMatch(a3), basicMatch(a4)]; return result; } -function basicMatch(ClosedFoo1 | ClosedFoo2 | ClosedFoo3 | ClosedFoo4 a) returns string { +function basicMatch(ClosedFoo1|ClosedFoo2|ClosedFoo3|ClosedFoo4 a) returns string { match a { var {var1, var2, var3} => { return "Matched with three vars : " + var1.toString() + ", " + @@ -146,16 +146,16 @@ function testStructuredMatchPatternComplex1() returns string[] { ClosedBar1 bar1 = {var1: "Ballerina", var2: 500}; ClosedBar2 bar2 = {var1: "Language", var2: bar1}; - ClosedBar1 | ClosedBar2 | string a1 = bar1; - ClosedBar1 | ClosedBar2 | string a2 = bar2; - ClosedBar1 | ClosedBar2 | string a3 = "bar2"; + ClosedBar1|ClosedBar2|string a1 = bar1; + ClosedBar1|ClosedBar2|string a2 = bar2; + ClosedBar1|ClosedBar2|string a3 = "bar2"; string[] result = [complexMatch(a1), complexMatch(a2), complexMatch(a3)]; return result; } -function complexMatch(ClosedBar1 | ClosedBar2 | string a) returns string { +function complexMatch(ClosedBar1|ClosedBar2|string a) returns string { match a { var {var1, var2: {var1: v1, var2}} => { return "Matched with three vars : " + var1.toString() + ", " + v1.toString() + @@ -172,16 +172,22 @@ function complexMatch(ClosedBar1 | ClosedBar2 | string a) returns string { function testRuntimeCheck() returns string[] { [int, boolean] tuple = [50, true]; - Foo foo1 = {s: "S", i: 23, f: 5.6, "t": tuple}; - Foo foo2 = {s: "S", i: 23, f: 5.6}; - Foo foo3 = {s: "S", i: 23, f: 5.6, "t": 12}; - - string[] values = [matchRuntimeCheck(foo1), matchRuntimeCheck(foo2), matchRuntimeCheck(foo3), - matchRuntimeCheckWithAny(foo1), matchRuntimeCheckWithAny(foo2), matchRuntimeCheckWithAny(foo3)]; + FooSRMP foo1 = {s: "S", i: 23, f: 5.6, "t": tuple}; + FooSRMP foo2 = {s: "S", i: 23, f: 5.6}; + FooSRMP foo3 = {s: "S", i: 23, f: 5.6, "t": 12}; + + string[] values = [ + matchRuntimeCheck(foo1), + matchRuntimeCheck(foo2), + matchRuntimeCheck(foo3), + matchRuntimeCheckWithAny(foo1), + matchRuntimeCheckWithAny(foo2), + matchRuntimeCheckWithAny(foo3) + ]; return values; } -function matchRuntimeCheck(Foo foo) returns string { +function matchRuntimeCheck(FooSRMP foo) returns string { match foo { var {s, i, f, t: [i2, b]} => { return "Matched with five vars : " + s.toString() + ", " + i.toString() + diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/anydata/anydata_test.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/anydata/anydata_test.bal index c135cd5ec49d..085c964a8034 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/anydata/anydata_test.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/anydata/anydata_test.bal @@ -14,6 +14,8 @@ // specific language governing permissions and limitations // under the License. +import ballerina/lang.regexp; + type Foo record { int a = 0; }; @@ -272,7 +274,7 @@ function testAnydataArray() returns anydata[] { } type ValueType int|float|string|boolean|byte|decimal; -type DataType ValueType|table>|json|xml|ClosedFoo|Foo|map|anydata[]|(); +type DataType ValueType|table>|json|xml|regexp:RegExp|ClosedFoo|Foo|map|anydata[]|(); function testUnionAssignment() returns anydata[] { anydata[] rets = []; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/never/never_type_is_expr_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/never/never_type_is_expr_negative.bal new file mode 100644 index 000000000000..ecb769e0f29d --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/never/never_type_is_expr_negative.bal @@ -0,0 +1,47 @@ + // Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). + // + // WSO2 LLC. licenses this file to you under the Apache License, + // Version 2.0 (the "License"); you may not use this file except + // in compliance with the License. + // You may obtain a copy of the License at + // + // http://www.apache.org/licenses/LICENSE-2.0 + // + // Unless required by applicable law or agreed to in writing, + // software distributed under the License is distributed on an + // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + // KIND, either express or implied. See the License for the + // specific language governing permissions and limitations + // under the License. + +function testNeverRuntime10() { + int x = 100; + boolean _ = x is never; +} + +type Record record {| + int i; + never[] j; +|}; + +function testNeverRuntime11() { + Record x = {i: 1, j: []}; + boolean _ = x is never; +} + +function testNeverFieldTypeCheck() { + record {int x;} r2 = {x: 2, "color": "blue"}; + boolean _ = r2 is record {never x?;}; + + record {never? x;} r3 = {x: (), "color": "blue"}; + boolean _ = r3 is record {never x?;}; + + record {int? x;} r4 = {x: 2, "color": "blue"}; + boolean _ = r4 is record {never x?;}; + + record {int x;} & readonly v3 = {x: 2, "color": "blue"}; + boolean _ = v3 is record {never x?;}; + + record {never? x;} & readonly v4 = {x: (), "color": "blue"}; + boolean _ = v4 is record {never x?;}; +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/never/never_type_runtime.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/never/never_type_runtime.bal index 645ac4b21f79..2d6620d7f835 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/never/never_type_runtime.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/never/never_type_runtime.bal @@ -72,12 +72,6 @@ function testNeverRuntime9() { assertEquality(true, b); } -function testNeverRuntime10() { - int x = 100; - boolean b = x is never; - assertEquality(false, b); -} - type Record record {| int i; never[] j; @@ -88,12 +82,6 @@ type Record2 record {| never[] j = []; |}; -function testNeverRuntime11() { - Record x = { i: 1, j: [] }; - boolean b = x is never; - assertEquality(false, b); -} - function testNeverRuntime12() { Record x = {i: 1, j: []}; boolean b = x is Record2; @@ -141,15 +129,6 @@ function testNeverFieldTypeCheck() { record {} r1 = {"x": 2, "color": "blue"}; assertEquality(false, r1 is record {never x?;}); - record {int x;} r2 = {x: 2, "color": "blue"}; - assertEquality(false, r2 is record {never x?;}); - - record {never? x;} r3 = {x: (), "color": "blue"}; - assertEquality(false, r3 is record {never x?;}); - - record {int? x;} r4 = {x: 2, "color": "blue"}; - assertEquality(false, r4 is record {never x?;}); - record {} r5 = {}; assertEquality(false, r5 is record {never x?;}); @@ -207,12 +186,6 @@ function testNeverFieldTypeCheck() { record {} & readonly v2 = {"x": 2}; assertEquality(false, v2 is record {never x?;}); - record {int x;} & readonly v3 = {x: 2, "color": "blue"}; - assertEquality(false, v3 is record {never x?;}); - - record {never? x;} & readonly v4 = {x: (), "color": "blue"}; - assertEquality(false, v4 is record {never x?;}); - record {never? x;} & readonly v5 = {x: (), "color": "blue"}; assertEquality(true, v5 is record {never? x;}); diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_inherently_immutable_type_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_inherently_immutable_type_negative.bal index 9603606618b0..de9645cfba02 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_inherently_immutable_type_negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_inherently_immutable_type_negative.bal @@ -19,11 +19,6 @@ any x = y; } - function cannotDeepEqualReadonly() returns boolean { - readonly arr = [1, 2 , 3]; - return arr == [1, 2 , 3]; - } - function testReadOnlyAssignabilityToUnions() { readonly readonlyVal = 1; error? errOrNil = readonlyVal; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_selectively_immutable_type.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_selectively_immutable_type.bal index 89489482b63d..459d0ee84059 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_selectively_immutable_type.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_selectively_immutable_type.bal @@ -64,12 +64,12 @@ function testSimpleInitializationForSelectivelyImmutableXmlTypes() { assertEquality(c, r4); } -type Employee record {| - Details details; +type EmployeeFoo record {| + DetailsBar details; string department; |}; -type Details record {| +type DetailsBar record {| string name; int id; |}; @@ -78,9 +78,9 @@ function testSimpleInitializationForSelectivelyImmutableListTypes() { int[] & readonly a = [1, 2]; readonly r1 = a; assertTrue(r1 is int[] & readonly); - assertEquality( [1, 2], r1); + assertEquality([1, 2], r1); - Employee & readonly emp = { + EmployeeFoo & readonly emp = { details: { name: "Emma", id: 1234 @@ -88,60 +88,60 @@ function testSimpleInitializationForSelectivelyImmutableListTypes() { department: "finance" }; - [Employee, Employee] & readonly b = [emp, {details: {name: "Jo", id: 5678}, department: "IT"}]; + [EmployeeFoo, EmployeeFoo] & readonly b = [emp, {details: {name: "Jo", id: 5678}, department: "IT"}]; readonly r2 = b; - assertTrue(r2 is [Employee, Employee] & readonly); - assertTrue(r2 is Employee[2] & readonly); + assertTrue(r2 is [EmployeeFoo, EmployeeFoo] & readonly); + assertTrue(r2 is EmployeeFoo[2] & readonly); - [Employee, Employee] & readonly empTup = <[Employee, Employee] & readonly> checkpanic r2; + [EmployeeFoo, EmployeeFoo] & readonly empTup = <[EmployeeFoo, EmployeeFoo] & readonly>checkpanic r2; assertEquality(emp, empTup[0]); record {} rec = empTup[0]; - assertTrue(rec is Employee & readonly); + assertTrue(rec is EmployeeFoo & readonly); assertTrue(rec.isReadOnly()); rec = empTup[1]; - assertTrue(rec is Employee & readonly); + assertTrue(rec is EmployeeFoo & readonly); assertTrue(rec.isReadOnly()); assertEquality("IT", rec["department"]); - assertTrue(rec["details"] is Details & readonly); + assertTrue(rec["details"] is DetailsBar & readonly); assertTrue(rec["details"].isReadOnly()); - Details & readonly details = { + DetailsBar & readonly details = { name: "Jo", id: 9876 }; - [Details[], Employee...] & readonly detEmpTup = [ - [{name: "May", id: 1234}, details], - {details, department: "finance"} - ]; + [DetailsBar[], EmployeeFoo...] & readonly detEmpTup = [ + [{name: "May", id: 1234}, details], + {details, department: "finance"} + ]; readonly r3 = detEmpTup; - assertTrue(r3 is [Details[], Employee...] & readonly); - assertTrue(r3 is [[Details, Details], Employee] & readonly); + assertTrue(r3 is [DetailsBar[], EmployeeFoo...] & readonly); + assertTrue(r3 is [[DetailsBar, DetailsBar], EmployeeFoo] & readonly); - [[Details, Details], Employee] & readonly vals = <[[Details, Details], Employee] & readonly> checkpanic r3; + [[DetailsBar, DetailsBar], EmployeeFoo] & readonly vals = <[[DetailsBar, DetailsBar], EmployeeFoo] & readonly>checkpanic r3; assertTrue(vals[0].isReadOnly()); - Details d1 = vals[0][0]; - assertEquality(
{name: "May", id: 1234}, d1); + DetailsBar d1 = vals[0][0]; + assertEquality({name: "May", id: 1234}, d1); assertTrue(d1.isReadOnly()); - Details d2 = vals[0][1]; + DetailsBar d2 = vals[0][1]; assertEquality(details, d2); assertTrue(d2.isReadOnly()); - Employee e = vals[1]; - assertEquality( {details, department: "finance"}, e); + EmployeeFoo e = vals[1]; + assertEquality({details, department: "finance"}, e); assertTrue(e.isReadOnly()); assertTrue(e.details.isReadOnly()); (int[] & readonly)|string[] arr = [1, 2]; - assertEquality( [1, 2], arr); + assertEquality([1, 2], arr); assertTrue(arr is int[] & readonly); assertTrue(arr.isReadOnly()); arr = ["hello"]; - assertEquality( ["hello"], arr); + assertEquality(["hello"], arr); assertTrue(arr is string[]); assertFalse(arr is string[] & readonly); assertFalse(arr.isReadOnly()); @@ -157,9 +157,9 @@ function testSimpleInitializationForSelectivelyImmutableMappingTypes() { }; readonly r1 = a; assertTrue(r1 is map & readonly); - assertEquality(> {a: true, bool: false, c: false}, r1); + assertEquality(>{a: true, bool: false, c: false}, r1); - Employee & readonly emp = { + EmployeeFoo & readonly emp = { details: { name: "Emma", id: 1234 @@ -168,20 +168,20 @@ function testSimpleInitializationForSelectivelyImmutableMappingTypes() { }; readonly r2 = emp; - assertTrue(r2 is Employee & readonly); + assertTrue(r2 is EmployeeFoo & readonly); assertEquality(emp, r2); any|error val = r2; assertFalse(val is error); - Employee rec = checkpanic val; - assertTrue(rec is Employee & readonly); + EmployeeFoo rec = checkpanic val; + assertTrue(rec is EmployeeFoo & readonly); assertTrue(rec.isReadOnly()); - Details det = rec.details; - assertTrue(det is Details & readonly); + DetailsBar det = rec.details; + assertTrue(det is DetailsBar & readonly); assertTrue(det.isReadOnly()); - Student & readonly st = { + StudentFoo & readonly st = { details: { name: "Jo", id: 4567 @@ -190,25 +190,25 @@ function testSimpleInitializationForSelectivelyImmutableMappingTypes() { "science": ["P", 65] }; readonly r3 = st; - assertTrue(r3 is Student & readonly); + assertTrue(r3 is StudentFoo & readonly); val = r3; assertFalse(val is error); - Student stVal = checkpanic val; + StudentFoo stVal = checkpanic val; assertTrue(stVal.isReadOnly()); assertTrue(stVal.details.isReadOnly()); - assertEquality(
{name: "Jo", id: 4567}, stVal.details); + assertEquality({name: "Jo", id: 4567}, stVal.details); - assertTrue(stVal["math"] is [RESULT, int] & readonly); + assertTrue(stVal["math"] is [RESULTFoo, int] & readonly); assertTrue(stVal["math"].isReadOnly()); - assertEquality(<[RESULT, int]> ["P", 75], stVal["math"]); + assertEquality(<[RESULTFoo, int]>["P", 75], stVal["math"]); - assertTrue(stVal["science"] is [RESULT, int] & readonly); + assertTrue(stVal["science"] is [RESULTFoo, int] & readonly); assertTrue(stVal["science"].isReadOnly()); - assertEquality(<[RESULT, int]> ["P", 65], stVal["science"]); + assertEquality(<[RESULTFoo, int]>["P", 65], stVal["science"]); } -type Identifier record {| +type IdentifierFoo record {| readonly string name; int id; |}; @@ -222,41 +222,41 @@ function testSimpleInitializationForSelectivelyImmutableTableTypes() { readonly r1 = a; assertTrue(r1 is table> & readonly); - table> tbString = >> checkpanic r1; + table> tbString = >>checkpanic r1; assertEquality(2, tbString.length()); map[] mapArr = tbString.toArray(); - assertTrue( mapArr[0] is map & readonly); - assertEquality(> {x: "x", y: "y"}, mapArr[0]); - assertTrue( mapArr[1] is map & readonly); - assertEquality(> {z: "z"}, mapArr[1]); + assertTrue(mapArr[0] is map & readonly); + assertEquality(>{x: "x", y: "y"}, mapArr[0]); + assertTrue(mapArr[1] is map & readonly); + assertEquality(>{z: "z"}, mapArr[1]); - table key(name) & readonly b = table [ + table key(name) & readonly b = table [ {name: "Jo", id: 4567}, {name: "Emma", id: 1234}, {name: "Amy", id: 678} ]; readonly r2 = b; - assertTrue(r2 is table key(name) & readonly); - assertTrue(r2 is table & readonly); + assertTrue(r2 is table key(name) & readonly); + assertTrue(r2 is table & readonly); - table tbDetails = > checkpanic r2; + table tbDetails = >checkpanic r2; assertEquality(3, tbDetails.length()); - Identifier[] detailsArr = tbDetails.toArray(); - assertTrue(detailsArr[0] is Identifier & readonly); - assertEquality( {name: "Jo", id: 4567}, detailsArr[0]); - assertTrue(detailsArr[1] is Identifier & readonly); - assertEquality( {name: "Emma", id: 1234}, detailsArr[1]); - assertTrue(detailsArr[2] is Identifier & readonly); - assertEquality( {name: "Amy", id: 678}, detailsArr[2]); + IdentifierFoo[] detailsArr = tbDetails.toArray(); + assertTrue(detailsArr[0] is IdentifierFoo & readonly); + assertEquality({name: "Jo", id: 4567}, detailsArr[0]); + assertTrue(detailsArr[1] is IdentifierFoo & readonly); + assertEquality({name: "Emma", id: 1234}, detailsArr[1]); + assertTrue(detailsArr[2] is IdentifierFoo & readonly); + assertEquality({name: "Amy", id: 678}, detailsArr[2]); } -type RESULT "P"|"F"; +type RESULTFoo "P"|"F"; -type Student record {| - Details details; - [RESULT, int]...; +type StudentFoo record {| + DetailsBar details; + [RESULTFoo, int]...; |}; type recursiveTuple [int, string|xml, recursiveTuple...]; @@ -296,7 +296,7 @@ function testRuntimeIsTypeForSelectivelyImmutableBasicTypes() { assertFalse(k is readonly); assertTrue(l is readonly); - Employee m = { + EmployeeFoo m = { details: { name: "Maryam", id: 9876 @@ -317,7 +317,7 @@ function testRuntimeIsTypeNegativeForSelectivelyImmutableTypes() { assertFalse(an1 is readonly); assertFalse(a1.isReadOnly()); - Employee emp = { + EmployeeFoo emp = { details: { name: "Emma", id: 1234 @@ -325,54 +325,54 @@ function testRuntimeIsTypeNegativeForSelectivelyImmutableTypes() { department: "finance" }; - [Employee, Employee] b = [emp, {details: {name: "Jo", id: 5678}, department: "IT"}]; + [EmployeeFoo, EmployeeFoo] b = [emp, {details: {name: "Jo", id: 5678}, department: "IT"}]; anydata a2 = b; any an2 = b; - assertFalse(an2 is [Employee, Employee] & readonly); + assertFalse(an2 is [EmployeeFoo, EmployeeFoo] & readonly); assertFalse(an2 is readonly); assertFalse(a2.isReadOnly()); - [Employee, Employee] empTup = <[Employee, Employee]> a2; + [EmployeeFoo, EmployeeFoo] empTup = <[EmployeeFoo, EmployeeFoo]>a2; assertEquality(emp, empTup[0]); record {} rec = empTup[0]; - assertTrue(rec is Employee); - assertFalse(rec is Employee & readonly); + assertTrue(rec is EmployeeFoo); + assertFalse(rec is EmployeeFoo & readonly); assertFalse(rec.isReadOnly()); rec = empTup[1]; - assertTrue(rec is Employee); - assertFalse(rec is Employee & readonly); + assertTrue(rec is EmployeeFoo); + assertFalse(rec is EmployeeFoo & readonly); assertFalse(rec.isReadOnly()); - assertTrue(rec["details"] is Details); - assertFalse(rec["details"] is Details & readonly); + assertTrue(rec["details"] is DetailsBar); + assertFalse(rec["details"] is DetailsBar & readonly); assertFalse(rec["details"].isReadOnly()); - Details & readonly details = { + DetailsBar & readonly details = { name: "Jo", id: 9876 }; - [Details[], Employee...] detEmpTup = [ - [{name: "May", id: 1234}, details], - {details, department: "finance"} - ]; + [DetailsBar[], EmployeeFoo...] detEmpTup = [ + [{name: "May", id: 1234}, details], + {details, department: "finance"} + ]; anydata a3 = detEmpTup; - assertTrue(a3 is [Details[], Employee...]); - assertFalse(a3 is [Details[], Employee...] & readonly); - assertFalse(a3 is [[Details, Details], Employee] & readonly); + assertTrue(a3 is [DetailsBar[], EmployeeFoo...]); + assertFalse(a3 is [DetailsBar[], EmployeeFoo...] & readonly); + assertFalse(a3 is [[DetailsBar, DetailsBar], EmployeeFoo] & readonly); - [Details[], Employee...] vals = <[Details[], Employee...]> a3; + [DetailsBar[], EmployeeFoo...] vals = <[DetailsBar[], EmployeeFoo...]>a3; assertFalse(vals[0].isReadOnly()); - Details d1 = vals[0][0]; + DetailsBar d1 = vals[0][0]; assertFalse(d1.isReadOnly()); - Details d2 = vals[0][1]; + DetailsBar d2 = vals[0][1]; assertEquality(details, d2); assertTrue(d2.isReadOnly()); - Employee e = vals[1]; - assertEquality( {details, department: "finance"}, e); + EmployeeFoo e = vals[1]; + assertEquality({details, department: "finance"}, e); assertFalse(e.isReadOnly()); assertTrue(e.details.isReadOnly()); @@ -396,8 +396,8 @@ function testRuntimeIsTypeNegativeForSelectivelyImmutableTypes() { assertFalse(an5 is readonly); assertFalse(a5.isReadOnly()); - json[] jsonVal = an5; - map a8 = > jsonVal[1]; + json[] jsonVal = an5; + map a8 = >jsonVal[1]; any an8 = a8; assertFalse(a8 is map & readonly); assertFalse(an8 is readonly); @@ -421,15 +421,15 @@ function testRuntimeIsTypeNegativeForSelectivelyImmutableTypes() { assertFalse(an7 is readonly); assertFalse(a7.isReadOnly()); - table key(name) j = table [ + table key(name) j = table [ {name: "Jo", id: 4567}, {name: "Emma", id: 1234}, {name: "Amy", id: 678} ]; anydata a9 = j; any an9 = j; - assertTrue(an9 is table); - assertFalse(an9 is table & readonly); + assertTrue(an9 is table); + assertFalse(an9 is table & readonly); assertFalse(an9 is readonly); assertFalse(a9.isReadOnly());